compileVersion 5.0.2(14)
音频播放
import media from '@ohos.multimedia.media';
import common from '@ohos.app.ability.common';
import { BusinessError } from '@ohos.base';@Entry
@Component
struct AudioPlayer {private avPlayer: media.AVPlayer | null = null;@State isPlaying: boolean = false;@State playProgress: number = 0;private timerId: number | null = null; private readonly audioPath: string = 'qingtian.mp3';aboutToAppear() {this.initAudioPlayer();}aboutToDisappear(): void {this.releasePlayer();}private async initAudioPlayer() {console.log('initAudioPlayer=====');const context = getContext(this) as common.UIAbilityContext;const resourceManager = context.resourceManager;try {const fdObj = await resourceManager.getRawFd(this.audioPath);const avFileDescriptor: media.AVFileDescriptor = {fd: fdObj.fd,offset: fdObj.offset, length: fdObj.length};media.createAVPlayer((err: BusinessError, player: media.AVPlayer) => {if (err) {console.error('创建播放器失败: ' + JSON.stringify(err));return;}console.info('创建播放器success');this.avPlayer = player;this.setupPlayerEvents();this.avPlayer.fdSrc = avFileDescriptor;});} catch (error) {console.error('文件加载失败: ' + JSON.stringify(error));}}private setupPlayerEvents() {if (!this.avPlayer) {return;}this.avPlayer.on('stateChange', (state: string) => {console.log('stateChange:' + state);switch (state) {case 'initialized': this.avPlayer?.prepare();break;case 'prepared': console.log('准备完成');break;case 'playing': this.isPlaying = true;this.startProgressTracking();break;case 'paused': this.isPlaying = false;this.stopProgressUpdate();break;case 'completed': this.isPlaying = false;this.playProgress = 100;this.stopProgressUpdate();break;}});this.avPlayer.on('error', (err: BusinessError) => {console.error('播放错误: ' + JSON.stringify(err));this.releasePlayer();this.initAudioPlayer();});}private startProgressTracking() {console.log('startProgressTracking=====');this.timerId = setInterval(() => {if (this.avPlayer && this.avPlayer.duration > 0) {console.log('setInterval currentTime=' + this.avPlayer.currentTime + ' duration=' + this.avPlayer.duration);this.playProgress = (this.avPlayer.currentTime / this.avPlayer.duration) * 100;}}, 1000);console.log('this.timerId=' + this.timerId);}private stopProgressUpdate() {console.log('stopProgressUpdate=====');if (this.timerId !== null) {clearInterval(this.timerId);this.timerId = null;}}private releasePlayer() {console.log('releasePlayer=====');if (this.avPlayer) {this.avPlayer.release();this.avPlayer = null;}}private togglePlayback() {if (!this.avPlayer) {return;}if (this.isPlaying) {this.avPlayer.pause();} else {if (this.avPlayer.currentTime >= this.avPlayer.duration) {this.avPlayer.seek(0);}this.avPlayer.play();}}build() {Column() {Row({ space: 20 }) {Button(this.isPlaying ? '暂停' : '播放').onClick(() => this.togglePlayback()).width(100).height(40)Progress({ value: this.playProgress, total: 100 }).width('60%').height(10).color('#409EFF')}.padding(20).width('100%')Text('当前播放:' + this.audioPath.split('/').pop()).fontSize(16).margin({ top: 20 })}.width('100%').height('100%').padding(20).backgroundColor('#F5F5F5')}
}
media.AVFileDescriptor
fd:文件描述符
- 含义:操作系统分配的唯一标识符,代表已打开的文件句柄(file descriptor)。
- 作用:
-
- 系统通过该标识符定位具体的媒体文件(如存储在rawfile目录下的音频文件或HAP包内嵌资源)
- 用于跨进程文件访问时传递文件引用(如播放器服务与UI界面的数据交互)
- 示例:通过
resourceManager.getRawFd('music.mp3')
获取打包资源文件的描述符
offset:文件偏移量
- 含义:从文件起始位置到目标数据的字节偏移量(单位:字节)。
- 技术细节:
-
- 当媒体文件被压缩或打包时(如HAP资源文件),需跳过文件头等非音频数据部分
- 支持精确指定播放起始点(如从视频第10秒开始播放,需计算对应的字节偏移)
- 示例:若资源文件在HAP包中的物理偏移为1024字节,则offset需设为1024
length:数据长度
- 含义:需要读取的媒体数据总长度(单位:字节)。
- 关键作用:
-
- 限制播放器读取范围,避免处理无关数据(如仅播放某段音频或视频片段)
- 防止越界读取导致的崩溃(如文件实际大小小于声明长度时触发错误码5400102)
- 示例:从HAP包中读取一个30秒的MP3片段时,需通过
fs.statSync
获取精确文件长度
参数关系与开发规范
| | |
---|
参数 | 典型取值范围 | 异常处理建议 |
fd | ≥0(0表示无效句柄) | 检查fs.open()返回值是否有效 |
offset | 0 ≤ offset ≤ 文件大小-1 | 配合fs.stat验证偏移有效性 |
length | 1 ≤ length ≤ 剩余字节数 | 动态计算:length = 文件大小 - offset |
示例
播放HAP内嵌资源
typescriptconst fdObj = await resourceManager.getRawFd('music.mp3');
const avFileDescriptor = {fd: fdObj.fd,offset: fdObj.offset, length: fdObj.length
};
avPlayer.fdSrc = avFileDescriptor;
分段播放大型文件
typescript
const startOffset = 60 * bitrate;
const playLength = 60 * bitrate;
avPlayer.fdSrc = { fd, offset: startOffset, length: playLength };
开发注意事项:
- 若
offset + length
超过实际文件大小,将触发BusinessError 5400102
(参数非法) - 使用
fs.close(fd)
在aboutToDisappear
生命周期关闭文件描述符,避免资源泄漏 - 在
on('error')
回调中处理文件访问异常(如权限不足或文件损坏)