===========================================
【FFmpeg入门系列】
FFmpeg入门:最简单的视频播放器
FFmpeg入门:最简单的音频播放器
FFmpeg入门:最简单的音视频播放器
FFmpeg入门:最简单的音视频播放器(Plus优化版)
FFmepg入门:最简单的音视频转码工具
基于ImGui+FFmpeg实现播放器
很久没有和大家分享FFmpeg了,也是因为最近工作上的事情比较多,上周花了一周时间读了一下FFmpeg的源码?对内部的很多实体和源码都仔细梳理了一下;所以往后的系列就是FFmpeg源码系列哈哈。
OK,进入今天的源码分析主题:avformat_open_input()
这个方法大家应该都不陌生,我们在做之前的音视频播放器的时候,打开一个文件的开始就是使用了avformat_open_input方法,今天我们就对这个方法,以及内部的实体进行详细分析
核心功能
⏺ avformat_open_input是FFmpeg中用于打开媒体文件并读取其头部信息的核心函数。其主要功能包括:
- 打开指定URL的媒体文件
- 自动探测并识别文件格式
- 为各种流(视频、音频等)创建AVStream结构
- 读取并解析文件头信息
- 分配并初始化AVFormatContext结构体
- 不打开编解码器,仅处理容器格式层面的操作
复用(muxing)和解复用(demuxing)
Demuxing(解复用)
定义: 将一个包含多种数据流的媒体文件分离成独立的数据流的过程。
特点:
- 输入: 一个完整的媒体文件(如MP4、AVI等容器格式)
- 输出: 多个独立的数据流(如视频流、音频流、字幕流等)
- 方向: 从一个复合文件到多个单独流
- 使用场景: 播放媒体文件、提取特定流、转码处理等 在FFmpeg中的实现:
- 使用avformat_open_input()打开媒体文件
- 使用av_read_frame()读取数据包
- 每个数据包属于特定的流(通过AVPacket.stream_index标识)
Muxing(复用)
定义: 将多个独立的数据流合并成一个包含多种数据流的媒体文件的过程。
特点:
- 输入: 多个独立的数据流(如视频流、音频流等
- 输出: 一个完整的媒体文件(指定格式的容器
- 方向:从多个单独流到一个复合文件
- 使用场景: 创建媒体文件、合并音视频流、格式转换等 在FFmpeg中的实现:
- 使用avformat_alloc_context()创建复用上下文
- 使用avformat_write_header()写入文件头
- 使用av_write_frame()或av_interleaved_write_frame()写入数据包
- 使用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流程
- 申请内存初始化AVFormatContext
- 打开AVIOContext
- 根据输入url匹配对应的protocol
- 调用对应protocol的url_open方法,打开文件IO
AVInputFormat open流程
- 迭代支持的FFInputFormat,probe找到对应的FFInputFormat
- FFInputFormat->read_header读取文件头
- 申请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,
};