news 2026/10/30 2:29:16

Linux FFmpeg深度解析之avformat_seek_file调用流程与实战(九)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Linux FFmpeg深度解析之avformat_seek_file调用流程与实战(九)

简介:CSDN博客专家、《Android系统多媒体进阶实战》作者

博主新书推荐:《Android系统多媒体进阶实战》🚀
Android Audio工程师专栏地址:Audio工程师进阶系列【原创干货持续更新中……】🚀
Android多媒体专栏地址:多媒体系统工程师系列【原创干货持续更新中……】🚀
专题一 二:AAOS车载系统+AOSP14系统攻城狮入门视频实战课🚀
专题三:Android14 Binder之HIDL与AIDL通信实战课🚀
专题四:Android15快速自定义与集成音效实战课🚀
专题五:Android15音频策略实战课🚀
专题六:Android15音频性能实战课(无声/杂音/断音/爆音实战案例)🚀

人生格言:人生从来没有捷径,只有行动才是治疗恐惧和懒惰的唯一良药.

更多原创,欢迎关注:Android系统攻城狮

点击获取二维码


🍉🍉🍉文章目录🍉🍉🍉

      • 🌻1.前言
      • 🌻2.应用场景和用法
        • 2.1为寻址操作设置可接受范围
        • 2.2函数原型与范围语义
      • 🌻3.调用流程剖析
        • 3.1校验范围并选择Demuxer接口
        • 3.2寻址成功后继续读取和解码
      • 🌻4.实战案例
        • 4.1准备媒体流和合法时间范围
        • 4.2按范围寻址并读取视频包
        • 4.3完整代码
      • 🌻5.总结

🌻1.前言

本篇目的:理解avformat_seek_file()如何依据时间戳范围请求解复用器定位,并通过读取定位后的数据包观察边界参数的作用。

播放器和媒体分析程序常常不仅需要一个目标时间,还需要限定允许定位的时间范围。avformat_seek_file()通过min_ts、ts和max_ts表达这个范围,并尝试选择落在范围内、且适合活动流呈现的位置。

该函数负责请求解复用器寻址。它不保证所有Demuxer都能严格满足范围,也不会刷新调用者持有的解码器状态。

已打开的媒体上下文

最小时间 目标时间 最大时间

avformat_seek_file

Demuxer按范围尝试定位

后续从新位置读取数据包

🌻2.应用场景和用法

2.1为寻址操作设置可接受范围

avformat_seek_file()适合播放进度跳转、同步多路媒体流,以及希望控制目标时间附近可接受位置的场景。

场景参数组织作用
跳转到目标时间附近min_ts和max_ts设置为较宽范围允许Demuxer在区间内选择可用位置
限制最大跳转误差缩小min_ts和max_ts间距超出范围的位置不符合请求
多流同步指定参考流或使用-1为解复用器提供时间戳单位参考
允许非关键帧寻址设置AVSEEK_FLAG_ANY仅在Demuxer支持时生效
网络或特殊容器定位由输入格式实现read_seek2让Demuxer根据完整范围执行定位

指定stream_index时,三个时间戳都使用该流的time_base。stream_index=-1时使用AV_TIME_BASE单位。调用前必须保证min_ts <= ts <= max_ts,否则函数会直接返回错误。

2.2函数原型与范围语义
intavformat_seek_file(AVFormatContext*s,intstream_index,int64_tmin_ts,int64_tts,int64_tmax_ts,intflags);
参数含义
s已打开并分析流信息的AVFormatContext
stream_index时间基参考流;-1表示使用AV_TIME_BASE
min_ts可接受的最小时间戳
ts期望定位的目标时间戳
max_ts可接受的最大时间戳
flags寻址模式;AVSEEK_FLAG_BACKWARD在该接口中会被忽略
返回值非负表示成功,负值表示失败

例如,指定视频流后要定位到起点后5秒、允许范围为前后1秒,三个值都必须换算到视频流时间基。AVSEEK_FLAG_BACKWARD不是该接口控制方向的方式;范围由min_ts和max_ts表达。

AVStream*stream=format_ctx->streams[video_stream_index];int64_tstart_ts=stream->start_time==AV_NOPTS_VALUE?0:stream->start_time;int64_tmin_ts=start_ts+av_rescale_q((int64_t)4*AV_TIME_BASE,AV_TIME_BASE_Q,stream->time_base);int64_ttarget_ts=start_ts+av_rescale_q((int64_t)5*AV_TIME_BASE,AV_TIME_BASE_Q,stream->time_base);int64_tmax_ts=start_ts+av_rescale_q((int64_t)6*AV_TIME_BASE,AV_TIME_BASE_Q,stream->time_base);ret=avformat_seek_file(format_ctx,video_stream_index,min_ts,target_ts,max_ts,0);

avformat_seek_file()修改解复用器的位置和读取状态,不创建新的AVFormatContext或AVStream。如果程序已经把旧位置的数据包送入解码器,成功寻址后仍要由应用刷新解码器。

🌻3.调用流程剖析

3.1校验范围并选择Demuxer接口

函数首先检查范围是否满足min_ts <= ts <= max_ts,再检查stream_index是否有效。若上下文启用了允许任意帧寻址的策略,函数会相应设置AVSEEK_FLAG_ANY;随后清除AVSEEK_FLAG_BACKWARD,因为方向由上下界表示。

当输入格式实现read_seek2时,FFmpeg会先刷新解复用器内部读取状态,再把流索引、时间范围和标志传给Demuxer。若使用stream_index=-1且输入只有一条流,代码会把AV_TIME_BASE单位的时间范围换算到该流的时间基。

是

传入时间范围

校验上下界和流索引

Demuxer实现read_seek2

