目录

实现录制系统音频

从API版本26.0.0开始,Audio Kit支持应用使用AudioCapturer(ArkTS)或OH_AudioCapturer(C/C++)录制系统音频。本文介绍具体开发方法。

Phone、Tablet和TV设备支持SystemCapability.Multimedia.Audio.PlaybackCapture系统能力,其他设备可以通过canIUse()检查是否支持该系统能力。使用ArkTS API开发时,仅支持Stage模型。

创建AudioCapturer时通过playbackCaptureMode配置内录模式,创建OH_AudioCapturer时通过OH_AudioStreamBuilder_SetPlaybackCaptureMode()配置内录模式。

配置内录模式后,需分别通过requestPlaybackCaptureStart()或OH_AudioCapturer_RequestPlaybackCaptureStart()启动,不支持通过start()或OH_AudioCapturer_Start()接口启动。

如果需要同时录制屏幕画面和系统音频,请参考AVScreenCapture录屏基础流程。

开发指导

使用AudioCapturer(ArkTS)或OH_AudioCapturer(C/C++)采集内录音频的基本流程如下:

  1. 创建音频采集器,配置音频流参数和内录模式。
  2. 注册音频数据回调,准备接收采集到的PCM数据。
  3. 调用对应语言的内录启动接口,并在异步回调中确认启动结果。
  4. 启动成功后,通过音频数据回调处理内录PCM数据。
  5. 停止采集,取消监听并释放音频采集器资源。

ArkTS开发建议搭配AudioCapturer、AudioCapturerOptions和AudioPlaybackCaptureMode的API说明阅读。

C/C++开发建议搭配OH_AudioStreamBuilderStruct、OH_AudioCapturerStruct和OH_AudioStream_PlaybackCaptureMode的API说明阅读。

[h2]用户授权流程

应用调用内录启动接口后,系统会自动拉起隐私确认弹框,应用无需实现弹框。不同设备形态的弹框表现可能存在差异,请以实际设备表现为准。

用户选择允许后,异步回调返回启动成功;用户选择拒绝,或系统未能拉起弹框时,异步回调返回用户未授权。应用应根据异步回调结果进行处理:回调返回成功时开始处理内录PCM数据;回调返回未授权或启动失败时,提示用户并释放已创建的音频采集器。

启动接口为非阻塞接口。等待用户操作弹框和系统返回异步回调期间,应用不应通过循环等待、同步锁等待等方式阻塞当前线程。C API启动接口返回AUDIOSTREAM_SUCCESS仅表示启动请求提交成功,不能作为最终授权结果。

系统弹框示意图:

[h2]开发步骤及注意事项(ArkTS)

