FFmpeg AVFormatContext 分配函数详解

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 结构体及其内部字段分配内存

  • 默认初始化

    c 复制代码
    fmt_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
  • 类型:文件名或路径

  • 作用

    1. 通过文件扩展名推断格式
    2. 设置默认文件名
  • 使用方式

    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. 智能格式检测

函数按以下顺序检测输出格式:

  1. oformat 参数(如果非NULL):直接使用指定的输出格式
  2. format_name 参数(如果非NULL):按名称查找格式
  3. 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() 进行手动配置
相关推荐
CoderYanger2 小时前
C.滑动窗口-求子数组个数-越短越合法——LCP 68. 美观的花束
java·开发语言·数据结构·算法·leetcode
hweiyu002 小时前
数据结构:树状数组
数据结构
EXtreme352 小时前
【C语言/数据结构】零基础打造控制台游戏:贪吃蛇实战教程----链表与Win32 API的完美结合!
c语言·数据结构·链表·贪吃蛇·宽字符·win32 api·控制台编程
AI科技星3 小时前
伟大的跨越:从超距作用到时空运动——牛顿与张祥前引力场方程的终极对比
开发语言·数据结构·经验分享·线性代数·算法
刘 大 望3 小时前
JVM(Java虚拟机)
java·开发语言·jvm·数据结构·后端·java-ee
别动哪条鱼3 小时前
FFmpeg模块化架构
架构·ffmpeg
阿拉伯柠檬4 小时前
实现一个异步操作线程池
开发语言·数据结构·c++·面试
一线灵4 小时前
Axmol 引擎系列教程之 - Opus 音频压缩格式的使用
音视频
小年糕是糕手4 小时前
【C++】内存管理(下)
java·c语言·开发语言·数据结构·c++·算法