Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

M3U8分段视频在seekTo的时候需要很长时间才能播放,这时间跟拖动距离有关,怎样才能缩短时间? #2874

Open
drivedreams opened this issue Mar 21, 2017 · 44 comments

Comments

@drivedreams
Copy link

这是我用的M3U8地址:http://hot.vrs.sohu.com/ipad1479474_4601450223274_4680795.m3u8?plat=17&prod=ad

现象:1.播放整个的大视频seekTo时,没有问题,很快就可以继续播放。
2.对M3U8地址视频使用seekTo时可能出现两种情况:1.缓冲很长时间后可以播放 2.缓冲一段时间后视频暂停,点击播放视频从头开始播放。
3.如果seekTo的位置已经预缓冲完成则seekTo可以很快播放。
4.通过Log及播放信息可以看出,缓冲很长时间这个过程中,一直有数据进行下载,且速度并不慢。

请教各位大神帮忙分析下,这个问题很头疼,现在都是用这中分段的视频,缓冲时间过长太影响体验,谢谢了!

@0ct0cat
Copy link

0ct0cat commented Mar 22, 2017

maybe you need modify the ffmpeg.make the probe size smaller.

@0ct0cat
Copy link

0ct0cat commented Mar 22, 2017

the default probe is 5s. too long

@Nimger
Copy link

Nimger commented Mar 23, 2017

使用IJKFFMoviePlayerController的话,我也碰到这问题,后来我直接改用IJKAVMoviePlayerController来显示,拖动进度就很快了,看了下IJKFFMoviePlayerController的日志,拖动的时候,会一个个ts请求下去,直到找到需要的ts,这样不慢才怪,不知道有什么法子直接定位到所需要的ts

@Android4MediaPlayer
Copy link

有sample url吗

@0ct0cat
Copy link

0ct0cat commented Mar 27, 2017

IJKAVMoviePlayerController use the ios defalut mediaplayer. not ffplay.
if yout want use ffplay,pls use IJKFFMoviePlayerController.
when the play seek to position.Reduced the probe size make it fast.

@drivedreams
Copy link
Author

drivedreams commented Mar 28, 2017 via email

@drivedreams
Copy link
Author

Hi 0ct0cat,
I checked the codes of ijkplayer, IJKFFMoviePlayerController is used in IOS. How to resolve my problem on android?

@0ct0cat
Copy link

0ct0cat commented Mar 29, 2017

there is a member probesize in AVFormatContext ,you can set it's value to control the demuxer probe size.
this method can use on android & ios

@aasdsjk
Copy link

aasdsjk commented Mar 29, 2017

how to set probesize ,and where can i set it's value?

@0ct0cat
Copy link

0ct0cat commented Mar 30, 2017

just like this
pls->ctx->probesize = 32 * 1024;
in hls.c
hls_read_header

@aasdsjk
Copy link

aasdsjk commented Mar 30, 2017

thank you

@aasdsjk
Copy link

aasdsjk commented Mar 30, 2017

I haved tried this, and i make pls->ctx->probesize = 4 * 1024. But it is not helpful. Does it work?

@drivedreams
Copy link
Author

Hi 0ct0cat,
Could you try my Video address? I has tried a lot of ways to do it, but still can not resolved it. I have to use Vitamio as a temporary resolution.

@0ct0cat
Copy link

0ct0cat commented Apr 7, 2017

@drivedreams
i test this stream,it's can't seek.
maybe there is a problem in mpegts.c.
after seek, hls.c work normal,it's can find right url to read and down ts data.
but the av_read_frame stop work.i think it's demux's bug.

@drivedreams
Copy link
Author

drivedreams commented Apr 9, 2017 via email

@0ct0cat
Copy link

0ct0cat commented Apr 10, 2017

@drivedreams
this is not ijk's problem.
maybe sohu split the ts stream paramter have some problem.

@drivedreams
Copy link
Author

Maybe sohu url is not standard. But vitamio player can play it without that problem? I can't modify sohu. It's aware

@liuhy412484577
Copy link

@drivedreams 解决了么,我也遇到这个样的问题,怎么解决的?

@drivedreams
Copy link
Author

没有,可能需要更改源码,没时间研究。我查看搜狐的m m3u8格式应该是没有问题的。

@liuhy412484577
Copy link

@drivedreams 我本地文件也是这样的,不知道什么原因

@drivedreams
Copy link
Author

drivedreams commented Apr 20, 2017

Now, I test playing with IjkExoMediaPlayer. It can seek without the above problem. But I do not know whether it will cause any other problem.

@kandynan
Copy link

这个是因为视频源的dts时间戳远小于seek时间戳,导致一直在找合适的分片,可以在hls_read_packet中使用分片查找的方式解决该问题

