HarmonyOS 鸿蒙Next 无法播放rawfile里的wav文件

发布于 1周前 作者 vueper 来自 鸿蒙OS

HarmonyOS 鸿蒙Next 无法播放rawfile里的wav文件

结合文档中的示例https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/using-avplayer-for-playback-V5,使用AVPlayer播放音频

点击播放音乐2文本调用setAVPlayerCallBack()方法

通过fdSrc播放rawfile中的音频,结果播放不出来,使用debug信息如下图所示

求助大佬

cke_174.png

3 回复

本地测试过正常播放的呀,下面是我的demo。在resource/rawfile下面放一个01.wav。你也可以修改代码中的avPlayerFdSrcDemo方法中的音频文件名。

import { media } from '[@kit](/user/kit).MediaKit';
import { fileIo as fs } from '[@kit](/user/kit).CoreFileKit';
import { common } from '[@kit](/user/kit).AbilityKit';
import { BusinessError } from '[@kit](/user/kit).BasicServicesKit';
import { audio } from '[@kit](/user/kit).AudioKit';

export class AVPlayerDemo {
 private count: number = 0;
 private isSeek: boolean = true; // 用于区分模式是否支持seek操作
 private fileSize: number = -1;
 private fd: number = 0;
 // 注册avplayer回调函数
 setAVPlayerCallback(avPlayer: media.AVPlayer) {
   // seek操作结果回调函数
   avPlayer.on('seekDone', (seekDoneTime: number) => {
     console.info(`AVPlayer seek succeeded, seek time is ${seekDoneTime}`);
   })
   // error回调监听函数,当avPlayer在操作过程中出现错误时调用 reset接口触发重置流程
   avPlayer.on('error', (err: BusinessError) => {
     console.error(`Invoke avPlayer failed, code is ${err.code}, message is ${err.message}`);
     avPlayer.reset(); // 调用reset重置资源,触发idle状态
   })
   // 状态机变化回调函数
   avPlayer.on('stateChange', async (state: string, reason: media.StateChangeReason) => {
     switch (state) {
       case 'idle': // 成功调用reset接口后触发该状态机上报
         console.info('AVPlayer state idle called.');
         avPlayer.release(); // 调用release接口销毁实例对象
         break;
       case 'initialized': // avplayer 设置播放源后触发该状态上报
         console.info('AVPlayer state initialized called.');
         avPlayer.audioRendererInfo = {
           usage: audio.StreamUsage.STREAM_USAGE_MUSIC,
           rendererFlags: 0
         }
         avPlayer.prepare();
         break;
       case 'prepared': // prepare调用成功后上报该状态机
         console.info('AVPlayer state prepared called.');
         avPlayer.play(); // 调用播放接口开始播放
         break;
       case 'playing': // play成功调用后触发该状态机上报
         console.info('AVPlayer state playing called.');
         if (this.count !== 0) {
           if (this.isSeek) {
             console.info('AVPlayer start to seek.');
             avPlayer.seek(avPlayer.duration); //seek到音频末尾
           } else {
             // 当播放模式不支持seek操作时继续播放到结尾
             console.info('AVPlayer wait to play end.');
           }
         } else {
           setTimeout(() => {
             console.info('AVPlayer is playing wait to pause');
             avPlayer.pause(); // 播放3s后调用暂停接口暂停播放
           }, 3000)
         }
         this.count++;
         break;
       case 'paused': // pause成功调用后触发该状态机上报
         console.info('AVPlayer state paused called.');
         setTimeout(() => {
           console.info('AVPlayer paused wait to play again');
           avPlayer.play(); // 暂停3s后再次调用播放接口开始播放
         }, 3000)
         break;
       case 'completed': // 播放结束后触发该状态机上报
         console.info('AVPlayer state completed called.');
         avPlayer.stop(); //调用播放结束接口
         break;
       case 'stopped': // stop接口成功调用后触发该状态机上报
         console.info('AVPlayer state stopped called.');
         avPlayer.reset(); // 调用reset接口初始化avplayer状态
         break;
       case 'released':
         console.info('AVPlayer state released called.');
         break;
       default:
         console.info('AVPlayer state unknown called.');
         break;
     }
   })
 }

 // 以下demo为使用fs文件系统打开沙箱地址获取媒体文件地址并通过url属性进行播放示例
 async avPlayerUrlDemo() {
   // 创建avPlayer实例对象
   let avPlayer: media.AVPlayer = await media.createAVPlayer();
   // 创建状态机变化回调函数
   this.setAVPlayerCallback(avPlayer);
   let fdPath = 'fd://';
   // 通过UIAbilityContext获取沙箱地址filesDir,以Stage模型为例
   let context = getContext(this) as common.UIAbilityContext;
   let pathDir = context.filesDir;
   let path = pathDir + '/01.mp3';
   // 打开相应的资源文件地址获取fd,并为url赋值触发initialized状态机上报
   let file = await fs.open(path);
   fdPath = fdPath + '' + file.fd;
   this.isSeek = true; // 支持seek操作
   avPlayer.url = fdPath;
 }

