自适应码流(Adaptive Bitrate Streaming, ABR)播放是指在播放过程中根据用户的网络状况动态地、无缝地切换到最合适的清晰度,从而在保障视频流畅播放的同时,为用户提供最佳的观看体验。播放器 SDK 支持以下两种视频源的 ABR 播放:
本文指导您以 Vid 播放模式实现火山引擎自研协议 DASH 和 HLS 视频的 ABR 播放。
在集成 ABR 播放功能前,请确保您已完成以下操作:
获取 License:ABR 播放为增值功能。请确保您的 License 已包含 ABR 播放功能,详细步骤请见 License 管理。
集成高级版 SDK 和 ABR 模块:参考集成 SDK 文档集成 1.47.2.8 或之后版本的高级版 SDK。
def ttsdk_version = "1.47.3.6" implementation "com.bytedanceapi:ttsdk-player_premium:$ttsdk_version" implementation "com.bytedanceapi:ttsdk-ttabr:$ttsdk_version"
完成自适应码流转码:请确保您已通过火山引擎视频点播的自适应码流转码功能生成了 DASH 或 HLS 格式的视频流。详细信息,请参见自适应码流转码。
在 Application 中,除了 SDK 的常规初始化外,还需要初始化 ABR 策略。
public class App extends Application { @Override public void onCreate() { super.onCreate(); initVodSDK(); } } public static void initVodSDK() { // 1. 初始化 SDK Env.init(/*省略*/); // 2. 初始化 ABR 全局配置 TTVideoABRStrategy.init(); // 3. (可选)初始化预加载策略。若您使用自定义预加载,则无需调用 initPreloadStrategy(); // 4. 初始化预渲染策略 initPreRenderStrategy(); }
为播放器实例 TTVideoEngine 设置 ABR 配置 TTVideoABRConfig。具体步骤如下:
创建一个 TTVideoABRConfig 对象定义 ABR 的行为。您可以根据不同的业务场景(如短视频、中视频、全屏播放)和网络条件(Wi-Fi、移动网络)来配置不同的显示尺寸和清晰度上限。
public static TTVideoABRConfig createABRConfig() { TTVideoABRConfig abrConfig = new TTVideoABRConfig(); // 设置当前设备屏幕宽高,用于 ABR 算法计算最佳清晰度 abrConfig.screenWidth = screenWidth; abrConfig.screenHeight = screenHeight; // 设置默认清晰度,在无网络测速信息时使用 abrConfig.defaultResolution = Resolution.High; // 480P // 设置 Wi-Fi 网络下的最高清晰度 abrConfig.wifiMaxResolution = Resolution.ExtremelyHigh; // 1080P // 设置移动网络下的最高清晰度 abrConfig.mobileMaxResolution = Resolution.SuperHigh; // 720P // 根据业务场景设置视频的显示尺寸,以下为短视频场景示例 abrConfig.displayWidth = Math.min(screenWidth, screenHeight); abrConfig.displayHeight = (int) (abrConfig.displayWidth / 9f * 16); /* // 示例:中视频场景 abrConfig.displayWidth = Math.min(screenWidth, screenHeight); abrConfig.displayHeight = (int) (displaySizeConfig.displayWidth / 16f * 9); // 示例:横版全屏播放场景 abrConfig.displayHeight = Math.min(screenWidth, screenHeight); abrConfig.displayWidth = (int) (abrConfig.displayHeight / 9f * 16); */ return abrConfig; }
将 ABR 配置应用到播放器:在创建 TTVideoEngine 实例并设置 Vid 播放源后、首次调用 play() 方法前,调用 TTVideoABRStrategy.initEngine() 方法将 ABR 配置应用到播放器。
if (mVideoEngine == null) { mVideoEngine = new TTVideoEngine(context); mVideoEngine.setIntOption(PLAYER_OPTION_USE_VIDEOMODEL_CACHE, 1); // 设置播放源,必须为 VidPlayAuthTokenSource mVideoEngine.setStrategySource(source); TTVideoABRStrategy.initEngine(mVideoEngine, createABRConfig()); } mVideoEngine.play();
为了在视频开始播放时选择最合适的清晰度,您需要实现一套逻辑来决定起播清晰度。以下是一个起播清晰度的决策逻辑示例:
Resolution.Auto,让 ABR 策略自动选择。DEFAULT_RESOLUTION 选择一个最接近的可用清晰度。// 定义默认的起播清晰度 public static final Resolution DEFAULT_RESOLUTION = Resolution.SuperHigh; // 720P public static Resolution getStartPlayResolution(IVideoModel videoModel) { // 1. 优先使用用户已选择的清晰度 Resolution userSelectedResolution = getUserSelectedResolution(); if (userSelectedResolution == null) { if (videoModel != null && (videoModel.isSupportBash() || videoModel.isSupportHLSSeamlessSwitch())) { // 2. 如果视频支持 ABR,则设置为自动挡 return Resolution.Auto; } else { // 3. 如果视频不支持 ABR,则根据默认值选择最接近的清晰度 return BestResolution.findDefaultResolution(videoModel, DEFAULT_RESOLUTION); } } return userSelectedResolution; }
在播放器 onFetchedVideoInfo 回调中,调用步骤 3 中的方法获取起播清晰度,并配置给播放器。
// 在 onFetchedVideoInfo 回调中设置起播清晰度 if (mVideoEngine == null) { // ... mVideoEngine.setVideoInfoListener(new VideoInfoListener() { @Override public boolean onFetchedVideoInfo(VideoModel videoModel) { Resolution startResolution = getStartPlayResolution(videoModel); mVideoEngine.configResolution(startResolution); return false; } }); } mVideoEngine.play();
在播放过程中,您需要向用户展示可用的清晰度列表,并处理用户的手动切换操作。同时,您也可以在特定场景下(如全屏切换)动态更新 ABR 的配置。
如果播放过程中场景发生变化(例如,从列表页进入全屏播放),您可能需要更新 ABR 的配置以适应新的 View 大小或调整清晰度策略。调用 TTVideoABRStrategy.setABRConfig() 方法可以动态更新配置。
// 当播放场景变化时(如全屏切换),更新 ABR 配置 TTVideoABRStrategy.setABRConfig(mVideoEngine, newABRConfig);
在播放器 onFetchedVideoInfo 回调中获取到 VideoModel 之后,您可以获取当前视频支持的所有清晰度,并在 UI 上展示给用户。如果视频支持 ABR,建议在列表的开头添加“自动”选项。
List<Resolution> mResolutions = new ArrayList<>(); // 在 onFetchedVideoInfo 回调之后,获取支持的清晰度列表 Resolution[] supportedResolutions = mVideoEngine.supportedResolutionTypes(); if (supportedResolutions != null && supportedResolutions.length > 0) { VideoModel videoModel = mVideoEngine.getVideoModel(); // 如果支持 ABR,在列表首位添加“自动”选项 if (videoModel != null && (videoModel.isSupportBash() || videoModel.isSupportHLSSeamlessSwitch())) { mResolutions.add(Resolution.Auto); } for (Resolution r : supportedResolutions) { mResolutions.add(r); } } // 更新 UI 以展示清晰度列表
当用户从清晰度列表中选择一个选项时,调用 mVideoEngine.configResolution() 方法来切换清晰度。同时,建议将用户的选择持久化存储,以便下次播放时使用。
public void setResolution(Resolution resolution) { if (mCurrentSelectedResolution == resolution) return; mCurrentSelectedResolution = resolution; mVideoEngine.configResolution(resolution); // 持久化用户选择,以便下次播放时使用 setUserSelectedResolution(resolution); }
通过 VideoEngineCallback 的 onVideoStreamBitrateChanged 回调监听清晰度变化,包括 ABR 自动切换和用户手动切换两种情况。
// 记录用户选择的清晰度(手动选择或自动) Resolution mCurrentSelectedResolution; mVideoEngine.setVideoEngineCallback(new VideoEngineCallback() { @Override public void onVideoStreamBitrateChanged(Resolution newResolution, int bitrate) { if (mCurrentSelectedResolution == Resolution.Auto) { // 在自动模式下,ABR 会根据网络状况自动切换清晰度 Log.d("VideoPlay", "[ABR] onVideoStreamBitrateChanged: auto switch to " + newResolution); } else { // 用户手动切换清晰度完成 Log.d("VideoPlay", "[ABR] onVideoStreamBitrateChanged: user switch to " + newResolution); } } });
调用 mVideoEngine.getCurrentResolution() 可以获取当前正在播放的视频流的实际清晰度。注意,此方法不会返回 Resolution.Auto。
// 获取当前正在播放的实际清晰度(非 Resolution.Auto) Resolution currentResolution = mVideoEngine.getCurrentResolution();
如果您使用的是自定义预加载逻辑,可以在创建预加载任务时,让 ABR 策略参与选择预加载的清晰度。
final PreloaderVidItem item = new PreloaderVidItem(vidSource, preloadSize); item.setFetchEndListener((videoModel, error) -> { if (videoModel == null) { return; } Resolution resolution = getStartPlayResolution(videoModel); if (resolution == Resolution.Auto) { resolution = TTVideoABRStrategy.preloadSelect(videoModel, createABRConfig()); } item.mResolution = resolution; });
如果您使用 SDK 提供的预加载策略,可以通过设置 PreloadTaskFactory 来让 ABR 参与决策。
public static void initPreloadStrategy() { // 1. 全局开启预加载策略,需要在主线程调用 TTVideoEngine.enableEngineStrategy(StrategyManager.STRATEGY_TYPE_PRELOAD, StrategyManager.STRATEGY_SCENE_SMALL_VIDEO); // 2. 设置 PreloadTaskFactory,使用 ABR 选档结果作为预加载清晰度 StrategyManager.instance().setPreloadTaskFactory(new PreloadTaskFactory() { @Override public PreloaderVidItem createVidItem(VidPlayAuthTokenSource source, long preloadSize) { final PreloaderVidItem item = PreloadTaskFactory.super.createVidItem(source, preloadSize); // 同自定义预加载 VidPlayAuthTokenSource 的逻辑 item.setFetchEndListener((videoModel, error) -> { if (videoModel == null) { return; } Resolution resolution = getStartPlayResolution(videoModel); if (resolution == Resolution.Auto) { resolution = TTVideoABRStrategy.preloadSelect(videoModel, createABRConfig()); } item.mResolution = resolution; }); return item; } }); }
如果您使用 SDK 提供的预渲染策略,在创建用于预渲染的 TTVideoEngine 实例时,也需要像正常播放一样,为其初始化 ABR 配置。这样,当预渲染的播放器转为实际播放时,ABR 功能才能无缝衔接。
public static void initPreRenderStrategy() { // 开启预渲染策略 TTVideoEngine.enableEngineStrategy(STRATEGY_TYPE_PRE_RENDER, STRATEGY_SCENE_SMALL_VIDEO); StrategyManager.instance().enableReleasePreRenderEngineInstanceByLRU(true); // 设置 EngineStrategyListener,在创建预渲染引擎时进行 ABR 初始化 TTVideoEngine.setEngineStrategyListener(new EngineStrategyListener() { @Override public TTVideoEngine createPreRenderEngine(StrategySource strategySource) { TTVideoEngine videoEngine = new TTVideoEngine(context); videoEngine.setIntOption(PLAYER_OPTION_USE_VIDEOMODEL_CACHE, 1); videoEngine.setStrategySource(strategySource); // 关键步骤:为预渲染的播放器实例初始化 ABR TTVideoABRStrategy.initEngine(videoEngine, createABRConfig()); // 设置起播清晰度,逻辑与正常播放时一致 videoEngine.setVideoInfoListener(new VideoInfoListener() { @Override public boolean onFetchedVideoInfo(VideoModel videoModel) { Resolution resolution = getStartPlayResolution(videoModel); videoEngine.configResolution(resolution); return false; } }); return videoEngine; } }); }
当命中预渲染并转为正式播放时,需要正确同步清晰度状态。
public void play() { if (mVideoEngine == null) { TTVideoEngine preRenderEngine = TTVideoEngine.removePreRenderEngine(vid); if (preRenderEngine != null) { // 命中预渲染,预渲染的播放器已经设置清晰度,并且当前清晰度已经回调 // 所以这里需要给 mCurrentSelectedResolution 和 mCurrentResolution 赋值。 mVideoEngine = preRenderEngine; // 同步清晰度相关状态 mCurrentSelectedResolution = getStartPlayResolution(mVideoEngine.getVideoModel()); mCurrentResolution = mVideoEngine.getCurrentResolution(); } else { // 未命中预渲染,按常规流程创建播放器 // ... } } // ... mVideoEngine.play(); }