为了帮助您快速搭建“抖音”同款短视频场景,解决用户滑动切换视频时首帧加载慢、资源消耗高的问题,播放器 SDK 基于抖音亿级日活跃用户的真实反馈和大规模实践经验,提供两大最佳策略:预加载策略(提前缓存视频数据,提升起播速度)和预渲染策略(提前创建播放器实例,优化切换体验)。本文将指导您如何开启和配置这两项策略,帮助您在性能与体验之间达到平衡。详细功能介绍,请见抖音同款短视频最佳实践。
注意
该功能仅高级版支持。请确保您已购买高级版的 License,详见播放器 License。
注意
预加载策略支持 Vid、 DirectUrl 和 VideoModel 三种形式的播放源。为了确保预加载的缓存能够被成功命中,在设置预加载列表时需要注意以下几点:
resolution 字段,用以确定需要预加载哪一个清晰度的视频。后续实际播放该视频时,通过 setMediaSource 设置的播放源,其 resolution 字段也必须与预加载时设置的 resolution 保持一致,否则将无法命中缓存。cacheKey 字段,该 cacheKey 只需保证其全局唯一即可。后续实际播放该视频时,通过 setMediaSource 设置的播放源,其 cacheKey 字段也必须与预加载时设置的 cacheKey 保持一致,否则将无法命中缓存。// 方法定义见 TTVideoEngineStrategy // 开启预加载策略,建议在进入短视频页面开启 TTVideoEngineStrategy.enableEngineStrategy( strategyType: TTVideoEngineStrategyType.preload, scene: TTVideoEngineStrategyScene.smallVideo); // 设置预加载列表 static Future<void> setStrategyVideoSources({required List<TTVideoEngineMediaSource> videoSources}) // 更新预加载列表 static Future<void> addStrategyVideoSources({required List<TTVideoEngineMediaSource> videoSources}) // 指定预加载列表的起始位置。setStrategyVideoSources 后,可通过此方法立即启动预加载任务。 // 若未设置 index,用户进入 feed 后,默认从第二个视频开始加载 N 个;若设置了 index,则从所设 index 位置开始加载 N 个。 static Future<void> startPreloadWithIndex({required int index}) // 关闭全部策略 TTVideoEngineStrategy.clearAllEngineStrategy();
监听命中预加载缓存:预加载成功后,播放器播放对应的播放源时,会优先使用预加载的缓存起播,从而加快起播速度。要验证预加载是否生效,您可监听播放器实例的 onMediaCacheChange 回调。当播放器开始播放一个视频时,如果该视频成功命中了预加载的缓存,此回调便会被触发,且 cacheSize 会大于 0。
// Media cache change callback (MDL hit cache info). // - key: MDL task key // - cacheSize: hit cache size (bytes) void Function(String key, int cacheSize)? onMediaCacheChange;
在类抖音(TikTok)的 Feed 流场景中,为了加快用户上下滑动切换视频时的首帧速度,除了采用预加载提前缓存视频数据外,另一种常见的手段是提前创建下一个视频的播放器实例。当用户滑动到下一个视频时,播放器已准备就绪,从而节省了创建实例的时间,加快视频首帧展示。然而,手动管理多实例存在诸多弊端:
预渲染策略正是为了解决上述问题而设计的。它是一种更智能的多实例管理方案,能够在播放任务较为空闲的时机,自动提前为您创建并准备好下一个播放器实例,从而在性能与体验之间达到平衡。
在您的短视频播放页面(例如 Feed 流页面)初始化时,开启预渲染策略。
// 方法定义见 TTVideoEngineStrategy // 开启预渲染策略,建议在进入播放页面开启 TTVideoEngineStrategy.enableEngineStrategy( strategyType: TTVideoEngineStrategyType.preRender, scene: TTVideoEngineStrategyScene.smallVideo, );
与预加载策略一样,预渲染策略也需要您提供一个视频源列表,以便 SDK 知道接下来可能要播放哪些视频。
// 设置、更新预渲染列表的方法与预加载策略完全一致,只需设置一次即可 TTVideoEngineStrategy.setStrategyVideoSources({ required List<TTVideoEngineMediaSource> videoSources, });
预渲染策略通过播放源的唯一 vid 来匹配和复用提前创建的播放器实例。因此,为每个播放源设置一个稳定且唯一的 vid 至关重要。对于 DirectURL 播放源,请务必在构造时传入 vid。
TTVideoEngineUrlSource.initWithURLVidCacheKey 工厂函数快速创建:// 1.49.10+ 推荐 final urlSource = TTVideoEngineUrlSource.initWithURLVidCacheKey( url: mainPlayUrl, vid: widget.episode.vid, // 必须传入唯一的 vid cacheKey: cacheKey, );
final urlSource = TTVideoEngineUrlSource( urls: [url], cacheKey: cacheKey, vid: widget.episode.vid, // 同样需要传入唯一的 vid );
由于 Android 和 iOS 底层的预渲染实现存在差异,Flutter SDK 提供了两个平台专属的回调,用于在预渲染的播放器创建完成后,进行一些必要的起播前配置。
onCreatePreRenderEngine 回调。在该回调中,您需要手动创建播放器实例、设置播放源,并可以进行其他自定义配置(如开启硬解)。// 初始化策略模块,以便设置回调 TTVideoEngineStrategy.init(); TTVideoEngineStrategy.onCreatePreRenderEngine = ((source) async { // 该回调仅在 Android 端生效 VodPlayerFlutter player = VodPlayerFlutter(); // 1. 创建播放器,必须传入 source.getVid 和 preCreated: true String coreHashHex = await player.createPlayer(vid: source.getVid, preCreated: true); // 2. [必须] 为预渲染的播放器设置播放源 await player.setMediaSource(source); // 3. [可选] 根据需求自定义设置,如是否开启硬解等 player.setHardwareDecode(<enable>); // ... // 4. 注意:createPlayer 和 setMediaSource 必须 await 执行完毕后再返回 player return player; });
onPlayerCreated 回调,即可获取到已准备好的播放器实例,并进行起播前的自定义配置。TTVideoEngineStrategy.onPlayerCreated = ((player) { // 该回调仅在 iOS 端生效 // 不同于 Android,您无需在此处创建播放器或设置播放源 // [可选] 根据需求自定义设置,如是否开启硬解等 player.setHardwareDecode(<enable>); });
在您的 Feed 流滑动逻辑中,当需要播放下一个视频时,只需在调用 createPlayer 时传入该视频对应的 vid。如果预渲染已成功完成,SDK 将立即返回一个已经提前创建好的播放器实例。
// 在用户滑动到下一页时... VodPlayerFlutter player = VodPlayerFlutter(); // 传入目标视频的 vid。如果命中预渲染,SDK 会直接返回已创建的实例 await player.createPlayer(vid: source.getVid);
预渲染并不总是成功(例如,在用户快速滑动时,策略可能还未执行)。您可以在 createPlayer 之后,通过播放器的 isPreRender 属性来判断获取到的实例是否是预渲染的。
// 在 createPlayer 调用之后 bool isPreRender = await _player!.isPreRender; if (isPreRender) { // 成功命中了预渲染 } else { // 未命中,这是一个新创建的播放器 }
您可以将预渲染完成的视频首帧直接用作视频封面。相比于传统的图片封面,这种方式不仅展示速度更快,还能因为复用了视频数据而更节省流量。在创建下一个视频的播放器之前,调用静态方法 VodPlayerFlutter.preRenderFirstFrame。
// 方法定义见 VodPlayerFlutter // vid: 待预渲染首帧视频的 vid // viewId: 用于展示视频首帧的渲染 View 的 ID // displayMode: 首帧的填充模式 (TTVideoEngineScalingMode) String? res = await VodPlayerFlutter.preRenderFirstFrame( vid: vid, viewId: viewId, displayMode: displayMode, ); // 在调用 preRenderFirstFrame 之后,再正常创建播放器并播放 VodPlayerFlutter player = VodPlayerFlutter(); String coreHashHex = await player.createPlayer(vid: vid); player.setMediaSource(source); // 注意:这里的 viewId 必须与 preRenderFirstFrame 中使用的 viewId 一致 player.setPlayerContainerView(viewId); player.play();