FFmepg源码系列-avformat_open_input()

发布于:2025-08-12 ⋅ 阅读:(21) ⋅ 点赞:(0)

===========================================
【FFmpeg入门系列】
FFmpeg入门:最简单的视频播放器
FFmpeg入门:最简单的音频播放器
FFmpeg入门:最简单的音视频播放器
FFmpeg入门:最简单的音视频播放器(Plus优化版)
FFmepg入门:最简单的音视频转码工具
基于ImGui+FFmpeg实现播放器

很久没有和大家分享FFmpeg了,也是因为最近工作上的事情比较多,上周花了一周时间读了一下FFmpeg的源码?对内部的很多实体和源码都仔细梳理了一下;所以往后的系列就是FFmpeg源码系列哈哈。

OK,进入今天的源码分析主题:avformat_open_input()
这个方法大家应该都不陌生,我们在做之前的音视频播放器的时候,打开一个文件的开始就是使用了avformat_open_input方法,今天我们就对这个方法,以及内部的实体进行详细分析

核心功能

⏺ avformat_open_input是FFmpeg中用于打开媒体文件并读取其头部信息的核心函数。其主要功能包括:

  1. 打开指定URL的媒体文件
  2. 自动探测并识别文件格式
  3. 为各种流(视频、音频等)创建AVStream结构
  4. 读取并解析文件头信息
  5. 分配并初始化AVFormatContext结构体
  6. 不打开编解码器,仅处理容器格式层面的操作

复用(muxing)和解复用(demuxing)

Demuxing(解复用)
定义: 将一个包含多种数据流的媒体文件分离成独立的数据流的过程。
特点:

  1. 输入: 一个完整的媒体文件(如MP4、AVI等容器格式)
  2. 输出: 多个独立的数据流(如视频流、音频流、字幕流等)
  3. 方向: 从一个复合文件到多个单独流
  4. 使用场景: 播放媒体文件、提取特定流、转码处理等 在FFmpeg中的实现:
  5. 使用avformat_open_input()打开媒体文件
  6. 使用av_read_frame()读取数据包
  7. 每个数据包属于特定的流(通过AVPacket.stream_index标识)

