比如上面的 telegram 上的视频播放,在进度条能看到预先加载了一段。同样的 infuse 和 IINA 等播放器也都是会做预加载功能(可以明显在进度条中看到)。
那么预加载的视频是在内存中还是硬盘中?因为视频占用体积很大,不可能缓存在内存中吧?缓存在硬盘中,播放策略是什么?缓存的内容是视频文件本身,还是需要解码的 Packet?
![]() |
1
ysc3839 91 天前 via Android
一般是内存
|
![]() |
2
MrDream 91 天前 via iPhone
是在内存里。缓存的是 h264 和 h265 这种压缩后的数据,体积也还好,不算大。像你图中缓存了 1/3 的话,大概 30 多 MB 吧。又不是缓存解码后的 YUV ,那个太大了。
|
![]() |
3
Kinnikuman OP |
![]() |
4
IvanLi127 91 天前
那个缓存不是指远程文件下载到本地的缓存嘛?我感觉是在磁盘的临时文件中存着原始文件内容,拖动进度条时再读到内存处理
我凭直觉猜的,蹲大佬科普 |
![]() |
5
kuanat 91 天前 via Android
@Kinnikuman
如果是本地文件,缓存进度条可能和内存没有太大关系。 缓存的真实量取决于内核管理的 page cache ,假如剩余空间足够大,这个 page cache 会在第一次访问文件的时候把存储中的内容全部加载,不内存不足的话会加载一部分。在应用程序看来,fd 已经在了,stream 读取也开始了,就看你读多少。 这个 page cache 的容量主要影响拖动进度条,超出去了就会去读存储,没超的话,内核会有自己算法加载新的内容进有限的 page cache 里。 |
![]() |
6
PTLin 91 天前
这完全不就是软件策略的原因吗,有可能是下载到临时文件夹然后又加载到内存一部分
|
![]() |
7
MrDream 91 天前 via iPhone
我们做的手机端播放器,缓存的几秒视频,就放在 malloc 出来的内存里,没落盘。
|
8
changxiangzhong 91 天前 via Android
4k uhd Netflix 的 typical bitrate 是 15mbps 。其实缓存在磁盘/nand/emmc 是足够的。没必要缓存在内存的。假设缓存 8 秒,也 15MB 了。还是很大的,没必要
|
![]() |
9
expy 89 天前
一般都放内存吧,存的是解码之前的视频音频,hevc aac 之类的。
https://mpv.io/manual/stable/#cache |
![]() |
10
Kinnikuman OP |
![]() |
11
expy 88 天前
@Kinnikuman 我没有播放器开发经验,实现细节不知道。方案一应该不用下载两遍,比如搞个固定大小的缓冲区,下载线程往里面写,播放线程读。
mpv 的 FAQ 有一点相关描述。 https://github.com/mpv-player/mpv/wiki/FAQ#user-content-Why_were_some_cache_options_removed_or_changed_stream_cache |
![]() |
12
kuanat 83 天前 ![]() @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 ,用手动切换缓存后端简化速率匹配。 |