FFmpeg模块化架构

FFmpeg是一个模块化架构 的项目,各组件在功能上相互独立,又通过标准的数据结构(如AVPacketAVFrame)协同工作,共同完成多媒体任务。

下面的关系图和表格概括了其核心架构:

flowchart TD subgraph A [FFmpeg 核心架构层次] direction LR Protocol["协议层
(HTTP, FILE, RTMP)"] Format["封装/解封装层
(MP4, FLV, MKV)"] Codec["编解码层
(H.264, AAC)"] Filter["滤镜处理层
(scale, overlay)"] Device["设备层
(摄像头, 扬声器)"] Util["基础工具库
(公共函数, 数据结构)"] end subgraph B [数据处理流程] Input[输入源] --> Format Format -- AVPacket
压缩数据 --> Codec Codec -- AVFrame
原始数据 --> Filter Filter -- AVFrame
处理后数据 --> Codec Codec -- AVPacket
压缩数据 --> Format Format --> Output[输出目标] end subgraph C [支持与工具库] Sws["视频处理
(swscale)"] Swr["音频处理
(swresample)"] end subgraph D [用户接口层] Ffmpeg[命令行工具 ffmpeg] Ffplay[播放器 ffplay] Ffprobe[分析器 ffprobe] end C -- 支撑 --> B Util -- 基础支撑 --> A B -- 实现核心功能 --> D

🔧 核心库与职责

FFmpeg的核心功能主要由以下库实现,其职责联系清晰:

核心库 主要职责与说明 依赖关系与协作
libavformat 封装/解封装层 :处理媒体容器格式 (如MP4、FLV)和网络协议(如RTMP、HLS),负责读取/写入音视频流。 所有I/O的起点与终点。它依赖libavcodec进行编解码,为libavfilter提供数据源。
libavcodec 编解码层 :提供各种音视频编解码器(如H.264、AAC),实现数据压缩与解压缩。 核心处理枢纽。接收来自libavformatAVPacket进行解码,或接收来自libavfilterAVFrame进行编码。
libavfilter 滤镜处理层 :提供强大的音视频滤镜框架,可进行裁剪、叠加、缩放、降噪等复杂处理。 处理链中间层。接收libavcodec解码后的AVFrame,处理后再送回libavcodec编码。
libavutil 基础工具库:提供公共辅助函数,如数学计算、数据结构、内存管理等,是其他所有库的基础。 基石,被所有其他核心库依赖。
libswscale 视频处理库 :专门负责图像缩放、像素格式转换(如YUV转RGB)。 通常被libavfilter的滤镜或播放器ffplay调用,处理视频帧。
libswresample 音频处理库 :专门负责音频重采样、格式转换、声道布局调整 通常被libavfilter的滤镜或播放器ffplay调用,处理音频帧。
libavdevice 设备交互库 :用于采集或输出到硬件设备,如摄像头、屏幕。 可视为特殊的libavformat,将硬件设备抽象为I/O设备。

💡 如何理解他们之间的联系

理解FFmpeg的秘诀是追踪数据。核心数据结构定义了组件间的"对话语言":

  • AVPacket :存放压缩后的编码数据 (例如一个H.264帧)。它主要在libavformat(解封装/封装)和libavcodec(解码/编码)之间传递。
  • AVFrame :存放解码后的原始数据 (例如一帧YUV图像或PCM音频)。它主要在libavcodeclibavfilter以及处理库(libswscale/libswresample)之间传递。

🛠️ 联系实例:以转码流程为例

假设你用 ffmpeg -i input.mp4 -vf scale=1280:720 output.mp4 转换视频,其内部组件协作如下:

  1. 读取libavformat 打开 input.mp4,解封装得到视频流(压缩的 AVPacket)。
  2. 解码libavcodec(H.264解码器)将 AVPacket 解码成原始的 AVFrame(YUV图像)。
  3. 处理libavfilterscale 滤镜调用 libswscale,将 AVFrame 缩放到1280x720。
  4. 编码libavcodec(H.264编码器)将处理后的 AVFrame 重新编码成 AVPacket
  5. 写入libavformat 将新的 AVPacket 封装进 output.mp4

希望以上解释能帮助你清晰地理解FFmpeg的架构。为了能更好地解答你的疑问,可以告诉我你是对某个具体组件的内部实现细节感兴趣,还是想了解在特定开发场景(如播放器开发)中如何组织这些组件呢?### 第1步:读取与解封装 (libavformat)

类别 API/数据结构 说明
核心结构 AVFormatContext 统领整个媒体文件上下文,包含所有流信息、封装格式等,是后续所有操作的根。
流信息 AVStream 代表文件中一路独立的媒体流(如视频流、音频流)。
压缩数据包 AVPacket 本步骤的产出 。存放从文件中读取出来的一帧压缩的音/视频数据。
关键函数 avformat_open_input() 打开输入文件,并填充AVFormatContext
avformat_find_stream_info() 读取文件头,获取流的详细信息(如编码格式、时长等)。
av_read_frame() 核心读取函数 。从AVFormatContext中读取下一个AVPacket
avcodec_find_decoder() / avcodec_parameters_to_context() 根据AVStream中的编码信息找到对应的解码器,并将参数复制到解码器上下文。

流程简述 :打开上下文 -> 探测信息 -> 找到解码器 -> 循环调用 av_read_frame() 获取 AVPacket

第2步:解码 (libavcodec)

类别 API/数据结构 说明
核心结构 AVCodecContext 编解码器上下文,包含了特定编解码器(如H.264)的所有参数和状态。
原始数据帧 AVFrame 本步骤的产出 。存放解码后的原始音视频数据(如YUV像素、PCM音频)。
关键函数 avcodec_alloc_context3() 为指定的编解码器分配AVCodecContext
avcodec_open2() 使用特定编解码器(如AVCodec *decoder)初始化AVCodecContext
avcodec_send_packet() 发送 一个AVPacket给解码器。这是新的"发送-接收"API的一部分。
avcodec_receive_frame() 接收 解码器输出的一帧AVFrame。需循环调用,直到返回AVERROR(EAGAIN)AVERROR_EOF

流程简述 :初始化解码器上下文 -> 循环将上一步的AVPacket通过 send_packet() 送入 -> 循环通过 receive_frame() 取出 AVFrame

第3步:处理 (libavfilterlibswscale)

这步是可选的,当需要进行缩放、裁剪等处理时才需要。libavfilter是一套复杂的滤镜图框架,这里以简单的 scale 滤镜(内部会调用libswscale)为例。

类别 API/数据结构 说明
核心结构 AVFilterGraph 管理整个滤镜链(滤镜图)的容器。
AVFilterContext 代表滤镜图中的一个具体滤镜实例(如 scale 滤镜)。
AVFilterInOut 用于帮助构建滤镜图的输入/输出端链表。
关键函数 avfilter_get_by_name() 根据名称(如"scale")获取滤镜。
avfilter_graph_create_filter() 用指定的滤镜创建一个AVFilterContext并加入滤镜图。
avfilter_graph_parse_ptr() 更高级的API,可以用字符串描述(如"scale=1280:720")直接解析并构建滤镜图。
avfilter_graph_config() 配置并验证构建好的滤镜图。
av_buffersrc_add_frame_flags() 将一个AVFrame送入滤镜图的输入端。
av_buffersink_get_frame() 从滤镜图的输出端获取 处理后的AVFrame
libswscale SwsContext 图像缩放和格式转换的上下文。
sws_getContext() 创建或获取一个SwsContext,指定源和目标尺寸、格式等参数。
sws_scale() 执行实际的缩放/转换操作

流程简述(滤镜方式):

  1. 创建滤镜图,添加buffer(输入)、scale(处理)、buffersink(输出)滤镜并链接。
  2. 配置滤镜图。
  3. 将第2步的AVFrameav_buffersrc_add_frame_flags() 送入滤镜图。
  4. av_buffersink_get_frame() 取出处理后的新AVFrame

第4步:编码 (libavcodec)

此步骤与第2步镜像,方向相反。

类别 API/数据结构 说明
核心结构 AVCodecContext 编码器上下文,包含了目标编码格式(如H.264)的所有参数(码率、GOP等)。
关键函数 avcodec_find_encoder() 根据编码器ID(如AV_CODEC_ID_H264)或名称找到编码器。
avcodec_open2() 使用编码器初始化AVCodecContext
avcodec_send_frame() 发送 一个AVFrame给编码器。
avcodec_receive_packet() 接收 编码器输出的一帧AVPacket。需循环调用。
av_frame_get_buffer() 为新创建的AVFrame分配数据缓冲区(在处理完准备送入编码器时可能需要)。

流程简述 :初始化编码器上下文 -> 将第3步处理后的AVFrame通过 send_frame() 送入 -> 循环通过 receive_packet() 取出压缩后的 AVPacket

第5步:写入与封装 (libavformat)

此步骤与第1步镜像,方向相反。

类别 API/数据结构 说明
核心结构 AVFormatContext 输出文件的上下文。
流信息 AVStream 需要在输出文件中创建对应的流,并从编码器上下文复制参数。
关键函数 avformat_alloc_output_context2() 根据格式或文件名,为输出文件分配AVFormatContext
avio_open() 打开输出文件的I/O上下文。
avformat_new_stream() 在输出上下文中创建一个新的AVStream
avcodec_parameters_from_context() 关键 :将编码器AVCodecContext中的参数复制到输出AVStreamcodecpar中。
avformat_write_header() 写入文件头。
av_interleaved_write_frame() / av_write_frame() 核心写入函数 。将编码得到的AVPacket写入文件,前者能保证交错写入的正确性。
av_write_trailer() 写入文件尾。
时间处理 av_packet_rescale_ts() 至关重要 :将AVPacket的时间戳从编码器的时基转换为输出流的时基。

流程简述 :创建输出上下文 -> 创建流并复制参数 -> 写文件头 -> 循环将第4步的AVPacket 进行时间戳转换后 写入 -> 写文件尾。

总结与联系

整个过程的核心就是数据结构 AVPacketAVFrame 在几个核心上下文(AVFormatContext, AVCodecContext, AVFilterGraph)之间,通过 send/receiveread/write 系列函数进行流转

相关推荐
milanyangbo1 小时前
像Git一样管理数据:深入解析数据库并发控制MVCC的实现
服务器·数据库·git·后端·mysql·架构·系统架构
张人大 Renda Zhang2 小时前
Spring Cloud / Dubbo 是 2 楼,Kubernetes 是 1 楼,Service Mesh 是地下室:Java 微服务的“三层楼模型”
spring boot·spring cloud·云原生·架构·kubernetes·dubbo·service_mesh
weixin_307779132 小时前
Jenkins Metrics 插件全解析:从数据采集到智能监控的实践指南
运维·开发语言·架构·jenkins
特拉熊2 小时前
23种设计模式之桥接模式
java·架构
小白|2 小时前
【OpenHarmony × Flutter】混合开发高阶实战:如何统一管理跨平台状态?Riverpod + ArkTS 共享数据流架构详解
flutter·架构
虚伪的空想家2 小时前
arm架构TDengine时序数据库及应用使用K8S部署
服务器·arm开发·架构·kubernetes·arm·时序数据库·tdengine
拾忆,想起2 小时前
Dubbo服务降级全攻略:构建韧性微服务系统的守护盾
java·前端·网络·微服务·架构·dubbo
闲人编程2 小时前
FastAPI框架架构与设计哲学
python·架构·api·fastapi·异步·codecapsule
weixin_307779132 小时前
简化多维度测试:Jenkins Matrix Project 的核心概念与最佳实践
运维·开发语言·架构·jenkins