刷新解复用状态

按需要换算单流时间基

调用Demuxer范围寻址

返回寻址结果

Demuxer寻址成功后,FFmpeg还会处理附带图片数据。若格式没有read_seek2,该版本源码会走旧式寻址兼容路径,并根据目标与范围尝试av_seek_frame()及边界重试。因此,不能把API参数范围误解成所有输入格式都能完全兑现的保证。

3.2寻址成功后继续读取和解码

成功返回后,解复用器内部包队列和解析器状态已经按寻址流程刷新。应用接下来仍需调用av_read_frame()获取新位置的数据包。

如果应用使用解码器,解码器可能保留跳转前的参考帧和延迟帧。应在成功寻址后调用avcodec_flush_buffers(),并妥善处理仍由应用持有的旧AVPacket。这两个清理动作面向不同对象:Demuxer状态由寻址路径处理,解码器状态由调用者处理。

🌻4.实战案例

4.1准备媒体流和合法时间范围

示例从命令行接收媒体文件,打开文件并分析流信息,然后选择最佳视频流。代码要求文件至少覆盖视频流起点后的6秒,以便构造4至6秒的寻址窗口。

4.2按范围寻址并读取视频包

程序把起点后4秒、5秒和6秒分别换算到视频流的time_base,依次作为最小时间、目标时间和最大时间传入avformat_seek_file()。调用成功后,程序读取数据包并筛选视频流,观察寻址后的数据包时间戳。

4.3完整代码
#include<libavformat/avformat.h>#include<libavutil/mathematics.h>#include<inttypes.h>#include<stdio.h>intmain(intargc,char**argv){AVFormatContext*format_ctx=NULL;AVPacket*packet=NULL;AVStream*video_stream;intvideo_stream_index;intret;intexit_code=1;int64_tstream_start;int64_tmin_ts;int64_ttarget_ts;int64_tmax_ts;if(argc!=2){fprintf(stderr,"Usage: %s input-file\n",argv[0]);return1;}/* 打开媒体并读取流信息 */ret=avformat_open_input(&format_ctx,argv[1],NULL,NULL);if(ret<0)gotodone;ret=avformat_find_stream_info(format_ctx,NULL);if(ret<0)gotodone;/* 选择视频流,时间范围以该流的time_base表示 */video_stream_index=av_find_best_stream(format_ctx,AVMEDIA_TYPE_VIDEO,-1,-1,NULL,0);if(video_stream_index<0)gotodone;video_stream=format_ctx->streams[video_stream_index];stream_start=video_stream->start_time;if(stream_start==AV_NOPTS_VALUE)stream_start=0;/* 构造起点后4到6秒的范围,目标时间为起点后5秒 */min_ts=stream_start+av_rescale_q((int64_t)4*AV_TIME_BASE,AV_TIME_BASE_Q,video_stream->time_base);target_ts=stream_start+av_rescale_q((int64_t)5*AV_TIME_BASE,AV_TIME_BASE_Q,video_stream->time_base);max_ts=stream_start+av_rescale_q((int64_t)6*AV_TIME_BASE,AV_TIME_BASE_Q,video_stream->time_base);/* 让Demuxer在给定范围内选择可用位置 */ret=avformat_seek_file(format_ctx,video_stream_index,min_ts,target_ts,max_ts,0);if(ret<0)gotodone;packet=av_packet_alloc();if(packet==NULL)gotodone;/* 读取寻址后的数据包,只观察目标视频流 */while((ret=av_read_frame(format_ctx,packet))>=0){if(packet->stream_index==video_stream_index){printf("video packet pts=%"PRId64", dts=%"PRId64"\n",packet->pts,packet->dts);av_packet_unref(packet);exit_code=0;break;}av_packet_unref(packet);}done:/* 释放应用持有的数据包和解复用上下文 */av_packet_free(&packet);avformat_close_input(&format_ctx);returnexit_code;}

这段程序验证时间单位换算、范围参数传递和寻址后读取数据包的路径。能否在给定区间内找到合适位置,仍取决于容器格式和Demuxer能力。

🌻5.总结

avformat_seek_file()把最小时间、目标时间和最大时间交给Demuxer寻址,并由调用者继续处理解码器状态。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/30 2:27:56

【第70期】Adam二阶矩不是方差:偏差校正、动量与AdamW完整实验

系列&#xff1a;《从小白到 AI 大模型开发工程师的进阶之路》 技术点&#xff1a;AI-0224 Adam 与自适应优化 主人公&#xff1a;小蓝伞 前置&#xff1a;AI-0223 梯度下降 本期产出&#xff1a;独立实验脚本、原始输出、失败样本和验收清单 小蓝伞把优化器第二个状态命名为方…

作者头像 李华
网站建设 2026/10/30 2:27:42

Web 加载 DWG 实战(五):缩放、平移与图纸坐标怎么对应

在浏览器里查看 DWG&#xff0c;滚轮放大和拖动平移是最常用的两个操作。真正接入业务页面时&#xff0c;细节问题很快就会出现&#xff1a;鼠标指着一个节点&#xff0c;放大后节点为什么跑了&#xff1f;图纸明明没有修改&#xff0c;状态栏坐标为什么一直变化&#xff1f;换…

作者头像 李华
网站建设 2026/10/30 2:26:04

python中的静态方法和类方法

在版本号为.2的情况之下, 静态方法与类方法是被进行引用的, 不管是经典类还是新式类, 都是可以直接被使用的。与此同时, 存在一对内建函数, 是分别被称为和被的这两个内建函数被引入到了进来, 它们的作用是用于把一个类之中的某种方法进行转化, 从而把其转化为上述这两种方法当…

作者头像 李华