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

视频点播

复制全文
短剧场景
基于 VePlayer 的 H5 短剧应用开发教程
复制全文
基于 VePlayer 的 H5 短剧应用开发教程

本文档将通过剖析火山引擎官方示例仓库 veplayer-demo 中的 React 示例,指导开发者基于火山引擎 Web 播放器 SDK (VePlayer),快速搭建一个功能完善、并实现“零首帧”滑动切换体验的 H5 短剧应用。

体验 Demo 和源码地址

您可以在线体验完整的 H5 短剧应用 Demo。如需深入了解其实现细节,可以前往 GitHub 代码仓库查看全部源码。应用预览效果:
Image

项目结构

正式开发前,您需要先了解 veplayer-demo 仓库中 vod-h5-demo 的关键目录结构。本文所有讲解将围绕此项目展开。

veplayer-demo/
├─ packages/
│  ├─ vod-h5-demo/          # React H5 短剧 Demo (本文主线)
│  └─ vod-h5-vue-demo/      # Vue 版本 (不涉及)

veplayer-demo/packages/vod-h5-demo/
└─ src/
   ├─ main.tsx              # 应用入口,挂载 React
   ├─ App.tsx               # 全局路由与 VePlayer.prepare 初始化
   ├─ player.ts             # VePlayer SDK 模块封装
   ├─ components/
   │  ├─ video-swiper/      # 核心:短剧竖滑播放器容器
   │  ├─ slider-item/       # 每一屏的 UI 卡片与封面
   │  └─ ...
   ├─ pages/
   │  ├─ square/            # 广场页 (手动预加载场景)
   │  └─ theater/           # 剧场页 (自动预加载场景)
   ├─ utils/
   │  ├─ index.ts           # 业务工具函数 (如 selectDef)
   │  └─ preload.ts         # 预加载数据格式化工具
   └─ ...

以下是各核心功能在 packages/vod-h5-demo 中的代码位置,方便您快速定位和修改:

核心功能

代码位置

全局初始化 VePlayer.prepare

src/App.tsx

数据流处理

  • 拉取剧集数据:src/pages/theater/index.tsx
  • 解析与格式化工具:
    • src/utils/index.ts
    • src/utils/preload.ts

播放器核心实现 video-swiper

src/components/video-swiper/index.tsx(涉及单实例创建、playNext 调用、DOM 移动)

预加载策略切换

  • 手动模式:src/pages/square/index.tsx
  • 自动模式:src/components/video-swiper/index.tsx

动态 Buffer 配置

src/components/video-swiper/index.tsx(在initPlayeradaptRange选项中)

封面 UI 实现

  • 播放器poster配置:src/components/video-swiper/index.tsx
  • ImageX Viewer封面:src/components/slider-item/index.tsx

架构与数据流

短剧应用的数据处理流程,始于从业务接口获取原始媒资信息,经过前端一系列的解析与格式化后,最终传递给 VePlayer 用于播放和预加载。

  1. 获取 videoModel:后端接口通常会将视频的详细信息(如多清晰度地址、封面、时长等)打包成一个 JSON 字符串,置于 videoModel 字段中返回。
  2. 解析与选流:前端通过 JSON.parse 解析 videoModel 字符串,然后利用 selectDef 工具函数,根据当前网络环境从 PlayInfoList 中智能选择最合适的播放流。
  3. 格式化:将选定的视频流信息格式化为 VePlayer playList 和预加载模块所需的标准结构,其中必须包含 vid 以便 SDK 识别和调度。
  4. 播放与预加载:
    • 首次播放:将格式化后的视频信息作为 playList 参数,在 video-swiper 组件中初始化 VePlayer 实例。
    • 预加载:将整个剧集或推荐列表格式化后,通过 VePlayer.setPreloadList 传递给预加载模块。

播放器 UI 与交互定制

需要对播放器进行精细化的 UI 和交互定制,以适配 H5 短剧场景。

裁剪插件

通过初始化配置项的 ignores 字段移除不需要的内置插件,以简化播放器界面。

const options = {
  // ... 其他配置
  ignores: [
    'moreButtonPlugin', // 右上角更多按钮
    'enter',            // 播放进入时的 loading 动画
    'fullscreen',       // 全屏按钮
    'volume',           // 静音按钮
    'play',             // 底部播放/暂停按钮
    'time',             // 时间显示
    'pip',              // PC 画中画按钮
    'replay',           // 播放结束时的重播按钮
    'playbackrate',     // PC 倍速按钮
    'sdkDefinitionPlugin', // 移动端清晰度切换按钮
  ],
};

