You need to enable JavaScript to run this app.
视频点播

视频点播

Copy page
Download PDF
不含 UI 集成方案
抖音同款短视频最佳实践
Copy page
Download PDF
抖音同款短视频最佳实践

本文介绍如何通过 Android 播放器 SDK 搭建“抖音”同款短视频场景,实现“零首帧”的短视频播放效果。

背景信息

在短视频场景中,缩短视频首帧耗时和提升视频上下滑动的流畅度是提升用户观看体验的重要手段:

  • 视频首帧耗时是指从用户点击开始播放按钮到视频首帧画面展现出来的时间。视频首帧耗时是视频类应用的核心指标之一,直接影响用户的观看体验。如果首帧加载需要几秒钟,用户可能会感到不耐烦,甚至选择放弃观看。短视频场景致力于实现“零首帧”,并不是真正的 0 毫秒起播,而是用户几乎感知不到有首帧耗时的存在。
  • 在短视频场景中,用户通常通过上下滑动来浏览不同的视频内容。如果视频在滑动过程中出现卡顿,会阻碍用户观看。提升视频上下滑动的流畅度可以确保用户更为轻松地浏览视频和享受视频内容,提高整体观看流畅度和连贯性,从而增加人均播放量和用户停留时长。

为了帮助您快速搭建“抖音”同款短视频场景,播放器 SDK 基于抖音亿级日活跃用户的真实反馈和大规模实践经验,提供两大最佳策略:预加载策略预渲染策略。此外,您还可参考开源示例项目,实现播放提前播放器异步调用设置封面图等策略。这些策略可单独应用,也可相互搭配。
通过采用这些策略,您可将视频首帧耗时缩短至 100-300 毫秒,为用户呈现极速首帧和流畅丝滑的播放体验,让用户享受到与抖音相媲美的视觉盛宴。

效果对比

以下视频展示了接入短视频场景最佳实践前后的播放效果对比

接入短视频场景最佳实践前

接入短视频场景最佳实践后

上下滑动视频时有明显的卡顿感

播放流畅丝滑

开源示例项目

下载地址:Android 示例项目

策略介绍

策略

说明

优化效果

预加载策略

预加载是指在播放当前视频时,提前下载后续视频的数据,以便在切换到下一个视频时实现快速起播,从而优化用户的播放体验。

缩短视频首帧耗时

预渲染策略

预渲染是指在播放当前视频时,提前创建播放器,对下一个视频进行解码和渲染操作。同时,您还可将预渲染的首帧用作视频封面,提前展示给用户,增强视觉效果。

缩短视频首帧耗时

播放提前

在上下滑动视频时,建议您在用户手指抬起后、下一个页面将要显示时调用播放接口,而非等待滑动停止后再调用播放接口。具体实现可参考 Android 示例项目

缩短视频首帧耗时

播放器异步调用

建议您使用播放器 SDK 提供的异步接口(例如 releaseAsync() 异步关闭播放器),避免上下滑动视频时造成 UI 卡顿。具体实现可参考 Android 示例项目

提升视频上下滑动的流畅度

设置封面图

建议您使用视频默认抽取的非黑首帧作为封面图,以避免在画面尚未渲染时出现黑屏无内容的情况。如果您不自行设置封面图,播放器 SDK 的预渲染策略也会抽取首帧作为封面图,但该策略仅适用于向下滑动的视频,并且不能预渲染过多的视频,以避免性能消耗。因此,建议您选择自行设置封面图以提升用户体验。

避免出现黑屏,提升用户体验

初始化策略

初始化播放器 SDK 前,参考以下代码初始化策略,开启 V2 版本策略:

tip

播放器 SDK 自 1.43.3.4 版本起支持 V2 版本策略,优化了预加载策略与预渲染策略的实现逻辑,在弱网和快速滑动的场景下进一步提升流畅性。

// 1. 开启 logcat 日志
if (BuildConfig.DEBUG) {
    VodSDK.openAllVodLog();
}

// 2. 开启策略 V2 版本
// 注意:这句代码控制 StrategyManager 构造方法中初始化的策略版本,需要在 StrategyManager.instance() 调用前调用。
StrategyManager.setVersion(StrategyManager.VERSION_2);

// 3. 初始化播放器 SDK
Env.init(/* 省略初始化代码 */);

设置播放源

为实现预加载策略或预渲染策略,您需要使用以下方式设置播放源。

方式一:设置 Vid 播放源

参考以下示例代码设置 Vid 播放源。除了 vidplayAuthToken 外,您还需指定 resolution,且此处设置的 resolution 必须和设置预加载源时的 resolution 保持一致。

