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')
回调中处理文件访问异常(如权限不足或文件损坏)