配置播控栏样式

自定义播控栏的样式和交互形式。

const options = {
  // ... 其他配置
  commonStyle: {
    // 进度条已播放部分的颜色
    playedColor: '#ffffff',
  },
  controls: {
    // 进度条显示在底部
    mode: 'bottom',
  },
  sdkErrorPlugin: {
    // 播放报错时不显示重试按钮
    isNeedRefreshButton: false,
  },
  start: {
    // 禁用播放/暂停按钮的显隐动画
    disableAnimate: true,
    // 暂停时在画面中央显示暂停图标
    isShowPause: true,
  },
};

配置移动端交互

根据业务需要对移动端的交互手势进行配置。

const options = {
  // ... 其他配置
  mobile: {
    // 播放器聚焦时无阴影遮罩
    gradient: 'none',
    // 关闭左侧上下滑动手势调整亮度
    darkness: false,
    // 是否禁用所有手势操作
    disableGesture: false,
    // 触摸滑动时是否实时更新播放进度
    isTouchingSeek: false,
    // 是否禁用垂直手势(音量/亮度)
    gestureY: false,
  },
};

播放器实例与切换

在短剧滑动切换场景下,推荐采用单播放器实例复用策略,以规避浏览器自动播放策略限制并优化性能。核心要点如下:

  • 单实例:在核心组件 video-swiper 中,仅创建一个 VePlayer 实例,并用 useRef 持有。
  • playNext 切流:当用户滑动切换视频时,不销毁播放器,而是调用 sdk.playNext() 方法,传入新的播放源信息。此方法会无缝切换到下一个视频,并能有效利用预加载缓存。
  • DOM 移动:播放器的 playerContainer DOM 节点会随着 Swiper 的滑动,动态地从上一个 slide 移动到当前激活的 slide 中,从而实现视觉上的切换。

这种“逻辑单实例,DOM 动态挂载”的模式,是实现“零首帧”的关键,同时从根本上避免了多实例方案带来的自动播放与性能问题。

预加载策略

Demo 中灵活串联了手动和自动两种预加载模式,以应对不同场景的需求。经内部测试验证,在开启预加载后,首帧加载速度得到极大提升。

  • 测试结论:在典型 Android 设备上,滑动切换视频的平均首帧耗时可以稳定在 150ms 以下
  • 测试环境:
    • 视频:720P H.264 格式
    • 网络:移动数据 (5G)
    • 设备:Xiaomi 12 Pro
    • 浏览器:Chrome 135

说明

预加载功能目前仅支持 MP4 + DirectUrl + MSE 的组合,兼容 PC、Android 及 iOS 17.1+ 的设备。

短剧广场页:手动模式

剧集广场页 (pages/square),用户的意图是浏览并选择一部剧。为了优化用户点击进入剧场页后第一集的起播速度,采用手动预加载策略。

  • 时机:进入广场页,获取到推荐剧集列表后。
  • 操作:调用 VePlayer.setPreloadScene(0) 切换到手动模式,并用 VePlayer.setPreloadList() 主动提交要预加载的视频列表(如推荐流的前 N 个剧集的第一集)。
  • 效果:当用户点击这些热门剧集时,第一集的内容很可能已缓存完毕,从而实现进入剧场页后的快速起播。

剧场页:自动模式

当用户进入剧场页 (pages/theater) 进行竖向滑动时,切换到自动预加载模式。

  • 时机video-swiper 组件挂载或激活时。
  • 操作:调用 VePlayer.setPreloadScene(1, { prevCount: 1, nextCount: 2 }) 切换到自动模式,并提供完整的剧集列表。SDK 会以当前播放视频为中心,自动管理前后视频的预加载队列。
  • 效果:当用户观看第 N 集时,SDK 已在后台下载第 N-1, N+1, N+2 集。用户滑动到下一集时,数据已缓存在本地,从而实现无缝切换。

动态 Buffer

动态 Buffer 是一种智能缓冲策略,旨在平衡播放流畅性与带宽成本。它会根据当前网络状况动态调整播放器的缓冲时长。

  • 配置方式:在播放器初始化时,通过 adaptRange 参数开启并配置。
  • 推荐区间
    • minCacheDuration: 缓冲时长下限,推荐 10-15 秒。
    • maxCacheDuration: 缓冲时长上限,推荐 30-40 秒。

