FFmpeg 基本API avformat_find_stream_info函数内部调用流程分析
1、avformat_find_stream_info 函数定义说明
avformat_find_stream_info是 FFmpeg 库中的一个函数,用于获取 AVFormatContext 中的流信息。在使用 FFmpeg 进行音视频处理时,通常需要先调用这个函数来获取流信息。用于获取媒体文件详细流信息的关键函数,它会读取并分析文件的部分数据来获取精确的编解码器参数、时长等信息。
int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options);
参数说明:
- ic: 一个指向 AVFormatContext 结构体的指针,用于存储 AVFormatContext 对象。
- options: 一个指向 AVDictionary 结构体指针的指针,用于传递额外的查找选项。
返回值: - 如果查找成功,则返回 0;如果查找失败,则返回一个负值。
在调用 avformat_find_stream_info 函数之后,可以通过 AVFormatContext 结构avcodec_parameters_to_context体中的成员来获取流信息,例如 nb_streams、streams 和 metadata 等。注意:在使用 FFmpeg 进行音视频处理时,通常需要先调用 avformat_open_input 函数打开输入 URL,然后调用 avformat_find_stream_info 函数获取流信息。
2、avformat_find_stream_info 内部调用流程
2.1 avformat_find_stream_info初始化和参数准备
FFFormatContext *const si = ffformatcontext(ic);
int64_t max_analyze_duration = ic->max_analyze_duration;
int64_t probesize = ic->probesize;
// ...
av_opt_set_int(ic, "skip_clear", 1, AV_OPT_SEARCH_CHILDREN);
/*
作用:初始化内部状态结构体(FFFormatContext)
关键参数:max_analyze_duration:最大分析时长(默认5秒)probesize:最大探测数据量(默认4096字节)特殊格式调整:FLV格式设为90秒,MPEG/MPEG-TS设为7秒
*/
2.2 流初始化循环
for (unsigned i = 0; i < ic->nb_streams; i++) {AVStream *const st = ic->streams[i];FFStream *const sti = ffstream(st);// 初始化解析器if (!sti->parser) {sti->parser = av_parser_init(st->codecpar->codec_id);}// 创建解码器上下文ret = avcodec_parameters_to_context(avctx, st->codecpar);// 查找并打开解码器codec = find_probe_decoder(ic, st, st->codecpar->codec_id);avcodec_open2(avctx, codec, ...);
}/*核心操作:为每个流创建解析器(av_parser_init)将编解码参数复制到解码器上下文(avcodec_parameters_to_context)查找并打开匹配的解码器(find_probe_decoder + avcodec_open2)
*/
2.3 主探测循环(核心逻辑)
for (;;) {// 1. 检查停止条件if (ff_check_interrupt(...)) break; // 用户中断if (read_size >= probesize) break; // 达到探测大小限制// 2. 读取数据包ret = read_frame_internal(ic, pkt1);// 3. 处理数据包st = ic->streams[pkt->stream_index];sti = ffstream(st);read_size += pkt->size;// 4. DTS连续性检查if (pkt->dts != AV_NOPTS_VALUE) {// 检测DTS不连续和回退}// 5. 关键分析操作extract_extradata(si, st, pkt); // 提取extradatatry_decode_frame(ic, st, pkt); // 尝试解码帧// 6. 时长检查if (analyzed_duration > max_analyze_duration) break;
}
/*
循环控制:中断检查:响应用户取消操作数据量检查:防止无限读取时长检查:限制分析时间
关键操作:DTS验证:检测时间戳不连续性Extradata提取:获取编解码器特殊配置数据试探性解码:通过实际解码获取精确参数
*/
2.4 特殊场景处理
// 1. 文件结束处理
if (eof_reached) {for (each stream) {if (!has_codec_parameters) {avcodec_open2(...); // 强制打开解码器}update_dts_from_pts(...); // 从PTS重建DTS}
}// 2. 刷新解码器
if (flush_codecs) {av_packet_unref(empty_pkt);for (each stream) {try_decode_frame(ic, st, empty_pkt); // 发送flush包}
}// 3. 帧率计算
ff_rfps_calculate(ic); // 基于DTS计算真实帧率
/*
EOF处理:文件结束时尝试最后获取参数
解码器刷新:清空解码器缓存获取残留帧
帧率计算:基于时间戳间隔计算精确帧率
*/
2.5 流参数后处理
for (each stream) {// 视频流处理if (avctx->codec_type == AVMEDIA_TYPE_VIDEO) {// 计算平均帧率av_reduce(&st->avg_frame_rate, ...);// 推导宽高比st->sample_aspect_ratio = av_mul_q(...);}// 音频流处理else if (avctx->codec_type == AVMEDIA_TYPE_AUDIO) {// 设置比特率/声道布局avctx->bits_per_coded_sample = ...;// 根据音频服务类型设置dispositionswitch (avctx->audio_service_type) {case AV_AUDIO_SERVICE_TYPE_EFFECTS: ...}}
}
/*
视频流:计算帧率、推导宽高比
音频流:设置比特率、根据音频服务类型标记disposition
*/
2.6 最终验证与清理
// 1. 时间基估计
estimate_timings(ic, old_offset);// 2. 参数有效性检查
for (each stream) {if (!has_codec_parameters(st, &errmsg)) {av_log(...); // 输出警告}
}// 3. 更新流参数
avcodec_parameters_from_context(st->codecpar, sti->avctx);// 4. 资源清理
for (each stream) {av_parser_close(sti->parser);avcodec_close(sti->avctx);
}
/*
关键操作:时间基校准:estimate_timings()参数回写:将解码器参数复制回流编解码器关闭:释放解码资源边数据处理:CPB参数封装
*/
2.7 错误处理机制
find_stream_info_err:for (each stream) {av_freep(&sti->info); // 释放流信息结构codec_close(sti); // 关闭编解码器}return ret;
/*
统一错误出口:使用goto跳转
资源释放:确保所有解码器和解析器被正确关闭
日志记录:记录分析前后的文件位置信息
*/