以下ArkTS示例为片段代码,开发者可根据实际业务将采集到的PCM数据写入文件、送入编码器或交给自定义音频处理模块。

  1. 导入模块。

    import { audio } from '@kit.AudioKit';
    import { BusinessError } from '@kit.BasicServicesKit';
  2. 配置音频采集参数并创建AudioCapturer实例。

    创建AudioCapturer时,通过AudioCapturerOptions中的playbackCaptureMode配置内录模式。该字段支持AudioPlaybackCaptureMode枚举值、按位或组合。未配置playbackCaptureMode时,AudioCapturer按普通音频采集器创建。

    当前支持的内录模式如下:

    内录模式 录制范围
    MODE_DEFAULT 默认模式,录制大部分音频流,不包括提示音流和隐私流。
    MODE_MEDIA 媒体模式,录制媒体、语音消息和未知类型的音频流。
    MODE_EXCLUDING_SELF 排除自身模式,录制除应用自身播放音频以外的音频流。
    MODE_MEDIA | MODE_EXCLUDING_SELF 录制媒体类音频,同时排除应用自身播放的音频。

    内录启动时还会进行用户授权检查,部分设备会展示系统授权或隐私提示弹窗,授权结果通过requestPlaybackCaptureStart()回调返回。

    let audioStreamInfo: audio.AudioStreamInfo = {
      samplingRate: audio.AudioSamplingRate.SAMPLE_RATE_48000,
      channels: audio.AudioChannel.CHANNEL_2,
      sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE,
      encodingType: audio.AudioEncodingType.ENCODING_TYPE_RAW
    };
    
    let audioCapturerInfo: audio.AudioCapturerInfo = {
      source: audio.SourceType.SOURCE_TYPE_MIC,
      capturerFlags: 0
    };
    
    let audioCapturerOptions: audio.AudioCapturerOptions = {
      streamInfo: audioStreamInfo,
      capturerInfo: audioCapturerInfo,
      playbackCaptureMode: audio.AudioPlaybackCaptureMode.MODE_MEDIA |
        audio.AudioPlaybackCaptureMode.MODE_EXCLUDING_SELF
    };
    
    let audioCapturer: audio.AudioCapturer | undefined =
      await audio.createAudioCapturer(audioCapturerOptions);
  3. 调用on('readData')订阅音频数据读入回调。

    回调返回PCM数据,应用可根据业务写入文件、送入编码器或交给自定义音频处理模块。以下示例统计接收到的PCM数据字节数。

    let audioCapturer: audio.AudioCapturer | undefined = undefined;
    let isPlaybackCaptureStarted: boolean = false;
    let readBytes: number = 0;
    let playbackCaptureRequestId: number = 0;
    let playbackCaptureStartState: audio.PlaybackCaptureStartState | undefined = undefined;
      private readDataCallback: Callback<ArrayBuffer> = (buffer: ArrayBuffer): void => {
        readBytes += buffer.byteLength;
        this.bytes = readBytes;
      };
    
    audioCapturer.on('readData', this.readDataCallback);

    readData回调中不建议执行耗时任务。若需要编码、网络发送或复杂算法处理,建议将PCM数据转交给独立任务处理,避免数据回调阻塞导致丢帧、卡顿或杂音。

  4. 调用requestPlaybackCaptureStart()请求启动内录。

    该接口为非阻塞接口,系统会继续处理用户授权检查和内录流启动,最终结果通过PlaybackCaptureStartState返回。

    示例通过递增playbackCaptureRequestId标识每次启动请求。回调返回时同时校验采集器实例和请求ID,避免旧请求回调修改当前内录状态。等待授权期间,应用应继续处理其他业务,避免同步阻塞当前线程。

    内录采集器不能通过start()接口启动。调用requestPlaybackCaptureStart()后,只有收到STATE_SUCCESS时,应用才应认为内录已启动成功。

    if (audioCapturer === undefined) {
      return;
    }
    // 为每次异步启动请求分配唯一ID,避免旧请求回调修改当前状态。
    const requestId: number = ++playbackCaptureRequestId;
    const capturer: audio.AudioCapturer = audioCapturer;
    capturer.requestPlaybackCaptureStart((state: audio.PlaybackCaptureStartState): void => {
      if (audioCapturer !== capturer || playbackCaptureRequestId !== requestId) {
        return;
      }
      playbackCaptureStartState = state;
      const stateText: string = playbackCaptureStateToText(state);
      if (state === audio.PlaybackCaptureStartState.STATE_SUCCESS) {
        isPlaybackCaptureStarted = true;
        this.message = `当前状态:${stateText}`;
        return;
      }
      this.message = `当前状态:${stateText}`;
      this.releasePlaybackCapture().catch((error: BusinessError): void => {
        console.error(`Release playback capture failed, code is ${error.code}, message is ${error.message}`);
      });
    });

    启动结果请参考PlaybackCaptureStartState。如果启动结果不是STATE_SUCCESS,应用应根据业务提示用户,并及时释放已创建的AudioCapturer实例。应用主动停止内录流后,还应忽略旧请求返回的异步回调。

  5. 调用stop停止采集,并调用release释放AudioCapturer资源。

    应用结束内录后,需要停止AudioCapturer并释放资源。释放前应取消readData监听,避免对象释放后仍处理回调。

    private async releasePlaybackCapture(): Promise<void> {
      if (audioCapturer === undefined) {
        return;
      }
      const capturer: audio.AudioCapturer = audioCapturer;
      audioCapturer = undefined;
      playbackCaptureRequestId++;
      capturer.off('readData', this.readDataCallback);
      try {
        if (isPlaybackCaptureStarted) {
          await capturer.stop();
        }
      } finally {
        isPlaybackCaptureStarted = false;
        await capturer.release();
      }
    }

