1. FFmpeg介绍与裁剪

1.1 命令行工具

 FFmpeg框架中还提供了几个用于执行命令行完成音视频数据处理工具,包括ffplay、ffprobe、ffserver,具体解释如下:

  • ffplay Fast forword play,用ffmpeg实现的播放器

  • ffserver Fast forword server,用ffmpeg实现的rtsp服务器

  • ffprobe Fat forword probe,用来输入分析输入流

2. FFmpeg架构分析

 在1.1小节中,我们对FFmpeg整体架构进行了简单介绍,阐述了框架中各个模块的功能。本节将在此基础上,重点阐述在利用FFmpeg进行音视频开发中牵涉到的重要步骤,数据结构体以及相关函数。

2.1 FFmpeg处理要点

 总体来说,FFmpeg框架主要的作用在于对多媒体数据进行解协议、解封装、解码以及转码等操作,为了对FFmpeg在视音频中的应用有个更直观理解,下面给出解析rtsp网络流的流程图,该图演示了从打开rtsp流,到最终提取出解码数据或转码的大概过程,如下所示:

术语解释:

  • muxer:视音频复用器(封装器),即将视频文件、音频文件和字幕文件(如果有的话)合并为某一个视频格式,比如讲a.avi、a.mp3、a.srt合并为mkv格式的视频文件;
  • demuxer:视音频分离器(解封装器),即muxer的逆过程;
  • transcode:转码,即将视音频数据从某一种格式转换成另一种格式;
  • RTP包:Real-time Transport Protocol,实时传输协议,是一种基于UDP的网络传输协议,它介于应用层和传输层之间,负责对流媒体数据进行封包并实现媒体流的实时传输;
  • ES流:Elementary Streams,即原始流,也称视/音频裸流,是直接从编码器输出的数据流,可为视频数据流(如H.264、MJPEG等)或音频数据流(如AAC等);
  • PES流:Packetized Elementary Streams,分组ES流,PES流是ES流经过PES打包器将ES分组、打包、加入包头信息等处理后形成的数据流,是用来传递ES的一种数据结构。
  • 解协议:取出网络数据流无关报文信息,以获取真正的视音频数据,常见的协议有rtsp、rtmp、http和mms等;
  • 解封装:即demuxer,封装格式可以为.mp4/.avi/.flv/.mkv等;
  • 解码:将编码数据还原成原始内容,比如将H.264解码为YUV、AAC解码为PCM等;

【腾讯文档】FFmpegWebRTCRTMPRTSPHLSRTP播放器-音视频流媒体高级开发-资料领取
https://docs.qq.com/doc/DYU5ORlBOdkpCUkNxhttps://docs.qq.com/doc/DYU5ORlBOdkpCUkNx


2.1 FFmpeg重要的结构体

 FFmpeg中有很多比较重要的结构体,比如与输入输出(I/O)有关的结构体AVIOContext、URLContext、URLProtocol ,与封装格式有关的结构体AVFormatContext、AVInputFormat、AVOutputFormat,与编解码有关的结构体AVCodec、AVCodecContext,以及与音视频数据有关的结构体AVStream、AVPacket、AVFrame等等。刚开始接触FFmpeg时,个人感觉一时间要理解区分这些结构体还是有点困难的,好在这些结构体当中有个“老大哥”-AVFormatContext,AVFormatContext可以说是贯穿整个FFmpeg开发,"犹如神一般的存在"。下面我们就在分析AVFormatContext结构体的基础上,阐述上述结构体的作用与区别。

AVFormatContext

 AVFormatContext结构体描述了一个多媒体文件或流的构成和基本信息,是FFmpeg中最为基本的一个结构体,也是其他所有结构的根。其中,成员变量iformat和oformat为指向对应的demuxing(解封装)和muxing(封装)指针,变量类型分别为AVInputFormat、AVOutputFormat;pb为指向控制底层数据读写的指针,变量类型为AVIOContext;nb_streams表示多媒体文件或多媒体流中数据流的个数;streams为指向所有流存储的二级指针,变量类型AVStream;video_codec和audio_codec分别表示视频和音频编解码器,变量类型为AVCodec等等。AVFormatContext结构体(位于libavformat/avformat.h中)部分源码如下:

typedef struct AVFormatContext {const AVClass *av_class;// 输入容器格式// 只在调用avformat_open_input()时被设置,且仅限Demuxingstruct AVInputFormat *iformat;// 输出容器格式// 只在调用avformat_alloc_output_context2()函数时被设置,且仅限封装(Muxing)struct AVOutputFormat *oformat;/*** Format private data. This is an AVOptions-enabled struct* if and only if iformat/oformat.priv_class is not NULL.** - muxing: set by avformat_write_header()* - demuxing: set by avformat_open_input()*/void *priv_data;// 输/入输出(I/O)的缓存// 说明:解封装(demuxing):值由avformat_open_input()设置//         封装(muxing):  值由avio_open2设置,需在avformat_write_header()之前AVIOContext *pb;// stream infoint ctx_flags;// AVFormatContext.streams中数据流的个数// 说明:值由avformat_new_stream()设置unsigned int nb_streams;// 文件中所有流stream列表。创建一个新stream,调用avformat_new_stream()函数实现// 当调用avformat_free_context()后,streams所占资源被释放// 说明:解封装(demuxing):当调用avformat_open_input()时,streams值被填充//        封装(muxing):streams在调用avformat_write_header()之前被用户创建// AVStream **streams;// 输入或输出文件名,如输入:rtsp://184.72.239.149/vod/mp4:BigBuckBunny_115k.mov// 说明:demuxing:当调用avformat_open_input()后被设置//        muxing: 当调用avformat_alloc_output_context2()后被设置,且需要调用avformat_write_header()之前char filename[1024];// component的第一帧位置,仅限Demuxing时由libavformat设置int64_t start_time;// stream的时长,仅限Demuxing时由libavformat设置int64_t duration;// 总比特率(bit/s),包括音频、音频int64_t bit_rate;...// 视频编解码器ID// 说明:Demuxing时由用户设置enum AVCodecID video_codec_id;// 音频编解码器ID// 说明:Demuxing时由用户设置enum AVCodecID audio_codec_id;// 字幕(subtitle)编解码器ID// 说明:Demuxing时由用户设置enum AVCodecID subtitle_codec_id;...// 文件元数据,即Metadata// 说明:demuxing:当调用avformat_open_input时被设置//        muxing:在调用avformat_write_header()之前被设置// 注:当调用avformat_free_context()时metadata的资源被libavformat释放AVDictionary *metadata;// 实时流启动的真实时间int64_t start_time_realtime;...// 视频编解码器,Demuxing时由用户指定AVCodec *video_codec;// 音频编解码器,Demuxing时由用户指定AVCodec *audio_codec;// 字幕编解码器,Demuxing时由用户指定AVCodec *subtitle_codec;// 数据编解码器,Demuxing时由用户指定AVCodec *data_codec;...// 数据编解码器IDenum AVCodecID data_codec_id;...} AVFormatContext

1. 复用(muxing)/解复用(demuxing)

(1) AVInputFormat结构体

 AVInputFormat为解复用/解封装(demuxing)器对象,它包含了解复用器的相关信息和操作函数,比如name成员变量为指定封装格式的名称,如"aac"、"mov"等;read_header成员函数为读取封装头部数据;read_packet成员函数为读取一个AVPacket等等。AVInputFormat结构体(位于libavformat/avformat.h中)部分源码如下:

typedef struct AVInputFormat {// 封装格式名称,如"mp4"、"mov"等const char *name;// 封装格式别称const char *long_name;int flags;const char *extensions;const struct AVCodecTag * const *codec_tag;const AVClass *priv_class; const char *mime_type;// struct AVInputFormat *next;int raw_codec_id;// 具体format对应Context的size,如MovContextint priv_data_size;int (*read_probe)(AVProbeData *);// 读取format header,同时初始化AVFormatContext结构// 若成功,返回0int (*read_header)(struct AVFormatContext *);// 读取packet大小数据,并将其存放到pkt指向的内存中// 若成功,返回0;若失败,返回负数且pkt不会被分配内存int (*read_packet)(struct AVFormatContext *, AVPacket *pkt);// 关闭流,但不释放AVFormatContext和AVStreams所占内存 int (*read_close)(struct AVFormatContext *);/*** seek相对于流索引中帧的时间戳* @param stream_index 流index,不能为-1* @param flags 用于方向,如果么有精确的匹配* @return >= 0 操作成功*/int (*read_seek)(struct AVFormatContext *,int stream_index, int64_t timestamp, int flags);/*** 获取流[stream_index]的下一个时间戳* @return 时间戳或AV_NOPTS_VALUE(当发生错误时)*/int64_t (*read_timestamp)(struct AVFormatContext *s, int stream_index,int64_t *pos, int64_t pos_limit);// Start/resume playing -只适用于RTSPint (*read_play)(struct AVFormatContext *);// Pause playing - 只适用于RTSPint (*read_pause)(struct AVFormatContext *);// 获取设备列表,详解avdevice_list_devices() int (*get_device_list)(struct AVFormatContext *s, struct AVDeviceInfoList *device_list);// 初始化设备功能子模块,详见avdevice_capabilities_create()函数int (*create_device_capabilities)(struct AVFormatContext *s, struct AVDeviceCapabilitiesQuery *caps);// 释放设备功能子模块,详见avdevice_capabilities_free()函数int (*free_device_capabilities)(struct AVFormatContext *s, struct AVDeviceCapabilitiesQuery *caps);
} AVInputFormat;

通过调用av_register_all()函数,FFmpeg所有的解复用器保存在以first_iformat为头部指针、last_iformat为尾部指针的链表中。这里以AAC(音频压缩编码格式)解复用器为例,来分析AVInputFormat结构体的初始化流程,相关源码详见libavformat/Aacdec.c:

AVInputFormat ff_aac_demuxer = {.name         = "aac",  // 指定解复用器名称.long_name    = NULL_IF_CONFIG_SMALL("raw ADTS AAC (Advanced Audio Coding)"),  // 指定AAC对应的文件格式.read_probe   = adts_aac_probe, // 探测函数.read_header  = adts_aac_read_header, // 读取头部数据函数.read_packet  = adts_aac_read_packet, // 读取数据包函数.flags        = AVFMT_GENERIC_INDEX, .extensions   = "aac",   // 后缀.mime_type    = "audio/aac,audio/aacp,audio/x-aac",.raw_codec_id = AV_CODEC_ID_AAC,// AAC解码器ID
};

(2) AVOutputFormat结构体

 与AVInputFormat相反,AVOtputFormat为复用/封装(muxing)器对象,它包含了复用器的相关信息和操作函数,比如name成员变量为指定封装格式的名称,如"mp4"、"3gp"等;write_header成员函数为读取封装头部数据;write_packet成员函数为写入一个AVPacket等等。AVOutputFormat结构体(位于libavformat/avformat.h中)部分源码如下:

typedef struct AVOutputFormat {// 封装格式名称,如"mp4"const char *name;// 文件格式const char *long_name;// mime类型const char *mime_type;const char *extensions; /**< 逗号分隔的文件扩展名 *//* output support */enum AVCodecID audio_codec;    /**< 默认音频codec(编解码器) */enum AVCodecID video_codec;    /**< 默认视频codec */enum AVCodecID subtitle_codec; /**< 默认subtitle codec *//*** flags可取值:AVFMT_NOFILE, AVFMT_NEEDNUMBER,* AVFMT_GLOBALHEADER, AVFMT_NOTIMESTAMPS, AVFMT_VARIABLE_FPS,* AVFMT_NODIMENSIONS, AVFMT_NOSTREAMS, AVFMT_ALLOW_FLUSH,* AVFMT_TS_NONSTRICT, AVFMT_TS_NEGATIVE*/int flags;const struct AVCodecTag * const *codec_tag;const AVClass *priv_class; ///< AVClass for the private contextstruct AVOutputFormat *next;// private data的大小int priv_data_size;// 写headerint (*write_header)(struct AVFormatContext *);// 写一个packet。如果flags=AVFMT_ALLOW_FLUSH,pkt可为NULL,以便flush muxer中的缓冲数据// 返回0,表示缓冲区仍还有数据可flush;返回1,表示缓冲区无可flush得数据 int (*write_packet)(struct AVFormatContext *, AVPacket *pkt);int (*write_trailer)(struct AVFormatContext *);// 如果不是YUV420P,目前只支持设置像素格式int (*interleave_packet)(struct AVFormatContext *, AVPacket *out,AVPacket *in, int flush);// 测试给定的编解码器是否可以存储在这个容器中                      int (*query_codec)(enum AVCodecID id, int std_compliance);void (*get_output_timestamp)(struct AVFormatContext *s, int stream,int64_t *dts, int64_t *wall);int (*control_message)(struct AVFormatContext *s, int type,void *data, size_t data_size);// 写未编码的AVFrame帧数据,详见av_write_uncoded_frame()int (*write_uncoded_frame)(struct AVFormatContext *, int stream_index,AVFrame **frame, unsigned flags);/*** Returns device list with it properties.* @see avdevice_list_devices() for more details.*/int (*get_device_list)(struct AVFormatContext *s, struct AVDeviceInfoList *device_list);/*** Initialize device capabilities submodule.* @see avdevice_capabilities_create() for more details.*/int (*create_device_capabilities)(struct AVFormatContext *s, struct AVDeviceCapabilitiesQuery *caps);// 释放设备功能子模块,详见avdevice_capabilities_free()int (*free_device_capabilities)(struct AVFormatContext *s, struct AVDeviceCapabilitiesQuery *caps);enum AVCodecID data_codec; /**< default data codec *//*** 初始化format. 分配数据内存,设置AVFormatContext或  AVStream参数,与deinit()配合使用,释放分配的内存资源* 返回0,配置成功;返回1,配置失败。*/int (*init)(struct AVFormatContext *);/** 释放init分配的内存资源,无论调用init()是否成功*/void (*deinit)(struct AVFormatContext *);/** 检测比特流* 如果返回0,表示需要检测流的更多packets;返回-1,则不需要*/int (*check_bitstream)(struct AVFormatContext *, const AVPacket *pkt);
} AVOutputFormat;

同样,通过调用av_register_all()函数,FFmpeg所有的复用器保存在以first_oformat为头部指针、last_oformat为尾部指针的链表中。这里以mp4(视频压缩编码格式)复用器为例,来分析AVOutputFormat结构体的初始化流程,相关源码详见libavformat/Movenc.c:

AVOutputFormat ff_mp4_muxer = {.name              = "mp4", //复用器名称.long_name         = NULL_IF_CONFIG_SMALL("MP4 (MPEG-4 Part 14)"),              //mp4对应的文件格式.mime_type         = "video/mp4",// MIME类型.extensions        = "mp4",      // 文件扩展名.priv_data_size    = sizeof(MOVMuxContext),.audio_codec       = AV_CODEC_ID_AAC,// 音频编码器ID.video_codec       = CONFIG_LIBX264_ENCODER ?AV_CODEC_ID_H264 : AV_CODEC_ID_MPEG4,// 视频编码器ID.init              = mov_init,   // 初始化函数.write_header      = mov_write_header, // 写入头部.write_packet      = mov_write_packet, // 写入Packet.write_trailer     = mov_write_trailer,.deinit            = mov_free, // 释放资源.flags             = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH | AVFMT_TS_NEGATIVE,.codec_tag         = (const AVCodecTag* const []){ codec_mp4_tags, 0 },.check_bitstream   = mov_check_bitstream,.priv_class        = &mp4_muxer_class,
};

2. 输入/输出(I/O)

(1) AVIOContext结构体

 AVIOContext是FFmpeg管理输入输出(I/O)数据的结构体,它是协议(文件)操作的顶层结构,提供带缓冲的读写操作。有关读写操作和成员变量的含义,可见如下源码中给出的注释示意图:

AVIOContext结构体位于libavformat/avio.h中,部分源码如下:

typedef struct AVIOContext {const AVClass *av_class;unsigned char *buffer;  // 数据缓冲区int buffer_size;        // 缓存的大小unsigned char *buf_ptr; // 指针指向缓存区的当前位置,可小于buffer+buffer.sizeunsigned char *buf_end; // 读取/写入缓存区数据的末尾位置 // 私有指针,关联URLContext结构,作为read/write/seek/...函数参数// 用于完成对广义输入文件的读写等操作,指向一个URLContext对象void *opaque;          // 读packet数据int (*read_packet)(void *opaque, uint8_t *buf, int buf_size);// 写数据到packetint (*write_packet)(void *opaque, uint8_t *buf, int buf_size);// 定位int64_t (*seek)(void *opaque, int64_t offset, int whence);int64_t pos;      // 当前缓存区域在文件中的位置int eof_reached;  // 是否到达文件末尾,true表示已经到末尾int write_flag;   // 是否可写标志,true表示open可写int max_packet_size; // packet最大尺寸unsigned long checksum;unsigned char *checksum_ptr;unsigned long (*update_checksum)(unsigned long checksum, const uint8_t *buf, unsigned int size);// 错误代码,0表示没有错误出现int error;      //网络流媒体协议暂停或恢复播放int (*read_pause)(void *opaque, int pause); int64_t (*read_seek)(void *opaque, int stream_index,int64_t timestamp, int flags);// 0表示网络流不可seekint seekable;// 写入缓冲区中向后查找之前的最大到达位置,用于跟踪已写入的数据,以便稍后刷新unsigned char *buf_ptr_max;// packet最小尺寸int min_packet_size;// 以下字段大部分仅限libavformat内部使用或用的不多,这里不作解释int64_t maxsize;int direct;int64_t bytes_read;int seek_count;int writeout_count;int orig_buffer_size;int short_seek_threshold;const char *protocol_whitelist;const char *protocol_blacklist;int (*write_data_type)(void *opaque, uint8_t *buf, int buf_size,enum AVIODataMarkerType type, int64_t time);int ignore_boundary_point;enum AVIODataMarkerType current_type;int64_t last_time;int (*short_seek_get)(void *opaque);int64_t written;
} AVIOContext;

其中,AVIOContext的成员变量opaque指向一个URLContext对象,URLContext中是对具体资源文件进行操作的上下文,它包括一个URLProtocol结构体类型的指针变量prot。URLProtocol则是在将资源进行分类的基础上,对某一类资源操作的函数集。URLContext结构体源码如下:

typedef struct URLContext {const AVClass *av_class;   // 关联/指向相应的广义输入文件const struct URLProtocol *prot;  // 关联具体广义输入文件的句柄,如fd为文件句柄,socket为网络句柄void *priv_data;          char *filename;             // 指定的URLint flags;int max_packet_size;        int is_streamed;            // true为流,默认为falseint is_connected;AVIOInterruptCB interrupt_callback;int64_t rw_timeout;        // read/write操作超时时间const char *protocol_whitelist;const char *protocol_blacklist;int min_packet_size;
} URLContext;

(2) URLProtocol结构体

 URLProtocol结构体表示广义的输入文件,是FFmpeg操作I/O的结构,包括文件(file)、网络数据流(tcp、rtp、... )等等,每种协议都对应着一个URLProtocol结构。该结构位于libavformat/url.h文件中,包括open、close、read、write、seek等操作,部分源码如下:

typedef struct URLProtocol {// 协议名称const char *name;int     (*url_open)( URLContext *h, const char *url, int flags);int     (*url_open2)(URLContext *h, const char *url, int flags, AVDictionary **options);int     (*url_accept)(URLContext *s, URLContext **c);int     (*url_handshake)(URLContext *c);/*** Read data from the protocol.*/int     (*url_read)( URLContext *h, unsigned char *buf, int size);int     (*url_write)(URLContext *h, const unsigned char *buf, int size);int64_t (*url_seek)( URLContext *h, int64_t pos, int whence);int     (*url_close)(URLContext *h);int (*url_read_pause)(URLContext *h, int pause);int64_t (*url_read_seek)(URLContext *h, int stream_index,int64_t timestamp, int flags);int (*url_get_file_handle)(URLContext *h);int (*url_get_multi_file_handle)(URLContext *h, int **handles,int *numhandles);int (*url_get_short_seek)(URLContext *h);int (*url_shutdown)(URLContext *h, int flags);int priv_data_size;const AVClass *priv_data_class;int flags;int (*url_check)(URLContext *h, int mask);int (*url_open_dir)(URLContext *h);int (*url_read_dir)(URLContext *h, AVIODirEntry **next);int (*url_close_dir)(URLContext *h);int (*url_delete)(URLContext *h);int (*url_move)(URLContext *h_src, URLContext *h_dst);const char *default_whitelist;
} URLProtocol;

接下来,这里以HTTP协议为例,阐述URLProtocol结构体的初始化流程,同时也证明了每一种协议(包括文件)相对应一个URLProtocol对象。具体源码如下,位于libavformat/Http.c:

const URLProtocol ff_http_protocol = {.name                = "http", //协议名称.url_open2           = http_open, // open操作.url_accept          = http_accept,//accept操作.url_handshake       = http_handshake,// 握手操作.url_read            = http_read,    // 读取数据操作.url_write           = http_write,    // 写入数据操作.url_seek            = http_seek, // seek操作.url_close           = http_close,    // close操作.url_get_file_handle = http_get_file_handle,.url_get_short_seek  = http_get_short_seek,.url_shutdown        = http_shutdown,.priv_data_size      = sizeof(HTTPContext),.priv_data_class     = &http_context_class,.flags               = URL_PROTOCOL_FLAG_NETWORK,.default_whitelist   = "http,https,tls,rtp,tcp,udp,crypto,httpproxy"
};

3.编/解码

(1) AVCodec结构体

 AVCodec是与编解码器(codec)息息相关的数据结构体,它包含了与codec相关的属性参数以及编解码操作函数等,比如name为codec的名称、pix_fmts为codec的视频帧像素格式等等,每一个codec都对应着一个AVCodec结构体。 AVCodec结构体源码如下:

typedef struct AVCodec {// 编解码器名称const char *name;// 描述编解码器的名称const char *long_name;// media typeenum AVMediaType type;// 该codec的IDenum AVCodecID id;int capabilities;// 该codec相关的参数const AVRational *supported_framerates; // 该codec支持的像素格式,针对视频帧/图像而言const enum AVPixelFormat *pix_fmts;    // 该codec支持的采样率,针对音频而言const int *supported_samplerates;      // 该codec支持的采样格式,针对音频而言const enum AVSampleFormat *sample_fmts; // 该codec的通道布局const uint64_t *channel_layouts;      // 解码器支持的低分辨率的最大值uint8_t max_lowres;                     const AVClass *priv_class;             const AVProfile *profiles;             const char *wrapper_name;int priv_data_size;struct AVCodec *next;int (*init_thread_copy)(AVCodecContext *);int (*update_thread_context)(AVCodecContext *dst, const AVCodecContext *src);const AVCodecDefault *defaults;// 执行avcodec_register()函数被调用,// 用于初始化codec的静态数据void (*init_static_data)(struct AVCodec *codec);// 初始化int (*init)(AVCodecContext *);int (*encode_sub)(AVCodecContext *, uint8_t *buf, int buf_size,const struct AVSubtitle *sub);/*** 编码操作:将编码后的数据保存到AVPacket** @param      avctx          codec上下文(context)* @param      avpkt          输出的AVPacket* @param[in]  frame          AVFrame存储的是要被压缩编码的裸数据* @param[out] got_packet_ptr 编码器设置为0或1,以指示avpkt中返回的非空包* @return 0 操作成功*/int (*encode2)(AVCodecContext *avctx, AVPacket *avpkt, const AVFrame *frame,int *got_packet_ptr);// 解码操作int (*decode)(AVCodecContext *, void *outdata, int *outdata_size, AVPacket *avpkt);// 关闭codecint (*close)(AVCodecContext *);// Encode API with decoupled packet/frame dataflow. int (*send_frame)(AVCodecContext *avctx, const AVFrame *frame);int (*receive_packet)(AVCodecContext *avctx, AVPacket *avpkt);// Decode API with decoupled packet/frame dataflow. int (*receive_frame)(AVCodecContext *avctx, AVFrame *frame);// flush缓冲区,执行seeking操作是被调用void (*flush)(AVCodecContext *);...
} AVCodec;

(2) AVCodecContext结构体

 也许你会发现,对于编解码而言,除了AVCodec这个非常重要的结构体,在AVCodec的成员函数中还有一个出现频率非常高的结构体,可以这么说大部分与编解码有关的函数都需要传入一个结构体参数,这个结构体就是AVCodecContext。AVCodecContext结构体存储视频流或音频流使用的编解码相关信息,比如codec_type表示编解码器的类型、codec表示采用的编解码器等等。AVCodecContext结构体源码如下:

typedef struct AVCodecContext {enum AVMediaType codec_type; /* 编解码器的类型(视频,音频...) */const struct AVCodec  *codec;// 采用的解码器AVCodec(H.264,MPEG2...)enum AVCodecID     codec_id; /* see AV_CODEC_ID_xxx */// 比特率(音频和视频的平均比特率)int64_t bit_rate;// 压缩编码的等级int compression_level;// 针对特定编码器包含的附加信息(例如对于H.264解码器来说,存储SPS,PPS等)uint8_t *extradata; int extradata_size;// 时基// 根据该参数,可以把PTS转化为实际的时间(单位为秒s)AVRational time_base;// 图像宽、高,针对视频而言int width, height;// 像素格式,针对视频而言enum AVPixelFormat pix_fmt;// 获取像素格式enum AVPixelFormat (*get_format)(struct AVCodecContext *s, const enum AVPixelFormat * fmt);// 非B帧之间的最大B帧数int max_b_frames;// I/P帧和B帧之间的qscale因子float b_quant_factor;// 采样纵横比AVRational sample_aspect_ratio;// 音频一帧采样样本个数int frame_size;// 音频通道布局uint64_t channel_layout;// 帧率AVRational framerate;...
} AVCodecContext;

4.数据相关结构体

(1) AVStream结构体

 AVStream结构体用于存储一个视频或音频流信息,其中,字段nb_frames表示该流包含多少帧数据、字段duration表示该流的长度、字段index标志是音频流还是视频流等等。

typedef struct AVStream {// 标志视频流或音频流,存储在AVFormatContext中int index;    /**< stream index in AVFormatContext */// 指向该视频/音频流的AVCodecContext// @deprecated use the codecpar struct insteadAVCodecContext *codec;// 时基。通过该值可以把PTS,DTS转化为真正的时间AVRational time_base;// 该视频/音频流的长度int64_t duration;// 该视频/音频流的帧数int64_t nb_frames;              // 元数据信息AVDictionary *metadata;// 帧率(对视频来说很重要)AVRational avg_frame_rate;// 附带的图片。比如说一些MP3,AAC音频文件附带的专辑封面AVPacket attached_pic;...// 与该视频流或音频流相关的Codec参数// 由avformat_new_stream()分配、avformat_free_context()释放AVCodecParameters *codecpar;
} AVStream;

(2) AVPacket结构体

 AVPacket结构体用于存储压缩编码的视频或音频数据相关信息,其中,字段stream_index标志AVPacket所属的是音频流还是视频流。比如对于H.264来说,通常一个AVPacket的data对应着一个NAL,而一个NAL存储着一帧图像。 AVPacket结构体源码如下:

typedef struct AVPacket {AVBufferRef *buf;/*** Presentation timestamp in AVStream->time_base units; the time at which* the decompressed packet will be presented to the user.*/// Presentation timestamp,即显示时间戳int64_t pts;/*** Decompression timestamp in AVStream->time_base units; the time at which* the packet is decompressed.*/// Decompression timestamp,即解码时间戳int64_t dts;// 压缩编码的视频或音频数据uint8_t *data;// data的大小int   size;// 标志该AVPacket所属的是音频流还是视频流int   stream_index;int   flags;AVPacketSideData *side_data;int side_data_elems;/*** Duration of this packet in AVStream->time_base units, 0 if unknown.* Equals next_pts - this_pts in presentation order.*/// 该AVPacket的长度int64_t duration;// 该AVPacket在流中的字节位置,-1表示未知int64_t pos;
} AVPacket;

(3) AVFrame结构体

 AVFrame结构体用于存储解码后的视/音频数据相关信息,表示一帧数据。如果AVFrame为视频帧数据结构体,字段data数组存储的是一帧图像、字段width、height为图像的宽、高、key_frame为是否为关键帧标志等等;如果AVFrame为音频数据结构体,字段data数组存储的是音频数据,可包含多帧音频、字段sample_rate为音频的采样率、字段channels为音频通道数量等等。 AVFrame结构体源码如下:

typedef struct AVFrame {// 解码后的原始数据(视频-YUV或RGB;音频-PCM)// 对于packed格式的数据(如RGB24),会存储在data[0]// 对于plannar格式的数据(如YUV420P),Y分量存储在data[0]、U分量存储在data[1]、V分量存储在data[2]uint8_t *data[AV_NUM_DATA_POINTERS];// data一行数据的长度// 注意:如果是图像不一定等于图像的宽度,往往大于图像的宽int linesize[AV_NUM_DATA_POINTERS];// 视频帧的宽、高int width, height;// 该AVFrame包含几个音频帧int nb_samples;// 解码后原始数据类型,比如YUV420、RGB..// 音频,详见AVSampleFormat// 视频,详见AVPixelFormatint format;// 是否为关键帧,对视频来说非常重要// 1 -> keyframe, 0-> notint key_frame;// 帧类型,比如I帧、B帧、P帧...enum AVPictureType pict_type;// 视频帧宽高比,如16:9、4:3...AVRational sample_aspect_ratio;// 显示时间戳int64_t pts;// 编码图像帧序号int coded_picture_number;// 显示图像帧序号int display_picture_number;// 音频采样率int sample_rate;// 音频通道layoutuint64_t channel_layout;// YUV颜色空间类型enum AVColorSpace colorspace;// 元数据AVDictionary *metadata;// 音频通道数量int channels;...
} AVFrame;

至此,FFmpeg框架中最为重要的结构体,我们基本讲解梳理完毕。

FFmpeg从入门到入魔(1):初探FFmpeg框架相关推荐

  1. FFmpeg从入门到入魔(2):保存流到本地MP4

    1 . FFmpeg裁剪移植 之前我们简单地讲解了下如何在Linux系统中编译FFmpeg,但是编译出来的so体积太大,而且得到的多个so不便于使用.本节在此基础上,将详细讲解在编译FFmpeg时如何 ...

  2. FFmpeg从入门到入魔(5):浅析滤镜(filter)原理

    1. 什么是滤镜  滤镜(filter)是指将未经过处理的原始音频帧(如PCM)或视频帧(如YUV.RGB)经过滤镜器处理后,得到具体"特殊效果"的音频帧或视频帧,比如音频帧被添加 ...

  3. FFmpeg从入门到入魔(4):OpenSL ES播放PCM音频

    1. OpenSL ES原理  OpenSL ES(Open Sound Library for Embedded Systems),即嵌入式音频加速标准,是一个无授权费.跨平台.针对嵌入式系统精心优 ...

  4. Windows下FFmpeg高速入门

    本系列文章导航 Windows下FFmpeg高速入门 ffmpeg參数解释 mencoder和ffmpeg參数具体解释(Java处理视频) Java 生成视频缩略图(ffmpeg) 使用ffmpeg进 ...

  5. FFmpeg从入门到出家(HEVC在RTMP中的扩展)

    由金山云视频云技术团队提供:FFmpeg从入门到出家第三季: 为推进HEVC视频编码格式在直播方案中的落地,经过CDN联盟讨论,并和主流云服务厂商达成一致,规范了HEVC在RTMP/FLV中的扩展,具 ...

  6. FFmpeg从入门到精通:SEI那些事

    本文是"FFmpeg从入门到精通"系列的第三篇,由金山云供稿,并授权LiveVideoStack发布.此前两篇为FFmpeg代码导读--基础篇和FFmpeg代码导读--HEVC在R ...

  7. FFmpeg自学入门笔记

    命令行 PS:我自己使用过的命令行,便于自己查阅和使用FFmpeg. 1.转格式 ffmpeg -i input.mp4 output.avi 2.转分辨率 ffmpeg -i in.mp4 -vf ...

  8. FFmpeg从入门到精通-云享读书会

    前言 FFmpeg是一款开源软件,用于生成处理多媒体数据的各类库和程序.FFmpeg可以转码.处理视频和图片(调整视频.图片大小,去噪等).打包.传输及播放视频.作为最受欢迎的视频和图像处理软件,它被 ...

  9. FFmpeg从入门到牛掰(二):转封装(remux)讲解

    转载请注明出处:https://blog.csdn.net/impingo 项目地址:https://github.com/im-pingo/pingos 转封装讲解 转封装流程 函数调用流程 函数接 ...

最新文章

  1. Ubuntu 14.04 64bit上安装有道词典Linux版本
  2. Windows的端口列表(转载)
  3. UA MATH574M 统计学习II 二元分类
  4. Caching Best Practices--reference
  5. 第三节:快速编译TypeScript,提高开发效率
  6. 200个化工网站批量爬取
  7. CSS 图片去色处理
  8. C语言游戏开发——打飞机游戏1.0
  9. 网络和计算机加密驱动,如何解决笔记本电脑连接不上加密无线网络的问题
  10. 拉格朗日对偶问题一定是凸优化问题的证明
  11. Win10打开文件夹闪退怎么解决
  12. 南京邮电大学java实验报告_南京邮电大学java第二次实验报告
  13. 小米会升级鸿蒙系统吗,小米要自研系统对鸿蒙有何影响
  14. 海信85U7G和海信85U7G-PRO有什么区别 哪个好详细性能配置对比
  15. mysql查看指定全局变量_【全局变量】mysql查看全局变量以及设置全局变量的值...
  16. 小度计算机模式,小度机器人怎么用 小度机器人使用教程-电脑教程
  17. 将Qt QCheckBox 默认选中样式改为对号选中
  18. catia零件隐藏显示
  19. 操作系统课程,绕了很多弯路,转身回归本质
  20. 可作为MCU外扩SRAM芯片的型号推荐

热门文章

  1. java filesaver获取文件名_FileSaver导出excel
  2. SQL语句关键字大全
  3. 家用机器人扫机外毛刷_家用扫地机毛刷的选择技巧
  4. clearfix的用法
  5. 伟福怎么编译c语言,用C语言发声
  6. 光耦工作原理功能电气参数应用电路
  7. 简单开发调用百度翻译api(java)
  8. 秒杀多线程系列(摘录)
  9. 网络编程中的 SIGPIPE 信号
  10. galaxy 生信安装软件_如何在三星Galaxy Smartwatches上安装Google Assistant