final String vid = "YOUR_VIDEO_ID"; // appServer 下发
final String playAuthToken = "YOUR_PLAYAUTHTOKEN"; // appServer 下发

final int encodeType = TTVideoEngine.CODEC_TYPE_H264;

// final int encodeType = TTVideoEngine.CODEC_TYPE_h265;
// final int encodeType = TTVideoEngine.CODEC_TYPE_h266;
// 1. 组装 Vid 播放源
StrategySource vidSource = new VidPlayAuthTokenSource.Builder()
        .setVid(vid)
        .setPlayAuthToken(playAuthToken)
        .setEncodeType(encodeType)
        // 设置起播清晰度,此处为 480P。不传使用默认值 360P
        .setResolution(Resolution.High)
        .build();
// 2. 设置播放源
ttVideoEngine.setStrategySource(vidSource);
// 3. 播放
ttVideoEngine.play()

方式二:设置 DirectUrl 播放源

参考以下示例代码设置 DirectUrl 播放源。urlvidcacheKey 均为必填参数。其中:

  • vid:视频 ID,可以使用 Vid 或者 Vid 的 MD5 值。
  • cacheKey:缓存 key,即 URL 的唯一标识。每个视频可能存在多个不同清晰度的 URL,每个 URL 对应一个 cacheKey,但是多个 URL 对应同一个视频 ID。您可以使用 URL 中不变的部分进行 MD5 操作,将 MD5 值用作 cacheKey。例如,对于 https://www.example.volcengine.com/xxx/x/numbernumber 之前的部分是不变的。

warning

  • addItem 至少设置 1 个。
  • 必须传入 vidcacheKey ,不能为空。
  • 如果已知 Codec 类型,可通过 setEncodeType 设置。
final String vid = "YOUR_VIDEO_ID"; // 替换成您自己的视频 ID
final String url = "http://www.example.com/h264.mp4"; // 替换成您自己的视频 URL
final String cacheKey = TTVideoEngine.computeMD5(h264Url);

// 1. 组装播放源
StrategySource directUrlSource = new DirectUrlSource.Builder()
        .setVid(vid)
        .addItem(
                new DirectUrlSource.UrlItem.Builder()
                        .setEncodeType(TTVideoEngine.CODEC_TYPE_H264)
                        .setUrl(url)
                        .setCacheKey(cacheKey)
                        .build())
        .build();
       
// 2. 设置播放源
ttVideoEngine.setStrategySource(directUrlSource);
// 3. 播放
ttVideoEngine.play()

实现预加载策略

为实现预加载策略,您需要将视频播放列表传入 SDK 作为预加载源。SDK 会根据您提供的播放列表,在播放当前视频时提前下载后续的视频内容。

使用默认的预加载策略

您可使用默认的预加载策略,使用火山引擎内部沉淀的、可平衡播放器体验和成本的预加载策略配置。SDK 会根据当前的播放状态自动管理预加载的时机和配置,例如预加载的并发数和数据大小。
具体步骤如下:

  1. 在进入短视频播放页面时,调用 enableEngineStrategy 开启预加载策略。
  2. 在适当的时机设置预加载源:
    1. 在首次加载播放源数据或在刷新页面以显示新数据的场景中,调用 setStrategySources
    2. 在加载更多数据的场景中,调用 addStrategySources

示例代码如下:

// 1. 全局开启预加载策略,需要在主线程调用
TTVideoEngine.enableEngineStrategy(STRATEGY_TYPE_PRELOAD, STRATEGY_SCENE_SMALL_VIDEO);

List<StrategySource> sources = new ArrayList<>();
// 2. 重置视频播放列表,首次加载或者下拉刷新时调用
TTVideoEngine.setStrategySources(sources);
// 播放列表有新增时(如上滑加载更多数据),可通过此接口向视频播放列表中增加数据。
// 会在之前设置给 SDK 的列表后累加。
// TTVideoEngine.addStrategySources(sources);

// 3. 设置视频源并开始播放,见上文《设置播放源》章节
ttVideoEngine.setStrategySource(strategySource);
ttVideoEngine.play()

warning

  • 错误的播放列表会造成预加载失败。
  • 预加载策略会查询到当前播放视频在播放列表中的 index,并预加载 index+1、index+2 ... index+n 的视频。
  • 如果您需要自定义预加载机制,可通过 SDK 提供的预加载接口自行实现,详见自定义预加载

自定义预加载策略配置

如果您对于预加载策略有特殊的需求,例如在业务初期成本压力不大且想要获得更好的播放体验,则可自定义预加载策略配置。

