視頻播放

2024-02-16 13:58 更新

在HarmonyOS系統(tǒng)中,提供兩種視頻播放開發(fā)的方案:

  • AVPlayer:功能較完善的音視頻播放ArkTS/JS API,集成了流媒體和本地資源解析,媒體資源解封裝,視頻解碼和渲染功能,適用于對媒體資源進行端到端播放的場景,可直接播放mp4、mkv等格式的視頻文件。
  • Video組件:封裝了視頻播放的基礎(chǔ)能力,需要設(shè)置數(shù)據(jù)源以及基礎(chǔ)信息即可播放視頻,但相對擴展能力較弱。Video組件由ArkUI提供能力,相關(guān)指導請參考UI開發(fā)文檔-Video組件。

本開發(fā)指導將介紹如何使用AVPlayer開發(fā)視頻播放功能,以完整地播放一個視頻作為示例,實現(xiàn)端到端播放原始媒體資源。

開發(fā)指導

播放的全流程包含:創(chuàng)建AVPlayer,設(shè)置播放資源和窗口,設(shè)置播放參數(shù)(音量/倍速/縮放模式),播放控制(播放/暫停/跳轉(zhuǎn)/停止),重置,銷毀資源。在進行應用開發(fā)的過程中,開發(fā)者可以通過AVPlayer的state屬性主動獲取當前狀態(tài)或使用on('stateChange')方法監(jiān)聽狀態(tài)變化。如果應用在視頻播放器處于錯誤狀態(tài)時執(zhí)行操作,系統(tǒng)可能會拋出異常或生成其他未定義的行為。

圖1 播放狀態(tài)變化示意圖

狀態(tài)的詳細說明請參考AVPlayerState。當播放處于prepared / playing / paused / completed狀態(tài)時,播放引擎處于工作狀態(tài),這需要占用系統(tǒng)較多的運行內(nèi)存。當客戶端暫時不使用播放器時,調(diào)用reset()或release()回收內(nèi)存資源,做好資源利用。

開發(fā)步驟及注意事項

詳細的API說明請參考AVPlayer API參考。

  1. 創(chuàng)建實例createAVPlayer(),AVPlayer初始化idle狀態(tài)。
  2. 設(shè)置業(yè)務需要的監(jiān)聽事件,搭配全流程場景使用。支持的監(jiān)聽事件包括:

    事件類型

    說明

    stateChange

    必要事件,監(jiān)聽播放器的state屬性改變。

    error

    必要事件,監(jiān)聽播放器的錯誤信息。

    durationUpdate

    用于進度條,監(jiān)聽進度條長度,刷新資源時長。

    timeUpdate

    用于進度條,監(jiān)聽進度條當前位置,刷新當前時間。

    seekDone

    響應API調(diào)用,監(jiān)聽seek()請求完成情況。

    當使用seek()跳轉(zhuǎn)到指定播放位置后,如果seek操作成功,將上報該事件。

    speedDone

    響應API調(diào)用,監(jiān)聽setSpeed()請求完成情況。

    當使用setSpeed()設(shè)置播放倍速后,如果setSpeed操作成功,將上報該事件。

    volumeChange

    響應API調(diào)用,監(jiān)聽setVolume()請求完成情況。

    當使用setVolume()調(diào)節(jié)播放音量后,如果setVolume操作成功,將上報該事件。

    bitrateDone

    響應API調(diào)用,用于HLS協(xié)議流,監(jiān)聽setBitrate()請求完成情況。

    當使用setBitrate()指定播放比特率后,如果setBitrate操作成功,將上報該事件。

    availableBitrates

    用于HLS協(xié)議流,監(jiān)聽HLS資源的可選bitrates,用于setBitrate()。

    bufferingUpdate

    用于網(wǎng)絡播放,監(jiān)聽網(wǎng)絡播放緩沖信息。

    startRenderFrame

    用于視頻播放,監(jiān)聽視頻播放首幀渲染時間。

    videoSizeChange

    用于視頻播放,監(jiān)聽視頻播放的寬高信息,可用于調(diào)整窗口大小、比例。

    audioInterrupt

    監(jiān)聽音頻焦點切換信息,搭配屬性audioInterruptMode使用。

    如果當前設(shè)備存在多個媒體正在播放,音頻焦點被切換(即播放其他媒體如通話等)時將上報該事件,應用可以及時處理。

  3. 設(shè)置資源:設(shè)置屬性url,AVPlayer進入initialized狀態(tài)。
    說明

    下面代碼示例中的url僅作示意使用,開發(fā)者需根據(jù)實際情況,確認資源有效性并設(shè)置:

  4. 設(shè)置窗口:獲取并設(shè)置屬性SurfaceID,用于設(shè)置顯示畫面。

    應用需要從XComponent組件獲取surfaceID,獲取方式請參考XComponent。

  5. 準備播放:調(diào)用prepare(),AVPlayer進入prepared狀態(tài),此時可以獲取duration,設(shè)置縮放模式、音量等。
  6. 視頻播控:播放play(),暫停pause(),跳轉(zhuǎn)seek(),停止stop() 等操作。
  7. (可選)更換資源:調(diào)用reset()重置資源,AVPlayer重新進入idle狀態(tài),允許更換資源url。
  8. 退出播放:調(diào)用release()銷毀實例,AVPlayer進入released狀態(tài),退出播放。