在弱网环境下,SDK 会自动提高缓冲时长至 maxCacheDuration,以抵御网络抖动;在良好网络下,则会降低缓冲至 minCacheDuration,避免带宽浪费。

封面策略与切换优化

在用户滑动切换视频的瞬间,若直接等待下一集视频加载并渲染出首帧,可能会出现短暂的黑屏或卡顿,破坏“零首帧”的流畅体验。为解决这一问题,可以采用一种双层封面策略:即在视频加载完成前,先展示一张高质量的静态封面图;当视频首帧渲染完成后,再无缝地衔接到视频画面。

视频画面填充方式

短剧视频一般是竖屏。在播放器初始化时设置 videoFillModefillWidth,表示视频宽度铺满容器,高度溢出则进行裁剪,这样用户看到的视频是完全填满容器的。

const options = {
  // ...
  videoFillMode: 'fillWidth',
};

Swiper 列表封面

在每个滑动卡片 slider-item 中,使用 @volcengine/imagex-reactViewer 组件来渲染封面图,并设置 objectFit='cover'。由于封面图(通常是视频首帧)与视频内容和裁剪方式高度一致,因此从静态封面到动态视频的过渡几乎无感知。
Image

说明

如果您的视频已上传至视频点播服务进行存储和管理,系统会自动生成首帧作为封面图。

播放器 Poster 配置

在播放器初始化时,对 poster 插件进行如下配置:

  • hideCanplay: true:H5 推荐配置。只有当视频确认可播放时才隐藏封面,有效避免首帧加载过程中的黑屏问题。
  • fillMode: 'fixWidth':对于竖屏短剧,宽度铺满容器,实现沉浸式视觉效果。

常见问题

如何解决浏览器底部工具栏遮挡播放器的问题?

  • 问题表现:在 H5 feed 流场景下,Safari 等带有底部工具栏的浏览器,在向下滑动时会显示工具栏,导致播放器进度条等组件被遮挡。如下图所示:
    Image
  • 问题原因:这是因为播放器容器的上层 DOM 的 CSS vh 单位无法响应浏览器工具栏的显隐。
  • 解决方案:通过 window.innerHeight 属性结合 CSS 变量及 calc() 函数来动态计算容器高度。
    • 添加代码:

      function setVh() {
          // 使用 0.01 倍的 innerHeight 作为自定义的 vh 变量
          const vh = window.innerHeight * 0.01;
          document.documentElement.style.setProperty('--vh', `${vh}px`);
      }
      
      setVh();
      
      // reset vh unit on page resize
      window.addEventListener('resize', () => {
        setVh();
      });
      
    • 修改 CSS:

      .wrapper {
         /* height: 100vh; */
         /* 使用自定义的 CSS 变量,该值会随工具栏显隐而变化 */
         height: calc(var(--vh, 1vh) * 100);
      }
      

自动播放为何会静音?

由于浏览器限制,首次带声音的自动播放需要用户手势触发。Demo 中采用“单实例 + enableDegradeMuteAutoplay + 统一静音按钮”的方案,用户只需交互一次,后续即可流畅播放。

MSE 兼容性与降级如何处理?

预加载和动态 Buffer 依赖 MSE (Media Source Extensions)。VePlayer 在不支持 MSE 的环境(如旧版浏览器)或 iOS 17.1 之前版本会自动降级为标准的 video 标签播放。此时播放器的进阶功能不可用,但基础播放不受影响。

如何排查预加载未命中?

  1. 确认 VePlayer.prepare 中是否已设置 strategies.preload: true
  2. 确认是否已设置 enableMp4MSE: true
  3. 检查 setPreloadList 传入的视频列表 vid 是否与 playNext 时的 vid 一致。
  4. 确认当前环境是否满足预加载的兼容性要求。
  5. 使用 playerSdk.getPlugin('mp4encryptplayer').hitpreload 判断是否命中了预加载缓存。

为何 Demo 在 PC 浏览器模拟器中无法点击进度条?

这是因为 swiper.js 在模拟器中会模拟 touch 事件,导致 click 事件无法正常冒泡到播放器的进度条。可以通过设置 <Swiper/> 组件的 touchStartPreventDefault={false} 解决。

最近更新时间:2026.01.28 14:49:30
这个页面对您有帮助吗?
有用
有用
无用
无用