当前位置: 首页 > news >正文

ffmepg介绍(二)——解码

AVCodec

1. avcodec_register_all()

作用
全局注册所有内置的音频/视频编解码器(如 H.264、AAC、MP3 等),使得后续通过编码 ID 或名称查找解码器时能直接匹配到已注册的编解码器。在最新版本的 FFmpeg(如 ​FFmpeg 4.0 及以上)中,avcodec_register_all() ​已被标记为弃用(deprecated)

关键点
必须先调用:在调用任何编解码器查找函数(如 avcodec_find_decoder())前,必须确保已调用此函数注册所有编解码器。
自动注册:FFmpeg 默认会在程序启动时自动注册编解码器,但在某些动态加载场景(如插件化支持)中可能需要手动调用。

示例

#include <libavcodec/avcodec.h>

int main() {
    avcodec_register_all(); // 注册所有编解码器
    // 后续代码...
    return 0;
}

2. avcodec_find_decoder()

作用
根据编码 ID 查找对应的解码器结构体 AVCodec

参数
codec_id:目标编解码器的唯一标识符(如 AV_CODEC_ID_H264)。
• 返回值:找到的解码器指针 AVCodec*,若未找到则返回 NULL

使用场景
已知编码 ID 时(例如从媒体文件头中解析出的编码类型)。

示例

AVCodec *decoder = avcodec_find_decoder(AV_CODEC_ID_H264);
if (!decoder) {
    fprintf(stderr, "H.264 decoder not found\n");
    exit(1);
}

3. avcodec_find_decoder_by_name()

作用
根据编解码器名称(字符串)查找对应的解码器结构体 AVCodec

参数
name:编解码器名称(如 "h264""aac",区分大小写)。
• 返回值:找到的解码器指针 AVCodec*,若未找到则返回 NULL

使用场景
需要通过用户输入或配置文件动态指定编解码器名称时。

示例

AVCodec *decoder = avcodec_find_decoder_by_name("h264");
if (!decoder) {
    fprintf(stderr, "Decoder 'h264' not found\n");
    exit(1);
}

常见问题与注意事项

  1. 忘记调用 avcodec_register_all()
    如果未注册编解码器,avcodec_find_decoder()avcodec_find_decoder_by_name() 将始终返回 NULL
    解决方案:确保在程序入口处调用 avcodec_register_all()

  2. 编码 ID 与名称的映射
    • 编码 ID 是整数常量(如 AV_CODEC_ID_H264),定义在 libavcodec/avcodec.h 中。
    • 名称是字符串(如 "h264"),需与编解码器实现中的名称严格匹配。

  3. 动态加载编解码器
    某些场景(如插件系统)可能需要动态加载编解码器库,此时需结合 dlopen()avcodec_register,strlen) 手动注册。

  4. 错误处理
    • 检查 avcodec_findDecoder() 的返回值是否为 NULL
    • 检查 avcodec_open2() 的返回值是否小于 0。


附录:编码 ID 与名称的对应关系

编码 ID名称常见用途
AV_CODEC_ID_H264“h264”视频编码
AV_CODEC_ID_AAC“aac”音频编码
AV_CODEC_ID_MP3“mp3”音频编码
AV_CODEC_ID_MPEG4“mpeg4”视频/音频编码
AV_CODEC_ID_H265“hevc”视频编码(H.265)

AVCodecContext

关键字段

