已开启
该库设置循环播放次数时。会导致音频播放不出来。是因为端能力实现有问题。Harmony原生API循环本身播放不是通过次数设置。他是设置boolean属性。 #11
天涯与晴天创建于 2025年11月25日
2025年11月25日 修改了issue 的描述
天涯与晴天
2025年12月4日 评论:
2025年12月4日 评论:
/**
* MIT License
*
* Copyright (C) 2024 Huawei Device Co., Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import media from '@ohos.multimedia.media';
import audio from '@ohos.multimedia.audio';
import { avSession as AVSessionManager } from '@kit.AVSessionKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { backgroundTaskManager } from '@kit.BackgroundTasksKit';
import { AbilityConstant, Want, wantAgent, WantAgent } from '@kit.AbilityKit';
import fs from '@ohos.file.fs';
import Logger from './Logger';
import { AVAudioSessionCategory, AvplayerSessionCategory, AvplayerStatus } from './ts';
const TAG: string = "[RNOH] Sound"
export interface PrepareProps {
duration: number
numberOfChannels?: number
}
interface E {
code: number,
message: string
}
enum Speed {
ZERO = 0,
ONE = 1,
TWO = 2,
THREE = 3,
FOUR = 4
}
// 播放器状态接口
interface PlayerState {
isPlaying: boolean;
mediaPlayer: media.AVPlayer;
}
export class AVPlayerController {
public playerPool = new Map<number, PlayerState>(); // 修改为存储 PlayerState
private audioManager = audio.getAudioManager();
private category: AVAudioSessionCategory = "Playback";
private mixWithOthers: Boolean = true;
//因为这个模块是三方源码本地依赖,它是作为ts文件使用的,而不是ets 所以会导致校验错误。无需修改正。
private context = getContext(this);
private previousSrc: fs.File | string | undefined = undefined
//创建后台长任务
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
let wantAgentInfo: wantAgent.WantAgentInfo = {
// 点击通知后,将要执行的动作列表
wants: [
{
bundleName: "com.example.myapplication",
abilityName: "EntryAbility"
}
],
// 点击通知后,动作类型
actionType: wantAgent.OperationType.START_ABILITY,
// 使用者自定义的一个私有值
requestCode: 0,
// 点击通知后,动作执行属性
wantAgentFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG]
};
try {
// 通过wantAgent模块下getWantAgent方法获取WantAgent对象
wantAgent.getWantAgent(wantAgentInfo).then((wantAgentObj: WantAgent) => {
try {
backgroundTaskManager.startBackgroundRunning(this.context,
backgroundTaskManager.BackgroundMode.LOCATION, wantAgentObj,
(error: BusinessError, data: void) => {
})
} catch (error) {
}
});
} catch (error) {
}
}
// 创建session
async createSession() {
let type: AVSessionManager.AVSessionType = 'audio';
let session = await AVSessionManager.createAVSession(this.context, 'SESSION_NAME', type);
// 激活接口要在元数据、控制命令注册完成之后再执行
await session.activate();
}
setAudioRendererInfo(mediaPlayer: media.AVPlayer) {
if (this.category != null) {
switch (this.category) {
case AvplayerSessionCategory.AMBIENT: //游戏模式
mediaPlayer.audioRendererInfo = {
usage: audio.StreamUsage.STREAM_USAGE_GAME,
rendererFlags: 0
}
break;
case AvplayerSessionCategory.PLAYANDRECORD:
mediaPlayer.audioRendererInfo = {
usage: audio.StreamUsage.STREAM_USAGE_VOICE_COMMUNICATION,
rendererFlags: 0
}
break;
case AvplayerSessionCategory.PLAYBACK:
this.createSession()
break;
default:
Logger.info(TAG, `RNSoundModule Unrecognised category ${this.category}`);
break;
}
}
}
// 开始创建并激活媒体会话
mediaPrepare(mediaPlayer: media.AVPlayer, callBack: (error: E | null, props: PrepareProps | null) => void) {
mediaPlayer.prepare().then(() => {
const props: PrepareProps = {
duration: mediaPlayer.duration,
}
callBack(null, props);
}, (err: BusinessError) => {
const e: E = {
code: -1,
message: `avPlay prepare error:${err.name} message ${err.message}}`
}
callBack(e, null);
});
}
closeFsRawFd() {
if (typeof this.previousSrc === "string") {
try {
this.context.resourceManager.closeRawFdSync(this.previousSrc);
Logger.debug(TAG, `aboutToDisappear, closeRawFd file succeed`);
} catch (error) {
Logger.debug(TAG, `aboutToDisappear, closeRawFd error is${error.message} error code:${error.code}`);
}
} else if (typeof this.previousSrc === "object") {
try {
fs.closeSync(this.previousSrc)
Logger.debug(TAG, `aboutToDisappear, close file succeed`);
} catch (error) {
Logger.debug(TAG,
`aboutToDisappear, close file failed with error message:${error.message},error code:${error.code}`);
}
}
}
async prepare(fileName: string, key: number, option: object,
callBack: (error: E | null, props: PrepareProps | null) => void) {
this.closeFsRawFd()
const mediaPlayer: media.AVPlayer = await media.createAVPlayer();
let fileUrl: string = '';
if (fileName.startsWith('asset')) { //在npm run dev起服务的时候 用require('./frog.wav') 引入的文件
fileUrl = fileName.split('//')[1];
let fileDescriptor = await this.context.resourceManager.getRawFd(`assets/${fileUrl}`);
this.previousSrc = `assets/${fileUrl}`
mediaPlayer.fdSrc = fileDescriptor;
} else if (fileName.startsWith("http://") ||
fileName.startsWith("https://")) { //音频资源是网络资源和npm run start起服务的时候 用require('./frog.wav') 引入的文件
mediaPlayer.url = fileName;
this.previousSrc = undefined
} else if (fileName.startsWith('/data') || fileName.startsWith('file:')) {
this.previousSrc = fs.openSync(fileName, fs.OpenMode.READ_ONLY)
mediaPlayer.url = `fd://${this.previousSrc.fd}`;
} else { //资源在resources/rawfile 下的资源 url: 'whoosh.mp3',
let fileDescriptor = await this.context.resourceManager.getRawFd(fileName);
this.previousSrc = fileName
mediaPlayer.fdSrc = fileDescriptor;
}
mediaPlayer.on('stateChange', async (state, reason) => {
switch (state) {
case AvplayerStatus.IDLE:
Logger.info(TAG, 'stateChange AVPlayerstate initialized called. idle');
break;
case AvplayerStatus.INITIALIZED: // avplayer 设置播放源后触发该状态上报
this.setAudioRendererInfo(mediaPlayer);


dingchengjie
5月20日 评论:
5月20日 评论:
感谢开发者的反馈。该问题已经在后续版本修复,可以查看该pr https://gitcode.com/openharmony-sig/rntpc_react-native-sound/pull/12


此处端能力实现应该这样。
他和ios不一样不能完全按照ios 方法来实现。