You need to enable JavaScript to run this app.
文档中心
视频点播

视频点播

复制全文
下载 pdf
HarmonyOS NEXT 播放器 SDK
基础功能
复制全文
下载 pdf
基础功能

本文为您介绍如何实现 HarmonyOS NEXT 播放器 SDK 的基础功能。

注意

本文适用于播放器 SDK 3.1.2-tob 及之后的版本。若您使用更早版本,请见HarmonyOS NEXT 播放器 SDK 使用文档(历史版本)

开始播放

步骤 1:开启日志调试

初始化播放器 SDK 之前,开启日志,便于调试和排查问题。

注意

线上版本请务必关闭日志,减少性能开销。

VodEnv.openLog()

步骤 2:初始化 SDK 并配置 License

初始化 SDK 并配置 License 文件。这是全局接口,app 生命周期内仅需调用一次。

注意

自 3.0.45-tob 版本起支持 License 文件自动更新。若您使用 3.0.45-tob 之前的版本,License 文件需由应用服务端下发,定期更新,否则 License 过期后您将无法使用播放器 SDK。更多信息,请见如何处理 License 相关错误?

export default class EntryAbility extends UIAbility {

    onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    
        // 初始化 applog,需传入您在视频点播控制台获取的 App ID
        ApplogWrapper.intApplog(this.context, 'your app id')
        // 初始化 license 模块,在 EntryAbility 中调用必须传入 this.context,不能使用 getContext()
        VodEnv.init(this.context)
        // 传入 license 文件,必须由 appServer 下发,定期更新,否则 license 过期后无法使用火山引擎播放器 SDK。
        VodEnv.addLicenseFile('{"Signature":"A1VA/mZ63eEUKLZEs4mzMa8xLVqUHkEzeUdC........')
        // 初始化播放器 SDK,在 EntryAbility 中调用必须传入 this.context,不能使用 getContext()
        let model = new SimKitInitModel();
        model.globalPlayOptions = GlobalOptions.create()
           // 支持 BASH(经火山引擎优化的升级版 DASH 协议)视频流的播放
          .setEnableDash(true)
          .setEnableMdlConfig(true)
          .setMdlConfigs(mdlConfig);
        SimKitService.instance().init(model, context);
    }
}

步骤 3:构造播放源

播放器 SDK 支持多种播放源,您可以根据视频的来源和业务场景选择最合适的方式。

适用于播放已上传至火山引擎视频点播服务的视频。通过视频的 Vid 和其对应的临时播放凭证 PlayAuthToken 来指定播放内容。这两个参数通常由您的业务服务端下发,客户端直接使用即可。详情请见通过临时播放 Token 播放

let vid = "your vid"; // appServer 下发的 vid
let playAuthToken = "your video id's play auth token"; // appServer 下发的临时播放 Token
// 构造播放源
let dataSource = new VidSource(vid, playAuthToken)

步骤 4:创建播放器

this.player = new VePlayer(this.dataSource)

步骤 5:构造视图控件

build() {
  Stack() {
      TTAVPlayerView2({
        // 设置显示模式
        // FillXY :可能会变形;画面宽高都充满控件;画面不被裁剪;无黑边
        // AspectFit:无变形;等比例缩放;画面不被裁剪;可能有黑边
        // AspectFill:无变形;等比例缩放;画面可能被裁剪;无黑边
        scaleType: TTAVScaleType.AspectFit,
        onLoad: (window) => {
          // window 加载成功绑定到 player
          this.player?.bindWindow(window)
        }
      })
  }
  .width('100%')
  .height('100%')
}

步骤 6:播放视频

调用 play 开始播放。SDK 默认会边播边缓存。

this.player.play()

步骤 7:释放播放器

this.player.release()

播放回调

订阅与取消

// 订阅播放回调,每次调用 SDK 内部会创建新的 IPlayEventObserver 并返回。
// 同一次播放可订阅多次,使用完需要解除订阅。
this.playEventObserver = this.player.subscribeObserver('ShortVideoView')
  .onPrepared((sourceID) => {
  })
  .onPaused((sourceID) => {
  })
  .onPlaying((sourceID) => {
  })
  .onBuffering((sourceID, start) => {
  })
  .onTimeChange((sourceID, currentDuration) => {
  })
  .onBufferingPercent((sourceID, percent) => {
  })
  .onError((sourceID, error) => {
  })
  
// 结束播放时,取消订阅
this.player.unsubscribeObserver(this.playEventObserver)

回调事件说明

export interface IPlayEventObserver {
    
  /**
   * 首帧回调
   */
  onRenderFirstFrame(callback: Callback<string | undefined>): IPlayEventObserver
  
  /**
   * prepare 完成回调
   */
  onPrepared(callback: Callback<string | undefined>): IPlayEventObserver

  /**
   * 开始播放回调
   */
  onPlaying(callback: Callback<string | undefined>): IPlayEventObserver

