视频播放器播放网络视频时的缓存播放策略问题请教

166 天前
 Kinnikuman

比如上面的 telegram 上的视频播放,在进度条能看到预先加载了一段。同样的 infuse 和 IINA 等播放器也都是会做预加载功能(可以明显在进度条中看到)。

那么预加载的视频是在内存中还是硬盘中?因为视频占用体积很大,不可能缓存在内存中吧?缓存在硬盘中,播放策略是什么?缓存的内容是视频文件本身,还是需要解码的 Packet?

1587 次点击
所在节点    程序员
12 条回复
ysc3839
166 天前
一般是内存
MrDream
166 天前
是在内存里。缓存的是 h264 和 h265 这种压缩后的数据,体积也还好,不算大。像你图中缓存了 1/3 的话,大概 30 多 MB 吧。又不是缓存解码后的 YUV ,那个太大了。
Kinnikuman
166 天前
@ysc3839
@MrDream
就像 infuse 观看一个 100 多 G 的视频,我看进度条也能缓存很多,这内存吃不消吧。

缓存肯定是 Packet 而不是 Frame ,解码后的 Frame 太大了。
IvanLi127
166 天前
那个缓存不是指远程文件下载到本地的缓存嘛?我感觉是在磁盘的临时文件中存着原始文件内容,拖动进度条时再读到内存处理
我凭直觉猜的,蹲大佬科普
kuanat
166 天前
@Kinnikuman

如果是本地文件,缓存进度条可能和内存没有太大关系。

缓存的真实量取决于内核管理的 page cache ,假如剩余空间足够大,这个 page cache 会在第一次访问文件的时候把存储中的内容全部加载,不内存不足的话会加载一部分。在应用程序看来,fd 已经在了,stream 读取也开始了,就看你读多少。

这个 page cache 的容量主要影响拖动进度条,超出去了就会去读存储,没超的话,内核会有自己算法加载新的内容进有限的 page cache 里。
PTLin
166 天前
这完全不就是软件策略的原因吗,有可能是下载到临时文件夹然后又加载到内存一部分
MrDream
166 天前
我们做的手机端播放器,缓存的几秒视频,就放在 malloc 出来的内存里,没落盘。
changxiangzhong
166 天前
4k uhd Netflix 的 typical bitrate 是 15mbps 。其实缓存在磁盘/nand/emmc 是足够的。没必要缓存在内存的。假设缓存 8 秒,也 15MB 了。还是很大的,没必要
expy
164 天前
一般都放内存吧,存的是解码之前的视频音频,hevc aac 之类的。
https://mpv.io/manual/stable/#cache
Kinnikuman
163 天前
@MrDream @PTLin @expy @kuanat 大佬们请看我的附言,有了比较详细的描述,帮我指正下。
expy
163 天前
@Kinnikuman 我没有播放器开发经验,实现细节不知道。方案一应该不用下载两遍,比如搞个固定大小的缓冲区,下载线程往里面写,播放线程读。

mpv 的 FAQ 有一点相关描述。
https://github.com/mpv-player/mpv/wiki/FAQ#user-content-Why_were_some_cache_options_removed_or_changed_stream_cache
kuanat
158 天前
@Kinnikuman #10

这个场景我也没什么经验,结合 #9 #11 楼的引用随便说说。

1. 硬盘、内存速度

目前常见的硬件,即便是机械硬盘,其加载速度也远大于被加载视频的播放速度,更不用说内存了。

2. 硬盘文件的缓存机制

我在前面的回复里解释了一部分。再进一步说,你的应用不会直接读写硬盘,操作系统内核替你“智能”完成硬盘文件在内存中的缓存工作。

当然你也可以手动申请内存,然后读取硬盘文件内容之后保存在申请到的内存中,同时自己编写缓存内容更新的逻辑。这样做只适用于非常有限的场景,比如磁盘 IO 长期被后台应用占用,或者需要反复在多个超大(超过内存容量)文件之间切换。对于一个视频播放器而言,不需要考虑这些事情。

3. 缓存容量设定

我理解你设想中的方案都有一个隐式前提,视频文件非常大,需要尽可能多缓存。但是一般来说,除非你要提供“保存视频文件到本地以备无网络时观看”这个本质上名为“下载”功能,绝大多数时间只缓存一定量的数据即可。换个说法,下载功能可以是按需( on-demand )的,填满缓存容量就可以暂停。

也就是说,内存、硬盘的二级缓存机制是没有必要的。用内存缓存的唯一目的就是避免硬盘读写,硬盘缓存是解决内存缓存不够的才用的,一旦使用硬盘作为缓存就没必要再做内存缓存。

4. 缓存的内容取决于来源协议或者格式

根据上面的分析,本地文件技术上说是不需要缓存的,或者说不需要你手动缓存的。

网络视频有可能是以网盘作为后端,本质上还是特定格式视频文件的形式,也有可能是基于某种流媒体协议。对于前者,缓存的就是文件。对于后者,缓存的是视频流意义上的 packet (非网络意义的 packet )。

5. 播放器本身不关心缓存后端的来源是什么,只关心从特定的来源流式读取。

比如桌面浏览器可以指定缓存后端是内存还是硬盘,但内嵌播放器是无需关心的。播放器这类本地应用,也只需要为数据流的消费者提供一个来源。即播放逻辑永远是固定的,至于来源(缓存)与播放行为是无关的。

6. 下载、解码和播放之间解耦

前面有人引用 mpv 处理缓存的逻辑,它缓存的对象是 packet 经过 demuxer 处理后的数据,即缓存发生于 demuxer 和 decoder 之间。前面 3/4/5 点综合起来说的也是这个意思,下载并不是直接对接播放的,缓存发生于中间解码的部分。不需要对下载和播放逻辑做特殊处理,所有的特殊行为都在中间解码阶段。

7. 缓存的底层数据结构

缓存确实可以用 FIFO 数据结构表达,考虑到播放器的使用场景,固定容量的缓存一定会遇到周期性完整替换的需求。

可以考虑 ring buffer ,这样就需要控制生产者写入速率与消费者播放速率一致。或者考虑 double buffering ,用手动切换缓存后端简化速率匹配。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1047868

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX