一、libavcodec 是什么?
libavcodec 是 FFmpeg 框架中的编解码库。它提供了一个完整的、跨平台的、高性能的音视频编解码器实现集合。
- 编码:将未压缩的原始音视频数据(如 YUV 视频帧、PCM 音频样本)转换为压缩格式(如 H.264, AAC)的过程。
- 解码:将压缩的音视频数据还原为未压缩的原始数据的过程。
简单来说,libavcodec 就是 FFmpeg 的心脏,负责处理音视频最核心的压缩和解压缩工作。
二、核心特性
- 极其丰富的编解码器支持:支持几乎所有你能想到的主流和冷门音视频编解码器,包括 H.264/AVC, H.265/HEVC, AV1, VP9, MPEG-2, AAC, MP3, AC-3, FLAC 等。
- 跨平台:可在 Windows, Linux, macOS, Android, iOS 等众多平台上运行。
- 高性能优化:大量代码使用了汇编指令(如 MMX, SSE, AVX, NEON)进行优化,以最大限度地利用 CPU 性能。
- 纯软件实现:libavcodec 本身是软件编解码器,但它提供了与硬件编解码器(如 NVIDIA NVENC/CUVID, Intel Quick Sync Video, VA-API)的集成接口。
- 模块化设计:每个编解码器都是一个独立的模块,可以在运行时动态加载,使得库非常灵活。
三、核心数据结构
要理解 libavcodec,必须掌握其三个最重要的数据结构:AVCodecContext, AVPacket, AVFrame。它们之间的关系是理解编解码流程的关键。
1. AVCodecContext - 编解码器上下文
这是一个编解码过程的核心控制器。它包含了某个编解码会话所需的全部信息。
- 作用:像一个“编解码器实例的设置中心”。你打开一个编解码器时,就是在创建和配置这个结构。
- 包含的信息:
- 编解码器类型:是视频、音频还是字幕?
- 编解码器 ID:指定具体的编解码算法(如
AV_CODEC_ID_H264)。 - 宽高、像素格式:对于视频(如 1920x1080,
AV_PIX_FMT_YUV420P)。 - 采样率、声道数、采样格式:对于音频(如 44100 Hz, 2声道,
AV_SAMPLE_FMT_FLTP)。 - 码率、GOP 大小:用于编码的控制参数。
- 时间基:定义时间戳的单位,对音视频同步至关重要。
简单比喻:AVCodecContext就像是你为一次“烹饪”(编解码)准备的整个厨房和菜谱,规定了用什么厨具(编解码器)、做什么菜(格式)、放多少调料(参数)。
2. AVPacket - 压缩数据包
这是存储压缩后数据的容器。
- 作用:存放从流中读取出来的一帧(或几帧)压缩数据。对于视频,一个 Packet 通常包含一帧;对于音频,可能包含多个音频帧。
- 包含的信息:
data:指向压缩数据的指针。size:压缩数据的大小。- 时间戳信息:
pts:显示时间戳,指示这个数据包应该在什么时候被显示。dts:解码时间戳,指示这个数据包应该在什么时候被解码。(主要在有 B 帧的编码中存在差异)
stream_index:标识这个包属于哪个音视频流。flags:标志位,例如是否为关键帧(I帧)。
简单比喻:AVPacket就像一个快递包裹,里面是经过压缩的“货物”(音视频数据),包裹上贴着发货/收货时间(PTS/DTS)。
3. AVFrame - 原始数据帧
这是存储解码后原始数据的容器。
- 作用:存放解码后或待编码的原始数据。对于视频,它存储像素数据;对于音频,它存储采样数据。
- 包含的信息(视频):
data[]:指针数组,指向实际的图像平面数据(如 Y, U, V 分量)。linesize[]:数组,表示每个图像平面中一行的字节大小(用于处理内存对齐)。width,height:图像的宽高。format:像素格式(如AV_PIX_FMT_YUV420P)。pts:该帧的显示时间戳。
- 包含的信息(音频):
data[]:指针数组,指向每个声道的音频数据。nb_samples:此帧中包含的采样数。sample_rate:采样率。format:采样格式(如AV_SAMPLE_FMT_FLTP,即 32 位浮点型)。channel_layout:声道布局(如立体声、5.1 环绕声)。
简单比喻:AVFrame就像拆开快递后摆上桌的成品。视频帧是展开的画,音频帧是可以直接播放的声音波形。
四、核心流程详解
解码流程(Demuxer -> Decoder)
- 初始化:
avcodec_find_decoder():根据编解码器 ID(如从容器中读取)查找对应的解码器。avcodec_alloc_context3():为找到的解码器分配一个AVCodecContext。avcodec_open2():使用给定的参数打开解码器。
- 循环处理:
- 读取 Packet:从解复用器(Demuxer, libavformat)读取一个
AVPacket。 - 发送 Packet:
avcodec_send_packet(codec_ctx, packet)- 将压缩数据包送入解码器的输入队列。
- 可以一次发送多个 packet,再依次接收 frame。如果 packet 为 NULL,会刷新解码器内部缓冲(在流结束时非常重要)。
- 接收 Frame:
avcodec_receive_frame(codec_ctx, frame)- 从解码器的输出队列中尝试获取一个解码完成的
AVFrame。 - 返回值
AVERROR(EAGAIN)表示需要发送更多 packet 才能产出 frame。 - 返回值
AVERROR_EOF表示解码器已被刷新,没有更多 frame 了。
- 从解码器的输出队列中尝试获取一个解码完成的
- 读取 Packet:从解复用器(Demuxer, libavformat)读取一个
- 清理:
avcodec_free_context(&codec_ctx):释放上下文。
关键点:send/receive模型是异步的,并且 send和 receive的调用次数不是 1:1 的关系。发送一个 packet 可能收到 0 个、1 个或多个 frames(例如,在解码延迟处理的 B 帧时)。
编码流程(Encoder -> Muxer)
- 初始化:
avcodec_find_encoder():根据编解码器 ID 查找编码器。avcodec_alloc_context3():分配编码器上下文。- 设置参数:手动设置
width,height,pix_fmt,bit_rate等,或从源AVFrame拷贝。 avcodec_open2():打开编码器。
- 循环处理:
- 发送 Frame:
avcodec_send_frame(codec_ctx, frame)- 将未压缩的原始数据帧送入编码器的输入队列。
- frame 为 NULL 时,会刷新编码器,获取所有缓存的编码数据。
- 接收 Packet:
avcodec_receive_packet(codec_ctx, packet)- 从编码器输出队列中尝试获取一个编码完成的
AVPacket。
- 从编码器输出队列中尝试获取一个编码完成的
- 发送 Frame:
- 清理:
- 将刷新编码器后得到的所有 packet 写入文件。
avcodec_free_context(&codec_ctx)。
关键点:同样是异步模型。发送一个 frame 可能收到 0 个或多个 packets(由于编码器内部的帧重排序和延迟)。
五、重要概念与高级特性
1. 硬件加速
libavcodec 通过“硬件上下文”来支持硬件编解码。
- hwaccel:硬件解码加速。在解码 H.264 等格式时,可以使用 GPU(如 NVIDIA NVDEC, Intel Quick Sync)来解码,显著降低 CPU 负载。流程是:创建硬件设备上下文 -> 在
AVCodecContext中指定硬件像素格式 -> libavcodec 会自动将数据输出到 GPU 内存。 - hwncode:硬件编码加速。类似地,可以使用 GPU(如 NVIDIA NVENC, Intel Quick Sync)进行编码,速度极快,但压缩效率可能略低于高质量的软件编码器。
2. 线程模型
为了提高多核 CPU 的利用率,libavcodec 支持多线程编解码。
- 帧级多线程:将一帧图像分割成多个切片,由多个线程并行处理。通过设置
AVCodecContext.thread_count和thread_type来开启。 - 片级多线程:更细粒度的并行化。
3. 码率控制
对于编码,libavcodec 提供了多种码率控制算法,通过 AVCodecContext的参数设置:
CBR:固定码率,质量波动,但输出稳定。VBR:可变码率,在简单场景用低码率,复杂场景用高码率,以保持相对恒定的质量。CRF:恒定速率因子,一种最常用的 VBR 模式,通过指定一个质量因子(如 23)来全局控制质量,非常推荐使用。
六、使用 libavcodec 的最佳实践
- 始终检查返回值:FFmpeg 函数有丰富的错误码,如
EAGAIN,EOF,正确处理它们是稳定运行的基础。 - 理解并正确处理时间戳:正确设置
AVCodecContext的time_base和pkt_timebase,并正确转换AVPacket和AVFrame的 PTS/DTS,是避免音视频不同步的关键。 - 记得刷新:在流结束时,向编解码器发送
NULL来刷新其内部缓冲区,否则可能会丢失最后几帧数据。 - 管理内存:使用
av_packet_unref()和av_frame_unref()来正确释放AVPacket和AVFrame内部的资源,防止内存泄漏。
总结
|
概念 |
作用 |
数据流向 |
|---|---|---|
|
|
编解码会话的“大脑”和“控制中心” |
- |
|
|
压缩后数据的容器 |
解复用器 -> 解码器 |
|
|
原始数据的容器 |
解码器 -> 渲染/滤镜 |
libavcodec 通过其强大而精细的 API,为开发者提供了对音视频编解码底层过程的完全控制能力。理解其核心数据结构和异步处理模型,是掌握 FFmpeg 并进行高效多媒体应用开发的基石。
6236

被折叠的 条评论
为什么被折叠?