@drivedreams
Copy link
Author

@kandynan
你的意思是修改ffmpeg源码重新编译吗,可以通过setoption之类的方法实现吗,不知道改动源码会不会导致其他问题。

@kandynan
Copy link

kandynan commented May 4, 2017 via email

@drivedreams
Copy link
Author

@kandynan
我尝试修改了hls_read_packet 但是没解决,你知道怎么修改吗,给个思路也行

@drivedreams
Copy link
Author

drivedreams commented May 5, 2017

问题还没解决,我把我最近几天的分析写出来,大家帮忙分析下

  • 我这几天首先仔细研究了ijkplayer和ffmpeg, 基本把播放和seek逻辑搞清楚了。就像 @0ct0cat 说的seek是没有问题的,hls成功找到了正确的url,而且进行了读取,但是视频仍然无法播放。(分析了好几天,回到了你的结论😢,明白了逻辑才明白你的)。

  • 按照这个结论继续分析,发现搜狐的M3U8带有EXT-X-DISCONTINUITY标签,如果seek在下一个EXT-X-DISCONTINUITY 之前随意seek都不会出现问题,但seek超出下一个EXT-X-DISCONTINUITY就会无法播放,是格式变化导致的吗?

  • 继续分析hls对M3U8进行解析(应该没理解错,哈哈): 每个EXT-X-DISCONTINUITY划分为一个playlist, 每个EXTINF为一个segment,一个playlist有多个segment. Playlist 中cur_seq_no是当前使用的segment的序号。通过增加log,并分析发现seek时使用的cur_seq_no是正确的。所选的playlist也没有问题,进一步说明seek位置没有问题,理论上playlist的选择没有问题应该是可以播放的,难道分配的解析器有问题?

  • 未完待续...

@dongdong945
Copy link

等待后续结论

@halfhq
Copy link

halfhq commented Jun 12, 2017

同等待

@CloudIAU
Copy link

CloudIAU commented Aug 11, 2017

遇到这个问题主要是视频源里有#EXT-X-DISCONTINUITY这个标签,ffmpeg目前对这个标签是不支持的,可以参考:https://trac.ffmpeg.org/ticket/5419#comment:17 。如果使用官方原生的ffmpeg,带这个标签的视频是无法播放的。
但是ijkplayer对ffpmeg做了处理,使得ijkplayer可以支持这个标签,所以现在可以播放,但ijkplayer并没有添加带这个标签的视频seek的处理,所以导致seek时会长时间卡主,这应该是个Bug。
产生这个问题的原因是:
#EXT-X-DISCONTINUITY标签后面的视频的pts和标签之前的视频是不连续的,而ffmpeg在seek时,会去将要seek的timestamp与当前包的pts区间进行对比,来查看查找的是否是当前包,但由于pts不连续的问题,会导致seek的timestamp总是大于包的pts区间,所以到会导致一直卡着,无法播放。
我目前是这样修复的,测试了seek没问题:
首先在hls.c文件里添加一个函数,用于查找当前packet之前的视频的总时长,用于得出正确的pts:

static int find_timestamp_in_seq_no( struct playlist *pls,int64_t *timestamp, int seq_no)
{
    int i;
    *timestamp=0;
    for (i = 0; i < seq_no; i++) {
        *timestamp += pls->segments[i]->duration ;
    }
    return 0;
}

并且在hls_read_packet函数中声明一个变量,来保存这个时长:

int64_t timestamp = AV_NOPTS_VALUE;//add

