1/* 2 * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15import media from '@ohos.multimedia.media'; 16import Logger from '../utils/Logger' 17 18const TAG: string = '[AVPlayerModel]' 19 20export default class AVPlayerModel { 21 private avPlayer: media.AVPlayer; 22 private count: number = 1; 23 private surfaceID: string = ''; // surfaceID用于播放画面显示,具体的值需要通过Xcomponent接口获取,相关文档链接见上面Xcomponent创建方法 24 private isSeek: boolean = true; // 用于区分模式是否支持seek操作 25 26 private context; 27 28 constructor(context) { 29 this.context = context; 30 } 31 32 // 注册avplayer回调函数 33 setAVPlayerCallback() { 34 // seek操作结果回调函数 35 this.avPlayer.on('seekDone', (seekDoneTime) => { 36 Logger.info(TAG, `AVPlayer seek succeeded, seek time is ${seekDoneTime}`); 37 }) 38 // error回调监听函数,当avPlayer在操作过程中出现错误时调用reset接口触发重置流程 39 this.avPlayer.on('error', (err) => { 40 Logger.info(TAG, `Invoke avPlayer failed, code is ${err.code}, message is ${err.message}`); 41 this.avPlayer.reset(); // 调用reset重置资源,触发idle状态 42 }) 43 // 状态机变化回调函数 44 this.avPlayer.on('stateChange', async (state, reason) => { 45 switch (state) { 46 case 'idle': // 成功调用reset接口后触发该状态机上报 47 Logger.info(TAG, 'AVPlayer state idle called.'); 48 this.avPlayer.release(); // 调用release接口销毁实例对象 49 break; 50 case 'initialized': // avplayer 设置播放源后触发该状态上报 51 Logger.info(TAG, 'AVPlayerstate initialized called.'); 52 if (this.surfaceID) { 53 this.avPlayer.surfaceId = this.surfaceID; // 设置显示画面,当播放的资源为纯音频时无需设置 54 } 55 this.avPlayer.prepare().then(() => { 56 Logger.info(TAG, 'AVPlayer prepare succeeded.'); 57 }, (err) => { 58 Logger.info(TAG, `Invoke prepare failed, code is ${err.code}, message is ${err.message}`); 59 }); 60 break; 61 case 'prepared': // prepare调用成功后上报该状态机 62 Logger.info(TAG, 'AVPlayer state prepared called.'); 63 this.avPlayer.play(); // 调用播放接口开始播放 64 break; 65 case 'playing': // play成功调用后触发该状态机上报 66 Logger.info(TAG, 'AVPlayer state playing called.'); 67 if (this.count !== 0) { 68 if (this.isSeek) { 69 Logger.info(TAG, 'AVPlayer start to seek.'); 70 } else { 71 // 当播放模式不支持seek操作时继续播放到结尾 72 Logger.info(TAG, 'AVPlayer wait to play end.'); 73 } 74 } else { 75 this.avPlayer.pause(); // 调用暂停接口暂停播放 76 } 77 this.count++; 78 break; 79 case 'paused': // pause成功调用后触发该状态机上报 80 Logger.info(TAG, 'AVPlayer state paused called.'); 81 this.avPlayer.loop = true; 82 break; 83 case 'completed': // 播放结束后触发该状态机上报 84 Logger.info(TAG, 'AVPlayer state completed called.'); 85 this.avPlayer.play(); // 再次播放接口开始播放 86 break; 87 case 'stopped': // stop接口成功调用后触发该状态机上报 88 Logger.info(TAG, 'AVPlayer state stopped called.'); 89 this.avPlayer.reset(); // 调用reset接口初始化avplayer状态 90 break; 91 case 'released': 92 Logger.info(TAG, 'AVPlayer state released called.'); 93 break; 94 default: 95 Logger.info(TAG, 'AVPlayer state unknown called.'); 96 break; 97 } 98 }) 99 } 100 101 public play(){ 102 if (this.avPlayer) { 103 this.avPlayer.play(); 104 } 105 } 106 public paused(){ 107 if (this.avPlayer) { 108 this.avPlayer.pause(); 109 } 110 } 111 112 // 以下demo为使用资源管理接口获取打包在HAP内的媒体资源文件并通过fdSrc属性进行播放示例 113 async avPlayerFdSrcDemo(avName: string, surfaceID?: string) { 114 if (surfaceID) { 115 this.surfaceID = surfaceID; 116 } 117 // 创建avPlayer实例对象 118 this.avPlayer = await media.createAVPlayer(); 119 // 创建状态机变化回调函数 120 this.setAVPlayerCallback(); 121 // 通过UIAbilityContext的resourceManager成员的getRawFd接口获取媒体资源播放地址 122 // 返回类型为{fd,offset,length},fd为HAP包fd地址,offset为媒体资源偏移量,length为播放长度 123 let context = this.context; 124 let fileDescriptor = await context.resourceManager.getRawFd(avName); 125 this.isSeek = false; // 支持seek操作 126 // 为fdSrc赋值触发initialized状态机上报 127 this.avPlayer.fdSrc = fileDescriptor; 128 } 129 130 // 以下demo为通过url设置网络地址来实现播放直播码流的demo 131 async avPlayerLiveDemo() { 132 // 创建avPlayer实例对象 133 this.avPlayer = await media.createAVPlayer(); 134 // 创建状态机变化回调函数 135 this.setAVPlayerCallback(); 136 this.isSeek = false; // 不支持seek操作 137 this.avPlayer.url = 'http://xxx.xxx.xxx.xxx:xx/xx/index.m3u8'; // 播放hls网络直播码流 138 } 139} 140