warning

仅 1.42.2.6 或之后版本支持自定义预加载策略。

示例代码如下:

// 1. 全局开启预加载策略,需要在主线程调用
TTVideoEngine.enableEngineStrategy(STRATEGY_TYPE_PRELOAD, STRATEGY_SCENE_SMALL_VIDEO);
// 2. 自定义预加载策略配置
StrategyPreloadConfig config = new StrategyPreloadConfig.Builder()
            .setCount(count) // 预加载视频个数,不建议值设置过大会造成成本的浪费,短视频场景建议设置 3-5 个
            .setSize(size)// 预加载视频大小,单位为 kb,不建议值设置过大会造成成本的浪费,10 分钟内短视频建议设置 800k
            .setStartBufferLimit(start_buffer_limit)// 预加载开始时机,单位为秒,例如设置 15 则表示播放器的缓冲水位达到 15 秒及以上开始去预加载视频,原则是优先保证当前播放视频的网络带宽,短视频建议设置 15 秒
            .setStopBufferLimit(stop_buffer_limit)// 预加载停止时机,单位为秒,例如设置 5 则表示播放器的缓冲水位小于 5 秒则会取消当前的预加载任务,优先保证当前播放视频的网络带宽,短视频建议设置 5 秒
            .build();
StrategyManager.instance().setCustomPreloadConfig(config);

List<StrategySource> sources = new ArrayList<>();
// 3. 重置视频播放列表,首次加载或者下拉刷新时调用
TTVideoEngine.setStrategySources(sources);
// 播放列表有新增时(如上滑加载更多数据),可通过此接口向视频播放列表中增加数据。
// 会在之前设置给 SDK 的列表后累加。
// TTVideoEngine.addStrategySources(sources);

// 4. 设置视频源并开始播放,见上文《设置播放源》章节
ttVideoEngine.setStrategySource(strategySource);
ttVideoEngine.play()

设置预加载任务并发数

// 设置预加载任务的并发数,默认值为 1,串行执行预加载任务
TTVideoEngine.setIntValue(DataLoaderHelper.DATALOADER_KEY_INT_PARALLEL_NUM, 2);

实现预渲染策略

为实现预渲染策略,您需要将视频播放列表传入 SDK 作为预渲染源。SDK 会根据您提供的播放列表,在播放当前视频时,提前创建播放器,对播放列表中的下一个视频进行解码和渲染操作。播放器 SDK 当前只支持预渲染下一个视频,不支持预渲染上一个视频。
接入预渲染策略的具体步骤如下:

  1. 调用 enableEngineStrategy 全局开启预渲染策略。此外,推荐调用 enableReleasePreRenderEngineInstanceByLRU 开启预渲染 engine 释放策略,即根据 LRU (Least Recently Used) 机制释放。
  2. 在适当的时机设置预渲染源:
    1. 在首次加载播放源数据或在下拉刷新页面以显示新数据的场景中,调用 setStrategySources 重置播放列表。
    2. 在上滑加载更多数据的场景中,调用 addStrategySources 向播放列表中增加数据。
  3. 调用 setEngineStrategyListener 设置预渲染策略回调,再根据自身业务对 engine 实例进行其它操作。例如您可通过 createPreRenderEngine,设置视频填充模式、起播时间等。
  4. 调用 getPreRenderEngine 判断预渲染是否完成,处理预渲染封面逻辑。
  5. 调用 removePreRenderEngine 判断预渲染是否完成,处理播放逻辑。

    tip

    调用 getPreRenderEngineremovePreRenderEngine 均可获取 engine 对象,但存在以下区别:

    • 调用 getPreRenderEngine 获取到的 engine 对象,不会脱离 SDK 内部的预渲染模块,建议不要在业务逻辑中持有该对象。
    • 调用 removePreRenderEngine 获取到的 engine 对象,会脱离 SDK 内部的预渲染模块,您需在业务逻辑中持有该对象并自行管理其释放逻辑。

实现预渲染策略的示例代码如下:

// 1. 全局开启预渲染策略,需要在主线程调用
TTVideoEngine.enableEngineStrategy(STRATEGY_TYPE_PRE_RENDER, STRATEGY_SCENE_SMALL_VIDEO);
// (推荐)开启预渲染 engine 根据 LRU 机制释放
StrategyManager.instance().enableReleasePreRenderEngineInstanceByLRU(true);