Muxing(复用)
定义: 将多个独立的数据流合并成一个包含多种数据流的媒体文件的过程。
特点:

  1. 输入: 多个独立的数据流(如视频流、音频流等
  2. 输出: 一个完整的媒体文件(指定格式的容器
  3. 方向:从多个单独流到一个复合文件
  4. 使用场景: 创建媒体文件、合并音视频流、格式转换等 在FFmpeg中的实现:
  5. 使用avformat_alloc_context()创建复用上下文
  6. 使用avformat_write_header()写入文件头
  7. 使用av_write_frame()或av_interleaved_write_frame()写入数据包
  8. 使用av_write_trailer()写入文件尾

核心区别总结

特性 Demuxing(解复用) Muxing(复用)
操作方向 分解:1个文件 → 多个流 合并:多个流 → 1个文件
主要用途 播放、分析、提取媒体内容 创建、封装媒体文件
API函数 avformat_open_input(), av_read_frame() avformat_write_header(), av_write_frame()
数据流向 文件 → 内存中的独立流 内存中的独立流 → 文件
时间处理 读取已存在的时间戳 读取已存在的时间戳 需要正确设置时间戳

AVFormatContext实体

这个实体是封装和解封装(复用和解复用)这一层最最关键的实体,它包含了输入/输出格式的相关信息,如文件的URL、流信息、编解码器参数等。

详细参数说明:

  基础信息字段

  1. const AVClass *av_class; // 日志和选项类,由avformat_alloc_context()设置
  2. const struct AVInputFormat *iformat; // 输入容器格式,仅解复用时使用
  3. const struct AVOutputFormat *oformat; // 输出容器格式,仅复用时使用
  4. void *priv_data; // 格式私有数据,由iformat/oformat.priv_class决定是否启用AVOptions
  5. AVIOContext *pb; // I/O上下文,处理输入/输出操作

  流信息字段

  8. AVStream **streams; // 文件中所有流的列表
  9. unsigned int nb_stream_groups; // AVFormatContext.stream_groups中的元素数量
  10. AVStreamGroup **stream_groups; // 文件中所有流组的列表
  11. unsigned int nb_chapters; // AVChapter数组中的章节数量
  12. AVChapter **chapters; // 章节指针数组
  13. char *url; // 输入或输出URL

  时间和持续时间字段

  14. int64_t start_time; // 第一帧位置,以AV_TIME_BASE为单位
  15. int64_t duration; // 流持续时间,以AV_TIME_BASE为单位
  16. int64_t bit_rate; // 总比特率,bit/s
  17. unsigned int packet_size; // 包大小

  控制标志和选项字段

  19. int flags; // 修改(解)复用器行为的标志,如AVFMT_FLAG_GENPTS等
  20. int64_t probesize; // 用于确定流属性的最大读取字节数
  21. int64_t max_analyze_duration; // avformat_find_stream_info()中读取数据的最大持续时间
  22. const uint8_t *key; // 密钥
  23. int keylen; // 密钥长度
  24. unsigned int nb_programs; // 程序数量
  25. AVProgram **programs; // 程序指针数组
  26. enum AVCodecID video_codec_id; // 强制视频编解码器ID
  27. enum AVCodecID audio_codec_id; // 强制音频编解码器ID
  28. enum AVCodecID subtitle_codec_id; // 强制字幕编解码器ID
  29. enum AVCodecID data_codec_id; // 强制数据编解码器ID
  63. const struct AVCodec *video_codec; // 强制视频编解码器
  64. const struct AVCodec *audio_codec; // 强制音频编解码器
  65. const struct AVCodec *subtitle_codec; // 强制字幕编解码器
  66. const struct AVCodec *data_codec; // 强制数据编解码器

  待逐个分析

  30. AVDictionary *metadata; // 应用于整个文件的元数据
  31. int64_t start_time_realtime; // 流开始时间,以微秒为单位
  32. int fps_probe_size; // 用于确定帧率的帧数
  33. int error_recognition; // 错误识别级别
  34. AVIOInterruptCB interrupt_callback; // I/O层的自定义中断回调
  35. int debug; // 调试标志
  36. int max_streams; // 最大流数
  37. unsigned int max_index_size; // 每个流索引的最大内存大小
  38. unsigned int max_picture_buffer; // 从实时捕获设备缓冲帧的最大内存
  39. int64_t max_interleave_delta; // 交错的最大缓冲持续时间
  40. int max_ts_probe; // 等待第一个时间戳时读取的最大包数
  41. int max_chunk_duration; // 最大块时间
  42. int max_chunk_size; // 最大块大小
  43. int max_probe_packets; // 可探测的最大包数
  44. int strict_std_compliance; // 允许非标准和实验性扩展
  45. int event_flags; // 指示文件上发生的事件的标志
  46. int avoid_negative_ts; // 避免复用期间出现负时间戳
  47. int audio_preload; // 音频预加载时间
  48. int use_wallclock_as_timestamps; // 强制使用wallclock时间戳
  49. int skip_estimate_duration_from_pts; // 跳过estimate_timings_from_pts中的持续时间计算
  50. int avio_flags; // avio标志,用于强制AVIO_FLAG_DIRECT
  51. enum AVDurationEstimationMethod duration_estimation_method; // 持续时间估算方法
  52. int64_t skip_initial_bytes; // 跳过初始字节
  53. unsigned int correct_ts_overflow; // 纠正单个时间戳溢出
  54. int seek2any; // 强制寻找到任何帧
  55. int flush_packets; // 每个包后刷新I/O上下文
  56. int probe_score; // 格式探测分数
  57. int format_probesize; // 识别输入格式的最大读取字节数
  58. char *codec_whitelist; // 允许的解码器列表
  59. char *format_whitelist; // 允许的解复用器列表
  60. char *protocol_whitelist; // 允许的协议列表
  61. char *protocol_blacklist; // 禁止的协议列表
  62. int io_repositioned; // I/O重新定位标志
  67. int metadata_header_padding; // 元数据头部填充字节数
  68. void *opaque; // 用户私有数据
  69. av_format_control_message control_message_cb; // 设备与应用程序通信的回调
  70. int64_t output_ts_offset; // 输出时间戳偏移
  71. uint8_t *dump_separator; // 转储格式分隔符
  72. int (*io_open)(); // 打开新IO流的回调
  73. int (*io_close2)(); // 关闭IO流的回调
  74. int64_t duration_probesize; // 确定流持续时间时读取的最大字节数

使用场景:

解复用:

  • 通过avformat_open_input()创建和初始化
  • 包含从文件中解析出的所有流信息
  • 用于读取媒体文件的数据包

复用:

  • 通过avformat_alloc_context()或avformat_alloc_output_context2()创建
  • 需要手动设置输出格式和流信息
  • 用于将数据包写入媒体文件

关键流程图

在这里插入图片描述
说一下关键的流程

AVIOContext open流程

  1. 申请内存初始化AVFormatContext
  2. 打开AVIOContext
  3. 根据输入url匹配对应的protocol
  4. 调用对应protocol的url_open方法,打开文件IO

AVInputFormat open流程

  1. 迭代支持的FFInputFormat,probe找到对应的FFInputFormat
  2. FFInputFormat->read_header读取文件头
  3. 申请AVStream,并分配的AVFormatContext上下文

举例:file协议的URLProtocol

const URLProtocol ff_file_protocol = {
    .name                = "file",
    .url_open            = file_open,
    .url_read            = file_read,
    .url_write           = file_write,
    .url_seek            = file_seek,
    .url_close           = file_close,
    .url_get_file_handle = file_get_handle,
    .url_check           = file_check,
    .url_delete          = file_delete,
    .url_move            = file_move,
    .priv_data_size      = sizeof(FileContext),
    .priv_data_class     = &file_class,
    .url_open_dir        = file_open_dir,
    .url_read_dir        = file_read_dir,
    .url_close_dir       = file_close_dir,
    .default_whitelist   = "file,crypto,data"
};

举例 aac文件的FFInputFormat

const FFInputFormat ff_aac_demuxer = {
    .p.name       = "aac",
    .p.long_name  = NULL_IF_CONFIG_SMALL("raw ADTS AAC (Advanced Audio Coding)"),
    .p.flags      = AVFMT_GENERIC_INDEX,
    .p.extensions = "aac",
    .p.mime_type  = "audio/aac,audio/aacp,audio/x-aac",
    .read_probe   = adts_aac_probe,
    .read_header  = adts_aac_read_header,
    .read_packet  = adts_aac_read_packet,
    .raw_codec_id = AV_CODEC_ID_AAC,
};

aac的probe探测

在这里插入图片描述