FFmpeg AVFormatContext 分配函数详解
一、avformat_alloc_context() - 基础格式上下文分配
函数原型
c
AVFormatContext *avformat_alloc_context(void);
参数
- 无参数:这是一个无参数的简单分配函数
返回值
- 成功:返回新分配的
AVFormatContext结构体指针 - 失败:返回
NULL
作用与解释
1. 核心功能
c
// 创建一个空的AVFormatContext
AVFormatContext *fmt_ctx = avformat_alloc_context();
if (!fmt_ctx) {
// 内存分配失败处理
return;
}
avformat_alloc_context() 用于创建一个全新的、空的 AVFormatContext 结构体。它主要做以下几件事:
2. 分配和初始化操作
-
内存分配 :为
AVFormatContext结构体及其内部字段分配内存 -
默认初始化 :
cfmt_ctx->nb_streams = 0; // 流数量初始化为0 fmt_ctx->streams = NULL; // 流数组初始化为NULL fmt_ctx->oformat = NULL; // 输出格式初始化为NULL fmt_ctx->iformat = NULL; // 输入格式初始化为NULL fmt_ctx->pb = NULL; // I/O上下文初始化为NULL fmt_ctx->start_time = AV_NOPTS_VALUE; // 开始时间设为无效值 fmt_ctx->duration = AV_NOPTS_VALUE; // 时长设为无效值 fmt_ctx->bit_rate = 0; // 比特率初始化为0 -
设置默认值 :
- 音频预加载:
av_opt_set_defaults(fmt_ctx) - 一些内部标志位的初始化
- 音频预加载:
3. 使用场景
主要用于解复用(读取)场景:
c
// 典型的使用模式
AVFormatContext *fmt_ctx = avformat_alloc_context();
if (!fmt_ctx) return;
// 后续需要手动设置输入格式
fmt_ctx->iformat = av_find_input_format("mp4");
// 或者通过avformat_open_input自动检测
if (avformat_open_input(&fmt_ctx, "input.mp4", NULL, NULL) < 0) {
// 错误处理
}
输出场景也可以使用,但不够方便:
c
AVFormatContext *fmt_ctx = avformat_alloc_context();
if (!fmt_ctx) return;
// 需要手动查找并设置输出格式
AVOutputFormat *ofmt = av_guess_format("mp4", NULL, NULL);
if (!ofmt) {
// 处理错误
}
fmt_ctx->oformat = ofmt;
4. 内存管理
- 使用
avformat_free_context()释放 - 注意:如果已经调用了
avformat_open_input(),应使用avformat_close_input()释放
5. 优点与局限
优点:
- 简单直接,不需要预先知道格式信息
- 适合需要完全手动控制格式设置的场景
局限:
- 需要额外的步骤来设置输入/输出格式
- 不适合快速创建输出上下文
二、avformat_alloc_output_context2() - 智能输出上下文分配
函数原型
c
int avformat_alloc_output_context2(AVFormatContext **ctx,
AVOutputFormat *oformat,
const char *format_name,
const char *filename);
参数详解
1. AVFormatContext **ctx
- 类型:双重指针
- 作用 :接收新创建的
AVFormatContext地址 - 注意 :
- 如果
*ctx不为 NULL,函数会尝试重用现有的上下文 - 通常传入
NULL指针的地址:&fmt_ctx
- 如果
2. AVOutputFormat *oformat
-
类型:输出格式指针
-
作用:指定输出格式
-
使用方式 :
c// 方式1:明确指定格式 AVOutputFormat *ofmt = av_guess_format("mp4", NULL, NULL); avformat_alloc_output_context2(&fmt_ctx, ofmt, NULL, NULL); // 方式2:传NULL,让函数自动检测 avformat_alloc_output_context2(&fmt_ctx, NULL, NULL, "output.mp4");
3. const char *format_name
-
类型:格式名称字符串
-
作用:通过名称指定格式(如 "mp4", "flv", "avi")
-
使用方式 :
c// 明确指定格式名称 avformat_alloc_output_context2(&fmt_ctx, NULL, "flv", NULL);
4. const char *filename
-
类型:文件名或路径
-
作用 :
- 通过文件扩展名推断格式
- 设置默认文件名
-
使用方式 :
c// 通过文件名推断格式 avformat_alloc_output_context2(&fmt_ctx, NULL, NULL, "output.mp4"); // 会自动推断为MP4格式
参数优先级和检测顺序
c
// 参数优先级:oformat > format_name > filename
int result = avformat_alloc_output_context2(&fmt_ctx,
oformat, // 第一优先:直接指定格式
format_name, // 第二优先:格式名称
filename // 第三优先:通过文件扩展名推断
);
作用与解释
1. 核心功能
c
AVFormatContext *fmt_ctx = NULL;
int ret = avformat_alloc_output_context2(&fmt_ctx, NULL, NULL, "output.mp4");
if (ret < 0 || !fmt_ctx) {
// 错误处理
}
avformat_alloc_output_context2() 是专门为**输出(复用/编码)**场景设计的智能分配函数:
2. 智能格式检测
函数按以下顺序检测输出格式:
- oformat 参数(如果非NULL):直接使用指定的输出格式
- format_name 参数(如果非NULL):按名称查找格式
- filename 参数(如果非NULL):按文件扩展名推断格式
c
// 示例:不同参数组合的使用方式
// 1. 通过文件名推断格式(最常用)
avformat_alloc_output_context2(&fmt_ctx, NULL, NULL, "video.mp4");
// 2. 明确指定格式名称
avformat_alloc_output_context2(&fmt_ctx, NULL, "flv", "output"); // 创建FLV格式
// 3. 直接使用已有的AVOutputFormat
AVOutputFormat *mp4_format = av_guess_format(NULL, "video.mp4", NULL);
avformat_alloc_output_context2(&fmt_ctx, mp4_format, NULL, NULL);
3. 内部执行流程
c
int avformat_alloc_output_context2(AVFormatContext **ctx, ...)
{
// 1. 查找输出格式
AVOutputFormat *fmt = find_output_format(oformat, format_name, filename);
// 2. 分配AVFormatContext(或重用传入的)
if (!*ctx) {
*ctx = avformat_alloc_context();
}
// 3. 设置输出格式
(*ctx)->oformat = fmt;
// 4. 初始化格式相关参数
if (fmt->priv_data_size > 0) {
// 分配私有数据
}
// 5. 设置默认选项
av_opt_set_defaults(*ctx);
return 0;
}
4. 额外初始化工作
除了分配内存,该函数还会:
- 设置输出格式 :
ctx->oformat = detected_format - 分配私有数据:如果格式需要私有数据(如MP4的mov上下文)
- 初始化默认选项 :调用
av_opt_set_defaults() - 设置I/O缓冲区:初始化默认的I/O缓冲区设置
5. 使用场景
主要用于复用(写入)场景:
c
// 完整的使用示例
AVFormatContext *fmt_ctx = NULL;
// 创建输出上下文(智能检测格式)
int ret = avformat_alloc_output_context2(&fmt_ctx, NULL, NULL, "output.mkv");
if (ret < 0) {
fprintf(stderr, "无法创建输出上下文\n");
return ret;
}
// 添加音视频流
// ...
// 打开输出文件
if (!(fmt_ctx->oformat->flags & AVFMT_NOFILE)) {
ret = avio_open(&fmt_ctx->pb, "output.mkv", AVIO_FLAG_WRITE);
if (ret < 0) {
fprintf(stderr, "无法打开输出文件\n");
return ret;
}
}
// 写入文件头
ret = avformat_write_header(fmt_ctx, NULL);
// ...
6. 错误处理
c
AVFormatContext *fmt_ctx = NULL;
int ret = avformat_alloc_output_context2(&fmt_ctx, NULL, NULL, filename);
if (ret < 0) {
// 常见错误:
// AVERROR(ENOMEM): 内存不足
// AVERROR(EINVAL): 无效参数
// AVERROR(ENOENT): 无法识别格式
fprintf(stderr, "创建输出上下文失败: %s\n", av_err2str(ret));
// 如果无法识别格式,可以尝试指定默认格式
ret = avformat_alloc_output_context2(&fmt_ctx, NULL, "flv", filename);
if (ret < 0) {
// 仍然失败
return ret;
}
}
三、两个函数的对比
| 特性 | avformat_alloc_context() |
avformat_alloc_output_context2() |
|---|---|---|
| 主要用途 | 通用分配,主要用于输入 | 专门用于输出 |
| 格式设置 | 需要手动设置 | 自动检测并设置 |
| 参数 | 无参数 | 多个参数支持智能检测 |
| 初始化程度 | 基础初始化 | 完整初始化(包括格式相关) |
| 使用便利性 | 较低,需要额外步骤 | 较高,一站式初始化 |
| 典型场景 | 解复用(读取文件) | 复用(写入文件) |
| 错误处理 | 返回NULL表示失败 | 返回负数错误码 |
四、实际应用建议
1. 读取文件时
c
// 推荐:直接使用avformat_open_input,它内部会调用avformat_alloc_context
AVFormatContext *fmt_ctx = NULL;
if (avformat_open_input(&fmt_ctx, "input.mp4", NULL, NULL) < 0) {
// 错误处理
}
2. 写入文件时
c
// 推荐:使用avformat_alloc_output_context2
AVFormatContext *fmt_ctx = NULL;
// 方式1:通过文件扩展名智能检测(最常用)
if (avformat_alloc_output_context2(&fmt_ctx, NULL, NULL, "output.mp4") < 0) {
// 如果失败,尝试指定格式
if (avformat_alloc_output_context2(&fmt_ctx, NULL, "flv", "output.flv") < 0) {
fprintf(stderr, "无法创建输出上下文\n");
return -1;
}
}
// 方式2:明确指定格式名称
const char *format_name = "mp4";
const char *filename = "video.mp4";
avformat_alloc_output_context2(&fmt_ctx, NULL, format_name, filename);
3. 需要完全控制时
c
// 如果需要特殊处理,可以先分配再手动设置
AVFormatContext *fmt_ctx = avformat_alloc_context();
if (!fmt_ctx) {
return AVERROR(ENOMEM);
}
// 手动设置各种参数
fmt_ctx->oformat = av_guess_format("mp4", NULL, NULL);
fmt_ctx->pb = ...; // 自定义I/O
// ... 其他设置
五、总结
-
avformat_alloc_context():是基础的分配函数,创建一个"空白"的格式上下文,需要后续手动配置。主要用于需要精细控制的场景。 -
avformat_alloc_output_context2():是高级的智能分配函数,专门为输出场景设计,可以自动检测和设置输出格式,简化了输出文件的创建流程。
最佳实践:
- 读取文件:直接使用
avformat_open_input() - 写入文件:使用
avformat_alloc_output_context2()+ 文件扩展名 - 特殊需求:使用
avformat_alloc_context()进行手动配置