V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
fgodt
V2EX  ›  程序员

关于播放器无缝切换清晰度

  •  
  •   fgodt ·
    FgoDt · 2020-01-08 14:37:02 +08:00 · 4689 次点击
    这是一个创建于 1790 天前的主题,其中的信息可能已经有所发展或是发生改变。
    最近在自己实现播放器,有一个无缝切换清晰度的功能不知道怎么实现,希望有大佬给点指导
    我能想到的实现有两种
    1. 两个播放器并行,一个前台播放一个后台 seek 到前台播放器附近然后解码,等后台和前台 pts 相同再显示。这样的话必定有一段时间有两个播放器并行,开销很大
    2. 两个流,一个流正在解码播放,另一个要切换的流先 seek 再读取等到关键帧与播放的 pts 相同时切换解码器。这种方式我感觉过于复杂,而且不太好完美实现
    希望有大佬指点下
    第 1 条附言  ·  2020-01-08 17:55:03 +08:00

    搜索了一下午,如果视频源非切片的话最好的方式还是方案2

    下面是我的一个不成熟想法

    由于我用的是ffmpeg来开发,其他分辨率的流可以seek 到正在播放的流后面一点的关键帧,然后通过side_data 通知解码器播放的数据变化了,然后重新开启一个新的解码器

    A---->read---------------> 
                              | pkt = A.pts< B.pts ?A.pkt:B.pkt  |---->cache---->decoder----->render
    B->seek(a.pts+2s)--read-->
    

    这样整个播放器就会需要所有模块自己处理切换事件

    13 条回复    2020-01-09 10:14:01 +08:00
    royzxq
        1
    royzxq  
       2020-01-08 14:45:22 +08:00
    方案 1 是 B 站 3 年前的解决方案,高切低几乎无感知但是低切高会感知到并且有一丝卡顿。
    royzxq
        2
    royzxq  
       2020-01-08 14:46:20 +08:00   ❤️ 1
    要么看看 MEPG-DASH, 现在 B 站的无感知切换应该和这个有很大关系。
    fgodt
        3
    fgodt  
    OP
       2020-01-08 14:51:29 +08:00
    @royzxq 非常感谢,网页上的用 DASH 是可以实现的,他们用的 MSE 接口。不过我正在写原生平台的播放器,感觉要实现 DASH 的方法就只能方案 2
    wanguorui123
        4
    wanguorui123  
       2020-01-08 14:58:43 +08:00   ❤️ 1
    把不同分辨率的视频切片成 TS 格式,播放器切换后切到其他清晰度的 TS 分片上,可以无缝切换清晰度。
    fgodt
        5
    fgodt  
    OP
       2020-01-08 15:07:13 +08:00
    @wanguorui123 这样的话就要求视频源必须处理成 ts 切片,如果用户的视频源是 mp4 mkv 就无法切换
    hardwork
        6
    hardwork  
       2020-01-08 21:09:59 +08:00 via Android
    用 2 肯定可行,原理上没啥问题,剩下的都是特定场景的有特定业务场景吧,点播
    hardwork
        7
    hardwork  
       2020-01-08 21:16:08 +08:00 via Android   ❤️ 1
    用 2 肯定可行,原理上没啥问题,剩下的都是业务逻辑。收到切换事件后开始并行解码目标分辨率,然后保证无缝接入渲染队列,再把原分辨率解码停了,具体实现还是看你整个架构
    fgodt
        8
    fgodt  
    OP
       2020-01-08 21:46:57 +08:00
    @hardwork 是的必须要让每个模块都支持动态切换,其实 pc 两个播放器也能搞定,但是 Android 这样的设备就比较吃力
    hardwork
        9
    hardwork  
       2020-01-08 21:53:22 +08:00   ❤️ 1
    没看清楚,我好像说的是方案 1

    看了看你说的方案 2,意思是想要解码前在关键帧处拼成一段码流,这样完全没有两个并行解码了,是这个意思吧.

    不想要并行解码那么先停掉第一个解码器,再开启第二个解码器就可以了,事实也确实应该这么做,并行逻辑反而复杂了,当然这个切换期间保证渲染 buffer 不要耗尽,保证接缝处时间戳连续就可以了.

    除非是带 startcode 的 h264 码流,且 idr 帧前是有 sps,pps 的这种你可以接起来送到解码器,否则像 mp4,mkv 不同分辨率的还是要重开解码器的
    fgodt
        10
    fgodt  
    OP
       2020-01-08 22:14:40 +08:00
    @hardwork 是的如果要完美支持的话就应该就只能这样操作。
    根据你最后这种思路,还有一种想法如果我把 sps pps 放到 pkt 再传给解码器不知道 ffmpeg 里的 codec 支不支持这种操作。支持的话我人为的将 mp4 等容器的数据封装成 NAl 的数据,切换时附上 sps pps 是不是就无需重新开解码器了
    mxT52CRuqR6o5
        11
    mxT52CRuqR6o5  
       2020-01-09 09:39:27 +08:00 via Android   ❤️ 1
    爱奇艺似乎是切片,然后通过 js 把文件流推给播放器
    fgodt
        12
    fgodt  
    OP
       2020-01-09 10:07:31 +08:00
    @mxT52CRuqR6o5 是的网页的播放器都是通过 ts 或者 dash 切片传给浏览器,这样确实可以无缝
    fgodt
        13
    fgodt  
    OP
       2020-01-09 10:14:01 +08:00
    @hardwork 非常感谢大佬的提示,我做了一个简单的验证在两个文件都是 264 编码的情况下无论分辨率和码率都可以通过 NAl 的封装来达到无缝切换的效果。264 的解码器似乎都支持改变 sps pps
    由此我还有一个猜想 如果都是 AAC 的流我们使用 ADTS 的流是否也可以任意改变呢?
    希望这里的提示对其他还在困惑的人有帮助
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2990 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 30ms · UTC 14:53 · PVG 22:53 · LAX 06:53 · JFK 09:53
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.