最近优化了公司视频项目的视频Feed流缓存方案,在此记录一下。
1. 旧方案实现原理
旧方案采用了基本的 AVPlayer 播放机制,主要依靠以下核心组件:
-
AVPlayer:作为视频播放的核心控制器,每次播放新视频时都创建新实例
-
AVPlayerItem:每个视频内容对应一个独立的 AVPlayerItem 实例
整个方案基于点播式加载模式,即用户请求播放某个视频时,才开始加载该视频资源,没有提前预加载或资源复用机制。
2. 旧方案实现流程
- 视频请求阶段:
- 用户滑动到新视频
- 通过 URL 创建新的 AVPlayerItem 实例
- 通过 AShortVideoSourceControl.shared.sourceBy(url) 获取 AVPlayerItem
- 播放器替换阶段:
- 调用 player.replaceCurrentItem(with: item) 替换播放内容
- 从头开始加载视频流(没有预加载)
- 资源加载阶段:
- AVPlayerItem 开始从 URL 加载资源
- 需要等待初始缓冲完成才能开始播放
- 没有对即将播放的视频进行任何形式的预处理
- 播放控制阶段:
- 播放开始后通过 playerToPlay() 控制播放状态
- 通过 seekToPlay 等方法控制播放位置
- 依靠 playerPeriodicTime 等观察者监听播放进度
- 资源释放阶段:
- 播放新视频时,之前的资源没有被合理复用
- 前后台切换时直接暂停播放,无资源优化处理
3. 基于旧方案的痛点和需要解决的关键问题
播放流畅度和延迟问题
- 视频切换时的加载延迟:每次滑动切换视频都需要重新创建 AVPlayerItem 和重新加载资源,导致用户体验到明显的加载等待
- 播放开始延迟:没有预加载机制,必须等待初始缓冲完成才能开始播放
4. 新方案的架构设计
新方案采用多层架构设计,以解决旧方案的问题并提升用户体验。核心架构如下:
┌───────────────────────────────────────────────────────┐
│ AShortVideoPlayerManager │
│ (中央控制器) │
└───────────┬─────────────────┬──────────────┬─────────┘
│ │ │
▼ ▼ ▼
┌────────────────┐ ┌─────────────────┐ ┌──────────────────┐
│AShortVideoPool │ │AShortVideoSource│ │AShortVideoNetwork│
│ (播放器缓存池) │ │ (资源管理) │ │ (网络监控) │
└───────┬────────┘ └────────┬────────┘ └────────┬─────────┘
│ │ │
▼ ▼ ▼
┌────────────────┐ ┌─────────────────┐ ┌──────────────────┐
│ AVPlayer缓存 │ │ AVPlayerItem │ │ 网络策略调整 │
│ 实例管理 │ │ 创建管理 │ │ │
└────────────────┘ └─────────────────┘ └──────────────────┘
核心组件介绍:
- AShortVideoPlayerManager
- 作为整个系统的中央调度器
- 管理播放状态和提供播放控制接口
- 协调各个子系统之间的交互
- AShortVideoPlayerPool
- 管理预加载的 AVPlayer 实例
- 维护固定大小的播放器缓存池
- 提供缓存查询、更新和释放机制
- AShortVideoSourceControl
- 负责创建和管理 AVPlayerItem
- 解决 AVPlayerItem 复用限制问题
- AShortVideoNetworkMonitor
- 监控网络状态变化
- 根据网络类型调整加载策略
4. 新方案运作机制
预加载与缓存机制:
┌──────────────┐ 预加载请求 ┌──────────────┐
│ Feed 列表 │ ───────────────> │ 播放器管理器 │
└──────────────┘ └───────┬──────┘
│
▼
┌──────────────┐ ┌──────────────────┐
│ 网络监控器 │ ◀───────────── │ 优先级判断 │
└───────┬──────┘ └────────┬─────────┘
│ │
▼ ▼
┌──────────────────────┐ ┌──────────────────┐
│ 根据网络类型调整策略 │ ─────> │ 创建预加载任务 │
└──────────────────────┘ └────────┬─────────┘
│
▼
┌──────────────┐ ┌──────────────────┐
│ 更新缓存池 │ <────────────── │ 资源加载完成 │
└──────────────┘ └──────────────────┘
播放流程:
用户滑动到新视频
│
▼
┌──────────────────────────────┐
│ 检查缓存池是否有预加载播放器 │
└─────────────┬────────────────┘
│
┌───────┴───────┐
│ │
▼ ▼
┌──────────┐ ┌───────────────┐
│ 命中缓存 │ │ 未命中缓存 │
└─────┬────┘ └───────┬───────┘
│ │
▼ ▼
┌──────────────┐ ┌─────────────────┐
│立即播放预加载 │ │ 创建新播放器实例 │
│ 的播放器 │ │ 开始加载资源 │
└─────┬────────┘ └────────┬────────┘
│ │
└────────┬───────────┘
│
▼
┌─────────────────────────────────┐
│ 开始为下一个可能的视频预加载资源 │
└─────────────────────────────────┘
动态网络适应机制:
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ WiFi 网络 │ │ 移动数据网络 │ │ 无网络状态 │
└───────┬───────┘ └───────┬───────┘ └───────┬───────┘
│ │ │
▼ ▼ ▼
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│预加载更多内容 │ │ 限制预加载范围 │ │ 暂停所有预加载 │
│(预加载60秒视频) │ │ (预加载5秒视频) │ │ 仅保留当前播放 │
└───────┬───────┘ └───────┬───────┘ └───────┬───────┘
│ │ │
└───────────────┬───────────┘ │
│ │
▼ │
┌─────────────────────────┐ │
│根据网络状况动态调整缓存池 │ │
└──────────────┬──────────┘ │
│ │
└────────────────┬─────────────────┘
│
▼
┌─────────────────────────┐
│ 网络状态变化时 │
│ 策略自动切换 │
└─────────────────────────┘
新方案的优点
1. 用户体验大幅提升
- 即时响应:通过预加载机制实现滑动切换时几乎零等待的播放体验
- 无缝切换:用户上下滑动时,下一个视频已准备好播放,无需等待加载
- 播放流畅:预加载视频内容有效减少了播放过程中的卡顿
- 加载速度快:利用缓存池中已加载的内容,显著减少了加载时间
2. 资源利用效率提高
- 缓存复用:复用已加载的播放器实例,避免重复创建
- 内存管理优化:通过固定大小的缓存池控制内存占用
- 播放器实例控制:有效管理 AVPlayer 实例,避免资源泄漏
3. 网络适应性强
- 智能网络感知:根据网络类型(WiFi/移动网络)自动调整预加载策略
- 流量节省:移动网络下限制预加载时长,减少不必要的流量消耗
- 弱网优化:在网络状况不佳时依然能提供相对流畅的播放体验
4. 架构设计先进
- 模块化设计:各组件职责明确,便于维护和扩展
- 灵活配置:预加载深度、缓存池大小等参数可灵活配置
- 并发控制:限制同时预加载的任务数量,避免网络拥塞
5. 特殊情况处理完善
- 直播流区分处理:对直播内容采用不同的加载策略
- 前后台切换优化:应用进入后台时释放非必要资源
- 异常恢复机制:网络波动或加载失败时有完善的恢复策略
6. 技术优势
- AVPlayer 缓存优势:缓存完整的 AVPlayer 而非仅 AVPlayerItem,保留了所有播放状态
- 优先级队列:为不同重要程度的加载任务分配不同的系统优先级
- 预加载深度控制:根据实际情况动态调整预加载的视频数量和内容长度