HarmonyOS 多媒体开发:音频播放与媒体资源管理


一、引言
多媒体能力是移动应用的重要组成。无论是音乐播放器、视频应用、语音助手,还是拍照美颜,都离不开多媒体开发。HarmonyOS 提供了完整的媒体框架,涵盖音频播放、视频播放、音频录制、图像处理、相机等能力。
本文将以一个暗红金风格的音乐播放器页面为主线,深入讲解 HarmonyOS 音频播放的开发流程,并通过大量代码示例帮助读者掌握多媒体开发的核心技能。
二、媒体框架概览
HarmonyOS 媒体框架(@kit.MediaKit)提供以下核心能力:
| 能力 | 模块 | 说明 |
|---|---|---|
| 音频播放 | @ohos.multimedia.audio |
音频播放器、音频管理 |
| 视频播放 | @ohos.multimedia.media |
视频播放器 |
| 音频录制 | @ohos.multimedia.media |
麦克风录音 |
| 图像处理 | @ohos.multimedia.image |
图片解码、编辑 |
| 相机 | @ohos.multimedia.camera |
相机拍照、录像 |
| 媒体库 | @ohos.file.photoAccessHelper |
访问系统媒体库 |
2.1 音频播放的两种方式
HarmonyOS 提供两种音频播放方式:
- AVPlayer:高级播放器,支持本地和在线音频、视频播放,功能强大。
- AudioRenderer:底层音频渲染器,适合实时音频流处理。
本文主要讲解 AVPlayer 的使用。
三、AVPlayer 核心概念
3.1 播放状态机
AVPlayer 有一个完整的播放状态机:
| 状态 | 说明 |
|---|---|
| idle | 初始状态 |
| initialized | 已初始化,设置了数据源 |
| prepared | 已准备好,可以播放 |
| playing | 播放中 |
| paused | 已暂停 |
| completed | 播放完成 |
| stopped | 已停止 |
| released | 已释放资源 |
| error | 播放出错 |
3.2 播放流程
AVPlayer 的标准播放流程:
- 创建 AVPlayer 实例。
- 注册状态监听(
stateChange)。 - 设置播放源(
url或fdSrc)。 - 准备播放(
prepare)。 - 播放 / 暂停 / 跳转。
- 播放完成或停止后释放资源(
release)。
四、实战代码:音乐播放器页面
下面我们实现一个音乐播放器风格的演示页面。由于示例环境没有真实音频文件,页面以 UI 演示和状态管理为主,同时给出完整的 AVPlayer 播放代码。
4.1 定义数据结构
interface Track {
id: number;
title: string;
artist: string;
duration: string;
}
代码说明:
Track 接口描述一首歌曲的数据:
id:歌曲编号,用于标识和选中。title:歌曲名称。artist:歌手名称。duration:歌曲时长(展示用字符串)。
4.2 组件状态定义
@Entry
@Component
struct MultimediaPage {
@State tracks: Track[] = [
{ id: 1, title: 'HarmonyOS 序曲', artist: 'ArkTS 交响乐团', duration: '03:24' },
{ id: 2, title: '状态管理蓝调', artist: 'State 乐队', duration: '04:10' },
{ id: 3, title: '并发进行曲', artist: 'TaskPool 合唱团', duration: '02:58' },
{ id: 4, title: '动画圆舞曲', artist: 'Anim 三重奏', duration: '05:02' },
{ id: 5, title: '网络协奏曲', artist: 'HTTP 四重奏', duration: '03:47' },
{ id: 6, title: '持久化小夜曲', artist: 'Prefs 独奏', duration: '02:31' }
];
@State currentId: number = 1;
@State playing: boolean = false;
@State progress: number = 0;
代码说明:
@State tracks:歌曲列表数据。@State currentId:当前选中的歌曲编号。@State playing:播放状态标志。@State progress:播放进度(0-100)。
4.3 播放控制方法
togglePlay(id: number): void {
this.currentId = id;
this.playing = !this.playing;
if (this.playing) {
// 模拟进度推进
this.progress = 20;
} else {
this.progress = 0;
}
}
代码说明:
togglePlay 方法处理播放/暂停切换:
- 设置当前选中歌曲
currentId。 - 切换播放状态
playing。 - 演示环境中用
progress模拟播放进度。
4.4 完整 AVPlayer 播放代码
在真实项目中,音频播放的核心代码如下:
import { media } from '@kit.MediaKit';
import { audio } from '@kit.AudioKit';
// 创建音频会话(可选,用于音量控制等)
const audioManager = audio.getAudioManager();
const audioStreamInfo: audio.AudioStreamInfo = {
samplingRate: audio.AudioSamplingRate.SAMPLE_RATE_44100,
channels: audio.AudioChannel.CHANNEL_2,
sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE,
encodingType: audio.AudioEncodingType.ENCODING_TYPE_RAW
};
const audioRendererInfo: audio.AudioRendererInfo = {
usage: audio.StreamUsage.STREAM_USAGE_MUSIC,
rendererFlags: 0
};
// 创建 AVPlayer
let avPlayer: media.AVPlayer = await media.createAVPlayer();
// 注册状态监听
avPlayer.on('stateChange', (state: string) => {
switch (state) {
case 'idle':
console.info('状态:空闲');
break;
case 'initialized':
console.info('状态:已初始化');
break;
case 'prepared':
console.info('状态:已准备');
break;
case 'playing':
console.info('状态:播放中');
break;
case 'paused':
console.info('状态:已暂停');
break;
case 'completed':
console.info('状态:播放完成');
break;
case 'error':
console.error('状态:播放出错');
break;
}
});
// 设置播放源(本地文件)
avPlayer.url = 'file:///data/storage/el2/base/files/music.mp3';
// 准备播放
await avPlayer.prepare();
// 开始播放
avPlayer.play();
// 暂停
avPlayer.pause();
// 跳转到指定位置(毫秒)
avPlayer.seek(30000);
// 停止
avPlayer.stop();
// 释放资源
await avPlayer.release();
代码说明:
-
创建播放器:
media.createAVPlayer()创建 AVPlayer 实例。 -
状态监听:
avPlayer.on('stateChange', callback)注册状态变化监听,根据状态执行相应逻辑。这是 AVPlayer 使用中最重要的回调,所有操作都应在正确的状态下进行。 -
设置播放源:
avPlayer.url设置音频文件地址。支持:- 本地文件:
file://协议。 - 网络资源:
http://或https://协议。 - 应用沙箱文件:使用
fdSrc属性传入文件描述符。
- 本地文件:
-
准备播放:
avPlayer.prepare()异步准备,完成后状态变为prepared。 -
播放控制:
play()、pause()、seek()、stop()控制播放。 -
资源释放:
release()释放播放器资源,必须在不再使用时调用,避免资源泄漏。
4.5 播放进度监听
// 监听时间更新
avPlayer.on('timeUpdate', (time: number) => {
// time 为当前播放位置(毫秒)
const duration = avPlayer.duration; // 总时长(毫秒)
const percent = (time / duration) * 100;
console.info(`播放进度: ${percent.toFixed(1)}%`);
});
// 监听播放结束
avPlayer.on('playbackCompleted', () => {
console.info('播放完成');
});
代码说明:
timeUpdate事件在播放位置变化时触发,参数是当前播放位置(毫秒)。playbackCompleted事件在播放完成时触发。- 通过这两个事件可以实现进度条更新和自动播放下一曲等功能。
4.6 构建 UI
build() {
Scroll() {
Column({ space: 16 }) {
// 顶部暗红金标题
Column() {
Text('MUSIC')
.fontSize(12)
.fontColor('#FFD700')
.letterSpacing(8)
Text('多媒体播放')
.fontSize(26)
.fontWeight(FontWeight.Bold)
.fontColor(Color.White)
.margin({ top: 6 })
Text('AVPlayer · 音频播放器')
.fontSize(12)
.fontColor('#FFD700')
.margin({ top: 6 })
}
.width('100%')
.padding({ top: 48, bottom: 30 })
.backgroundColor('#1A0A0A')
// 唱片封面 + 圆形播放按钮
Row({ space: 20 }) {
// 唱片
Stack({ alignContent: Alignment.Center }) {
Circle({ width: 110, height: 110 })
.fill('#2B0A0A')
.stroke('#FFD700')
.strokeWidth(2)
Circle({ width: 60, height: 60 })
.fill('#4A1A1A')
Circle({ width: 18, height: 18 })
.fill('#FFD700')
}
.width(110)
.height(110)
Column({ space: 8 }) {
Text(this.tracks[this.currentId - 1].title)
.fontSize(18)
.fontWeight(FontWeight.Bold)
.fontColor(Color.White)
Text(this.tracks[this.currentId - 1].artist)
.fontSize(12)
.fontColor('#FFD700')
// 进度条
Progress({ value: this.progress, total: 100, type: ProgressType.Linear })
.width(160)
.color('#FFD700')
.backgroundColor('#3A1A1A')
}
.alignItems(HorizontalAlign.Start)
}
.width('100%')
.padding(16)
.backgroundColor('#2B0A0A')
.borderRadius(20)
.border({ width: 1, color: '#FFD70044' })
代码说明:
唱片封面区:
-
唱片图形:使用三层
Stack叠放的Circle组件绘制唱片:- 外层:深红圆盘,金色描边。
- 中层:略小的深色圆盘。
- 内层:金色小圆点(唱片中心标签)。
- 通过嵌套圆环模拟真实唱片的外观。
-
歌曲信息:显示当前选中歌曲的标题和歌手,标题白色、歌手金色。
-
进度条:
Progress组件显示播放进度,value绑定progress状态,金色进度条在深红背景上非常醒目。 -
风格统一:整个页面采用暗红(#1A0A0A、#2B0A0A)与金色(#FFD700)的搭配,营造复古唱片机的氛围。
// 圆形播放按钮
Button() {
Text(this.playing ? '⏸' : '▶')
.fontSize(28)
.fontColor('#1A0A0A')
}
.width(64)
.height(64)
.backgroundColor('#FFD700')
.borderRadius(32)
.shadow({ radius: 16, color: '#88FFD700', offsetY: 0 })
.onClick(() => { this.togglePlay(this.currentId); })
代码说明:
圆形播放/暂停按钮:
Button()无文本参数,使用Button() {}自定义内容(图标文字)。.borderRadius(32)(64 的一半)形成正圆。- 金色背景、深色图标,配合金色发光阴影。
- 图标根据播放状态切换:播放中显示暂停符号(⏸),暂停时显示播放符号(▶)。
// 播放列表表格
Column() {
Text('播放列表')
.fontSize(14)
.fontWeight(FontWeight.Bold)
.fontColor('#FFD700')
.alignSelf(ItemAlign.Start)
.margin({ bottom: 8 })
Row() {
Text('#').width(36).fontSize(11).fontColor('#FFD700')
Text('歌名').layoutWeight(1).fontSize(11).fontColor('#FFD700')
Text('歌手').layoutWeight(1).fontSize(11).fontColor('#FFD700')
Text('时长').width(50).fontSize(11).fontColor('#FFD700')
}
.width('100%')
.padding({ top: 8, bottom: 8 })
.border({ width: { bottom: 1 }, color: '#FFD70044' })
ForEach(this.tracks, (track: Track) => {
Row() {
Text(`${track.id}`)
.width(36)
.fontSize(12)
.fontColor(this.currentId === track.id ? '#FFD700' : '#888888')
.fontWeight(this.currentId === track.id ? FontWeight.Bold : FontWeight.Normal)
Text(track.title)
.layoutWeight(1)
.fontSize(12)
.fontColor(this.currentId === track.id ? Color.White : '#BBBBBB')
Text(track.artist)
.layoutWeight(1)
.fontSize(11)
.fontColor('#888888')
Text(track.duration)
.width(50)
.fontSize(11)
.fontColor('#FFD700')
}
.width('100%')
.padding({ top: 12, bottom: 12 })
.border({ width: { bottom: 1 }, color: '#3A1A1A' })
.onClick(() => { this.togglePlay(track.id); })
})
}
.width('100%')
.padding(16)
.backgroundColor('#1A0A0A')
.borderRadius(14)
.border({ width: 1, color: '#FFD70033' })
代码说明:
播放列表表格:
-
表头行:包含序号、歌名、歌手、时长四列,全部使用金色文字。
-
数据行:
ForEach遍历歌曲列表:- 当前选中歌曲(
currentId === track.id)的序号和歌名使用金色/白色高亮,其他歌曲使用灰色。 - 每行可点击,点击后调用
togglePlay切换播放。
- 当前选中歌曲(
-
高亮逻辑:通过条件判断
this.currentId === track.id控制文字颜色和字重,实现"当前播放"的视觉标识。 -
表格样式:行间用深红色边框分隔,与页面整体暗色风格统一。
五、音频录制
除了播放,HarmonyOS 还支持音频录制。使用 AudioCapturer 录制麦克风音频:
import { audio } from '@kit.AudioKit';
async function startRecord(): Promise<void> {
const audioManager = audio.getAudioManager();
// 申请录音权限
// 需要在 module.json5 中配置 ohos.permission.MICROPHONE
const audioCapturer = await audio.createAudioCapturer({
streamInfo: {
samplingRate: audio.AudioSamplingRate.SAMPLE_RATE_44100,
channels: audio.AudioChannel.CHANNEL_1,
sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE,
encodingType: audio.AudioEncodingType.ENCODING_TYPE_RAW
},
capturerInfo: {
source: audio.SourceType.SOURCE_TYPE_MIC,
capturerFlags: 0
}
});
await audioCapturer.start();
// 读取音频数据
const buffer = new ArrayBuffer(44100 * 2);
const readSize = await audioCapturer.read(buffer, true);
// 处理音频数据...
}
六、媒体权限配置
使用多媒体能力需要配置相应权限:
{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.MICROPHONE",
"reason": "用于录制音频",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "inuse"
}
},
{
"name": "ohos.permission.READ_MEDIA",
"reason": "用于读取媒体文件",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "inuse"
}
}
]
}
}
代码说明:
ohos.permission.MICROPHONE:麦克风权限,录制音频必需。ohos.permission.READ_MEDIA:读取媒体库权限。- 动态权限还需要在运行时通过
requestPermissionsFromUser申请。
七、最佳实践
7.1 资源管理
AVPlayer 使用完成后必须调用 release() 释放资源,否则会导致内存泄漏。
7.2 状态机管理
AVPlayer 的所有操作都必须在正确的状态下执行。例如,play() 必须在 prepared 状态之后调用。建议封装状态管理逻辑。
7.3 播放器封装
建议将 AVPlayer 封装为独立的播放器类,统一管理状态和事件:
export class MusicPlayer {
private avPlayer: media.AVPlayer | null = null;
async init(url: string): Promise<void> {
this.avPlayer = await media.createAVPlayer();
this.avPlayer.url = url;
await this.avPlayer.prepare();
}
play(): void {
this.avPlayer?.play();
}
pause(): void {
this.avPlayer?.pause();
}
async release(): Promise<void> {
await this.avPlayer?.release();
this.avPlayer = null;
}
}
7.4 后台播放
如果应用需要在后台播放音频,需要申请后台任务权限并配置:
{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.KEEP_BACKGROUND_RUNNING",
"reason": "用于后台播放音频",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "inuse"
}
}
]
}
}
八、常见问题
8.1 播放无声音
原因:可能是音频焦点未获取、音量设置过低、或播放源错误。
解决:检查播放源是否有效,设置合适的音量,必要时获取音频焦点。
8.2 播放器状态错误
原因:在错误的状态下调用了操作(如未 prepare 就 play)。
解决:严格遵循状态机流程,监听 stateChange 事件。
8.3 网络音频卡顿
原因:网络不稳定或缓冲策略不合理。
解决:使用合适的缓冲策略,或预先加载音频。
九、总结
本文深入讲解了 HarmonyOS 多媒体开发,重点介绍了音频播放技术。我们实现了一个暗红金风格的音乐播放器页面,并给出了完整的 AVPlayer 播放代码。
核心要点回顾:
- AVPlayer 是高级媒体播放器,支持本地和网络音频。
- 播放流程:创建 → 监听状态 → 设置源 → prepare → play。
- 状态机管理是 AVPlayer 使用的关键。
- 使用完成后必须
release()释放资源。 - 录制音频使用 AudioCapturer,需要麦克风权限。
- 多媒体能力需要配置相应权限。
多媒体开发让应用更加丰富多彩,掌握它能构建音乐、视频、语音等丰富的应用体验。下一篇我们将讲解 HarmonyOS 并发编程。
更多推荐



所有评论(0)