FFmepg源码系列-avformat_open_input()
===========================================
【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决定是否启用AVOptions5. 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/s17. 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; // 强制视频编解码器ID27. enum AVCodecID audio_codec_id; // 强制音频编解码器ID28. enum AVCodecID subtitle_codec_id; // 强制字幕编解码器ID29. enum AVCodecID data_codec_id; // 强制数据编解码器ID63. 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_DIRECT51. 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,
};