完整示例

  1. import media from '@ohos.multimedia.media';
  2. import fs from '@ohos.file.fs';
  3. import common from '@ohos.app.ability.common';
  4. export class AVPlayerDemo {
  5. private avPlayer;
  6. private count: number = 0;
  7. private surfaceID: string; // surfaceID用于播放畫面顯示,具體的值需要通過Xcomponent接口獲取,相關(guān)文檔鏈接見上面Xcomponent創(chuàng)建方法
  8. // 注冊avplayer回調(diào)函數(shù)
  9. setAVPlayerCallback() {
  10. // seek操作結(jié)果回調(diào)函數(shù)
  11. this.avPlayer.on('seekDone', (seekDoneTime) => {
  12. console.info(`AVPlayer seek succeeded, seek time is ${seekDoneTime}`);
  13. })
  14. // error回調(diào)監(jiān)聽函數(shù),當avPlayer在操作過程中出現(xiàn)錯誤時調(diào)用reset接口觸發(fā)重置流程
  15. this.avPlayer.on('error', (err) => {
  16. console.error(`Invoke avPlayer failed, code is ${err.code}, message is ${err.message}`);
  17. this.avPlayer.reset(); // 調(diào)用reset重置資源,觸發(fā)idle狀態(tài)
  18. })
  19. // 狀態(tài)機變化回調(diào)函數(shù)
  20. this.avPlayer.on('stateChange', async (state, reason) => {
  21. switch (state) {
  22. case 'idle': // 成功調(diào)用reset接口后觸發(fā)該狀態(tài)機上報
  23. console.info('AVPlayer state idle called.');
  24. this.avPlayer.release(); // 調(diào)用release接口銷毀實例對象
  25. break;
  26. case 'initialized': // avplayer 設(shè)置播放源后觸發(fā)該狀態(tài)上報
  27. console.info('AVPlayerstate initialized called.');
  28. this.avPlayer.surfaceId = this.surfaceID; // 設(shè)置顯示畫面,當播放的資源為純音頻時無需設(shè)置
  29. this.avPlayer.prepare().then(() => {
  30. console.info('AVPlayer prepare succeeded.');
  31. }, (err) => {
  32. console.error(`Invoke prepare failed, code is ${err.code}, message is ${err.message}`);
  33. });
  34. break;
  35. case 'prepared': // prepare調(diào)用成功后上報該狀態(tài)機
  36. console.info('AVPlayer state prepared called.');
  37. this.avPlayer.play(); // 調(diào)用播放接口開始播放
  38. break;
  39. case 'playing': // play成功調(diào)用后觸發(fā)該狀態(tài)機上報
  40. console.info('AVPlayer state playing called.');
  41. if (this.count !== 0) {
  42. console.info('AVPlayer start to seek.');
  43. this.avPlayer.seek(this.avPlayer.duration); //seek到視頻末尾
  44. } else {
  45. this.avPlayer.pause(); // 調(diào)用暫停接口暫停播放
  46. }
  47. this.count++;
  48. break;
  49. case 'paused': // pause成功調(diào)用后觸發(fā)該狀態(tài)機上報
  50. console.info('AVPlayer state paused called.');
  51. this.avPlayer.play(); // 再次播放接口開始播放
  52. break;
  53. case 'completed': // 播放結(jié)束后觸發(fā)該狀態(tài)機上報
  54. console.info('AVPlayer state completed called.');
  55. this.avPlayer.stop(); //調(diào)用播放結(jié)束接口
  56. break;
  57. case 'stopped': // stop接口成功調(diào)用后觸發(fā)該狀態(tài)機上報
  58. console.info('AVPlayer state stopped called.');
  59. this.avPlayer.reset(); // 調(diào)用reset接口初始化avplayer狀態(tài)
  60. break;
  61. case 'released':
  62. console.info('AVPlayer state released called.');
  63. break;
  64. default:
  65. console.info('AVPlayer state unknown called.');
  66. break;
  67. }
  68. })
  69. }
  70. // 以下demo為使用fs文件系統(tǒng)打開沙箱地址獲取媒體文件地址并通過url屬性進行播放示例
  71. async avPlayerUrlDemo() {
  72. // 創(chuàng)建avPlayer實例對象
  73. this.avPlayer = await media.createAVPlayer();
  74. // 創(chuàng)建狀態(tài)機變化回調(diào)函數(shù)
  75. this.setAVPlayerCallback();
  76. let fdPath = 'fd://';
  77. let context = getContext(this) as common.UIAbilityContext;
  78. // 通過UIAbilityContext獲取沙箱地址filesDir,以下為Stage模型獲方式,如需在FA模型上獲取請參考《訪問應用沙箱》獲取地址
  79. let pathDir = context.filesDir;
  80. let path = pathDir + '/H264_AAC.mp4';
  81. // 打開相應的資源文件地址獲取fd,并為url賦值觸發(fā)initialized狀態(tài)機上報
  82. let file = await fs.open(path);
  83. fdPath = fdPath + '' + file.fd;
  84. this.avPlayer.url = fdPath;
  85. }
  86. // 以下demo為使用資源管理接口獲取打包在HAP內(nèi)的媒體資源文件并通過fdSrc屬性進行播放示例
  87. async avPlayerFdSrcDemo() {
  88. // 創(chuàng)建avPlayer實例對象
  89. this.avPlayer = await media.createAVPlayer();
  90. // 創(chuàng)建狀態(tài)機變化回調(diào)函數(shù)
  91. this.setAVPlayerCallback();
  92. // 通過UIAbilityContext的resourceManager成員的getRawFd接口獲取媒體資源播放地址
  93. // 返回類型為{fd,offset,length},fd為HAP包fd地址,offset為媒體資源偏移量,length為播放長度
  94. let context = getContext(this) as common.UIAbilityContext;
  95. let fileDescriptor = await context.resourceManager.getRawFd('H264_AAC.mp4');
  96. // 為fdSrc賦值觸發(fā)initialized狀態(tài)機上報
  97. this.avPlayer.fdSrc = fileDescriptor;
  98. }
  99. }
以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號