然后在hls_read_packet函数中的这段代码中插入这个函数的调用:

 for (i = 0; i < c->n_playlists; i++) {
        struct playlist *pls = c->playlists[i];
        find_timestamp_in_seq_no(pls,&timestamp,pls->cur_seq_no);//add
        /* Make sure we've got one buffered packet from each open playlist
         * stream */
        if (pls->needed && !pls->pkt.data) {

最后在计算seek的timestamp是否在当前packet的区间的代码上加上这个时长:

ts_diff = timestamp + av_rescale_rnd(pls->pkt.dts, AV_TIME_BASE,
                                            tb.den, AV_ROUND_DOWN) -
                            pls->seek_timestamp;//edit
  if (ts_diff >= 0 && (pls->seek_flags  & AVSEEK_FLAG_ANY ||
                                        pls->pkt.flags & AV_PKT_FLAG_KEY)) {
          pls->seek_timestamp = AV_NOPTS_VALUE;
          break;
 }

这样即可正确的seek,不会一直在这个循环中查询了。

@drivedreams
Copy link
Author

@CloudIAU 感谢回复,最近一直忙别的项目没看视频。 有时间按照您的方法修改下。还有就是发现ios 研究起来方便些 因为方便调试

@drivedreams
Copy link
Author

@CloudIAU 按照你的修改后Seek正常了

@drivedreams
Copy link
Author

@raymond1012 是否应该把这个bug 上面的解决方案已经验证了

@drivedreams drivedreams reopened this Nov 14, 2017
@drivedreams
Copy link
Author

@raymond1012 是不是应该把上面的解决方案应用到源码中,这样可以关闭很多issue

@Android4MediaPlayer
Copy link

能提供下sample url吗

@drivedreams
Copy link
Author

@Android4MediaPlayer 我提供的Url现在都是动态变化的。给你了也很快就会失效不具备代表性。这个BUG应该对于所有的点播的M3U8视频都都会出现,而且是必现的。随便找个M3U8测试下呗。

Ricky-Yeh pushed a commit to CyberLinkCorp/ijkplayer that referenced this issue Mar 8, 2018
…ONTINUITY tag

[why] FFmpeg doesn't support such tag handling for hls streaming.
[how] Apply patch from bilibili#2874
reviewer: Todd_Chen
@yeshibuzhong
Copy link

yeshibuzhong commented Jun 21, 2018

@CloudIAU 你好, 我按照你说的方案添加了以后(我将ffplay里边的几个hls.c都做了修改), 使用编译好的ijk播放视频的时候只要快进就崩溃.信息如下:
c80117f4-e257-4761-afc1-c04b83ff094d
请问哪里出了问题, 万分感谢!!!

@linjiansheng
Copy link
Contributor

@yeshibuzhong 是不是 seq_no >= pls->n_segments?

@linjiansheng
Copy link
Contributor

linjiansheng commented Jun 26, 2018

@Android4MediaPlayer @CloudIAU
ffmpeg 9e9d67d5489be7403017b9279d33334a03835601 vformat/hls: fix duration
这个提交有一些问题:

        if (c->playlists[minplaylist]->finished) {
            struct playlist *pls = c->playlists[minplaylist];
            int seq_no = pls->cur_seq_no - pls->start_seq_no;
            if (seq_no < pls->n_segments && s->streams[pkt->stream_index]) {
                struct segment *seg = pls->segments[seq_no];
                int64_t pred = av_rescale_q(seg->previous_duration,
                                            AV_TIME_BASE_Q,
                                            s->streams[pkt->stream_index]->time_base);
                int64_t max_ts = av_rescale_q(seg->start_time + seg->duration,
                                              AV_TIME_BASE_Q,
                                              s->streams[pkt->stream_index]->time_base);
                /* EXTINF duration is not precise enough */
                max_ts += 2 * AV_TIME_BASE;
                if (s->start_time > 0) {
                    max_ts += av_rescale_q(s->start_time,
                                           AV_TIME_BASE_Q,
                                           s->streams[pkt->stream_index]->time_base);
                }
                if (pkt->dts != AV_NOPTS_VALUE && pkt->dts + pred < max_ts) pkt->dts += pred;
                if (pkt->pts != AV_NOPTS_VALUE && pkt->pts + pred < max_ts) pkt->pts += pred;
            }
        }

上面这段代码目的是为了在有 #EXT-X-DISCONTINUITY 修复pts,加上 previous_duration。
1,上面修复pts代码应该放在 av_read_frame 后面,即

                ret = av_read_frame(pls->ctx, &pls->pkt);
                if (ret < 0) {
                    if (!avio_feof(&pls->pb) && ret != AVERROR_EOF)
                        return ret;
                    reset_packet(&pls->pkt);
                    break;
                } else {
                    /* stream_index check prevents matching picture attachments etc. */
                    if (pls->is_id3_timestamped && pls->pkt.stream_index == 0) {
                        /* audio elementary streams are id3 timestamped */
                        fill_timing_for_id3_timestamped_stream(pls);
                    }
                    // 修复pts代码应该放在这里,否则 seek 到下一个 #EXT-X-DISCONTINUITY pts 会陷入死循环
                    if (pls->pkt.pts != AV_NOPTS_VALUE)
                        pkt_ts =  pls->pkt.pts;
                    else if (pls->pkt.dts != AV_NOPTS_VALUE)
                        pkt_ts =  pls->pkt.dts;
                    else
                        pkt_ts = AV_NOPTS_VALUE;


                    c->first_timestamp = s->start_time != AV_NOPTS_VALUE ? s->start_time : 0;

                }

                if (pls->seek_timestamp == AV_NOPTS_VALUE)
                    break;
                
                if (pls->seek_stream_index < 0 ||

另外一个问题就是为什么要做这个判断 seq_no < pls->n_segments?
这边测试 发现 seg no 没有对上,看过去像是 pls->cur_seq_no 已经+1, open 了新的 ts文件, 但是 av_read_frame 返回的还是 上一个 ts文件的最后一个包。这个是不是因为 read_data 读取buf 在ffmpeg io 中被缓存了?日志如下:

 E/IJKMEDIA: 6666 pkt->pts=-9223372036854775808, pkt->dts=-9223372036854775808, seq_no=23, previous_duration=300002000,
 E/IJKMEDIA: 6666 pkt->pts=-9223372036854775808, pkt->dts=-9223372036854775808, seq_no=23, previous_duration=300002000,
 E/IJKMEDIA: 6666 pkt->pts=-9223372036854775808, pkt->dts=-9223372036854775808, seq_no=23, previous_duration=300002000,
 E/IJKMEDIA: 6666 pkt->pts=-9223372036854775808, pkt->dts=-9223372036854775808, seq_no=23, previous_duration=300002000,
 E/IJKMEDIA: 6666 pkt->pts=-9223372036854775808, pkt->dts=-9223372036854775808, seq_no=23, previous_duration=300002000,
 E/IJKMEDIA: 6666 cur_seq_no=24
 I/IJKMEDIA: Opening 'http://vipgdbgp.vod.tv.itc.cn/m3u8?&start=0&end=5.32&k=hWODtfkIlB6OWhcsWFvHbD168Vebz89bDEWNoL8VP6kRDEKNPFXUyYbS0pbcWOoGyG
tUJlzSwdoSwdomNGopwGopxVoSrGoSrBhRODOpCCqmvm0pvUhRYRzSwWj9lvzSvUqCPCJSX3Tkk7oL1gqpxmqK3KJvsUTka5qkkBxvqfomkCo
ding
 I/lebotv.danmaku.ijk.media.player.IjkMediaPlayer: onNativeInvoke 1
 I/IJKMEDIA: Hit DNS cache hostname = vipgdbgp.vod.tv.itc.cn
 I/lebotv.danmaku.ijk.media.player.IjkMediaPlayer: onNativeInvoke 131073
 I/lebotv.danmaku.ijk.media.player.IjkMediaPlayer: onNativeInvoke 131074
 I/lebotv.danmaku.ijk.media.player.IjkMediaPlayer: onNativeInvoke 131073
 I/lebotv.danmaku.ijk.media.player.IjkMediaPlayer: onNativeInvoke 131074
 I/lebotv.danmaku.ijk.media.player.IjkMediaPlayer: onNativeInvoke 2

这里刚刚好是遇到#EXT-X-DISCONTINUITY,打开下一个 ts文件
pts 27907200 明显是没有修复成功

 E/IJKMEDIA: 6666 pkt->pts=27907200, pkt->dts=27903600, seq_no=24, previous_duration=600004000,
 E/IJKMEDIA: 6666 pkt->pts=54914760, pkt->dts=54907560, seq_no=24, previous_duration=600004000,
 I/hpplay-java: [main]:PhoenixPlayer:onBufferingUpdate percent: 24
 E/IJKMEDIA: 6666 pkt->pts=54929160, pkt->dts=54911160, seq_no=24, previous_duration=600004000,
 E/IJKMEDIA: 6666 pkt->pts=54921960, pkt->dts=54914760, seq_no=24, previous_duration=600004000,
 E/IJKMEDIA: 6666 pkt->pts=54918360, pkt->dts=54918360, seq_no=24, previous_duration=600004000,

@yeshibuzhong
Copy link

yeshibuzhong commented Jun 30, 2018

@drivedreams 你好, 我按照CloudIAU方法, 快进的时候回崩溃掉, 我用的是ijk0.88 最新版, 请问你知道怎么回事吗?
上边有我的崩溃信息

@xm2017
Copy link

xm2017 commented Oct 18, 2018

@CloudIAU 大佬这段代码有pull 到源码上么

@chenzh2017
Copy link

各位大佬,这个问题解决了吗,现在测试的情况来看,拖放的时候加载依然缓慢,比mp4格式的拖放要慢很多

@longshao1234
Copy link

@chenzh2017,一样的,遇到了,mp4播放不会慢,但是用么m3u8播放就会加长,让后循环播放不得行

@shenhuihui
Copy link

@CloudIAU 按照你的修改后Seek正常了

你好,请问iOS上怎么解决这个问题

@nicolasiJasica
Copy link

nicolasiJasica commented Apr 6, 2021

播放M3u8的视频的时候,最好:
1.将max-buffer-size探测的长度尽可能的缩小
2.enable-accurate-seek关闭这个属性

其他的都是用默认配置就好,亲测有效,播放下载的M3u8视频 拖动进度, 效果挺好(大概1-2秒),没那么长的缓冲时间了

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests