郑州建设信息网站环球网最新国际新闻
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);
}
常见问题与注意事项
-
忘记调用
avcodec_register_all():
如果未注册编解码器,avcodec_find_decoder()和avcodec_find_decoder_by_name()将始终返回NULL。
解决方案:确保在程序入口处调用avcodec_register_all()。 -
编码 ID 与名称的映射:
• 编码 ID 是整数常量(如AV_CODEC_ID_H264),定义在libavcodec/avcodec.h中。
• 名称是字符串(如"h264"),需与编解码器实现中的名称严格匹配。 -
动态加载编解码器:
某些场景(如插件系统)可能需要动态加载编解码器库,此时需结合dlopen()和avcodec_register,strlen)手动注册。 -
错误处理:
• 检查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_fmt | enum AVPixelFormat | 像素格式(如 AV_PIX_FMT_YUV420P) |
width / height | int | 视频分辨率 |
sample_rate | int | 音频采样率 |
bit_rate | int | 编码/解码比特率 |
flags | int | 编解码器标志(如低延迟模式) |
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()
作用:
初始化编解码器上下文,绑定到具体编解码器并应用配置参数。
参数:
• ctx:AVCodecContext* 指针。
• 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_id | enum AVCodecID | 视频编解码器 ID(如 AV_CODEC_ID_H264) |
width / height | int | 视频分辨率(像素) |
pix_fmt | enum AVPixelFormat | 像素格式(如 AV_PIX_FMT_YUV420P) |
frame_rate | AVRational | 帧率(分数形式,如 30/1) |
bit_rate | int | 编码/解码比特率(kbps) |
bits_per_raw_sample | int | 每个原始样本的位数(如 8、10、12) |
profile | char* | 编码器配置文件(如 H.264 的 “main” 或 “high”) |
level | int | 编码器级别(如 H.264 的 4.2) |
音频参数
| 字段名 | 类型 | 说明 |
|---|---|---|
codec_id | enum AVCodecID | 音频编解码器 ID(如 AV_CODEC_ID_AAC) |
sample_rate | int | 音频采样率(Hz,如 44100) |
channels | int | 音频通道数(如 2、5.1) |
channel_layout | uint64_t | 音频布局(如 AV_CH_LAYOUT_STEREO) |
bit_rate | int | 编码/解码比特率(kbps) |
sample_fmt | enum AVSampleFormat | 音频样本格式(如 AV_SAMPLE_FMT_FLTP) |
通用参数
| 字段名 | 类型 | 说明 |
|---|---|---|
codec_type | enum AVMediaType | 媒体类型(视频 AVMEDIA_TYPE_VIDEO,音频 AVMEDIA_TYPE_AUDIO) |
extradata | uint8_t* | 额外数据(如 H.264 的 SPS/PPS 数据) |
extradata_size | int | extradata 的长度 |
2. 与类似结构的对比
| 结构体 | 用途 | 关键区别 |
|---|---|---|
AVCodecContext | 存储编解码器的运行时状态(如缓冲区、线程池) | 包含硬件加速信息、内部状态机 |
AVCodecParameters | 存储编解码器的配置参数(静态信息) | 不包含执行状态,仅用于配置同步 |
AVStream | 表示容器中的一个媒体流(如视频流、音频流) | 包含 AVCodecParameters 和其他流元数据 |
3.avcodec_parameters_to_context()
将 AVCodecParameters 结构体中的编解码器参数配置同步到 AVCodecContext 上下文对象中。
核心用途:
- 动态参数更新:在编解码器上下文已初始化后,从外部(如容器元数据、用户配置)动态更新参数(如分辨率、帧率、比特率)。
- 参数迁移:将解码器参数迁移到编码器(需手动映射关键字段)。 避免重复分配:无需重新分配上下文即可修改参数,提升性能。
AVFrame
一、AVFrame 的核心字段
1. 基础信息字段
| 字段 | 类型/说明 | 示例值 |
|---|---|---|
codec_ctx | AVCodecContext* 编解码器上下文,包含编码/解码参数(如采样率、通道数等)。 | 解码时从 AVPacket 中获取 |
format | enum AVPixelFormat 或 enum AVSampleFormat,表示像素格式(视频)或采样格式(音频)。 | AV_PIX_FMT_YUV420P、AV_SAMPLE_FMT_PCM_S16LE |
width/height | 视频帧的宽度和高度(仅视频有效)。音频帧则用 sample_rate 和 nb_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) |
metadata | AVDictionary*,存储帧的元数据(如 EXIF 信息)。 | 可包含相机型号、GPS 等信息 |
2. 数据缓冲区字段
| 字段 | 类型/说明 | 示例行为 |
|---|---|---|
data | uint8_t* data[AV_NUM_DATA_POINTERS],指向实际数据缓冲区的指针数组。 | 视频帧:data[0] 是 Y 分量,data[1] 是 U 分量等。 |
linesize | int linesize[AV_NUM_DATA_POINTERS],每行的字节长度(仅视频有效)。 | YUV420P 中 linesize[0] = width * 2 |
nb_planes | 数据平面的数量(如 YUV420P 有 3 个平面)。 | 3 |
3. 内存管理字段
| 字段 | 类型/说明 | 作用 |
|---|---|---|
data_ptr | AVBufferRef* 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 分配数据缓冲区(根据 format、width、height 等参数)。 | 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() | 手动设置 data 和 linesize(需自行管理内存)。 | 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); |
三、注意事项
-
内存安全:
• 引用计数:AVFrame的data缓冲区通过引用计数管理,避免手动free。
• 正确释放:使用av_frame_unref()而非free(frame)。 -
数据对齐:
• 视频帧的linesize可能大于width(如 YUV420P 中linesize = width * 2),访问数据时需按linesize对齐。 -
类型匹配:
• 音频帧的data指向int16_t或float数组,视频帧的data指向uint8_t数组,需根据sample_fmt或pix_fmt正确解码。 -
多线程:
•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()。
• 内存安全:确保 AVPacket 和 AVFrame 的引用计数正确释放(av_packet_unref() 和 av_frame_unref())。
• 错误处理:重点处理 AVERROR(EAGAIN) 和负值错误,避免程序崩溃或数据丢失。
通过这两个函数的配合,FFmpeg 实现了高效的编解码流水线,是处理音视频数据的核心机制!
