AudioPlayerManager 音视频单例播放管理类操作文档附加案例
https://ohpm.openharmony.cn/#/cn/detail/@zyl%2Faudio_player_manager
一、概述
AudioPlayerManager 是鸿蒙 ArkTS 平台的音频播放单例管理类,基于鸿蒙 AVPlayer 封装,提供全局统一的音视频播放能力,支持多数据源适配与完整播放控制,保障播放资源安全与状态稳定。
核心特性
-
全局单例:避免多播放器实例冲突,统一管理播放资源
-
多源适配:支持 URL(网络/本地)、FD 句柄(fdSrc)两种播放方式
-
完整控制:播放/暂停/停止/跳转/倍速/音量调节全功能覆盖
-
智能处理:自动监听状态流转、音频打断,异常时自动重置与资源回收
-
错误兜底:完善的异常捕获机制,提供明确错误反馈
二、快速上手
2.1 导入单例实例🌞
ohpm install @zyl/audio_player_manager
2.2 基础播放示例
2.2.1 简单播放场景(推荐)
支持网络音频 URL 或本地沙箱文件路径
AudioPlayerManager.playAudioUrlOrFdSrc() 简单场景播放,可以传地址或者本地raw音频,
自动识别播放单独使用可以使用playByUrl和playByFdSrc进行播放
- @param url: string|media.AVFileDescriptor
- @param speed?: number 设置倍速
typescript
// 1. 播放网络音频
AudioPlayerManager.playAudioUrlOrFdSrc('https://vol.bczcdn.com/r/sa_103_6828_0_3_20150809024934.aac')
.then(() => console.log('播放成功/完成'))
.catch(() => console.log('播放失败或者被打断:'))
// 2. 播放本地沙箱音频(file:// 协议)
const context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;
let resource = context.resourceManager
const value: resourceManager.RawFileDescriptor = await resource.getRawFd('mp3/error.mp3') // 资源存在 entry\src\main\resources\rawfile\mp3\error.mp3
AudioPlayerManager.playAudioUrlOrFdSrc(value)
.then(() => console.log('播放成功/完成'))
.catch(() => console.log('播放失败或者被打断:'))
三、核心功能操作
3.1 播放控制
| 功能 | 代码示例 | 说明 |
|---|---|---|
| 开始播放 | AudioPlayerManager.play(); |
仅在 PREPARED/PAUSED 状态有效 |
| 暂停播放 | AudioPlayerManager.pause(); |
仅在 PLAYING 状态有效 |
| 停止播放 | AudioPlayerManager.stop(); |
仅在 PLAYING/PAUSED/PREPARED 状态有效,停止后可重新准备播放 |
| 销毁播放器 | AudioPlayerManager.destroy(); |
释放所有资源,实例不可复用,需重新获取单例 |
| 重置播放器 | AudioPlayerManager.reset(); |
回到 IDLE 状态,可重新设置数据源播放 |
3.2 进度与倍速调节
| 功能 | 代码示例 | 说明 |
|---|---|---|
| 进度跳转 | AudioPlayerManager.seekTo(10000); |
跳转至 10 秒位置(单位:毫秒),默认 SEEK_PREV_SYNC 模式;支持自定义模式(如 media.SeekMode.SEEK_NEXT_SYNC) |
| 设置倍速 | AudioPlayerManager.setSpeed(1.5); |
倍速范围 0.5~2.0,仅在 PREPARED/PLAYING/PAUSED/COMPLETED 状态有效 |
3.3 音量调节
typescript
// 设置音量为 50%(有效值:0.0~1.0,0 静音,1 最大音量)
AudioPlayerManager.setVolume(0.5);
3.4 视频播放补充(设置渲染Surface)
播放视频时需设置渲染 SurfaceId(仅在 INITIALIZED 状态有效):
typescript
// 从视频组件获取 SurfaceId 后设置
const surfaceId = "xxx"; // 实际值从 UI 组件获取
AudioPlayerManager.setSurfaceId(surfaceId);
四、状态与信息查询
| 查询项 | 代码示例 | 返回值说明 |
|---|---|---|
| 当前播放状态 | const state = AudioPlayerManager.getCurrentState; |
状态枚举:IDLE/INITIALIZED/PREPARED/PLAYING/PAUSED/COMPLETED/STOPPED/RELEASED/ERROR |
| 总时长 | const total = AudioPlayerManager.duration; |
音频总时长(单位:毫秒),未准备完成时返回 0 |
| 当前进度 | const current = AudioPlayerManager.currentTime; |
当前播放位置(单位:毫秒) |
| 当前数据源 | const source = AudioPlayerManager.getCurrentSource(); |
返回当前数据源信息(type/url/fdSrc)(返回副本) |
| 当前实例 | const source = AudioPlayerManager.getAvPlayer(); |
返回当前音频 |
| 当前实例,没有就创建 | const source = await AudioPlayerManager.createOrGetAvPlayer(); |
返回当前音频,没有就创建 |
五、回调监听配置
通过绑定回调函数,监听播放状态、进度、错误等事件,实现业务联动:
typescript
AudioPlayerManager.playByUrl('https://vol.bczcdn.com/r/sa_103_6828_0_3_20150809024934.aac').then(() => {
AudioPlayerManager.callbacks = {
// 状态变化回调
onStateChange: (state, reason) => {
console.log(`状态变更:${state},原因:${reason}`);
if (state === "COMPLETED") {
console.log("播放完成,可执行下一曲逻辑");
}
},
// 进度更新回调(实时触发)
onTimeUpdate: (currentTime, totalDuration) => {
console.log(`进度:${currentTime}/${totalDuration}ms`);
},
// 错误回调
onError: (error) => {
console.error(`播放错误:错误码=${error.code},描述=${error.message}`);
},
// 播放完成回调
onComplete: () => {
console.log("音频播放完成");
},
// 音频被打断回调(如闹钟、来电),如果不设置,默认就是下面的代码打断
onAudioInterrupt: (info) => {
console.log("音频打断信息:", info);
// 强制打断(系统强制执行,如闹钟),自动停止播放
if (info.forceType === 0) {
AudioPlayerManager.stop();
}
},
// 新播放地址打断当前播放回调
onResetUrl: () => {
console.log("当前播放被新地址打断");
}
};
})
六、完整案例演示(AudioDemo)
以下是基于 AudioPlayerManager 开发的完整 UI 演示案例,包含 URL 播放、rawfile 本地资源播放、回调自定义、直播设置及视频播放等场景,可直接在鸿蒙应用中复用。
6.1 案例代码(音频播放)
ts
import { AudioPlayerManager, AVPlayerState } from "../Index";
import { MyLog } from "../src/main/ets/utils/MyLog";
import { audio } from "@kit.AudioKit";
import { http } from "@kit.NetworkKit";
import { BusinessError } from "@kit.BasicServicesKit"
import { resourceManager } from "@kit.LocalizationKit";
import { common } from "@kit.AbilityKit";
import { media } from "@kit.MediaKit";
import { HashMap } from "@kit.ArkTS";
import { VideoView } from "./VideoView";
@Component
export struct AudioDemo {
@State isShowLoading2: boolean = false
build() {
Scroll() {
Column(){
// 播放按钮
Button('1.播放简单使用-播放在线地址或者本地地址').onClick(() => {
// URL 快捷播放
// 带倍速的 URL 播放
AudioPlayerManager.playAudioUrlOrFdSrc('https://vol.bczcdn.com/r/sa_103_6828_0_3_20150809024934.aac')
.then(() => console.log('播放成功/完成'))
.catch(() => console.log('播放失败或者被打断:'))
})
Button('2.播放简单使用-播放本地的rawfile文件').onClick(async () => {
// URL 快捷播放
// 带倍速的 URL 播放
const context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;
let resource = context.resourceManager
const value: resourceManager.RawFileDescriptor = await resource.getRawFd('mp3/error.mp3') // 存在 entry\src\main\resources\rawfile\mp3\error.mp3
AudioPlayerManager.playAudioUrlOrFdSrc(value)
.then(() => console.log('播放成功/完成'))
.catch(() => console.log('播放失败或者被打断:'))
})
Button('2.播放自定义使用回调').onClick(() => {
AudioPlayerManager.playByUrl('https://vol.bczcdn.com/r/sa_103_6828_0_3_20150809024934.aac').then(() => {
// console.log('AudioPlayerManagement-播放完了哈')
// 带倍速的 URL 播放
AudioPlayerManager.callbacks = {
onStateChange: (state: AVPlayerState) => {
// 状态变化回调
MyLog.log('状态变化回调-onStateChange', state)
if (state == 'idle') {
// 默认值就是idle,这儿只会在状态变化时候触发
} else if (state == 'prepared') { // AudioPlayerManager.setSpeed(1) // 设置速度
} else if (state == 'playing') {
} else if (state == 'paused') {
} else if (state == 'stopped') {
} else if (state == 'released') {
} else if (state == 'error') {
}
},
onTimeUpdate: (time) => {
// 进度更新回调
MyLog.log('onTimeUpdate-进度更新回调'+time)
},
onComplete: () => {
// 播放完毕
MyLog.log('onComplete-播放完毕')
},
onResetUrl: () => {
// 当上个视频没播放完毕,就播放下一个视频会被打断,就执行这里
},
onAudioInterrupt: (info) => {
// 不写也是打断,写了就自定义咯
MyLog.log('1AudioPlayerManagement-onAudioInterrupt', JSON.stringify(info))
// 被系统声音打断了,比如闹钟
if (info.forceType === audio.InterruptForceType.INTERRUPT_FORCE) {
// 强制打断类型,即具体操作已由系统强制执行。
AudioPlayerManager.stop()
// hintType
}
// eventType 音频中断事件类型,开始或是结束。
//INTERRUPT_TYPE_BEGIN ---1------音频播放中断事件开始。
// INTERRUPT_TYPE_END ---2-----音频播放中断事件结束。
// forceType 操作是由系统强制执行或是由应用程序执行。
// INTERRUPT_FORCE 0 强制打断类型,即具体操作已由系统强制执行。
// INTERRUPT_SHARE 1 共享打断类型,即系统不执行具体操作,通过InterruptHint建议并提示应用操作,应用可自行决策下一步处理方式。
// hintType 中断提示,用于提供中断事件的相关信息。
// INTERRUPT_HINT_NONE 0 无提示。 元服务API: 从API version 12开始,该接口支持在元服务中使用。
// INTERRUPT_HINT_RESUME 1 提示音频恢复,应用可主动触发开始渲染或开始采集的相关操作。 此操作无法由系统强制执行,其对应的InterruptForceType一定为INTERRUPT_SHARE类型。
}
}
})
})
Button('播放自定义使用回调播放本地的rawfile文件').onClick(async () => {
// URL 快捷播放
// 带倍速的 URL 播放
//播放 entry\src\main\resources\rawfile下面的音频文件mp3/error.mp3
try {
// 2. 第二步:构建 AVDataSrcDescriptor(定义播放规则)
// let dataSrc: media.AVDataSrcDescriptor = {
// fileSize: -1, // 媒体文件总大小(字节)
// callback: (buffer: ArrayBuffer, len: number, pos: number) => {
// // - 返回值,number类型,返回填充数据的长度。返回-1表示到达流的末尾,返回-2表示遇到不可恢复的错误。
// }
// };
// AudioPlayerManager.setDataSrc(dataSrcDescriptor)
const context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;
let resource = context.resourceManager
const value: resourceManager.RawFileDescriptor = await resource.getRawFd('mp3/error.mp3') // 存在 entry\src\main\resources\rawfile\mp3\error.mp3
AudioPlayerManager.playByFdSrc(value).then(() => {
// console.log('AudioPlayerManagement-播放完了哈')
AudioPlayerManager.callbacks = {
onStateChange: (state: AVPlayerState) => {
// 状态变化回调
MyLog.log('状态变化回调-onStateChange', state)
if (state == 'idle') {
} else if (state == 'prepared') { // AudioPlayerManager.setSpeed(1) // 设置速度
} else if (state == 'playing') {
} else if (state == 'paused') {
} else if (state == 'stopped') {
} else if (state == 'released') {
} else if (state == 'error') {
}
},
onTimeUpdate: (time) => {
// 进度更新回调
MyLog.log('onTimeUpdate-进度更新回调'+time)
},
onComplete: () => {
// 播放完毕
MyLog.log('onComplete-播放完毕')
},
onResetUrl: () => {
// 当上个视频没播放完毕,就播放下一个视频会被打断,就执行这里
},
onAudioInterrupt: (info) => {
MyLog.log('1AudioPlayerManagement-onAudioInterrupt', JSON.stringify(info))
// 被系统声音打断了,比如闹钟
if (info.forceType === audio.InterruptForceType.INTERRUPT_FORCE) {
// 强制打断类型,即具体操作已由系统强制执行。
AudioPlayerManager.stop()
// hintType
}
// eventType 音频中断事件类型,开始或是结束。
//INTERRUPT_TYPE_BEGIN ---1------音频播放中断事件开始。
// INTERRUPT_TYPE_END ---2-----音频播放中断事件结束。
// forceType 操作是由系统强制执行或是由应用程序执行。
// INTERRUPT_FORCE 0 强制打断类型,即具体操作已由系统强制执行。
// INTERRUPT_SHARE 1 共享打断类型,即系统不执行具体操作,通过InterruptHint建议并提示应用操作,应用可自行决策下一步处理方式。
// hintType 中断提示,用于提供中断事件的相关信息。
// INTERRUPT_HINT_NONE 0 无提示。 元服务API: 从API version 12开始,该接口支持在元服务中使用。
// INTERRUPT_HINT_RESUME 1 提示音频恢复,应用可主动触发开始渲染或开始采集的相关操作。 此操作无法由系统强制执行,其对应的InterruptForceType一定为INTERRUPT_SHARE类型。
}
}
})
}catch (e){
console.log('下载失败')
}
})
Button('直播设置').onClick(async ()=>{
let headers: Record<string, string> = {"User-Agent" : "User-Agent-Value"};
let mediaSource : media.MediaSource = media.createMediaSourceWithUrl("http://xxx", headers);
let uuid: number = 1;
let requests: HashMap<number, media.MediaSourceLoadingRequest> = new HashMap();
let mediaSourceLoader: media.MediaSourceLoader = {
open: (request: media.MediaSourceLoadingRequest) => {
console.info(`Opening resource: ${request.url}`);
// 成功打开资源,返回唯一的句柄, 保证uuid和request对应。
uuid += 1;
requests.set(uuid, request);
return uuid;
},
read: (uuid: number, requestedOffset: number, requestedLength: number) => {
console.info(`Reading resource with handle ${uuid}, offset: ${requestedOffset}, length: ${requestedLength}`);
// 判断uuid是否合法、存储read请求,不要在read请求阻塞去推送数据和头信息。
},
close: (uuid: number) => {
console.info(`Closing resource with handle ${uuid}`);
// 清除当前uuid相关资源。
requests.remove(uuid);
}
};
mediaSource.setMediaResourceLoaderDelegate(mediaSourceLoader);
let playStrategy : media.PlaybackStrategy = {
preferredBufferDuration: 20,
};
let player = await AudioPlayerManager.createOrGetAvPlayer();//单例播放,非单例不需要用这个播放器
player.setMediaSource(mediaSource, playStrategy);
})
Text('单例播放视频案例')
.margin({
bottom: 20
})
VideoView({
imgUrlPreview:"https://i-avatar.csdnimg.cn/72ceb7652a604f8daa327a5c2d7169e5_weixin_42301175.jpg!1"
})
}
}
.scrollBar(BarState.Auto)
.scrollBarColor(Color.Gray) // 滚动条颜色
.scrollBarWidth(0) // 滚动条宽度
.friction(0.6)
.edgeEffect(EdgeEffect.None) //设置滑动效果 //Fade
.width('100%')
.id('exampleIndex')
}
}
6.2 案例代码(视频播放)
ts
import { AudioPlayerManager, AVPlayerState } from "../Index"
import { MyLog } from "../src/main/ets/utils/MyLog"
import { audio } from "@kit.AudioKit"
@Component
export struct VideoView {
xcomponentController: XComponentController = new XComponentController()
@Prop imgUrlPreview?: string // 默认显示的地址
private surfaceId: string = 'surfaceId123'
play(){
AudioPlayerManager.playByUrl("https://test-videos.co.uk/vids/bigbuckbunny/mp4/h264/360/Big_Buck_Bunny_360_10s_1MB.mp4").then(() => {
// console.log('AudioPlayerManagement-播放完了哈')
// 带倍速的 URL 播放
AudioPlayerManager.callbacks = {
onStateChange: (state: AVPlayerState) => {
// 状态变化回调
MyLog.log('状态变化回调-onStateChange', state)
if (state == 'initialized') {
// AudioPlayerManager.setSurfaceId(this.surfaceId) // 视图跟音频结合
// let audioRenderInfo: audio.AudioRendererInfo = {
// usage: audio.StreamUsage.STREAM_USAGE_MOVIE,
// rendererFlags: 0
// }
// AudioPlayerManager.setAudioRendererInfo(audioRenderInfo)
// 或者
const AVPlayer = AudioPlayerManager.getAvPlayer()
if(AVPlayer){
AVPlayer.surfaceId = this.surfaceId
let audioRenderInfo: audio.AudioRendererInfo = {
usage: audio.StreamUsage.STREAM_USAGE_MOVIE,
rendererFlags: 0
}
AVPlayer.audioRendererInfo = audioRenderInfo
}
} else if (state == 'prepared') { // AudioPlayerManager.setSpeed(1) // 设置速度
} else if (state == 'playing') {
} else if (state == 'paused') {
} else if (state == 'stopped') {
} else if (state == 'released') {
} else if (state == 'error') {
}
},
onTimeUpdate: (time) => {
// 进度更新回调
MyLog.log('onTimeUpdate-进度更新回调'+time)
},
onError: (error) => {
// 错误回调
MyLog.log('onError-错误回调'+JSON.stringify(error))
},
onComplete: () => {
// 播放完毕
MyLog.log('onComplete-播放完毕')
},
onResetUrl: () => {
// 当上个视频没播放完毕,就播放下一个视频会被打断,就执行这里
},
onAudioInterrupt: (info) => {
// 不写也是打断,写了就自定义咯
MyLog.log('1AudioPlayerManagement-onAudioInterrupt', JSON.stringify(info))
// 被系统声音打断了,比如闹钟
if (info.forceType === audio.InterruptForceType.INTERRUPT_FORCE) {
// 强制打断类型,即具体操作已由系统强制执行。
AudioPlayerManager.stop()
// hintType
}
// eventType 音频中断事件类型,开始或是结束。
//INTERRUPT_TYPE_BEGIN ---1------音频播放中断事件开始。
// INTERRUPT_TYPE_END ---2-----音频播放中断事件结束。
// forceType 操作是由系统强制执行或是由应用程序执行。
// INTERRUPT_FORCE 0 强制打断类型,即具体操作已由系统强制执行。
// INTERRUPT_SHARE 1 共享打断类型,即系统不执行具体操作,通过InterruptHint建议并提示应用操作,应用可自行决策下一步处理方式。
// hintType 中断提示,用于提供中断事件的相关信息。
// INTERRUPT_HINT_NONE 0 无提示。 元服务API: 从API version 12开始,该接口支持在元服务中使用。
// INTERRUPT_HINT_RESUME 1 提示音频恢复,应用可主动触发开始渲染或开始采集的相关操作。 此操作无法由系统强制执行,其对应的InterruptForceType一定为INTERRUPT_SHARE类型。
}
}
})
}
build() {
Column(){
XComponent({
id: this.surfaceId, //组件的唯一标识,支持最大的字符串长度128。
type: XComponentType.TEXTURE,
// 用于EGL/OpenGLES和媒体数据写入,开发者定制的绘制内容会和XComponent组件的内容合成后展示到屏幕上。
// 元服务API: 从API version 11开始,该接口支持在元服务中使用。
controller: this.xcomponentController// 给组件绑定一个控制器,通过控制器调用组件方法,仅XComponent类型为"surface"时有效。
}) {
if (this.imgUrlPreview) {
// 字幕
Image(this.imgUrlPreview).width('100%').height('100%') //默认显示的图片
}
}
.onLoad(() => {
//设置XComponent持有Surface的宽度和高度,仅XComponent类型为"surface"时有效。
// this.xcomponentController.setXComponentSurfaceSize({ surfaceWidth: 1920, surfaceHeight: 1080 });
this.surfaceId =
this.xcomponentController.getXComponentSurfaceId() //获取XComponent对应Surface的ID,供@ohos接口使用,仅XComponent类型为"surface"时有效。
// 根据屏幕跳转宽高
// this.xcomponentController.setXComponentSurfaceRect({
// surfaceWidth:'100%',
// surfaceHeight: '100%',
// })
console.log('this.surfaceId', this.surfaceId)
this.play()
}).backgroundColor(Color.Black)
}.height(200)
}
}