【HarmonyOS-媒体技术-AVPlayer】手把手教你用 AVPlayer 实现流媒体播放(ArkTS 详解)

HarmonyOS开发者社区
发布于 2025-11-3 15:31
浏览
0收藏

 2025 年最新实战指南|从零搭建稳定、流畅的直播/点播播放器


一句话总结

AVPlayer 播放流媒体,不是“能播就行”,而是要“稳、准、快、可控”。本文带你掌握从创建到释放的全链路操作,覆盖


 一、前置准备:权限 & 环境配置

1. 添加网络权限(必须!)

module.json5 中添加:


{
  "reqPermissions": [
    {
      "name": "ohos.permission.INTERNET"
    }
  ]
}

否则访问任何网络资源都会失败!


2. 引入 MediaKit 模块

import { media } from '@kit.MediaKit';

 推荐使用 @kit.MediaKit,它是


 二、标准播放流程(必看!)

顺序不能乱!否则可能收不到事件、无法播放

async avSetupStreamingMediaVideo() {
    if (this.context == undefined) return;
    // 创建avPlayer实例对象。
    this.avPlayer = await media.createAVPlayer();

    // 创建状态机变化回调函数。
    await this.setAVPlayerCallback((avPlayer: media.AVPlayer) => {
      this.percent = avPlayer.width / avPlayer.height;  // 计算并保存视频的宽高比
      this.setVideoWH();  // 调用方法更新视频显示区域的宽高
      this.durationTime = this.getDurationTime();  // 获取视频总时长
      setInterval(() => { // 更新当前时间。
        if (!this.isSwiping) {
          this.currentTime = this.getCurrentTime();
        }
      }, SET_INTERVAL);
    });

    // 设置播放资源。
    this.avPlayer.url = "http://media.iyuns.top:1000/http/720p_1m.mp4";
   //开始播放
    avPlay(): void {
      if (this.avPlayer) {
        try {
          this.avPlayer.play();
        } catch (e) {
          console.error(`${this.tag}: avPlay = ${JSON.stringify(e)}`);
        }
      }
    }

三、核心监听事件详解(缺一不可)



事件






作用






说明




stateChange

监听播放器状态变化

必要事件,监听播放器的state属性改变。

需要播放器在idle状态下、未调用设置资源接口前完成设置监听,若在调用设置资源接口后再设置监听,可能导致无法收到资源设置过程中上报的stateChange事件。

error

捕获播放错误

网络异常、格式不支持、URL无效等,需要播放器在idle状态下、未调用设置资源接口前完成设置监听,若在调用设置资源接口后再设置监听,可能导致无法收到资源设置过程中上报的error事件。

durationUpdate

获取总时长

监听进度条长度,刷新资源时长。

timeUpdate

实时更新播放进度

监听进度条当前位置,刷新当前时间。

bufferingUpdate

监控缓冲状态

监听网络播放缓冲信息,上报缓冲百分比以及缓存播放进度。

seekDone

seek 跳转完成通知

监听seek()请求完成情况。

当使用seek()跳转到指定播放位置后,如果seek操作成功,将上报该事件。

speedDone

倍速设置完成通知

监听setSpeed()请求完成情况。

当使用setSpeed()设置播放倍速后,如果setSpeed操作成功,将上报该事件。

volumeChange

音量调节完成反馈

监听setVolume()请求完成情况。

当使用setVolume()调节播放音量后,如果setVolume操作成功,将上报该事件。

audioInterrupt

监听音频焦点切换信息

搭配属性audioInterruptMode使用。

如果当前设备存在多个音频正在播放,音频焦点被切换(即播放其他媒体如通话等)时将上报该事件,应用可以及时处理。


 示例:监听播放器状态变化和监听播放时间

// 状态机变化回调函数。
    this.avPlayer.on('stateChange', async (state, reason) => {
      if (this.avPlayer == null) {
        console.info(`${this.tag}: avPlayer has not init on state change`);
        return;
      }
   // 时间上报监听函数。
    this.avPlayer.on('timeUpdate', (time: number) => {
      this.currentTime = time;
    });

四、主流协议支持一览表



协议






是否支持






典型






重点适用场景






HLS


✅ 支持

​https://xxx/index.m3u8​

直播、点播、CDN 分发、自适应码率、DRM 加密



DASH


✅ 支持

​https://xxx.mpd​

直播、点播、CDN 分发、自适应码率、DRM 加密



HTTP/HTTPS


✅ 支持

​https://xxx.mp4​

点播、短片



HTTP-FLV


✅ 支持

​https://xxx.flv​

低延迟直播(如游戏推流)

✅ 所有协议均支持 setSource() 直接接入,无需额外封装。


五、高阶功能实战(让你的播放器“聪明”起来)

 1. 流媒体缓冲状态

当下载速率低于片源的码率时,会出现卡顿。此时,播放器检测到缓冲区数据不足,会先缓冲一些数据再播放,避免连续卡顿。一次卡顿对应的缓冲事件上报过程为:BUFFERING_START-> BUFFERING_PERCENT 0 -> ... -> BUFFERING_PERCENT 100 -> BUFFERING_END。CACHED_DURATION在卡顿过程和播放过程中都会持续上报,直至下载至资源末尾。


import { media } from '@kit.MediaKit';
// 类成员定义avPlayer
private avPlayer: media.AVPlayer | null = null;

// 创建avPlayer实例对象。
this.avPlayer = await media.createAVPlayer();
// 监听当前bufferingUpdate缓冲状态。
this.avPlayer.on('bufferingUpdate', (infoType : media.BufferingInfoType, value : number) => {
  console.info(`AVPlayer bufferingUpdate, infoType is ${infoType}, value is ${value}.`);
})

 适用于直播、弱网环境下保障连续播放。


 2. HLS 多码率切换(自定义清晰度)

​当前流媒体HLS协议流支持多码率播放,默认情况下,播放器会根据网络下载速度选择合适的码率。

通过​on('availableBitrates')​监听当前HLS协议流可用的码率。如果监听的码率列表长度为0,则不支持设置指定码率。​

import { media } from '@kit.MediaKit';
// 类成员定义avPlayer
private avPlayer: media.AVPlayer | null = null;

// 创建avPlayer实例对象。
this.avPlayer = await media.createAVPlayer();
// 监听当前HLS协议流可用的码率。
this.avPlayer.on('availableBitrates', (bitrates: Array<number>) => {
  console.info('availableBitrates called, and availableBitrates length is: ' + bitrates.length);
})

通过​setBitrate​​接口设置播放码率。若用户设置的码率不在可用码率中,播放器将选择最小且最接近的码率。该接口只能在prepared/playing/paused/completed状态下调用,可通过监听​bitrateDone​事件确认是否生效。

import { media } from '@kit.MediaKit';
// 类成员定义avPlayer
private avPlayer: media.AVPlayer | null = null;

// 创建avPlayer实例对象。
this.avPlayer = await media.createAVPlayer();
// 监听码率设置是否生效。
this.avPlayer.on('bitrateDone', (bitrate: number) => {
  console.info('bitrateDone called, and bitrate value is: ' + bitrate);
})
// 设置播放码率。
this.bitrate: number = 96000;
this.avPlayer.setBitrate(this.bitrate);

 可配合 UI 提供“清晰度选择”按钮。


 3. DASH 起播策略设置(首帧更快加载)


为了保证在弱网环境下的播放体验,AVPlayer将默认选择最低的视频分辨率开始播放,随后依据网络状况自动调整。开发者可以根据具体需求,自定义DASH视频的起播策略,包括设定视频的宽度、高度以及色彩格式等参数。


// 自定义起播分辨率:1920×1080
import { media } from '@kit.MediaKit';

let mediaSource : media.MediaSource = media.createMediaSourceWithUrl("http://test.cn/dash/aaa.mpd",  {"User-Agent" : "User-Agent-Value"});
let playbackStrategy : media.PlaybackStrategy = {preferredWidth: 1920, preferredHeight: 1080};
this.avPlayer.setMediaSource(mediaSource, playbackStrategy);

弱网环境下优先加载低码率,提升首帧速度。


 4. DASH 音视频轨道切换(手动选清晰度/语言)

​DASH流媒体资源包含多路不同分辨率、码率、采样率、编码格式的音频、视频及字幕资源。默认情况下,AVPlayer会依据网络状况自动切换不同码率的视频轨道。开发者可根据需求选择指定的音视频轨道播放,此时自适应码率切换策略将失效。

设置selectTrack生效的监听事件​trackChange​。​


import { media } from '@kit.MediaKit';
// 类成员定义avPlayer
private avPlayer: media.AVPlayer | null = null;

// 创建avPlayer实例对象。
this.avPlayer = await media.createAVPlayer();
this.avPlayer.on('trackChange', (index: number, isSelect: boolean) => {
  console.info(`trackChange info, index: ${index}, isSelect: ${isSelect}`);
})
});


调用​getTrackDescription​​获取所有音视频轨道列表。开发者可根据实际需求,基于​MediaDescription​各字段信息,确定目标轨道索引。


// 以获取1080p视频轨道索引为例。
import { media } from '@kit.MediaKit';
import { BusinessError } from '@kit.BasicServicesKit';
public videoTrackIndex: number = 0;
// 类成员定义avPlayer
private avPlayer: media.AVPlayer | null = null;

// 创建avPlayer实例对象。
this.avPlayer = await media.createAVPlayer();
this.avPlayer.getTrackDescription((error: BusinessError, arrList: Array<media.MediaDescription>) => {
  if (arrList != null) {
    for (let i = 0; i < arrList.length; i++) {
      let propertyIndex: Object = arrList[i][media.MediaDescriptionKey.MD_KEY_TRACK_INDEX];
      let propertyType: Object = arrList[i][media.MediaDescriptionKey.MD_KEY_TRACK_TYPE];
      let propertyWidth: Object = arrList[i][media.MediaDescriptionKey.MD_KEY_WIDTH];
      let propertyHeight: Object = arrList[i][media.MediaDescriptionKey.MD_KEY_HEIGHT];
      if (propertyType == media.MediaType.MEDIA_TYPE_VID && propertyWidth == 1920 && propertyHeight == 1080) {
        this.videoTrackIndex = parseInt(propertyIndex?.toString()); // 获取1080p视频轨道索引。
      }
    }
  } else {
    console.error(`getTrackDescription fail, error:${error}`);
  }
});


在音视频播放过程中调用​selectTrack​​选择对应的音视频轨道,或者调用​deselectTrack​取消选择的音视频轨道。


import { media } from '@kit.MediaKit';
public videoTrackIndex: number = 0;
    // 类成员定义avPlayer
private avPlayer: media.AVPlayer | null = null;

// 创建avPlayer实例对象。
this.avPlayer = await media.createAVPlayer();
// 切换至目标视频轨道。
this.avPlayer.selectTrack(this.videoTrackIndex);
// 取消选择目标视频轨道。
// this.avPlayer.deselectTrack(this.videoTrackIndex);


 适合教育类、影视类 App,让用户自由选择画质/字幕/语音。


 六、常见坑位 & 解决方案(避雷手册)



问题






原因






解法




play() 无效,无反应

没等prepared 状态

必须监听stateChange,在prepared 后调 play()

durationUpdate 不触发

未调用prepare() 或资源无效

检查

字幕不显示

路径错误或未加载

addSubtitleFromFd() 加载 .srt 文件

无法跳转(seek)

未监听seekDone 或资源不支持

检查是否为分段资源(如

多次播放报错

未释放旧实例

播放实例不使用后,调release()及时释放。

音量调节无效

未监听volumeChange 或未调 setVolume()

检查调用顺序和参数


 七、一个简单的开发实例

import { media } from '@kit.MediaKit';
import { emitter } from '@kit.BasicServicesKit';
import { display } from '@kit.ArkUI';

const TIME_ONE = 60000; // 1分钟的毫秒数。
const TIME_TWO = 1000;  // 1秒的毫秒数。
const SET_INTERVAL = 1000; // 每秒更新一次当前播放时间。
const SPEED_ZERO: number = 0; // 对应1.00x。
const SPEED_ONE: number = 1;  // 对应1.25x。
const SPEED_TWO: number = 2;  // 对应1.75x。
const SPEED_THREE: number = 3; // 对应2.00x。
const PROPORTION: number = 0.99;
let innerEventFalse: emitter.InnerEvent = {
  eventId: 1,
  priority: emitter.EventPriority.HIGH
};
let innerEventTrue: emitter.InnerEvent = {
  eventId: 2,
  priority: emitter.EventPriority.HIGH
};

let innerEventWH: emitter.InnerEvent = {
  eventId: 3,
  priority: emitter.EventPriority.HIGH
};
@Entry
@Component
struct Index {
  private avPlayer: media.AVPlayer | null = null;
  private context: Context | undefined = undefined;
  public videoTrackIndex: number = 0;
  public bitrate: number = 0;
  @State durationTime: number = 0;
  @State currentTime: number = 0;
  @State percent: number = 0;
  @State isSwiping: boolean = false;
  @State tag: string = 'StreamingMedia';
  private surfaceId: string = '';
  @State speedSelect: number = -1;
  public intervalID: number = -1;
  @State windowWidth: number = 300;
  @State windowHeight: number = 300;
  @State surfaceW: number | null = null;
  @State surfaceH: number | null = null;
  @State isPaused: boolean = true;
  @State XComponentFlag: boolean = false;
  getDurationTime(): number {
    return this.durationTime;
  }

  getCurrentTime(): number {
    return this.currentTime;
  }

  timeConvert(time: number): string {
    let min: number = Math.floor(time / TIME_ONE);
    let second: string = ((time % TIME_ONE) / TIME_TWO).toFixed(0);
    // return `${min}:${(+second < TIME_THREE ? '0' : '') + second}`;
    second = second.padStart(2, '0');
    return `${min}:${second}`;
  }

  async msleepAsync(ms: number): Promise<boolean> {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve(true)
      }, ms)
    })
  }

  async avSetupStreamingMediaVideo() {
    if (this.context == undefined) return;
    // 创建avPlayer实例对象。
    this.avPlayer = await media.createAVPlayer();

    // 创建状态机变化回调函数。
    await this.setAVPlayerCallback((avPlayer: media.AVPlayer) => {
      this.percent = avPlayer.width / avPlayer.height;
      this.setVideoWH();
      this.durationTime = this.getDurationTime();
      setInterval(() => { // 更新当前时间。
        if (!this.isSwiping) {
          this.currentTime = this.getCurrentTime();
        }
      }, SET_INTERVAL);
    });

    // 情况一:HTTP视频播放。
    this.avPlayer.url = "http://media.iyuns.top:1000/http/720p_1m.mp4";

    // 情况二:HLS视频播放。
    // this.avPlayer.url = "http://media.iyuns.top:1000/720-270-480.m3u8";

    // 情况三:DASH视频播放。
    // this.avPlayer.url = "http://media.iyuns.top:1000/dash/720p/720-1/720-1.mpd";

    // 情况四:通过setMediaSource设置自定义头域及播放优选参数实现初始播放参数设置,以流媒体HTTP点播为例。
    /*
    let mediaSource : media.MediaSource = media.createMediaSourceWithUrl("http://media.iyuns.top:1000/http/720p_1m.mp4", {"":""});
    // 设置播放策略,设置为缓冲区数据为20s。
    let playbackStrategy : media.PlaybackStrategy = {preferredBufferDuration: 20};
    // 为avPlayer设置媒体来源和播放策略。
    this.avPlayer.setMediaSource(mediaSource, playbackStrategy);
    * */

    // 情况五:HLS切码率。
    /*
    this.avPlayer.url = "https://upftimae.dailyworkout.cn/videos/course/c800f81a209b5ee7891f1128ed301db/4/master.m3u8";
    let bitrate: number = 0;
    // 监听当前HLS协议流可用的码率。
    this.avPlayer.on('availableBitrates', (bitrates: Array<number>) => {
      console.info('availableBitrates called, and availableBitrates length is: ' + bitrates.length);
      this.bitrate = bitrates[0]; // 保存需要切换的码率。
    })
    // 监听码率设置是否生效。
    this.avPlayer.on('bitrateDone', (bitrate: number) => {
      console.info('bitrateDone called, and bitrate value is: ' + bitrate);
    })
    * */

    // 情况六:DASH切换音视频轨道。
    /*
    this.avPlayer.url = "http://poster-inland.hwcloudtest.cn/AiMaxEngine/ProductionEnvVideo/DASH_SDR_MultiAudio_MultiSubtitle_yinHeHuWeiDui3/DASH_SDR_MultiAudio_MultiSubtitle_yinHeHuWeiDui3.mpd";
    //
    this.avPlayer.getTrackDescription((error: BusinessError, arrList: Array<media.MediaDescription>) => {
      if (arrList != null) {
        for (let i = 0; i < arrList.length; i++) {
          let propertyIndex: Object = arrList[i][media.MediaDescriptionKey.MD_KEY_TRACK_INDEX];
          let propertyType: Object = arrList[i][media.MediaDescriptionKey.MD_KEY_TRACK_TYPE];
          let propertyWidth: Object = arrList[i][media.MediaDescriptionKey.MD_KEY_WIDTH];
          let propertyHeight: Object = arrList[i][media.MediaDescriptionKey.MD_KEY_HEIGHT];
          if (propertyType == media.MediaType.MEDIA_TYPE_VID && propertyWidth == 1920 && propertyHeight == 1080) {
            this.videoTrackIndex = parseInt(propertyIndex.toString()); // 获取1080p视频轨道索引。
          }
        }
      } else {
        console.error(`getTrackDescription fail, error:${error}`);
      }
    });
    * */
  }

  // HLS切换码率。
  changeBitrate(bitrate: number) {
    if (this.avPlayer == null) {
      return;
    }
    // 设置播放码率。
    try {
      this.avPlayer.setBitrate(bitrate);
    } catch (error) {
      console.error(`${this.tag}: setBitrate failed, error message is = ${JSON.stringify(error.message)}`);
    }
  }

  // DASH切换音视频轨道。
  changeTrack(track: number) {
    if (this.avPlayer == null) {
      return;
    }
    // 切换至目标视频轨道。
    try {
      this.avPlayer.selectTrack(track);
    } catch (error) {
      console.error(`${this.tag}: selectTrack failed, error message is = ${JSON.stringify(error.message)}`);
    }
    // 取消选择目标视频轨道。
    /*
    try {
      this.avPlayer.deselectTrack(track);
    } catch (error) {
      console.error(`${this.tag}: deselectTrack failed, error message is = ${JSON.stringify(error.message)}`);
    }
    * */
  }

  avPlay(): void {
    if (this.avPlayer) {
      try {
        this.avPlayer.play();
      } catch (e) {
        console.error(`${this.tag}: avPlay = ${JSON.stringify(e)}`);
      }
    }
  }

  avPause(): void {
    if (this.avPlayer) {
      try {
        this.avPlayer.pause();
        console.info(`${this.tag}: avPause==`);
      } catch (e) {
        console.error(`${this.tag}: avPause== ${JSON.stringify(e)}`);
      }
    }
  }

  async avSeek(seekTime: number, mode: SliderChangeMode): Promise<void> {
    if (this.avPlayer) {
      try {
        console.info(`${this.tag}: videoSeek  seekTime== ${seekTime}`);
        this.avPlayer.seek(seekTime, 2);
        this.currentTime = seekTime;
      } catch (e) {
        console.error(`${this.tag}: videoSeek== ${JSON.stringify(e)}`);
      }
    }
  }

  avSetSpeed(speed: number): void {
    if (this.avPlayer) {
      try {
        this.avPlayer.setSpeed(speed);
        console.info(`${this.tag}: avSetSpeed enum ${speed}`);
      } catch (e) {
        console.error(`${this.tag}: avSetSpeed == ${JSON.stringify(e)}`);
      }
    }
  }

  // 注册avplayer回调函数。
  async setAVPlayerCallback(callback: (avPlayer: media.AVPlayer) => void, vType?: number): Promise<void> {
    // seek操作结果回调函数。
    if (this.avPlayer == null) {
      console.error(`${this.tag}: avPlayer has not init!`);
      return;
    }
    this.avPlayer.on('seekDone', (seekDoneTime) => {
      console.info(`${this.tag}: setAVPlayerCallback AVPlayer seek succeeded, seek time is ${seekDoneTime}`);
    });
    this.avPlayer.on('speedDone', (speed) => {
      console.info(`${this.tag}: setAVPlayerCallback AVPlayer speedDone, speed is ${speed}`);
    });
    // error回调监听函数,当avPlayer在操作过程中出现错误时调用reset接口触发重置流程。
    this.avPlayer.on('error', (err) => {
      console.error(`${this.tag}: setAVPlayerCallback Invoke avPlayer failed ${JSON.stringify(err)}`);
      if (this.avPlayer == null) {
        console.error(`${this.tag}: avPlayer has not init on error`);
        return;
      }
      this.avPlayer.reset();
    });
    // 状态机变化回调函数。
    this.avPlayer.on('stateChange', async (state, reason) => {
      if (this.avPlayer == null) {
        console.info(`${this.tag}: avPlayer has not init on state change`);
        return;
      }
      switch (state) {
        case 'idle': // 成功调用reset接口后触发该状态机上报。
          console.info(`${this.tag}: setAVPlayerCallback AVPlayer state idle called.`);
          break;
        case 'initialized': // avplayer 设置播放源后触发该状态上报。
          console.info(`${this.tag}: setAVPlayerCallback AVPlayer state initialized called.`);
          if (this.surfaceId) {
            this.avPlayer.surfaceId = this.surfaceId; // 设置显示画面,当播放的资源为纯音频时无需设置。
            console.info(`${this.tag}: setAVPlayerCallback this.avPlayer.surfaceId = ${this.avPlayer.surfaceId}`);
            this.avPlayer.prepare();
          }
          break;
        case 'prepared': // prepare调用成功后上报该状态机。
          console.info(`${this.tag}: setAVPlayerCallback AVPlayer state prepared called.`);
          this.avPlayer.on('bufferingUpdate', (infoType: media.BufferingInfoType, value: number) => {
            console.info(`${this.tag}: bufferingUpdate called, infoType value: ${infoType}, value:${value}}`);
          })
          this.durationTime = this.avPlayer.duration;
          this.currentTime = this.avPlayer.currentTime;
          this.avPlayer.play(); // 调用播放接口开始播放。
          console.info(`${this.tag}:
            setAVPlayerCallback speedSelect: ${this.speedSelect}, duration: ${this.durationTime}`);
          if (this.speedSelect != -1) {
            switch (this.speedSelect) {
              case SPEED_ZERO:
                this.avSetSpeed(media.PlaybackSpeed.SPEED_FORWARD_1_00_X);
                break;
              case SPEED_ONE:
                this.avSetSpeed(media.PlaybackSpeed.SPEED_FORWARD_1_25_X);
                break;
              case SPEED_TWO:
                this.avSetSpeed(media.PlaybackSpeed.SPEED_FORWARD_1_75_X);
                break;
              case SPEED_THREE:
                this.avSetSpeed(media.PlaybackSpeed.SPEED_FORWARD_2_00_X);
                break;
            }
          }
          callback(this.avPlayer);
          break;
        case 'playing': // play成功调用后触发该状态机上报。
          console.info(`${this.tag}: setAVPlayerCallback AVPlayer state playing called.`);
          if (this.intervalID != -1) {
            clearInterval(this.intervalID)
          }
          this.intervalID = setInterval(() => { // 更新当前时间。
            AppStorage.setOrCreate('durationTime', this.durationTime);
            AppStorage.setOrCreate('currentTime', this.currentTime);
          }, 100);
          let eventDataTrue: emitter.EventData = {
            data: {
              'flag': true
            }
          };
          let innerEventTrue: emitter.InnerEvent = {
            eventId: 2,
            priority: emitter.EventPriority.HIGH
          };
          emitter.emit(innerEventTrue, eventDataTrue);
          break;
        case 'completed': // 播放结束后触发该状态机上报。
          console.info(`${this.tag}: setAVPlayerCallback AVPlayer state completed called.`);
          let eventDataFalse: emitter.EventData = {
            data: {
              'flag': false
            }
          };
          let innerEvent: emitter.InnerEvent = {
            eventId: 1,
            priority: emitter.EventPriority.HIGH
          };
          emitter.emit(innerEvent, eventDataFalse);
          if (this.intervalID != -1) {
            clearInterval(this.intervalID)
          }
          this.avPlayer.off('bufferingUpdate')
          AppStorage.setOrCreate('currentTime', this.durationTime);
          break;
        case 'released':
          console.info(`${this.tag}: setAVPlayerCallback released called.`);
          break
        case 'stopped':
          console.info(`${this.tag}: setAVPlayerCallback AVPlayer state stopped called.`);
          break
        case 'error':
          console.error(`${this.tag}: setAVPlayerCallback AVPlayer state error called.`);
          break
        case 'paused':
          console.info(`${this.tag}: setAVPlayerCallback AVPlayer state paused called.`);
          break
        default:
          console.info(`${this.tag}: setAVPlayerCallback AVPlayer state unknown called.`);
          break;
      }
    });
    // 时间上报监听函数。
    this.avPlayer.on('timeUpdate', (time: number) => {
      this.currentTime = time;
    });
  }

  aboutToAppear() {
    this.windowWidth = display.getDefaultDisplaySync().width;
    this.windowHeight = display.getDefaultDisplaySync().height;
    if (this.percent >= 1) { // 横向视频。
      this.surfaceW = Math.round(this.windowWidth * PROPORTION);
      this.surfaceH = Math.round(this.surfaceW / this.percent);
    } else { // 纵向视频。
      this.surfaceH = Math.round(this.windowHeight * PROPORTION);
      this.surfaceW = Math.round(this.surfaceH * this.percent);
    }
    this.isPaused = true;
    this.context = this.getUIContext().getHostContext();
  }

  aboutToDisappear() {
    if (this.avPlayer == null) {
      console.info(`${this.tag}: avPlayer has not init aboutToDisappear`);
      return;
    }
    this.avPlayer.release((err) => {
      if (err == null) {
        console.info(`${this.tag}: videoRelease release success`);
      } else {
        console.error(`${this.tag}: videoRelease release failed, error message is = ${JSON.stringify(err.message)}`);
      }
    });
    emitter.off(innerEventFalse.eventId);
  }

  onPageHide() {
    this.avPause();
    this.isPaused = false;
  }

  onPageShow() {
    emitter.on(innerEventTrue, (res: emitter.EventData) => {
      if (res.data) {
        this.isPaused = res.data.flag;
        this.XComponentFlag = res.data.flag;
      }
    });
    emitter.on(innerEventFalse, (res: emitter.EventData) => {
      if (res.data) {
        this.isPaused = res.data.flag;
      }
    });
    emitter.on(innerEventWH, (res: emitter.EventData) => {
      if (res.data) {
        this.windowWidth = res.data.width;
        this.windowHeight = res.data.height;
        this.setVideoWH();
      }
    });
  }

  setVideoWH(): void {
    if (this.percent >= 1) { // 横向视频。
      this.surfaceW = Math.round(this.windowWidth * PROPORTION);
      this.surfaceH = Math.round(this.surfaceW / this.percent);
    } else { // 纵向视频。
      this.surfaceH = Math.round(this.windowHeight * PROPORTION);
      this.surfaceW = Math.round(this.surfaceH * this.percent);
    }
  }

  @Builder
  CoverXComponent() {
    // ...
  }

  build() {
    // ...
  }
}

八、立即行动,开启你的音视频播放开发之旅!


点击了解完整开发示例与

HarmonyOS AVPlayer 官方文档


 加入 HarmonyOS 社区,共创未来!

我们诚邀广大开发者一起参与

加入开发者社区,获取最新资讯和技术支持


HarmonyOS 官方社区


如果你觉得这篇指南有用,欢迎点赞、收藏、分享给更多开发者!

AVPlayer 成为你开发路上的得力助手,开启你的音视频播放新纪元!


分类
标签
已于2025-11-3 15:33:55修改
收藏
回复
举报
回复
    相关推荐