  /**
   * 调用暂停后回调
   */
  onPaused(callback: Callback<string | undefined>): IPlayEventObserver

  /**
   * 调用 stop 后回调
   */
  onStopped(callback: Callback<string | undefined>): IPlayEventObserver

  /**
   * 播放至结尾回调,不论用户是否开启loop,播放至结尾均触发回调
   */
  onPlayEnd(callback: Callback<string | undefined>): IPlayEventObserver

  /**
   * 出现卡顿回调
   * @param start 为 true 开始卡顿;start 为 false 卡顿结束。
   */
  onBuffering(callback: (vid: string | undefined, start: boolean) => void): IPlayEventObserver

  /**
   * 播放器已经缓存的可播放进度
   * @param percent 百分比
   */
  onBufferingPercent(callback: (vid: string | undefined, percent: number) => void): IPlayEventObserver

  /**
   * 播放进度变化回调,默认间隔 1s 上报,因用户操作(seek)产生的时间变化会立刻上报
   * @param currentDuration 当前播放进度,单位 ms
   */
  onTimeChange(callback: (vid: string | undefined, currentDuration: number) => void): IPlayEventObserver

  /**
   * seek 完成回调
   * @param seekDoneTime seek 到的位置,单位 ms。
   * 精准位置需要通过 onTimeChange 获取,seek 回调的 time 仅代表完成用户某一次请求。
   */
  onSeekDone(callback: (vid: string | undefined, seekDoneTime: number) => void): IPlayEventObserver
  
  /**
   * 播放错误回调
   * @param error 错误信息,error.code 为错误码
   */
  onError(callback: (vid: string | undefined, error: BusinessError) => void): IPlayEventObserver
  
  /**
   * 获取到 VideoModel 回调
   * @param videoModel,播放使用的 VideoModel
   */
  onFetchVideoModel(callback: (vid: string | undefined, videoModel: VideoModel) => void): IPlayEventObserver
  
  /**
   * 视频宽高变化回调
   * @param width 视频宽
   * @param height 视频高
   */
  onVideoSizeChange(callback: (vid: string | undefined, width: number, height: number) => void): IPlayEventObserver
}

播放控制

暂停与恢复播放

// 暂停播放
this.player.pause()

// 恢复播放
this.player.play()

Seek 到指定位置播放

调用 play 后,通过 seekTo 方法 Seek 到指定位置进行播放,实现拖拽进度条到指定时间开始播放的功能。

// 演示 seek 到 1 秒的位置
this.player.seek(1000);

从指定时间起播

在调用 play 前通过 setStartTime 方法指定开始播放时间点,用于实现从指定时间开始播放或跳过片头等功能。示例代码如下:

let options = PlayOptions.default()
    // 单位为毫秒,以下示例表示从 1 秒处起播
    .setInitialStartTimeMs(1000);
this.player = new VePlayer(this.dataSource, options)

倍速播放

// 默认值为 1,取值为 0.5, 1, 1.5, 2, 2.5, 3
this.player.setSpeed(2)

循环播放

// 循环播放默认关闭
let options = PlayOptions.default()
    .setLoop(true)
this.player = new VePlayer(this.dataSource, options)

播放器静音

// 静音
this.player.setVolume(0);

// 取消静音
this.player.setVolume(1);

获取视频时长

// 单位为毫秒
let duration =  this.player.getDuration()

设置多清晰度

Vid 模式下播放视频时,视频点播服务会根据生成临时 Token 时的参数配置下发一个或多个清晰度的播放地址。播放器触发 onFetchVideoModel 回调后,您可调用 getVideoList 方法获取包含所有清晰度信息的数组,基于该数组实现清晰度列表的展示和清晰度切换逻辑。

设置起播清晰度

播放 Vid 视频源时,起播分辨率的选择逻辑如下:

  • 如果你指定了起播分辨率:SDK 会采用该分辨率。如果找不到完全匹配的,则采用最接近的分辨率。
  • 如果你未指定起播分辨率:SDK 将使用清晰度列表中的第一个分辨率。例外情况:对于 BASH 视频,SDK 会选择最接近 540p 的那个分辨率。
let dataSource =  new VidSource(videoDetail.vid, videoDetail.playAuthToken, Resolution.resolution_360p)

获取清晰度列表

播放器触发 onFetchVideoModel 回调后,调用 getVideoList 方法获取包含所有清晰度信息的数组。

.onFetchVideoModel((sourceID, videoModel) => {
    let videoList = this.player.getVideoList();
    for (let videoInfo of videoList) {
      // 清晰度字段,见下面清晰度枚举
      console.info("definition is" + videoInfo.definition)
    }
})

播放中切换清晰度

获取清晰度列表后,传入要切换清晰度在列表中的 index。

this.player.switchVideo(index);

清晰度枚举

Resolution 枚举如下表所示:

key

视频清晰度

resolution_360p

360p

