動態

詳情 返回 返回

【HarmonyOS-媒體技術-AVPlayer】手把手教你用 AVPlayer 實現流媒體播放(ArkTS 詳解) - 動態 詳情

2025 年最新實戰指南|從零搭建穩定、流暢的直播/點播播放器

一句話總結

在 HarmonyOS 中,使用 AVPlayer 播放流媒體,不是"能播就行",而是要"穩、準、快、可控"

本文帶你掌握從創建到釋放的全鏈路操作,覆蓋 HLS/DASH/FLV 等主流協議,支持碼率切換、軌道選擇、自動重試、緩衝監控等高階能力。

一、前置準備:權限 & 環境配置

1. 添加網絡權限(必須!)

在 module.json5 中添加:

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

⚠️ 否則訪問任何網絡資源都會失敗!

2. 引入 MediaKit 模塊

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

推薦使用 @kit.MediaKit,它是 HarmonyOS 官方提供的多媒體核心庫。

二、標準播放流程(必看!)

順序不能亂!否則可能收不到事件、無法播放

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;
    });

四、主流協議支持一覽表

協議 是否支持 典型 URL 示例 重點適用場景
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() 或資源無效 檢查 URL、網絡、格式
字幕不顯示 路徑錯誤或未加載 addSubtitleFromFd() 加載 .srt 文件
無法跳轉(seek) 未監聽 seekDone 或資源不支持 檢查是否為分段資源(如 HLS)
多次播放報錯 未釋放舊實例 播放實例不使用後,調 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() {
    // ...
  }
}

八、立即行動,開啓你的音視頻播放開發之旅

點擊瞭解完整開發示例與 API 文檔

HarmonyOS AVPlayer 官方文檔

加入 HarmonyOS 社區,共創未來

我們誠邀廣大開發者一起參與 HarmonyOS 技術生態建設,共建更開放、更智能的未來世界!

加入開發者社區,獲取最新資訊和技術支持

HarmonyOS 官方社區

如果你覺得這篇指南有用,歡迎點贊、收藏、分享給更多開發者!
讓 AVPlayer 成為你開發路上的得力助手,開啓你的音視頻播放新紀元!

Add a new 評論

Some HTML is okay.