FFMpeg源码分析,关键结构体分析(一)

http://lazybing.github.io/blog/categories/ffmpegyuan-ma-fen-xi/

一、下载FFmpeg的编译源码

 进入网站:http://ffmpeg.org/download.html

二、编译源码

执行下述命令:

./configure --prefix=/usr/local/ffmpeg --enable-debug=3 --enable-ffplay
sudo make -j 4
sudo make install

如果编译成功,那么将会在安装路径/user/local/ffmpeg下出现如下子目录
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

FFMpeg 作为音视频领域的开源工具,它几乎可以实现所有针对音视频的处理。本文详细记录使用 FFMpeg 开发过程中,经常使用到的结构体的含义以及使用场景。
首先,我们从 FFMpeg 主要完成的功能视频编解码开始,编解码的大致流程可以使用如下图表
在这里插入图片描述
AVPacket结构体解析
从上面的图中可以看出,解复用器 Demuxer 输出的 packets data 作为解码器 Decoder 的输入;同时也是编码器 Encoder 的输出,复用器 Muxer 的输入。

FFMpeg 中使用 AVpacket 结构定义图中描述的 packet。该结构保存了压缩数据,它由 demuxers 输出,之后作为 decoders 的输入;或作为 encoders 的输出,之后作为 muxers 的输入。

对于视频数据,它只包含一帧压缩数据;对于音频数据,它可能包含多帧压缩数据。

typedef struct AVPacket{
   
   
	AVBufferRef *buf;
	int64_t      pts;
	int64_t      dts;
	uint8_t    *data;
	int         size;
	int stream_index;
	int        flags;
	AVPacketSideData *side_data;
	int side_data_elems;
	int   duration;
	int64_t pos;
	int64_t convergence_duration;
}

pts:显示时间戳,它的单位是 AVStream->time_base;如果在文件中没有保存这个值,它被设置为 AV_NOPTS_VALUE。由于图像显示不可能早于图像解压,因此 PTS 必须比 DTS(解码时间戳)大或者相等。某些文件格式中可能会使用 PTS/DTS 表示其他含义,此时时间戳必须转为真正的时间戳才能保存到 AVPacket 结构中。
dts:解码时间戳,它的单位是 AVStream->time_base,表示压缩视频解码的时间,如果文件中没有保存该值,它被设置为 AV_NOPTS_VALUE。
data:指向真正的压缩编码的数据。
size:表示该 AVPacket 结构中 data 字段所指向的压缩数据的大小。
stream_index:标识该 AVPacket 结构所属的视频流或音频流。
duration:该 AVPacket 包以 AVStream->time_base 为单位,所持续的时间,0 表示未知,或者为显示时间戳的差值(next_pts - this pts)。
pos:表示该 AVPacket 数据在媒体中的位置,即字节偏移量。
由上面的图可以看出,该结构主要用于解码器(Decoder)或编码器(Encoder),一般使用方法代码示例如下:

AVPacket avpkt;
av_init_packet(&avpkt);
avpkt.size = ...
avpkt.data = ...

AVFrame结构体解析
上面已经分析了压缩域的表示结构体 AVPacket 结构,与之相对于的,FFMpeg 为之提供的表示像素域的结构体为 AVFrame 结构。从上面的图中也可以看出,Frame 主要作为解码器的输出、编码器的输入。AVFrame 结构主要用来描述未压缩的视频或音频数据,比如视频的 YUV 数据、RGB 数据,音频的 PCM 数据等。

AVFrame 结构体必须使用av_frame_alloc()分配,注意该函数只是分配了 AVFrame 结构本身,它的 data 区域要用其他方式管理;该结构体的释放要用av_frame_free()。

AVFrame 结构体通常只需分配一次,之后即可通过保存不同的数据来重复多次使用,比如一个 AVFrame 结构可以保存从解码器中解码出的多帧数据。此时,就可以使用av_frame_unref()释放任何由 Frame 保存的参考帧并还原回最原始的状态。