resolution_480p

480p

resolution_540p

540p

resolution_720p

720p

resolution_1080p

1080p

resolution_2k

2k

resolution_4k

4k

生成 UnionInfo

UnionInfo 是播放端从设备中提取的用于标识访问或设备唯一性的信息。播放器 SDK 通过 UnionInfo 向应用服务端发起播放请求,应用服务端通过服务端 SDK 本地签发包含 UnionInfoPlayAuthToken 并下发给播放器 SDK,即可播放火山引擎私有加密视频。更多信息,请见火山引擎私有加密方案。您可通过以下代码生成 UnionInfo

let uniqueId = VodEnv.getEngineUniqueId();

播放 HDR 视频

let options = PlayOptions.default()
    .setEnableMediaCodecRender(true);
this.player = new VePlayer(this.dataSource, options)

纯音频播放

播放器 SDK 支持在播放视频时,只解码音频而不解码视频,适用于纯音频播放场景。相比您根据自身业务逻辑实现的纯音频播放,SDK 只解码音频会更省电。

// 开启纯音频播放,视频停止渲染
this.player.setRadioMode(true);

// 恢复音视频播放,视频恢复渲染 
this.player.setRadioMode(false);

常见问题

如何处理 License 相关错误?

当 License 过期或其他错误发生时,播放器会报错 -30001,无法正常使用。此时建议切换至鸿蒙系统播放器进行播放。系统播放器效果较差,因此建议及时更新 License,避免过期。

监听 License 错误

this.playEventObserver = this.player.subscribeObserver('FeedItemView')
  .onError((sourceID, error) => {
      // license 校验失败
      if (error.codec == TTPlayerErrorCode.LICENSE_FAIL) {
      }
  })

方案 1:使用播放器 SDK 封装的系统播放器

VodEnv.addLicenseFile(this.license)

.....
export struct ShortVideoView {
  // 检查 license,检查通过使用自研播放器 isOwnPlayer 设置为 true,检查失败使用系统播放器播放。
  private isOwnPlayer: boolean = VodEnv.isLicenseAvailable();
  // 系统播放器需使用 XComponent,需要 xComponentID 和 xComponentController
  private xComponentID: string = util.generateRandomUUID()
  private xComponentController: XComponentController = new XComponentController()
  // 使用系统播放器需自行调整 XComponent 宽、高
  @Local playerWidth: number = 100;
  @Local playerHeight: number = 100;
  
  aboutToAppear() {
    // 使用自研播放器 isOwnPlayer 设置为 true
    // 使用系统播放器 isOwnPlayer 设置为 false
    this.player = new VePlayer(this.dataSource, PlayOptions.default(), this.isOwnPlayer)
  }
  
  build() {
    Stack() {
      if (this.isOwnPlayer) {
        // 自研播放器使用 TTAVPlayerView2
        TTAVPlayerView2({
          scaleType: TTAVScaleType.AspectFit,
          onLoad: (window) => {
            // window 加载成功绑定到 player
            this.player?.bindWindow(window)
          }
        })
      } else {
        // 系统播放器使用 XComponent
        XComponent({
          id: this.xComponentID,
          type: XComponentType.SURFACE,
          controller: this.xComponentController
        }).width(this.playerWidth + 'px')
          .height(this.playerHeight + 'px')
          .onLoad(() => {
            // surfaceID 绑定到 player
            let surfaceID = this.xComponentController.getXComponentSurfaceId()
            this.player?.bindSurfaceID(surfaceID)
          })
      }
    }
  }
  
  subscribeObserver() {
    this.playEventObserver = this.player.subscribeObserver(TAG)
    .onVideoSizeChange((vid, width, height) => {
      // 使用系统播放器需要根据视频比例设置显示组件宽高
      if (!this.isOwnPlayer) {
        // 获取屏幕宽高比
        let displaySc = display.getDefaultDisplaySync().width / display.getDefaultDisplaySync().height
        // 获取视频宽高比
        let videoSc = width / height;
        if (videoSc > displaySc) {
          // 视图宽填满屏幕,高等比例适配留黑边
          this.playerWidth = display.getDefaultDisplaySync().width;
          this.playerHeight = this.playerWidth * height / width;
        } else {
          // 视图高填满屏幕,宽等比例适配留黑边
          this.playerHeight = display.getDefaultDisplaySync().height;
          this.playerWidth = this.playerHeight * width / height;
        }
      }
    })
  }
}

方案 2:自行封装鸿蒙系统播放器

VodEnv.addLicenseFile(this.license)

.....
// addLicenseFile 后,检查 license,如不可用,使用自己封装的系统播放器。
if (!VodEnv.isLicenseAvailable()) {
  // 接入方自行封装鸿蒙系统播放器。
}

详见以下鸿蒙官方文档:

最近更新时间:2026.05.29 15:26:27
这个页面对您有帮助吗?
有用
有用
无用
无用