字段名类型说明
pix_fmtenum AVPixelFormat像素格式(如 AV_PIX_FMT_YUV420P
width / heightint视频分辨率
sample_rateint音频采样率
bit_rateint编码/解码比特率
flagsint编解码器标志(如低延迟模式)

1. avcodec_alloc_context3()

作用
分配并初始化 AVCodecContext 结构体,存储编解码器配置参数(如分辨率、帧率、编码格式等)。

参数
codec:已找到的编解码器指针 AVCodec*
parent:父上下文(可选,通常为 NULL)。
flags:标志位(如 AV_CODEC_FLAG_LOW_LATENCY,主要用于音频)。

返回值
成功返回 AVCodecContext* 指针,失败返回 NULL

示例

AVCodec *decoder = avcodec_find_decoder(AV_CODEC_ID_H264);
AVCodecContext *ctx = avcodec_alloc_context3(decoder);
if (!ctx) {
    fprintf(stderr, "Failed to allocate decoder context\n");
    exit(1);
}

2. avcodec_free_context()

作用
释放 AVCodecContext 内存,防止泄漏。

参数
pp_ctx:指向 AVCodecContext* 的指针的指针(用于直接修改原指针)。

示例

avcodec_free_context(&ctx);
ctx = NULL; // 防止悬空指针

3. avcodec_open2()

作用
初始化编解码器上下文,绑定到具体编解码器并应用配置参数。

参数
ctxAVCodecContext* 指针。
codec:编解码器结构体 AVCodec*
options:编解码器参数表(如分辨率、帧率等),类型为 const AVOption* const[]

返回值
成功返回 0,失败返回负错误码。

示例

ctx->pix_fmt = AV_PIX_FMT_YUV420P;
ctx->width = 1280;
ctx->height = 720;

int ret = avcodec_open2(ctx, decoder, NULL);
if (ret < 0) {
    fprintf(stderr, "Failed to open decoder: %s\n", av_err2str(ret));
    avcodec_free_context(&ctx);
    exit(1);
}

AVCodecParameters

AVCodecParameters 是 FFmpeg 中用于存储编解码器参数的核心结构体,存储了流的编解码器参数。,包括视频、音频的分辨率、帧率、编码格式、比特率等关键参数。 它的主要目的是在解复用(Demuxing)时提取流的编解码信息,而不需要初始化完整的编解码器上下文(AVCodecContext)。


1. 结构体字段

AVCodecParameters 包含以下核心字段(视频、音频、通用参数):

视频参数

字段名类型说明
codec_idenum AVCodecID视频编解码器 ID(如 AV_CODEC_ID_H264
width / heightint视频分辨率(像素)
pix_fmtenum AVPixelFormat像素格式(如 AV_PIX_FMT_YUV420P
frame_rateAVRational帧率(分数形式,如 30/1)
bit_rateint编码/解码比特率(kbps)
bits_per_raw_sampleint每个原始样本的位数(如 8、10、12)
profilechar*编码器配置文件(如 H.264 的 “main” 或 “high”)
levelint编码器级别(如 H.264 的 4.2)

音频参数

字段名类型说明
codec_idenum AVCodecID音频编解码器 ID(如 AV_CODEC_ID_AAC
sample_rateint音频采样率(Hz,如 44100)
channelsint音频通道数(如 2、5.1)
channel_layoutuint64_t音频布局(如 AV_CH_LAYOUT_STEREO
bit_rateint编码/解码比特率(kbps)
sample_fmtenum AVSampleFormat音频样本格式(如 AV_SAMPLE_FMT_FLTP

通用参数

字段名类型说明
codec_typeenum AVMediaType媒体类型(视频 AVMEDIA_TYPE_VIDEO,音频 AVMEDIA_TYPE_AUDIO
extradatauint8_t*额外数据(如 H.264 的 SPS/PPS 数据)
extradata_sizeintextradata 的长度

2. 与类似结构的对比

结构体用途关键区别
AVCodecContext存储编解码器的运行时状态(如缓冲区、线程池)包含硬件加速信息、内部状态机
AVCodecParameters存储编解码器的配置参数(静态信息)不包含执行状态,仅用于配置同步
AVStream表示容器中的一个媒体流(如视频流、音频流)包含 AVCodecParameters 和其他流元数据

3.avcodec_parameters_to_context()

将 AVCodecParameters 结构体中的编解码器参数配置同步到 AVCodecContext 上下文对象中。
核心用途:

  • ​动态参数更新:在编解码器上下文已初始化后,从外部(如容器元数据、用户配置)动态更新参数(如分辨率、帧率、比特率)。
  • ​参数迁移:将解码器参数迁移到编码器(需手动映射关键字段)。 ​避免重复分配:无需重新分配上下文即可修改参数,提升性能。

AVFrame


一、AVFrame 的核心字段

1. 基础信息字段

字段类型/说明示例值
codec_ctxAVCodecContext* 编解码器上下文,包含编码/解码参数(如采样率、通道数等)。解码时从 AVPacket 中获取
formatenum AVPixelFormatenum AVSampleFormat,表示像素格式(视频)或采样格式(音频)。AV_PIX_FMT_YUV420PAV_SAMPLE_FMT_PCM_S16LE
width/height视频帧的宽度和高度(仅视频有效)。音频帧则用 sample_ratenb_samples 表示。1920、1080
sample_rate音频帧的采样率(Hz),如 44100 Hz。视频帧无此字段。44100
nb_samples音频帧的采样点数量。视频帧无此字段。1024
channel_layout声道布局(音频),如 AV_CH_LAYOUT_STEREO。视频帧无此字段。0x3(立体声)
pts呈现时间戳(Presentation Timestamp),表示帧在时间轴上的位置。视频帧:90000(假设时间基为 1/1000 ms)
tbn时间基准(Time Base),单位为 1/pts,用于计算时间差。视频帧:AV_RB(1000000, 30)(30 fps)
metadataAVDictionary*,存储帧的元数据(如 EXIF 信息)。可包含相机型号、GPS 等信息

2. 数据缓冲区字段

字段类型/说明示例行为
datauint8_t* data[AV_NUM_DATA_POINTERS],指向实际数据缓冲区的指针数组。视频帧:data[0] 是 Y 分量,data[1] 是 U 分量等。
linesizeint linesize[AV_NUM_DATA_POINTERS],每行的字节长度(仅视频有效)。YUV420P 中 linesize[0] = width * 2
nb_planes数据平面的数量(如 YUV420P 有 3 个平面)。3

3. 内存管理字段

字段类型/说明作用
data_ptrAVBufferRef* data_ptr[AV_NUM_DATA_POINTERS],指向数据缓冲区的引用计数指针。管理缓冲区的共享和释放
ref_count引用计数(内部使用),用于避免手动释放内存。ref_count 为 0 时自动释放

二、AVFrame 的核心函数

1. 分配与初始化

函数作用示例代码
av_frame_alloc()分配一个新的 AVFrame 结构体,初始化为零。AVFrame* frame = av_frame_alloc();
av_frame_get_buffer()AVFrame 分配数据缓冲区(根据 formatwidthheight 等参数)。if (av_frame_get_buffer(frame, 0) < 0) { ... }
av_frame_unref()减少 AVFrame 的引用计数,当计数为零时释放内存。av_frame_unref(frame);

2. 数据操作

函数作用示例代码
av_frame_copy()复制 src 的数据到 dst,包括格式、尺寸、缓冲区等。av_frame_copy(dst, src);
av_frame_move()移动 src 的数据到 dst(浅拷贝,接管内存)。av_frame_move(dst, src);
avcodec_decode_audio4()解码音频帧到 AVFrame(FFmpeg 内部函数,用户通常通过 AVCodecContext 调用)。avcodec_decode_audio4(..., (int16_t*)frame->data, ...);

3. 时间戳与元数据

函数作用示例代码
av_rescale_q()时间戳转换(将 a 从时间基 tb_a 转换为时间基 tb_b)。frame->pts = av_rescale_q(frame->pts, in_tb, out_tb);
av_dict_set()metadata 添加键值对。av_dict_set(frame->metadata, "key", "value", 0);

4. 自定义缓冲区管理

函数作用示例代码
av_frame_set_data()手动设置 datalinesize(需自行管理内存)。av_frame_set_data(frame, gpu_data, gpu_linesize);
av_frame_set_sample_fmt()设置采样格式(音频)或像素格式(视频)。av_frame_set_sample_fmt(frame, AV_SAMPLE_FMT_PCM_S16LE);

三、注意事项

  1. 内存安全
    引用计数AVFramedata 缓冲区通过引用计数管理,避免手动 free
    正确释放:使用 av_frame_unref() 而非 free(frame)

  2. 数据对齐
    • 视频帧的 linesize 可能大于 width(如 YUV420P 中 linesize = width * 2),访问数据时需按 linesize 对齐。

  3. 类型匹配
    • 音频帧的 data 指向 int16_tfloat 数组,视频帧的 data 指向 uint8_t 数组,需根据 sample_fmtpix_fmt 正确解码。

  4. 多线程
    AVFrame 不是线程安全的,跨线程操作时需加锁。


avcodec_send_packet() 和 avcodec_receive_frame()


1. 核心功能与设计目的

函数核心功能设计目的
avcodec_send_packet()向解码器发送压缩数据包(如 H.264、AAC)。触发解码器处理数据,生成解码后的帧(AVFrame)。
avcodec_receive_frame()从解码器接收解码后的原始帧(如 RGB、PCM)。获取解码结果,进行后续处理(如渲染、存储或转码)。

2. 函数原型与参数

avcodec_send_packet()
int avcodec_send_packet(AVCodecContext *ctx, const AVPacket *pkt);

参数
ctx:解码器上下文(AVCodecContext*)。
pkt:指向压缩数据包的指针(AVPacket*)。
返回值
≥0:成功处理的字节数(可能为 0,表示数据包未被完全消耗)。
AVERROR(EAGAIN):需要更多数据包才能完成解码。
• 负值:其他错误(如内存不足、格式不支持)。

avcodec_receive_frame()
int avcodec_receive_frame(AVCodecContext *ctx, AVFrame *frame);

参数
ctx:解码器上下文(AVCodecContext*)。
frame:用于存储解码后帧的指针(AVFrame*)。
返回值
≥0:成功接收的帧大小(通常无意义,仅表示成功)。
AVERROR(EAGAIN):无可用帧,需继续发送数据包。
• 负值:解码失败(如数据损坏、参数错误)。


3. 总结

函数用途场景关键行为常见错误处理
avcodec_send_packet()解码(发送压缩数据)、编码(发送元数据)推动编解码器处理数据AVERROR(EAGAIN)(需要更多数据)
avcodec_receive_frame()解码(接收帧)、编码(无)拉取解码后的原始帧AVERROR(EAGAIN)(无可用帧)

最佳实践
解码:始终循环调用 avcodec_send_packet()avcodec_receive_frame()
内存安全:确保 AVPacketAVFrame 的引用计数正确释放(av_packet_unref()av_frame_unref())。
错误处理:重点处理 AVERROR(EAGAIN) 和负值错误,避免程序崩溃或数据丢失。

通过这两个函数的配合,FFmpeg 实现了高效的编解码流水线,是处理音视频数据的核心机制!

相关文章:

  • FlauBERT:面向法语的无监督语言模型预训练
  • 《人工智能赋能网络拓扑分析:洞察关键节点与脆弱链路》
  • 好吧好吧,看一下达梦的模式与用户的关系
  • 数据库数值函数详解
  • 二分查找------查找区间
  • 进程间通信 ─── linux第22课
  • STM32 的tf卡驱动
  • DAY37 动态归化Ⅰ基础题目
  • 深入LangChain:LLM交互机制与RAG集成的技术
  • 三主热备架构
  • 原生微信小程序基础语法--快速总结
  • 架构师面试(二十):CAP 定理
  • 自定义mavlink 生成wireshark wlua插件错误(已解决)
  • 【拒绝算法PUA】LeetCode 2116. 判断一个括号字符串是否有效
  • VLM理解(一)——视觉文本信息的标注与数据集制作过程
  • 第十一章 | 智能合约主网部署与验证详解
  • 6、linux c 线程 -下
  • 同旺科技USB to I2C 适配器 ---- 多从机设备混合调试
  • 关于解决Ubuntu终端及系统字体大小的问题
  • Java 24 学习
  • 马上评丨市长信箱“已读乱回”,群众在意的是什么
  • 中公教育薪酬透视:董监高合计涨薪122万,员工精简近三成
  • 江西省国资委原副主任李键主动向组织交代问题,接受审查调查
  • 全过程人民民主研究基地揭牌,为推动我国民主政治建设贡献上海智慧
  • 美媒:受关税政策影响,美国电商平台近千种商品平均涨价29%
  • 原创话剧风向标!这个展演上《大宅门》《白鹿原》先后上演