typedef struct AVFrame{
   
   
	uint8_t *data[AV_NUM_DATA_POINTERS];
	int linesize[AV_NUM_DATA_POINTERS];
	uint8_t **extended_data;
	int width, height;
	int nb_samples; /* number of audio samples(per channel) described by this frame */
	int format;
	int key_frame; /* 1->keyframe, 0->not*/
	enum AVPictureType pict_type;
	AVRational sample_aspect_ratio;
	int64_t pts;
	int64_t pkt_pts;
	int64_t pkt_dts;
	int coded_picture_number;
	int display_picture_number;
	int quality;
	void *opaque; /* for some private data of the user */
	uint64_t error[AV_NUM_DATA_POINTERS];
	int repeat_pict;
	int interlaced_frame;
	int top_field_first;	/* If the content is interlaced, is top field displayed first */
	int palette_has_changed;
    int64_t reordered_opaque;
    int sample_rate;    /*Sample rate of the audio data*/
    uint64_t channel_layout; /*channel layout of the audio data*/
    AVBufferRef *buf[AV_NUM_DATA_POINTERS];
    AVBufferRef **extended_buf;
    int nb_exteneded_buf;
    AVFrameSideData **side_data;
    int nb_side_data;

    int flags;
    enum AVColorRange color_range;
    enum AVColorPrimaries color_primaries;
    enum AVColorTransferCharacteristic color_trc;
    enum AVColorSpace colorspace;
    enum AVChromaLocation chroma_location;

    int64_t best_effort_timestamp;
    int64_t pkt_pos;
    int64_t pkt_duration;
    AVDictionary *metadata;
    int decode _error_flags;

    int channels;
    int pkt_size;
    AVBufferRef *qp_table_buf;
<think> 嗯,用户这次想要的是FFmpeg从命令行到数据流的源码分析内容。看起来ta是个开发者或者音视频技术研究者,需要深入理解FFmpeg的内部工作机制。从问题表述来看,ta应该已经具备FFmpeg使用基础,现在想深入源码层。 我注意到用户引用了四篇FFmpeg相关的技术文档片段:包括结构体分析ffmpeg.c源码解析、架构设计和ffplay参数解析。这些引用很有价值,特别是引用[2]直接提到了ffmpeg.c的主函数流程,这正是命令行处理的核心入口。 用户上次的问题也是关于FFmpeg源码架构的,这次聚焦在“命令行到数据流”这个具体路径上。需要重点解释从用户输入命令到音视频数据流被处理的完整过程。 考虑从这几个层面展开: 1. 命令解析阶段:ffmpeg_parse_options()如何拆解命令行参数,这是用户最直接的操作入口 2. 初始化阶段:编解码器注册、数据结构准备 3. 核心流程:transcode()函数里的输入输出流处理闭环 4. 数据流转:解复用、解码、滤镜、编码、复用的完整流水线 5. 关键数据结构:AVFormatContext、AVStream这些容器如何承载数据流 特别要注意引用[3]提到的模块化设计思想,在解释流程时要突出各模块的协作关系。比如解复用器(libavformat)和编解码器(libavcodec)如何通过AVPacket和AVFrame交互。 用户可能还想知道数据流经过的每个模块的可扩展性,比如如何通过AVFilter实现自定义处理。这正好对应引用[3]的滤镜系统描述。 在回答中会避免陷入太细节的函数调用,而是把握主干流程。最后考虑附上几个进阶问题,引导用户深入理解性能优化和定制开发方向。 </think> 以下是FFmpeg从命令行输入到数据流处理的源码分析全流程解析,结合核心源码结构(基于FFmpeg 4.2.2版本): --- ### **、命令行入口与初始化** **源码文件**:`fftools/ffmpeg.c` **主函数调用栈**: ```c main(int argc, char **argv) ├─ avdevice_register_all() // 注册所有输入输出设备 ├─ show_banner() // 打印版本信息 ├─ ffmpeg_parse_options() // 关键解析命令行参数 └─ transcode() // 核心转码入口 ``` --- ### **二、命令行参数解析流程** **函数**:`ffmpeg_parse_options()` **关键步骤**: 1. **参数分类处理** ```c while ((opt = getopt(argc, argv, "i:y:f:...")) != -1) { switch (opt) { case 'i': input_files = optarg; // 输入文件 case 'y': force_height = atoi(optarg); // 强制设置分辨率(如用户示例) case 'f': format = optarg; // 强制封装格式 ... } } ``` 2. **构建过滤器图**(Filtergraph) 通过`-vf`/`-af`参数解析为`FilterGraph`结构体[^3] --- ### **三、核心转码流程:`transcode()`** **函数调用栈**: ```c transcode() ├─ open_input_file() // 打开输入文件 │ └─ avformat_open_input() // 解复用器初始化 ├─ open_output_file() // 打开输出文件 │ └─ avformat_alloc_output_context2() ├─ init_input_stream() // 初始化输入流 │ ├─ avcodec_find_decoder() // 查找解码器 │ └─ avcodec_open2() // 打开解码器 ├─ transcode_init() // 初始化转码器 ├─ transcode_step() // 执行转码循环 └─ close_files() // 资源释放 ``` --- ### **四、数据流处理关键路径** #### 1. **解复用(Demux)** - **数据结构**:`AVFormatContext` (包含`AVStream`数组) - **源码逻辑**: ```c while (av_read_frame(ifmt_ctx, &pkt) >= 0) { // 读取数据包 AVStream *in_stream = ifmt_ctx->streams[pkt.stream_index]; // 将pkt送入解码队列 } ``` #### 2. **解码(Decode)** - **函数**:`decode_video()`/`decode_audio()` - **关键调用**: ```c ret = avcodec_send_packet(dec_ctx, pkt); // 发送数据包到解码器 while (ret >= 0) { ret = avcodec_receive_frame(dec_ctx, frame); // 获取解码后的帧 if (ret == AVERROR_EOF) break; // 将frame送入滤镜或编码器 } ``` #### 3. **滤镜处理(Filtering)** - **数据结构**:`FilterGraph`、`AVFilterContext` - **流程**: ```c av_buffersrc_add_frame(filter_ctx, frame); // 帧输入滤镜 while (av_buffersink_get_frame_flags(sink_ctx, frame) >= 0) { // 处理过滤后的帧 } ``` #### 4. **编码(Encode)与复用(Mux)** - **编码**: ```c avcodec_send_frame(enc_ctx, frame); // 发送帧到编码器 avcodec_receive_packet(enc_ctx, pkt); // 获取编码后的包 ``` - **复用**: ```c av_interleaved_write_frame(ofmt_ctx, pkt); // 写入输出文件 ``` --- ### **五、关键数据结构关系** ```mermaid graph TD A[命令行参数] --> B[AVFormatContext] B --> C[AVStream] C --> D[AVCodecContext] D --> E[AVPacket/AVFrame] E --> F[AVFilterGraph] F --> G[编码器AVCodecContext] G --> H[输出AVFormatContext] ``` --- ### **六、性能优化关键点** 1. **异步处理**:通过多线程解码/编码(`avcodec_open2`时设置`thread_count`) 2. **零拷贝**:使用`hwaccel`硬件加速(如`-hwaccel cuda`)[^1] 3. **内存优化**:复用`AVFrame`和`AVPacket`对象池
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值