【音视频开发】第五章 FFmpeg基础
文章目录
- 【音视频开发】第五章 FFmpeg基础
- 一、播放器框架
- 1.媒体文件读取阶段
- 2.音频处理流程
- 3.视频处理流程
- 二、常用音视频概念
- 1.常用音视频术语
- 2.复用器
- 3.编解码器
- 三、FFmpeg 库
- 1.整体结构
- 四、FFmpeg 常用函数
- 1.libavformat 封装/解封装
- 2.libavcodec 编解码
- 3.libavutil 工具函数库
- 4.libswscale 图像缩放、像素格式转换
- 5.libswresample 音频重采样
- 6.内存与资源释放相关
- 五、FFmpeg 常用结构体
- 1.封装/解封装相关结构体(libavformat)
- 2.编解码相关结构体(libavcodec)
- 六、FFmpeg 内存模型
- 1.FFmpeg 内存模型核心思想
- 2.内存生命周期关系图
- 3.常用结构体的内存管理方式
- 4.典型内存释放策略
一、播放器框架
1.媒体文件读取阶段
- 媒体文件(输入)
通过 avformat_alloc_context() 和 avformat_open_input() 创建和打开输入文件,生成 AVFormatContext - 解复用器(音视频解封装)
使用 av_read_frame() 从媒体流中读取数据包(AVPacket),根据 AVStream 区分音频或视频数据
2.音频处理流程
- 音频包队列(Packet Queue)
接收音频AVPacket - 音频解码
使用 avcodec_send_packet() 送入解码器,avcodec_receive_frame() 获取 AVFrame,即解码后的音频帧 - 采样帧队列(Frame Queue)
将音频帧缓存用于后续播放 - 音频处理
包括重采样、音量调节等,最终送入扬声器播放
3.视频处理流程
- 视频包队列(Packet Queue)
接收视频 AVPacket - 视频解码
同样使用 avcodec_send_packet() 和 avcodec_recceive_frame() 获取视频帧 - 图像帧队列(Frame Queue)
存储待显示的图像帧 - 图像处理
包括格式转换、缩放、显示等,最终在显示器上播放
二、常用音视频概念
1.常用音视频术语
- 容器/文件(Container/File):即特定格式的多媒体文件,比如 mp4、flv、mkv 等
- 媒体流(Stream):表示时间轴上的一段连续数据,如一段声音数据、一段视频数据或一段字幕数据,可以是压缩的,也可以是非压缩的,压缩的数据需要关联特定的编解码器(有些码流音频是纯 PCM)
- 数据帧/数据包(Frame/Packet):通常,一个媒体流是由大量的数据帧组成的,对于压缩数据,帧对应着编解码器的最小处理单元,分属于不同媒体流的数据帧交错存储于容器之中
- 编解码器:编解码器是以帧为单位实现压缩数据和原始数据之间的相互转换的
2.复用器
“复用器”这个词,英文是 Multiplexer(简称 Mux),在音视频处理中是一个非常关键的概念,和“解复用器”(Demuxer)是相对的。
复用器的作用是把多个独立的音视频流、字幕流、元信息等数据,打包成一个统一的媒体文件格式,比如 .mp4、.mkv、.ts 等,把音频、视频、字幕合并打包成一个完整的视频文件,这个过程就叫复用。
假设有两段数据:
- 一段是 .aac 音频
- 一段是 .h264 视频
你想把它们合成一个 .mp4 文件,可以通过复用器完成
在 FFmpeg 命令行中就是:
ffmpeg -i video.h264 -i audio.aac -c copy output.mp4
这条命令里,-c copy 表示不重新编码,直接复用音视频流
3.编解码器
“编解码器”是音视频领域最核心的概念之一,英文叫 Codec,是 “编码器(Encoder)” 和 “解码器(Decoder)” 的合称。
- 编码器:把原始的音视频数据压缩,变成更小、更易传输的格式(比如 H.264,AAC)
- 解码器:把压缩后的数据还原为可播放的原始数据
拿手机拍了一个视频,这个过程可能涉及:
- 摄像头捕获的是“原始图像数据”(未压缩,体积很大)
- 编码器将图像压缩为 H.264 格式的视频流,生成 .mp4 文件
- 播放这个 .mp4 文件时,解码器会把 H.264 解码为图像帧显示出来
常见编解码器
三、FFmpeg 库
1.整体结构
上层工具(命令行工具)
- ffmpeg:核心命令行工具,用于转码、提取音视频、格式转换、滤镜处理等
- ffplay:简单播放器,基于 SDL 和 FFmpeg,可以用来测试播放
- ffprobe:分析媒体文件的信息(码率、时长、分辨率、流信息等)
中间核心库(libav 系列)
- libavformat:封装/解封装库(复用器/解复用器),处理媒体容器格式,比如 MP4/MKV/FLV
- libavcodec:编解码器库,支持各种音视频编码(如 H264、AAC、MP3)
- libavutil:工具库,提供常用的数学、时间戳、内存处理、数据结构等工具
- libswscale:图像缩放/像素格式转换库,比如 RGB -> YUV
- libswreasample:音频重采样库,比如立体声转单声道,变采样率
- libavfilter:滤镜处理库,用于音视频的剪辑、旋转、叠加等复杂处理
- libpostproc:后处理库,主要用于解码后图像增强
编解码器支持库(第三方)
这些库不属于 FFmpeg 本身,但可以被 libavcodec 调用来支持更多的格式
- x264:最常用的 H.264 编码器(开源)
- fdk-aac:高质量 AAC 编码器(非完全开源)
- voaac_enc:另一种 AAC 编码器实现
模块调用关系
ffmpeg 命令↓
libavformat (封装/解封装)↓
libavcodec (编解码)↓
调用第三方编解码器(如 x264、fdk-aac)↓
libswscale / libswresample(图像、音频处理)↓
libavfilter(可选滤镜)↓
libavutil(提供底层支持)
如果在开发中只想播放或分析视频,常用的是 libavformat + libavcodec + libavutil
如果要处理图像、声音或添加滤镜,就会涉及 libswscale、libswresample 和 libavfilter
四、FFmpeg 常用函数
1.libavformat 封装/解封装
2.libavcodec 编解码
3.libavutil 工具函数库
4.libswscale 图像缩放、像素格式转换
5.libswresample 音频重采样
6.内存与资源释放相关
五、FFmpeg 常用结构体
1.封装/解封装相关结构体(libavformat)
- AVFormatContext:媒体容器上下文,负责读写媒体文件(如 MP4、MKV)
- AVInputFormat/AVOutputFormat:输入/输出格式,(解封装器/封装器)
- AVStream:媒体流(视频、音频、字幕等)
- AVPacket:编码后的数据包(压缩数据,如一帧 H264)
2.编解码相关结构体(libavcodec)
- AVCodecContext:编解码上下文,保存参数、状态等
- AVCodec:编码器或解码器对象(如 H.264、AAC)
- AVFrame:原始数据帧(未压缩图像/音频)
- AVPicture(旧):图像帧结构体,已被 AVFrame 替代
- AVCodecParameters:编解码参数(音频采样率、分辨率、码率等)
六、FFmpeg 内存模型
FFmpeg 的内存模型涉及到多个模块(编解码、封装、滤镜等)中内存的分配、使用、释放策略。掌握 FFmpeg 的内存模型,有助于写出高效稳定、不泄露内存的多媒体程序。
1.FFmpeg 内存模型核心思想
FFmpeg 的内存分配分为两类:
- 短期内存:用于存储临时数据,如 AVPacket、AVFrame,每处理一帧就释放
- 长期内存:如 AVFormatContext、AVCodecContext,在整个生命周期内使用,最后统一释放
2.内存生命周期关系图
打开文件↓
[AVFormatContext] ← 用于封装/解封装,生命周期较长↓[AVStream] ← 每个轨道对应一个 AVStream↓[AVCodecParameters] ← 编解码参数打开编解码器↓
[AVCodecContext] ← 主要负责一条轨道的解码↓
[AVFrame] / [AVPacket] ← 每处理一帧数据时临时分配和释放处理结束↓
手动释放所有结构体及其 buffer
3.常用结构体的内存管理方式
4.典型内存释放策略
// 分配
AVFormatContext *fmt_ctx = NULL;
avformat_open_input(&fmt_ctx, filename, NULL, NULL);AVCodecContext *codec_ctx = avcodec_alloc_context3(codec);
avcodec_open2(codec_ctx, codec, NULL);AVPacket *pkt = av_packet_alloc();
AVFrame *frame = av_frame_alloc();// 使用
while(av_read_frame(fmt_ctx, pkt) >= 0){// 解码avcodec_send_packet(codec_ctx, pkt);avcodec_receive_frame(codec_ctx, frame);// 使用完后释放这帧av_packet_unref(pkt);av_frame_unref(frame);
}// 释放资源
av_packet_free(&pkt);
av_frame_free(&frame);
avcodec_free_context(&codec_ctx);
avformat_close_input(&fmt_ctx);