首页 > 其他分享 >Seek 策略以及在有 B 帧情况下的处理

Seek 策略以及在有 B 帧情况下的处理

时间:2022-12-09 20:32:48浏览次数:61  
标签:index 策略 处理 timestamp av DTS Seek GOP

在知识星球分享的文章,顺便也在公众号发表一下,不足之处,欢迎指正。

一个关于音视频领域专业问答的小圈子!!

最近在做 Seek 相关功能时遇到的问题排查,顺便也学到了一些新的东西,和大家分享下。

在视频播放时执行 Seek 到任意点的操作,一般都是 Seek 到任意点往前最近的 I 帧,然后再逐帧解码到指定时间点。

这里可以优化,假设当前时间和指定时间在一个 GOP 内,就可以不用 seek ,直接顺序向下解码就好。

而正是这个优化出现了一点问题,现象如下:

已经判断播放点 A 和 Seek 点 B 不在一个 GOP 内,然后执行 av_seek_frame 方法还是把时间点 A 所在 GOP 全部解码了,导致播放上出现了卡顿。

这里就很奇怪了,明明判断不在一个 GOP ,那 Seek 时就应该从时间点 B 所在 GOP 的 I 帧开始解码, 但执行时还是解码了上一个 GOP 的内容。

到底是判断是否同一个 GOP 的函数出问题了还是 Seek 方法有问题呢?

带着疑问开始深入源码探索。

FFmpeg 没有直接提供判断两帧是否同一个 GOP 的方法,所以通过 av_index_search_timestamp 方法得到传入时间点最近的 I 帧的 index 索引,如果两个时间点的索引相同则表示为同一个 GOP 内,因为最近的 I 帧相同。

然而 av_index_search_timestamp 方法是通过 AVIndexEntry 中的 timestamp 来判断的,它是一个 DTS 值,通过二分查找得到最近的索引。

在没有 B 帧的情况下,I 帧的 PTS 等于 DTS ,所以判断不会出问题。然而正是有了 B 帧,如果 I 帧的 PTS 和 DTS 不相等的话,那么上面的判断相当于是拿一个 PTS 值和 I 帧的 DTS 比较是否同一个 GOP 了。

如果将错就错,判断 GOP 时得到结论是非同一个 GOP ,那么 Seek 也应该是非同一个 GOP ,但现实恰恰相反,Seek 当做了同一个 GOP ,这里面肯定有计算出问题了,继续深入源码。

通过在 mov.c 源码中看到了如下的操作:

static int mov_seek_stream(AVFormatContext *s, AVStream *st, int64_t timestamp, int flags)
{
MOVStreamContext *sc = st->priv_data;
FFStream *const sti = ffstream(st);
int sample, time_sample, ret;
unsigned int i;
// Here we consider timestamp to be PTS, hence try to offset it so that we
// can search over the DTS timeline.
timestamp -= (sc->min_corrected_pts + sc->dts_shift);
ret = mov_seek_fragment(s, st, timestamp);
if (ret < 0)
return ret;
sample = av_index_search_timestamp(st, timestamp, flags);
av_log(s, AV_LOG_TRACE, "stream %d, timestamp %"PRId64", sample %d\n", st->index, timestamp, sample);
// 省略部分代码

注意到如下一行代码:

timestamp -= (sc->min_corrected_pts + sc->dts_shift);

也就是说我们传入的时间都会被减上一个值,然后再执行 av_index_search_timestamp 方法,而这个值导致判断 GOP 和 Seek 之间的关键帧索引出问题了。

正如代码中的注释所示,假设传入的时间是 PTS 值,然后给它减去偏移以得到 DTS 值,因为 av_index_search_timestamp 方法就通过 DTS 进行比较的嘛。

出现问题的原因就是 seek 的时间点正好在 I 帧的 PTS 和 DTS 范围之间了,执行 seek 时减去偏差值就小于 DTS 了,所以变成了同一个 GOP 。

现在要解决问题就是如何得到 sc->min_corrected_pts + sc->dts_shif 之和,然后判断 GOP 时减去它以修正得到 DTS 值。

还好通过遍历源码发现它的值是不会运行时改变的,一旦决定了就定下来了。另外我们可以用第一个 I 帧的 DTS 值作为偏移值。

auto indexEntry = avStream->index_entries;
auto nbIndexEntry = avStream->nb_index_entries;
for (int i = 0; i < nbIndexEntry; ++i) {
if (indexEntry[i].flags == AVINDEX_KEYFRAME) {
DTSOffset = indexEntry[i].timestamp;
return;
}
}

如果没有 B 帧,DTS 值为 0 ,有 B 帧,那么首帧的 DTS 值就可以用来做偏差值进行计算了。

Seek 策略以及在有 B 帧情况下的处理_Android



标签:index,策略,处理,timestamp,av,DTS,Seek,GOP
From: https://blog.51cto.com/u_12127193/5926458

相关文章

  • 字符串处理1(去除紧接着的相同的两个元素
    #include<bits/stdc++.h>usingnamespacestd;constintN=100010;charstk[N];inttop;intmain(){ intx;cin>>x; chark;scanf("%c",&k); for(inti=0;i<x;i++......
  • html5 大文件分片上传处理
    ​ 文件上传是一个老生常谈的话题了,在文件相对比较小的情况下,可以直接把文件转化为字节流上传到服务器,但在文件比较大的情况下,用普通的方式进行上传,这可不是一个好的办法......
  • Vue跨域请求处理
    Axios部署跨域针对网络请求的处理//指定axios请求类型axios.defaults.headers={ "Content-Type":"application/json;charset=utf-8", 'Access-Control-Allow-Or......
  • vue 大文件分片上传处理
    ​ 最近遇见一个需要上传百兆大文件的需求,调研了七牛和腾讯云的切片分段上传功能,因此在此整理前端大文件上传相关功能的实现。在某些业务中,大文件上传是一个比较重要的......
  • 报错处理-bash: fork: Cannot allocate memory
    SSH远程登录一台服务器时,报如下错误:-bash:fork:Cannotallocatememory出现这种情况,主要是因为进程跑满了,memory被消耗光了,无法为其他的操作。问题原因:[root@localh......
  • JavaScript 大文件分片上传处理
    ​ 一、功能性需求与非功能性需求要求操作便利,一次选择多个文件和文件夹进行上传;支持PC端全平台操作系统,Windows,Linux,Mac支持文件和文件夹的批量下载,断点续传。刷......
  • 转发:All in one:项目级 monorepo 策略最佳实践
    0.......
  • js 大文件分片上传处理
    ​ 前言文件上传是一个老生常谈的话题了,在文件相对比较小的情况下,可以直接把文件转化为字节流上传到服务器,但在文件比较大的情况下,用普通的方式进行上传,这可不是一个好......
  • 跟我学Python图像处理丨带你入门OpenGL
    摘要:介绍Python和OpenGL的入门知识,包括安装、语法、基本图形绘制等。本文分享自华为云社区《[Python图像处理]二十七.OpenGL入门及绘制基本图形(一)》,作者:eastmount。一.O......
  • csharp 大文件分片上传处理
    ​IE的自带下载功能中没有断点续传功能,要实现断点续传功能,需要用到HTTP协议中鲜为人知的几个响应头和请求头。 一. 两个必要响应头Accept-Ranges、ETag      ......