[h2]开发步骤及注意事项(C/C++)

以下C/C++示例为片段代码,展示创建、启动、停止和释放OH_AudioCapturer的完整生命周期。OH_AudioCapturer的基础录制流程可参考推荐使用OHAudio开发音频录制功能(C/C++)。

  1. 引入头文件,并定义用于保存内录采集器及启动状态的变量。

    内录启动接口为异步接口,需要在启动函数返回后继续保存OH_AudioCapturer实例,确保在启动结果回调中处理失败场景,并在业务结束时停止和释放实例。互斥锁(Mutual Exclusion Lock)用于保护全局采集器指针,避免启动结果回调、重复启动和主动停止同时访问该指针。

    #include <ohaudio/native_audiocapturer.h>
    #include <ohaudio/native_audiostreambuilder.h>
    constexpr int32_t PLAYBACK_CAPTURE_SAMPLE_RATE = 48000;
    constexpr int32_t PLAYBACK_CAPTURE_CHANNEL_COUNT = 2;
    constexpr uint32_t PLAYBACK_CAPTURE_MODE = AUDIOSTREAM_PLAYBACKCAPTURE_MODE_MEDIA |
        AUDIOSTREAM_PLAYBACKCAPTURE_MODE_EXCLUDING_SELF;
    std::mutex g_playbackCaptureMutex;
    OH_AudioCapturer* g_playbackCaptureCapturer = nullptr;
    std::atomic<int32_t> g_playbackCaptureStartState{-1};
    std::atomic<uint64_t> g_playbackCaptureReadBytes{0};
  2. 定义音频数据回调和内录启动结果回调。

    音频数据回调返回内录PCM数据,不建议在回调中执行耗时任务。内录启动失败或用户未授权时,及时释放已创建的采集器。

    void MyOnPlaybackCaptureReadData(
        OH_AudioCapturer* capturer,
        void* userData,
        void* audioData,
        int32_t audioDataSize)
    {
        if (audioData == nullptr || audioDataSize <= 0) {
            return;
        }
        g_playbackCaptureReadBytes.fetch_add(static_cast<uint64_t>(audioDataSize));
    }
    void MyOnPlaybackCaptureStart(
        OH_AudioCapturer* capturer,
        void* userData,
        OH_AudioStream_PlaybackCaptureStartState state)
    {
        int32_t stateValue = static_cast<int32_t>(state);
        g_playbackCaptureStartState.store(stateValue);
        OH_LOG_Print(LOG_APP, LOG_INFO, AUDIO_CAPTURE_LOG_DOMAIN, AUDIO_CAPTURE_LOG_TAG,
            "Playback capture start callback state=%{public}d", stateValue);
    
        if (state == AUDIOSTREAM_PLAYBACKCAPTURE_START_STATE_SUCCESS) {
            return;
        }
    
        OH_AudioCapturer* capturerToRelease = nullptr;
        {
            std::lock_guard<std::mutex> lock(g_playbackCaptureMutex);
            if (g_playbackCaptureCapturer == capturer) {
                capturerToRelease = g_playbackCaptureCapturer;
                g_playbackCaptureCapturer = nullptr;
            }
        }
        if (capturerToRelease != nullptr) {
            OH_AudioCapturer_Release(capturerToRelease);
        }
    }
  3. 创建音频采集器并配置内录模式。

    通过OH_AudioStreamBuilder_SetPlaybackCaptureMode()配置内录模式。内录启动时,系统会进行用户授权检查,并通过启动结果回调返回授权结果。

    当前支持的内录模式如下:

    内录模式 录制范围
    AUDIOSTREAM_PLAYBACKCAPTURE_MODE_DEFAULT 默认模式,录制大部分音频流,不包括提示音流和隐私流。
    AUDIOSTREAM_PLAYBACKCAPTURE_MODE_MEDIA 媒体模式,录制媒体、语音消息和未知流等。
    AUDIOSTREAM_PLAYBACKCAPTURE_MODE_EXCLUDING_SELF 排除自身模式,录制除应用自身播放音频以外的音频流。
    AUDIOSTREAM_PLAYBACKCAPTURE_MODE_MEDIA | AUDIOSTREAM_PLAYBACKCAPTURE_MODE_EXCLUDING_SELF 录制媒体类音频,同时排除应用自身播放的音频。

    单独使用排除自身模式时,会录制大部分允许被采集的音频流并排除应用自身播放的音频;与媒体模式组合时,只录制媒体类音频并排除应用自身播放的音频。

    bool ConfigurePlaybackCaptureBuilder(OH_AudioStreamBuilder* builder)
    {
        OH_AudioStreamBuilder_SetSamplingRate(builder, PLAYBACK_CAPTURE_SAMPLE_RATE);
        OH_AudioStreamBuilder_SetChannelCount(builder, PLAYBACK_CAPTURE_CHANNEL_COUNT);
        OH_AudioStreamBuilder_SetSampleFormat(builder, AUDIOSTREAM_SAMPLE_S16LE);
        OH_AudioStreamBuilder_SetEncodingType(builder, AUDIOSTREAM_ENCODING_TYPE_RAW);
        OH_AudioStreamBuilder_SetCapturerReadDataCallback(builder, MyOnPlaybackCaptureReadData, nullptr);
    
        OH_AudioStream_Result result =
            OH_AudioStreamBuilder_SetPlaybackCaptureMode(builder, PLAYBACK_CAPTURE_MODE);
        return result == AUDIOSTREAM_SUCCESS;
    }
    
    OH_AudioCapturer* CreatePlaybackCapture()
    {
        OH_AudioStreamBuilder* builder = nullptr;
        OH_AudioStream_Result result =
            OH_AudioStreamBuilder_Create(&builder, AUDIOSTREAM_TYPE_CAPTURER);
        if (result != AUDIOSTREAM_SUCCESS || builder == nullptr) {
            return nullptr;
        }
        if (!ConfigurePlaybackCaptureBuilder(builder)) {
            OH_AudioStreamBuilder_Destroy(builder);
            return nullptr;
        }
    
        OH_AudioCapturer* audioCapturer = nullptr;
        result = OH_AudioStreamBuilder_GenerateCapturer(builder, &audioCapturer);
        OH_AudioStreamBuilder_Destroy(builder);
        if (result != AUDIOSTREAM_SUCCESS || audioCapturer == nullptr) {
            return nullptr;
        }
        return audioCapturer;
    }
    
    bool StorePlaybackCapture(OH_AudioCapturer* audioCapturer)
    {
        std::lock_guard<std::mutex> lock(g_playbackCaptureMutex);
        if (g_playbackCaptureCapturer != nullptr) {
            return false;
        }
        g_playbackCaptureCapturer = audioCapturer;
        g_playbackCaptureStartState.store(-1);
        g_playbackCaptureReadBytes.store(0);
        return true;
    }
    
    <!-- @[RequestPlaybackCaptureStart](https://gitcode.com/openharmony/applications_app_samples/blob/master/code/DocsSample/Media/Audio/AudioCapturerSampleC/entry/src/main/cpp/AudioCapture.cpp) -->
    bool RequestPlaybackCaptureStart(OH_AudioCapturer* audioCapturer)
    {
        OH_AudioStream_Result result = OH_AudioCapturer_RequestPlaybackCaptureStart(
            audioCapturer, MyOnPlaybackCaptureStart, nullptr);
        if (result == AUDIOSTREAM_SUCCESS) {
            return true;
        }
    
        bool shouldRelease = false;
        {
            std::lock_guard<std::mutex> lock(g_playbackCaptureMutex);
            if (g_playbackCaptureCapturer == audioCapturer) {
                g_playbackCaptureCapturer = nullptr;
                shouldRelease = true;
            }
        }
        if (shouldRelease) {
            OH_AudioCapturer_Release(audioCapturer);
        }
        return false;
    }
    
    void StartPlaybackCapture()
    {
        {
            std::lock_guard<std::mutex> lock(g_playbackCaptureMutex);
            if (g_playbackCaptureCapturer != nullptr) {
                return;
            }
        }
    
        OH_AudioCapturer* audioCapturer = CreatePlaybackCapture();
        if (audioCapturer == nullptr) {
            return;
        }
        if (!StorePlaybackCapture(audioCapturer)) {
            OH_AudioCapturer_Release(audioCapturer);
            return;
        }
        RequestPlaybackCaptureStart(audioCapturer);
    }

    生成OH_AudioCapturer后,构造器不再使用,需要立即调用OH_AudioStreamBuilder_Destroy()释放构造器资源。设置内录模式失败时也需要销毁构造器。

    • OH_AudioCapturer_RequestPlaybackCaptureStart()返回AUDIOSTREAM_SUCCESS仅表示启动请求提交成功。只有异步回调返回AUDIOSTREAM_PLAYBACKCAPTURE_START_STATE_SUCCESS时,才表示内录启动成功。
    • 用户拒绝授权或系统未能拉起授权弹框时,回调返回AUDIOSTREAM_PLAYBACKCAPTURE_START_STATE_NOT_AUTHORIZED。
  4. 停止并释放内录采集器。

    业务结束后,通过OH_AudioCapturer_Stop()停止采集,并通过OH_AudioCapturer_Release()释放资源。

    napi_value StopPlaybackCapture(napi_env env, napi_callback_info info)
    {
        OH_AudioCapturer* capturerToRelease = nullptr;
        {
            std::lock_guard<std::mutex> lock(g_playbackCaptureMutex);
            capturerToRelease = g_playbackCaptureCapturer;
            g_playbackCaptureCapturer = nullptr;
        }
    
        std::stringstream ss;
        if (capturerToRelease == nullptr) {
            ss << "当前没有正在运行或等待授权的音频内录流\n";
            ss << "最后状态: " << PlaybackCaptureStateToText(g_playbackCaptureStartState.load());
            return CreateStringResult(env, ss.str());
        }
    
        OH_AudioStream_Result stopResult = OH_AudioCapturer_Stop(capturerToRelease);
        OH_AudioStream_Result releaseResult = OH_AudioCapturer_Release(capturerToRelease);
        ss << "停止音频内录完成\n";
        ss << "OH_AudioCapturer_Stop 返回值: " << stopResult << "\n";
        ss << "OH_AudioCapturer_Release 返回值: " << releaseResult << "\n";
        ss << "本次读取字节数: " << g_playbackCaptureReadBytes.load();
        g_playbackCaptureStartState.store(-1);
        g_playbackCaptureReadBytes.store(0);
        return CreateStringResult(env, ss.str());
    }

[h2]配置目标播放流的内录策略

播放端可以通过配置自身音频流的隐私属性,控制是否允许其他应用录制。系统建立内录采集链路时,会过滤标记为隐私保护的播放流,因此通话内容、账号信息或其他隐私内容不会被内录采集。该配置属于播放端能力,不需要内录应用额外处理。