// 2. 重置播放列表,首次加载或者下拉刷新时调用。如同时接入预加载策略和预渲染策略,仅需设置一次视频播放列表。
List<StrategySource> sources = new ArrayList<>();
TTVideoEngine.setStrategySources(sources);
// 播放列表有新增时(如上滑加载更多数据),可通过此接口向播放列表中增加数据。
// 会在先前设置给 SDK 的列表后累加。
// TTVideoEngine.addStrategySources(sources);

// 3. 设置 Listener,监听预渲染回调
TTVideoEngine.setEngineStrategyListener(new EngineStrategyListener() {
    @Override
    public TTVideoEngine createPreRenderEngine(final StrategySource source) {
        // 创建用于预渲染下个视频的 engine,返回给 SDK。
        TTVideoEngine engine = createVideoEngine(source);
        return engine;
    }
});

// 4. 设置预渲染封面。
// 建议在 TextureView 的 SurfaceTextureListener 的 onSurfaceTextureAvailable 中设置。
TTVideoEngine preRenderEngine = TTVideoEngine.getPreRenderEngine(mStrategySource)
if (preRenderEngine != null) {
    // 使用预渲染播放器渲染的首帧作为封面。这里只设置 Surface 渲染封面,业务不用持有 preRenderEngine。
    preRenderEngine.setSurface(surface);
    preRenderEngine.forceDraw();
} else {
    // 使用业务自己的图片加载库加载图片封面
}

// 5. 播放下个视频时,判断预渲染是否完成
TTVideoEngine mEngine;
TTVideoEngine preRenderEngine = TTVideoEngine.removePreRenderEngine(mStrategySource.vid())
if (preRenderEngine != null) {
    // 不为 null 说明预渲染完成,直接调用播放即可。业务持有这个播放器,负责实例 release。
    mEngine = preRenderEngine;
} else {
   // 为 null 说明无预渲染,您需构造 engine 后播放。
   mEngine = createVideoEngine(source);
}
// 设置当前页面可用 surface,建议在 TextureView 的 SurfaceTextureListener 的 onSurfaceTextureAvailable 中获取 surface。
mEngine.setSurface(surface);
// 播放
mEngine.play();

private static TTVideoEngine createVideoEngine(final StrategySource source) {
    TTVideoEngine engine = new TTVideoEngine(this, ITTVideoEngineInternal.PLAYER_TYPE_OWN);
    // (必须)设置播放源。
    engine.setStrategySource(source);
    // 根据需求自定义设置。
    // engine.setIntOption(PLAYER_OPTION_USE_TEXTURE_RENDER, 1);
    engine.setIntOption(....);
    return engine;
}

tip

  • SDK 的预渲染模块会查询到当前播放视频在播放列表中的 index,预渲染 index+1 视频源。
  • 仅支持预渲染 1 个视频。
  • 预渲染 engine 的释放时机说明如下:
    • 调用 getPreRenderEngineremovePreRenderEngine 获取预渲染 engine 时,如果该 engine 预渲染失败,则会被释放。
    • 调用 removePreRenderEngine 获取到的 engine 对象,会脱离 SDK 的预渲染模块,需要您在业务逻辑中持有并自行管理其释放逻辑。
    • 满足预渲染条件时,在创建新的预渲染 engine 之前,SDK 会释放上一个未被 removePreRenderEngine 移除的 engine。

关闭所有策略

在退出短视频页面或切换到其他页面时调用 clearAllStrategy 关闭所有策略,释放资源。

TTVideoEngine.clearAllStrategy();

注意事项

  • 如果您同时接入预加载策略和预渲染策略,仅需设置一次视频播放列表。
  • 如果您的 App 存在多个短视频页签切换或者多个短视频页面嵌套的场景时,需要注意清除策略的时机。假设 A 页面和 B 页面都是短视频场景,可以通过以下两种方式在页面跳转时处理策略和播放源列表:
    • (推荐)方式一:
      • 从 A 页面跳转至 B 页面:进入 A 页面时开启策略 -> 设置 A 页面的播放源列表 -> 离开 A 页面时关闭策略 -> 进入 B 页面时开启策略 -> 设置 B 页面的播放源列表
      • 从 B 页面跳转回 A 页面:离开 B 页面时关闭策略 -> 返回 A 页面时开启策略 -> 重新设置A页面的播放源列表
    • 方式二:
      • 从 A 页面跳转至 B 页面:进入 A 页面时开启策略 -> 设置 A 页面的播放源列表 -> 进入 B 页面时设置 B 页面的播放源列表
      • 从 B 页面跳转回 A 页面:返回 A 页面时重新设置 A 页面的播放源列表 -> 离开 A 页面时再关闭策略
Last updated: 2025.12.08 20:33:38