已开启
该库设置循环播放次数时。会导致音频播放不出来。是因为端能力实现有问题。Harmony原生API循环本身播放不是通过次数设置。他是设置boolean属性。 #11
天涯与晴天创建于  2025年11月25日
天涯与晴天
天涯与晴天
2025年11月25日 创建

image.png

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

likedislike
天涯与晴天天涯与晴天
2025年11月25日 修改了issue 的描述
天涯与晴天
天涯与晴天
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);
                    
likedislike
dingchengjie
dingchengjie成员
5月20日 评论:

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

likedislike