说明
本文讲解使用AVPlayer实现播放声音
注意
不能在 previewer 播放声音
只能在 模拟器 播放声音
播放流程
- 创建AVPlayer
- 设置播放资源
- 设置播放参数(音量/倍速/焦点模式)
- 播放控制(播放/暂停/跳转/停止)
- 重置
- 销毁资源
播放状态
通过播放状态,可以知道当前播放器是在 播放 或 播放结束 等,具体如下:
idle:闲置状态,AVPlayer刚被创建
createAVPlayer()或者调用了reset()方法之后,进入Idle状态。首次创建createAVPlayer(),所有属性都为默认值。调用reset()方法,url或fdSrc或dataSrc属性及loop属性会被重置,其他用户设置的属性将被保留。initialized:资源初始化,在Idle 状态设置
url或fdSrc属性,AVPlayer会进入initialized状态,此时可以配置窗口、音频等静态属性。prepared:已准备状态,在
initialized状态调用prepare()方法,AVPlayer会进入prepared状态,此时播放引擎的资源已准备就绪。playing:正在播放状态,在
prepared/paused/completed状态调用play()方法,AVPlayer会进入playing状态。paused:暂停状态,在
playing状态调用pause()方法,AVPlayer会进入paused状态。completed:播放至结尾状态,当媒体资源播放至结尾时,如果用户未设置循环播放(loop = true),AVPlayer会进入
completed状态,此时调用play()会进入playing状态和重播,调用stop()会进入stopped状态。stopped:停止状态,在
prepared/playing/paused/completed状态调用stop()方法,AVPlayer会进入stopped状态,此时播放引擎只会保留属性,但会释放内存资源,可以调用prepare()重新准备,也可以调用reset()重置,或者调用release()彻底销毁。released:销毁状态,销毁与当前AVPlayer关联的播放引擎,无法再进行状态转换,调用
release()方法后,会进入released状态,结束流程。error:错误状态,当播放引擎发生不可逆的错误(详见媒体错误码),则会转换至当前状态,可以调用
reset()重置,也可以调用release()销毁重建。
注意: 区分error状态和 on(‘error’) :- 进入
error状态时,会触发on('error')监听事件,可以通过on('error')事件获取详细错误信息; - 处于error状态时,播放服务进入不可播控的状态,要求客户端设计容错机制,使用
reset()重置或者release()销毁重建; - 如果客户端收到
on('error'),但未进入error状态:- 原因1: 客户端未按状态机调用API或传入参数错误,被AVPlayer拦截提醒,需要客户端调整代码逻辑;
- 原因2:播放过程发现码流问题,导致容器、解码短暂异常,不影响连续播放和播控操作的,不需要客户端设计容错机制。
- 进入
获取状态
通过AVPlayer的
state属性主动获取当前状态使用
on('stateChange')方法监听状态变化
创建对象
接口1
异步方式创建音视频播放实例,通过注册回调函数获取返回值。
createAVPlayer(callback: AsyncCallback<AVPlayer>): void
参数
- callback:类型:
AsyncCallback<AVPlayer>,必填,回调函数。异步返回AVPlayer实例,失败时返回null。可用于音视频播放。
接口2
异步方式创建音视频播放实例,通过Promise获取返回值
createAVPlayer(): Promise<AVPlayer>
返回值
Promise<AVPlayer>:Promise 对象。异步返回AVPlayer实例,失败时返回 null。可用于音视频播放。
例子
public async initAudioPlayer() {
this.avPlayer = await media.createAVPlayer()
}
属性
指定音频-url
url
类型:string,媒体URL,只允许在idle状态下设置。
应用场景:网络播放
支持的视频格式(mp4、mpeg-ts、mkv)。
支持的音频格式(m4a、aac、mp3、ogg、wav、flac、amr)。
支持路径示例:
fd类型播放:fd://xx。
http网络播放: http://xx。
https网络播放: https://xx。
hls网络播放路径:http://xx或者https://xx。
说明:
- 设置网络播放路径,需声明权限:
ohos.permission.INTERNET,相关错误码: 201。
指定音频-fdSrc
AVFileDescriptor
媒体文件描述,只允许在idle状态下设置。
使用场景:应用中的媒体资源放在 media 文件夹中
支持的视频格式(mp4、mpeg-ts、mkv)。
支持的音频格式(m4a、aac、mp3、ogg、wav、flac、amr)。
指定音频-dataSrc
dataSrc
类型:AVDataSrcDescriptor
流式媒体资源描述,只允许在idle状态下设置。
使用场景:应用播放从远端下载到本地的文件,在应用未下载完整音视频资源时,提前播放已获取的资源文件。
支持的视频格式(mp4、mpeg-ts、mkv)。
支持的音频格式(m4a、aac、mp3、ogg、wav、flac、amr)。
使用示例:
假设用户正在从远端服务器获取音视频媒体文件,希望下载到本地的同时播放已经下载好的部分:
用户需要获取媒体文件的总大小size(单位为字节),获取不到时设置为-1。
用户需要实现回调函数func用于填写数据,如果size = -1,则func形式为:func(buffer: ArrayBuffer, length: number),此时播放器只会按照顺序获取数据;否则func形式为:func(buffer: ArrayBuffer, length: number, pos: number),播放器会按需跳转并获取数据。
用户设置AVDataSrcDescriptor {fileSize = size, callback = func}。
注意事项:
如果播放的是mp4/m4a格式用户需要保证moov字段(媒体信息字段)在mdat字段(媒体数据字段)之前,或者moov之前的字段小于10M,否则会导致解析失败无法播放。
事件
监听错误事件(关键)
监听AVPlayer的错误事件,如果报错,需要通过 reset() 或者 release() 退出播放操作,否则无法继续播放
on(type: 'error', callback: ErrorCallback): void
例子
this.avPlayer.on('error', (err) => {
console.error(TAG,`播放器发生错误,错误码:${err.code}, 错误信息:${err.message}`,err);
// 调用reset重置资源,触发idle状态
this.avPlayer.reset();
})
监听播放状态机AVPlayerState
在不同的状态,需要调用相应的方法,否则不执行
on(type: 'stateChange', callback: OnAVPlayerStateChangeHandle): void
参数
- type:类型:string,必填,状态机切换事件回调类型,支持的事件:
stateChange,用户操作和系统都会触发此事件。 - callback:类型:OnAVPlayerStateChangeHandle,必填,状态机切换事件回调方法。
例子
// 状态机变化回调函数
this.avPlayer.on('stateChange', async (state, reason) => {
switch (state) {
case 'initialized':
// console.info(TAG,'资源初始化完成');
// 资源初始化完成,开始准备文件
this.avPlayer.prepare();
break;
case 'prepared':
// console.info(TAG,'资源准备完成');
// console.log("播放时间1:", (Date.now() - this.time )/1000)
// 设置音量
this.avPlayer.setVolume(this.volume)
// 资源准备完成,开始准备文件
this.avPlayer.play();
// console.log("播放时间1--:", (Date.now() - this.time )/1000)
break;
case 'completed':
// console.info(TAG,'播放完成');
// console.log("播放时间2:", (Date.now() - this.time )/1000)
this.avPlayer.stop();
break;
case 'stopped': // stop接口成功调用后触发该状态机上报
// console.info(TAG,'停止播放');
// console.log("播放时间3:", (Date.now() - this.time )/1000)
// 调用reset接口初始化avplayer状态,必须执行,否则第二次播放时会报错
this.avPlayer.reset();
break;
}
})
方法
准备(接口1)
准备播放音频/视频,需在 stateChange 事件成功触发 initialized状态后,才能调用。使用 callback 方式异步获取返回值。
prepare(callback: AsyncCallback<void>): void
参数
- callback:类型:
AsyncCallback<void>,必填,准备播放的回调方法
准备(接口2)
准备播放音频/视频,需在 stateChange 事件成功触发至 initialized 状态后,才能调用。通过 Promise 获取返回值。
prepare(): Promise<void>
返回值
类型: Promise<void>:准备播放的Promise返回值。
播放(接口1)
开始播放音视频资源,只能在 prepared/paused/completed 状态调用。使用 callback 方式异步获取返回值。
play(callback: AsyncCallback<void>): void
参数
- callback:类型:
AsyncCallback<void>,必填,开始播放的回调方法。
播放(接口1)
开始播放音视频资源,只能在 prepared/paused/completed 状态调用。通过Promise获取返回值。
play(): Promise<void>
返回值
类型:Promise<void>:开始播放的Promise返回值。
停止播放(接口1)
停止播放音视频资源,只能在 prepared/playing/paused/completed 状态调用。使用 callback 方式异步获取返回值。
stop(callback: AsyncCallback<void>): void
参数
- callback:类型:
AsyncCallback<void>,必填,停止播放的回调方法。
停止播放(接口2)
停止播放音视频资源,只能在 prepared/playing/paused/completed 状态调用。通过Promise获取返回值。
stop(): Promise<void>
返回值
类型:Promise<void>:停止播放的Promise返回值。
重置(接口1)(重要)
重置播放,只能在 initialized/prepared/playing/paused/completed/stopped/error 状态调用。使用 callback 方式异步获取返回值。
注意:报错后,必须调用该方法,否则不能再继续播放
reset(callback: AsyncCallback<void>): void
参数
- callback:类型:
AsyncCallback<void>,必填,重置播放的回调方法。
重置(接口2)(重要)
重置播放,只能在 initialized/prepared/playing/paused/completed/stopped/error 状态调用。通过 Promise 获取返回值。
注意:报错后,必须调用该方法,否则不能再继续播放
reset(): Promise<void>
返回值
类型:Promise<void>,重置播放的Promise返回值。
设置音量
设置媒体播放音量,只能在prepared/playing/paused/completed 状态调用,可以通过volumeChange 事件确认是否生效。
setVolume(volume: number): void
参数
- volume:类型:number,必填,指定的相对音量大小,取值范围为
[0.00-1.00],1表示最大音量,即100%。
案例
import media from '@ohos.multimedia.media';
import common from '@ohos.app.ability.common';
import { BusinessError } from '@kit.BasicServicesKit';
const TAG = 'AVPlayerUtil';
export default class AVPlayerUtil {
avPlayer !: media.AVPlayer;
// time = Date.now()
context = getContext(this) as common.UIAbilityContext;
/**
* 音量
*/
volume:number = 100
constructor() {
this.initAudioPlayer();
}
public async initAudioPlayer() {
this.avPlayer = await media.createAVPlayer()
this.setAVPlayerCallback();
}
private setAVPlayerCallback() {
// error回调监听函数,当avPlayer在操作过程中出现错误时调用 reset接口触发重置流程
this.avPlayer.on('error', (err) => {
console.error(TAG,`播放器发生错误,错误码:${err.code}, 错误信息:${err.message}`,err);
// 调用reset重置资源,触发idle状态
this.avPlayer.reset();
})
// 状态机变化回调函数
this.avPlayer.on('stateChange', async (state, reason) => {
switch (state) {
case 'initialized':
// console.info(TAG,'资源初始化完成');
// 资源初始化完成,开始准备文件
this.avPlayer.prepare();
break;
case 'prepared':
// console.info(TAG,'资源准备完成');
// console.log("播放时间1:", (Date.now() - this.time )/1000)
// 设置音量
this.avPlayer.setVolume(this.volume)
// 资源准备完成,开始准备文件
this.avPlayer.play();
// console.log("播放时间1--:", (Date.now() - this.time )/1000)
break;
case 'completed':
// console.info(TAG,'播放完成');
// console.log("播放时间2:", (Date.now() - this.time )/1000)
this.avPlayer.stop();
break;
case 'stopped': // stop接口成功调用后触发该状态机上报
// console.info(TAG,'停止播放');
// console.log("播放时间3:", (Date.now() - this.time )/1000)
// 调用reset接口初始化avplayer状态,必须执行,否则第二次播放时会报错
this.avPlayer.reset();
break;
}
})
}
/**
* 设置音量,最大值为1,即:100%
* @param volume
*/
public setVolume(volume:number):void{
this.volume = volume
}
/**
* 音频文件放在 rawfile 文件夹中
* @param fileName
*/
public async play(fileName:string) {
// this.time = Date.now()
// 停止
await this.avPlayer.reset()
try {
if(this.avPlayer) {
const fileDescriptor = await this.context.resourceManager.getRawFd(fileName)
this.avPlayer.fdSrc = fileDescriptor;
}
}catch (err){
console.error(TAG, `播放错误,错误码:${err.code},信息:${err.message}`)
}
}
}
调用:
player = new AudioPlayer()
// 设置音量
this.player.setVolume(1)
// 1.mp3在 rawfile 文件夹中
this.player.play("1.mp3")
