从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++)采集内录音频的基本流程如下:
ArkTS开发建议搭配AudioCapturer、AudioCapturerOptions和AudioPlaybackCaptureMode的API说明阅读。
C/C++开发建议搭配OH_AudioStreamBuilderStruct、OH_AudioCapturerStruct和OH_AudioStream_PlaybackCaptureMode的API说明阅读。
应用调用内录启动接口后,系统会自动拉起隐私确认弹框,应用无需实现弹框。不同设备形态的弹框表现可能存在差异,请以实际设备表现为准。
用户选择允许后,异步回调返回启动成功;用户选择拒绝,或系统未能拉起弹框时,异步回调返回用户未授权。应用应根据异步回调结果进行处理:回调返回成功时开始处理内录PCM数据;回调返回未授权或启动失败时,提示用户并释放已创建的音频采集器。
启动接口为非阻塞接口。等待用户操作弹框和系统返回异步回调期间,应用不应通过循环等待、同步锁等待等方式阻塞当前线程。C API启动接口返回AUDIOSTREAM_SUCCESS仅表示启动请求提交成功,不能作为最终授权结果。
系统弹框示意图:
以下ArkTS示例为片段代码,开发者可根据实际业务将采集到的PCM数据写入文件、送入编码器或交给自定义音频处理模块。
导入模块。
import { audio } from '@kit.AudioKit';
import { BusinessError } from '@kit.BasicServicesKit'; 配置音频采集参数并创建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); 调用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数据转交给独立任务处理,避免数据回调阻塞导致丢帧、卡顿或杂音。
调用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实例。应用主动停止内录流后,还应忽略旧请求返回的异步回调。
调用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();
}
} 以下C/C++示例为片段代码,展示创建、启动、停止和释放OH_AudioCapturer的完整生命周期。OH_AudioCapturer的基础录制流程可参考推荐使用OHAudio开发音频录制功能(C/C++)。
引入头文件,并定义用于保存内录采集器及启动状态的变量。
内录启动接口为异步接口,需要在启动函数返回后继续保存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}; 定义音频数据回调和内录启动结果回调。
音频数据回调返回内录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);
}
} 创建音频采集器并配置内录模式。
通过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_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());
} 播放端可以通过配置自身音频流的隐私属性,控制是否允许其他应用录制。系统建立内录采集链路时,会过滤标记为隐私保护的播放流,因此通话内容、账号信息或其他隐私内容不会被内录采集。该配置属于播放端能力,不需要内录应用额外处理。