 // 以下demo为使用资源管理接口获取打包在HAP内的媒体资源文件并通过fdSrc属性进行播放示例
 async avPlayerFdSrcDemo() {
   // 创建avPlayer实例对象
   let avPlayer: media.AVPlayer = await media.createAVPlayer();
   // 创建状态机变化回调函数
   this.setAVPlayerCallback(avPlayer);
   // 通过UIAbilityContext的resourceManager成员的getRawFd接口获取媒体资源播放地址
   // 返回类型为{fd,offset,length},fd为HAP包fd地址,offset为媒体资源偏移量,length为播放长度
   let context = getContext(this) as common.UIAbilityContext;
   let fileDescriptor = await context.resourceManager.getRawFd('01.wav');
   let avFileDescriptor: media.AVFileDescriptor =
     { fd: fileDescriptor.fd, offset: fileDescriptor.offset, length: fileDescriptor.length };
   this.isSeek = true; // 支持seek操作
   // 为fdSrc赋值触发initialized状态机上报
   avPlayer.fdSrc = avFileDescriptor;
 }

 // 以下demo为使用fs文件系统打开沙箱地址获取媒体文件地址并通过dataSrc属性进行播放(seek模式)示例
 async avPlayerDataSrcSeekDemo() {
   // 创建avPlayer实例对象
   let avPlayer: media.AVPlayer = await media.createAVPlayer();
   // 创建状态机变化回调函数
   this.setAVPlayerCallback(avPlayer);
   // dataSrc播放模式的的播放源地址,当播放为Seek模式时fileSize为播放文件的具体大小,下面会对fileSize赋值
   let src: media.AVDataSrcDescriptor = {
     fileSize: -1,
     callback: (buf: ArrayBuffer, length: number, pos: number | undefined) => {
       let num = 0;
       if (buf == undefined || length == undefined || pos == undefined) {
         return -1;
       }
       num = fs.readSync(this.fd, buf, { offset: pos, length: length });
       if (num > 0 && (this.fileSize >= pos)) {
         return num;
       }
       return -1;
     }
   }
   let context = getContext(this) as common.UIAbilityContext;
   // 通过UIAbilityContext获取沙箱地址filesDir,以Stage模型为例
   let pathDir = context.filesDir;
   let path = pathDir  + '/01.mp3';
   await fs.open(path).then((file: fs.File) => {
     this.fd = file.fd;
   })
   // 获取播放文件的大小
   this.fileSize = fs.statSync(path).size;
   src.fileSize = this.fileSize;
   this.isSeek = true; // 支持seek操作
   avPlayer.dataSrc = src;
 }

 // 以下demo为使用fs文件系统打开沙箱地址获取媒体文件地址并通过dataSrc属性进行播放(No seek模式)示例
 async avPlayerDataSrcNoSeekDemo() {
   // 创建avPlayer实例对象
   let avPlayer: media.AVPlayer = await media.createAVPlayer();
   // 创建状态机变化回调函数
   this.setAVPlayerCallback(avPlayer);
   let context = getContext(this) as common.UIAbilityContext;
   let src: media.AVDataSrcDescriptor = {
     fileSize: -1,
     callback: (buf: ArrayBuffer, length: number) => {
       let num = 0;
       if (buf == undefined || length == undefined) {
         return -1;
       }
       num = fs.readSync(this.fd, buf);
       if (num > 0) {
         return num;
       }
       return -1;
     }
   }
   // 通过UIAbilityContext获取沙箱地址filesDir,以Stage模型为例
   let pathDir = context.filesDir;
   let path = pathDir  + '/01.mp3';
   await fs.open(path).then((file: fs.File) => {
     this.fd = file.fd;
   })
   this.isSeek = false; // 不支持seek操作
   avPlayer.dataSrc = src;
 }

 // 以下demo为通过url设置网络地址来实现播放直播码流的demo
 async avPlayerLiveDemo() {
   // 创建avPlayer实例对象
   let avPlayer: media.AVPlayer = await media.createAVPlayer();
   // 创建状态机变化回调函数
   this.setAVPlayerCallback(avPlayer);
   this.isSeek = false; // 不支持seek操作
   avPlayer.url = 'http://xxx.xxx.xxx.xxx:xx/xx/index.m3u8';
 }
}

[@Entry](/user/Entry)
[@Component](/user/Component)
struct Index {
 av:AVPlayerDemo  = new AVPlayerDemo()
 build() {
   Column() {
     Button('play local')
       .onClick(() => {
         this.av.avPlayerFdSrcDemo();
       })
   }
 }
}
楼主您好,上述文档中的示例代码,在setAVPlayerCallback函数的playing的状态机里,有一个else的分支是 播放3s后调用暂停接口暂停播放,把这个分支注释掉试试能否播放呢

作为IT专家,针对您提出的“HarmonyOS 鸿蒙Next 无法播放rawfile里的wav文件”的问题,以下是一些可能的原因及解决方案:

  1. 文件格式及采样率:请确认wav文件的格式和采样率是否被HarmonyOS 鸿蒙Next系统支持。部分系统可能不支持低采样率的wav文件,如4000Hz,尝试使用更高采样率的wav文件看是否能正常播放。
  2. 文件放置位置:确保wav文件已正确放置在项目的resources/rawfile目录下,并在manifest.json中声明了该资源。
  3. 路径及权限:使用RawFileDescriptor或相关API访问时,确保路径正确无误,并检查应用是否有访问rawfile的权限(虽然通常不需要特殊权限)。
  4. API调用:如果使用的是特定的API或方法,请确保API的调用方式正确,参数无误。

如果问题依旧没法解决请联系官网客服,官网地址是:https://www.itying.com/category-93-b0.html 。在联系客服时,请提供详细的问题描述、设备型号、系统版本等信息,以便客服人员更快地定位并解决问题。

回到顶部