简介:CSDN博客专家、《Android系统多媒体进阶实战》作者
博主新书推荐:《Android系统多媒体进阶实战》🚀
Android Audio工程师专栏地址:Audio工程师进阶系列【原创干货持续更新中……】🚀
Android多媒体专栏地址:多媒体系统工程师系列【原创干货持续更新中……】🚀
专题一 二:AAOS车载系统+AOSP14系统攻城狮入门视频实战课🚀
专题三:Android14 Binder之HIDL与AIDL通信实战课🚀
专题四:Android15快速自定义与集成音效实战课🚀
专题五:Android15音频策略实战课🚀
专题六:Android15音频性能实战课(无声/杂音/断音/爆音实战案例)🚀
人生格言:人生从来没有捷径,只有行动才是治疗恐惧和懒惰的唯一良药.
🍉🍉🍉文章目录🍉🍉🍉
- 🌻1.前言
- 🌻2.应用场景和用法
- 2.1补齐容器头部缺失的流信息
- 2.2函数原型与返回语义
- 🌻3.调用流程剖析
- 3.1为每个流准备参数探测状态
- 3.2读取数据包并更新流参数
- 🌻4.实战案例
- 4.1准备媒体输入上下文
- 4.2探测流参数并检查结果
- 4.3完整代码
- 🌻5.总结
🌻1.前言
本篇目的:理解
avformat_find_stream_info()如何读取并分析媒体包以补充流参数,梳理探测结果与缓存数据的关系,并通过独立的C程序检查音视频流信息。
容器文件头可能没有提供完整的编码参数、帧率或时长。即使avformat_open_input()已经打开输入并读取头部,应用仍可能无法据此完成解码器配置。
avformat_find_stream_info()会读取一部分媒体包,结合解析器和探测解码器补充信息。默认缓冲模式下,探测过的包会为后续读取保留。该函数不等于应用的正式解码流程,也不保证所有解码器都会保持打开。
🌻2.应用场景和用法
2.1补齐容器头部缺失的流信息
avformat_find_stream_info()通常在avformat_open_input()成功后调用。它适用于容器头部信息不完整、需要在解码前获取编码参数,或需要估算帧率和时长的场景。
探测会读取媒体数据,因此可能增加打开媒体后的等待时间。probesize和max_analyze_duration等设置会影响探测范围;如果输入内容不足或编码信息无法从已读取的数据中取得,部分参数仍可能缺失。
2.2函数原型与返回语义
函数原型如下:
intavformat_find_stream_info(AVFormatContext*ic,AVDictionary**options);| 参数或返回值 | 含义 |
|---|---|
ic | 已由avformat_open_input()成功打开的媒体上下文 |
options | 可选的按流配置字典数组,每项对应一个流的解码器选项 |
| 非负返回值 | 探测流程成功结束 |
| 负值 | 输入读取、内存分配或后续处理发生错误 |
函数会更新AVStream及其codecpar中的可用信息。options里的未识别选项会保留给调用者;返回成功也不代表所有解码器都已经打开,或每个流的参数都完整。
相邻接口分工如下:avformat_open_input()打开输入并读取头部;avformat_find_stream_info()通过包探测补充流参数;应用创建自己的AVCodecContext并调用avcodec_open2()后,才进入正式解码准备;av_read_frame()则读取后续压缩包。
🌻3.调用流程剖析
3.1为每个流准备参数探测状态
函数先从AVFormatContext取得流列表,并为各流准备内部解码上下文。它会把AVCodecParameters复制到内部AVCodecContext,按需创建解析器,并尝试打开探测用解码器。
这些步骤用于从压缩数据中补齐容器未提供的信息。探测所用的内部解码上下文不等同于应用为正式解码创建的上下文。
3.2读取数据包并更新流参数
初始化完成后,函数循环调用内部读包路径读取数据。默认情况下,探测包加入内部packet_buffer,随后用于判断编码参数是否齐全、估算帧率及检查时间戳等。需要时,探测路径会调用内部解码逻辑,从帧数据中取得额外参数。
当所有流信息达到停止条件、达到探测大小或分析时长限制,或者读到文件末尾时,循环结束。函数随后把内部上下文中获得的信息更新到AVStream->codecpar等结构,并关闭探测阶段使用的解码器上下文。
默认缓冲模式下,探测读取的包可供后续av_read_frame()继续处理,因此接口文档描述的逻辑文件位置不会因为探测而改变。启用AVFMT_FLAG_NOBUFFER会改变包缓存行为,不能再按默认缓存路径理解。
🌻4.实战案例
4.1准备媒体输入上下文
程序从命令行取得媒体路径,先使用avformat_open_input()打开输入。此时容器头部信息已经读取,但示例暂不把它视为完整流信息。
4.2探测流参数并检查结果
调用avformat_find_stream_info()后,遍历AVFormatContext->streams,读取每个流的媒体类型和编码参数。音频流检查采样率和声道数,视频流检查宽高。字段仍为默认值时,表示当前输入和探测范围没有提供相应参数,不能仅凭函数返回非负值就断言所有信息齐全。
4.3完整代码
#include<libavformat/avformat.h>#include<libavcodec/avcodec.h>#include<libavutil/error.h>#include<stdio.h>staticvoidprint_av_error(interror_code){charerror_text[AV_ERROR_MAX_STRING_SIZE];av_strerror(error_code,error_text,sizeof(error_text));fprintf(stderr,"FFmpeg error: %s\n",error_text);}intmain(intargc,char*argv[]){AVFormatContext*format_context=NULL;unsignedinti;intret;if(argc<2){fprintf(stderr,"Usage: %s <input>\n",argv[0]);return1;}/* 先打开输入并读取容器头部 */ret=avformat_open_input(&format_context,argv[1],NULL,NULL);if(ret<0){print_av_error(ret);avformat_close_input(&format_context);return1;}/* 再读取媒体包,补充容器头部中缺失的流参数 */ret=avformat_find_stream_info(format_context,NULL);if(ret<0){print_av_error(ret);avformat_close_input(&format_context);return1;}/* 检查探测后每条流当前已知的参数 */for(i=0;i<format_context->nb_streams;i++){AVStream*stream=format_context->streams[i];AVCodecParameters*parameters=stream->codecpar;printf("stream %u, codec: %s\n",i,avcodec_get_name(parameters->codec_id));if(parameters->codec_type==AVMEDIA_TYPE_AUDIO){printf(" sample_rate: %d, channels: %d\n",parameters->sample_rate,parameters->ch_layout.nb_channels);}elseif(parameters->codec_type==AVMEDIA_TYPE_VIDEO){printf(" width: %d, height: %d\n",parameters->width,parameters->height);}}/* 释放输入上下文;探测阶段使用的内部解码状态由库处理 */avformat_close_input(&format_context);return0;}🌻5.总结
avformat_find_stream_info()通过读取和分析媒体包补充AVStream参数,并在默认缓冲模式下为后续读取保留探测包,但不替代应用的正式解码器初始化。