如何同时录制设备内部音频和麦克风音频，并合成两路音频
==========================

#### 问题现象

如何实现音频双录，即同时录制系统音频与麦克风音频，并在录制完成后将内录音频与外录音频合成。

#### 背景知识

*   音频内录与外录是两种不同的录音方式。音频内录是指直接录制设备系统内部播放的声音，音频外录是指通过麦克风录制外部环境声音。
*   开发者可以通过[AVScreenCapture](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-native-avscreen-capture-h)模块的C API，采集设备内外的音视频数据源。开发者可以设置屏幕录制所需要的音频参数[OH\_AudioInfo](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-avscreencapture-oh-audioinfo)包括内录和麦克风音频[OH\_AudioCaptureInfo](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-avscreencapture-oh-audiocaptureinfo)。
*   [AVCodec Kit](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/avcodec-kit-intro)提供音视频的编解码、媒体文件的解析、封装、媒体数据输入等原子能力。开发者可以调用AVCodec Kit的C API完成[音频编码](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/audio-encoding)和[媒体数据封装](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/audio-video-muxer)。
*   [@ohos/mp4parser(V2.0.7)](https://ohpm.openharmony.cn/#/cn/detail/@ohos%2Fmp4parser)是用于编辑音视频文件的三方库，可以调用ffmpeg命令完成音视频文件的编辑，比如通过音频滤镜对多个音频进行混音。
*   当需要使用麦克风时，需要申请[ohos.permission.MICROPHONE](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/permissions-for-all-user#ohospermissionmicrophone)麦克风权限。申请方式请参考：[向用户申请授权](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/request-user-authorization)。
*   如果需要持续录制或后台录制，请申请长时任务避免进入挂起（Suspend）状态。[具体参考长时任务开发指导](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/continuous-task)。

#### 解决方案

音频双录场景可以通过AVScreenCapture屏幕录制模块采集设备内外的音频数据，然后将内外音频数据输入[AudioCodec](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-audiocodec)音频编码模块分别编码内录与外录音频流，最后通过[AVMuxer](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-avmuxer)媒体文件封装模块将内录和外录音频流封装到音频文件的不同音频轨道实现。在录制完成后可以通过@ohos/mp4parser(V2.0.7)调用ffmpeg混音命令合成内录和外录的音频轨道完成混音。

具体实现步骤如下：

1.  创建AVScreenCapture实例，配置音频采集参数为同时采集内部和麦克风音频数据。
    
    void CAVScreenCaptureToStream::InitAVScreenCapture()
    {
        if (g\_avCapture != nullptr) {
            StopScreenCaptureRecording(g\_avCapture);
        }
    
        g\_avCapture = OH\_AVScreenCapture\_Create();
        if (g\_avCapture == nullptr) {
            OH\_LOG\_ERROR(LOG\_APP, "create screen capture failed");
        }
        OH\_LOG\_INFO(LOG\_APP, "ScreenCapture after create sc");
    
      _  // Set callback_
        OH\_AVScreenCapture\_SetErrorCallback(g\_avCapture, OnErrorToStream, nullptr);
        OH\_AVScreenCapture\_SetStateCallback(g\_avCapture, OnSurfaceStateChange, nullptr);
    
        OH\_AVScreenCapture\_SetMicrophoneEnabled(g\_avCapture, true);
        OH\_AVScreenCapture\_SetDataCallback(g\_avCapture, OnBufferAvailable, audioEncContext\_);
        OH\_AVScreenCapture\_SetCanvasRotation(g\_avCapture, true);
    
        OH\_AVScreenCaptureConfig config;
     _   // 麦克风音频采集参数_
        OH\_AudioCaptureInfo micCapInfo = {.audioSampleRate = 48000, .audioChannels = 2, .audioSource = OH\_MIC};
      _  // 内录音频采集参数_
        OH\_AudioCaptureInfo innerCapInfo = {.audioSampleRate = 48000, .audioChannels = 2, .audioSource = OH\_ALL\_PLAYBACK};
       _ // 音频输出规格_
        OH\_AudioEncInfo audioEncInfo = {.audioBitrate = 96000, .audioCodecformat = OH\_AudioCodecFormat::OH\_AAC\_LC};
       _ // 配置音频采集参数_
        OH\_AudioInfo audioInfo = {.micCapInfo = micCapInfo, .innerCapInfo = innerCapInfo, .audioEncInfo = audioEncInfo};
     _   // 录屏配置信息_
        config = {
            .captureMode = OH\_CAPTURE\_HOME\_SCREEN,
            .dataType = OH\_ORIGINAL\_STREAM,
            .audioInfo = audioInfo,
        };
    
        int result = OH\_AVScreenCapture\_Init(g\_avCapture, config);
        if (result != AV\_SCREEN\_CAPTURE\_ERR\_OK) {
            OH\_LOG\_INFO(LOG\_APP, "ScreenCapture OH\_AVScreenCapture\_Init failed %{public}d", result);
        }
        OH\_LOG\_INFO(LOG\_APP, "ScreenCapture OH\_AVScreenCapture\_Init %{public}d", result);
    }
    
2.  创建并配置内录与外录音频编码器。创建媒体文件封装器，并添加内录与外录的音频轨道。
    
    void CAVScreenCaptureToStream::InitMuxerAndEncoder(int32\_t outputFd)
    {
        sampleInfo\_.outputFd = outputFd;
    
      _  // 音频编码参数_
        sampleInfo\_.audioCodecMime = OH\_AVCODEC\_MIMETYPE\_AUDIO\_AAC;
        sampleInfo\_.audioSampleFormat = OH\_BitsPerSample::SAMPLE\_S16LE;
        sampleInfo\_.audioSampleRate = 48000;
        sampleInfo\_.audioChannelCount = 2;
        sampleInfo\_.audioBitRate = 96000;
        sampleInfo\_.audioChannelLayout = OH\_AudioChannelLayout::CH\_LAYOUT\_STEREO;
        sampleInfo\_.audioMaxInputSize = sampleInfo\_.audioSampleRate \* 0.02 \* sampleInfo\_.audioChannelCount \* sizeof(short);
      _  // 音频编码器_
        audioEncoderInner\_ = std::make\_unique<AudioEncoder>(); _// 内录音频编码器_
        audioEncoderMic\_ = std::make\_unique<AudioEncoder>();  _ // 外录音频编码器_
     _   // 媒体文件封装器_
        muxer\_ = std::make\_unique<Muxer>();
        muxer\_->Create(sampleInfo\_.outputFd);
        muxer\_->Config(sampleInfo\_);
    
        CreateEncoder();
    }
    
    int32\_t Muxer::Config(SampleInfo &sampleInfo)
    {
        OH\_AVFormat \*formatAudio = OH\_AVFormat\_CreateAudioFormat(sampleInfo.audioCodecMime.data(),
                                                                 sampleInfo.audioSampleRate, sampleInfo.audioChannelCount);
    
        OH\_AVFormat\_SetIntValue(formatAudio, OH\_MD\_KEY\_PROFILE, AAC\_PROFILE\_LC);
    
       _ // 内录编码音频轨道_
        int32\_t ret = OH\_AVMuxer\_AddTrack(muxer\_, &innerTrackId\_, formatAudio);
        if (ret != AV\_ERR\_OK) {
            OH\_LOG\_ERROR(LOG\_APP, "AddTrack failed");
        }
     _   // 外录音频编码轨道_
        ret = OH\_AVMuxer\_AddTrack(muxer\_, &micTrackId\_, formatAudio);
        if (ret != AV\_ERR\_OK) {
            OH\_LOG\_ERROR(LOG\_APP, "AddTrack failed");
        }
        OH\_AVFormat\_Destroy(formatAudio);
    
        return ret;
    }
    
3.  监听录屏数据获取回调[OH\_AVScreenCapture\_OnBufferAvailable](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-native-avscreen-capture-base-h#oh_avscreencapture_onbufferavailable)获取设备内部与麦克风采集的原始音频数据。
    
    void CAVScreenCaptureToStream::OnBufferAvailable(OH\_AVScreenCapture \*capture, OH\_AVBuffer \*buffer,
                                                     OH\_AVScreenCaptureBufferType bufferType, int64\_t timestamp,
                                                     void \*userData)
    {
        (void)capture;
        (void)timestamp;
        if (g\_isRunning) {
            CodecUserData \*codecUserData = static\_cast<CodecUserData \*>(userData);
            std::unique\_lock<std::mutex> lock(codecUserData->inputMutex);
            if (bufferType == OH\_AVScreenCaptureBufferType::OH\_SCREEN\_CAPTURE\_BUFFERTYPE\_AUDIO\_INNER) {
              _  // 内录音频原始数据，写入数据缓冲区_
                OH\_AVCodecBufferAttr attr;
                OH\_AVBuffer\_GetBufferAttr(buffer, &attr);
                uint8\_t \*addr = OH\_AVBuffer\_GetAddr(buffer);
                codecUserData->innerCache.WriteCache(addr, attr.size);
                OH\_AVBuffer\_Destroy(buffer);
            } else if (bufferType == OH\_AVScreenCaptureBufferType::OH\_SCREEN\_CAPTURE\_BUFFERTYPE\_AUDIO\_MIC) {
              _  // 外录音频原始数据，写入数据缓冲区_
                OH\_AVCodecBufferAttr attr;
                OH\_AVBuffer\_GetBufferAttr(buffer, &attr);
                uint8\_t \*addr = OH\_AVBuffer\_GetAddr(buffer);
                codecUserData->micCache.WriteCache(addr, attr.size);
                OH\_AVBuffer\_Destroy(buffer);
            }
        }
    }
    
4.  将内录与外录的原始音频数据通过内录与外录音频编码器分别编码，并通过媒体文件封装其写入对应的音频轨道。
    
    void CAVScreenCaptureToStream::AudioEncInputThread()
    {
        while (g\_isRunning) {
            if (!isStarted\_) {
                OH\_LOG\_INFO(LOG\_APP, "Audio encoder input thread out");
                break;
            }
            std::unique\_lock<std::mutex> lock(audioEncContext\_->inputMutex);
            audioEncContext\_->inputCond.wait\_for(lock, 1s);
            if (!isStarted\_) {
                OH\_LOG\_INFO(LOG\_APP, "Audio encoder input thread out");
                break;
            }
            if (audioEncContext\_->inputBufferInfoQueue.empty()) {
                OH\_LOG\_INFO(LOG\_APP, "Audio Buffer queue is empty");
                continue;
            }
            CodecBufferInfo bufferInfo = audioEncContext\_->inputBufferInfoQueue.front();
            if (bufferInfo.codec == audioEncoderInner\_->encoder\_) {
             _   // 从数据缓冲区中读取内录原始音频数据，写入内录音频编码器_
                if (audioEncContext\_->innerCache.remainLen < sampleInfo\_.audioMaxInputSize) {
                    continue;
                }
                audioEncContext\_->inputBufferInfoQueue.pop();
                uint8\_t \*inputBufferAddr = OH\_AVBuffer\_GetAddr(reinterpret\_cast<OH\_AVBuffer \*>(bufferInfo.buffer));
                audioEncContext\_->innerCache.ReadCache(inputBufferAddr, sampleInfo\_.audioMaxInputSize);
                lock.unlock();
                bufferInfo.attr.size = sampleInfo\_.audioMaxInputSize;
                bufferInfo.attr.flags = AVCODEC\_BUFFER\_FLAGS\_NONE;
                int32\_t ret = audioEncoderInner\_->PushInputData(bufferInfo);
                if (ret) {
                    OH\_LOG\_ERROR(LOG\_APP, "Push data failed, thread out");
                    break;
                }
            } else {
              _  // 从数据缓冲区中读取外录原始音频数据，写入外录音频编码器_
                if (audioEncContext\_->micCache.remainLen < sampleInfo\_.audioMaxInputSize) {
                    continue;
                }
                audioEncContext\_->inputBufferInfoQueue.pop();
                uint8\_t \*inputBufferAddr = OH\_AVBuffer\_GetAddr(reinterpret\_cast<OH\_AVBuffer \*>(bufferInfo.buffer));
                audioEncContext\_->micCache.ReadCache(inputBufferAddr, sampleInfo\_.audioMaxInputSize);
                lock.unlock();
                bufferInfo.attr.size = sampleInfo\_.audioMaxInputSize;
                bufferInfo.attr.flags = AVCODEC\_BUFFER\_FLAGS\_NONE;
                int32\_t ret = audioEncoderMic\_->PushInputData(bufferInfo);
                if (ret) {
                    OH\_LOG\_ERROR(LOG\_APP, "Push data failed, thread out");
                    break;
                }
            }
        }
    }
    
    void CAVScreenCaptureToStream::AudioEncOutputThread()
    {
        while (g\_isRunning) {
            if (!isStarted\_) {
                OH\_LOG\_INFO(LOG\_APP, "Audio encoder output thread out");
                break;
            }
            std::unique\_lock<std::mutex> lock(audioEncContext\_->outputMutex);
            audioEncContext\_->outputCond.wait\_for(lock, 1s);
    
            if (!isStarted\_) {
                OH\_LOG\_INFO(LOG\_APP, "Audio encoder output thread out");
                break;
            }
    
            if (audioEncContext\_->outputBufferInfoQueue.empty()) {
                OH\_LOG\_ERROR(LOG\_APP, "Buffer queue is empty");
                continue;
            }
    
            CodecBufferInfo bufferInfo = audioEncContext\_->outputBufferInfoQueue.front();
            audioEncContext\_->outputBufferInfoQueue.pop();
            lock.unlock();
            if (bufferInfo.codec == audioEncoderInner\_->encoder\_) {
             _   // 内录编码音频写入内录音频轨道_
                muxer\_->WriteSample(muxer\_->GetInnerTrackId(), reinterpret\_cast<OH\_AVBuffer \*>(bufferInfo.buffer),
                                    bufferInfo.attr);
                int32\_t ret = audioEncoderInner\_->FreeOutputData(bufferInfo.bufferIndex);
                if (ret) {
                    OH\_LOG\_ERROR(LOG\_APP, "Encoder output thread out");
                    break;
                }
            } else {
             _   // 外录编码音频流写入外录音频轨道_
                muxer\_->WriteSample(muxer\_->GetMicTrackId(), reinterpret\_cast<OH\_AVBuffer \*>(bufferInfo.buffer),
                                    bufferInfo.attr);
                int32\_t ret = audioEncoderMic\_->FreeOutputData(bufferInfo.bufferIndex);
                if (ret) {
                    OH\_LOG\_ERROR(LOG\_APP, "Encoder output thread out");
                    break;
                }
            }
        }
    }
    
5.  录制完成后，调用ffmpeg指令编辑录制生成的音频文件，混合内录和外录音频轨道，生成内录与外录混合后的音频文件。
    
    _/\*\*_
     _\* 调用ffmpeg指令混合两路音频_
     _\* @param inputFilePath 输入音频文件沙箱路径_
     _\* @param outputFilePath 输出音频文件沙箱路径_
     _\*/_
    async mixAudioTrack(inputFilePath: string, outputFilePath: string) {
      try {
        if (fileIo.accessSync(outputFilePath)) {
          fileIo.unlinkSync(outputFilePath);
        }
      } catch (error) {
        console.error(\`Failed to unlink output file, Cause: ${JSON.stringify(error)}\`);
        return;
      }
    
      let callback: ICallBack = {
        callBackResult: (code: number) => {
          if (code === 0) {
            console.info('Mixed audio succeed');
          } else {
            console.info('Mixed audio failed');
          }
        },
      };
      _// 混音命令，将原始音频文件的两路音频轨道合成一路，生成混音后的音频文件。_
      let cmd =
        \`ffmpeg -i ${inputFilePath} -filter\_complex "\[0:a:0\]volume=0.5\[a1\];\[0:a:1\]volume=1.0\[a2\];\[a1\]\[a2\]amix=inputs=2:duration=longest" ${outputFilePath}\`;
      MP4Parser.ffmpegCmd(cmd, callback);
    }
    

完整示例参考如下：

Index.ets:

import screenCapture from 'libentry.so';
import { Context } from '@kit.AbilityKit';
import { fileIo } from '@kit.CoreFileKit';
import { ICallBack, MP4Parser } from '@ohos/mp4parser';

@Entry
@Component
struct Index {
  filePath: string = '';
  recordFile?: fileIo.File;
  isRecording: boolean = false;

 _ /\*\*_
   _\* 调用ffmpeg指令混合两路音频_
   _\* @param inputFilePath 输入音频文件沙箱路径_
   _\* @param outputFilePath 输出音频文件沙箱路径_
   _\*/_
  async mixAudioTrack(inputFilePath: string, outputFilePath: string) {
    try {
      if (fileIo.accessSync(outputFilePath)) {
        fileIo.unlinkSync(outputFilePath);
      }
    } catch (error) {
      console.error(\`Failed to unlink output file, Cause: ${JSON.stringify(error)}\`);
      return;
    }

    let callback: ICallBack = {
      callBackResult: (code: number) => {
        if (code === 0) {
          console.info('Mixed audio succeed');
        } else {
          console.info('Mixed audio failed');
        }
      },
    };
   _ // 混音命令，将原始音频文件的两路音频轨道合成一路，生成混音后的音频文件。_
    let cmd =
      \`ffmpeg -i ${inputFilePath} -filter\_complex "\[0:a:0\]volume=0.5\[a1\];\[0:a:1\]volume=1.0\[a2\];\[a1\]\[a2\]amix=inputs=2:duration=longest" ${outputFilePath}\`;
    MP4Parser.ffmpegCmd(cmd, callback);
  }

  build() {
    Column({ space: 20 }) {
      Button('Start ScreenCapture')
        .fontSize(20)
        .onClick(() => {
          if (this.isRecording) {
            return;
          }
          try {
            let context = this.getUIContext().getHostContext() as Context;
            this.filePath = context.filesDir + \`/${new Date().getTime()}.m4a\`;
            this.recordFile = fileIo.openSync(this.filePath, fileIo.OpenMode.CREATE | fileIo.OpenMode.WRITE\_ONLY);
            screenCapture.startScreenCapture(this.recordFile.fd);
            this.isRecording = true;
          } catch (err) {
            console.error(\`Failed to start screen capture, ${JSON.stringify(err)}\`);
          }
        });

      Button('Stop ScreenCapture')
        .fontSize(20)
        .onClick(() => {
          if (!this.isRecording) {
            return;
          }
          try {
            screenCapture.stopScreenCapture();
            this.isRecording = false;
          } catch (err) {
            console.error(\`Failed to stop screen capture, ${JSON.stringify(err)}\`);
          } finally {
            if (this.recordFile) {
              fileIo.closeSync(this.recordFile.fd);
            }
          }
        });

      Button('Mix Audio')
        .fontSize(20)
        .onClick(() => {
          let context = this.getUIContext().getHostContext() as Context;
          let outputFilepath = context.filesDir + \`/output.m4a\`;
          this.mixAudioTrack(this.filePath, outputFilepath);
        });
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
    .alignItems(HorizontalAlign.Center);
  }
}

napi\_init.cpp:

_/\*_
 _\* Copyright (c) Huawei Technologies Co., Ltd. 2026-2026. All rights reserved._
 _\*/_
#include "./CAVScreenCaptureToStream/CAVScreenCaptureToStream.h"
#include "napi/native\_api.h"

static napi\_value StartScreenCaptureToStream(napi\_env env, napi\_callback\_info info)
{
    size\_t argc = 1;
    napi\_value args\[1\] = {nullptr};
    napi\_get\_cb\_info(env, info, &argc, args, nullptr, nullptr);

    int32\_t outputFd;
    napi\_get\_value\_int32(env, args\[0\], &outputFd);
    CAVScreenCaptureToStream::GetInstance().StartScreenCapture(outputFd);

    return nullptr;
}

static napi\_value StopScreenCaptureToStream(napi\_env env, napi\_callback\_info info)
{
    (void)env;
    (void)info;
    CAVScreenCaptureToStream::GetInstance().StopScreenCapture();
    return nullptr;
}

EXTERN\_C\_START
static napi\_value Init(napi\_env env, napi\_value exports)
{
    napi\_property\_descriptor desc\[\] = {
        {"startScreenCapture", nullptr, StartScreenCaptureToStream, nullptr, nullptr, nullptr, napi\_default, nullptr},
        {"stopScreenCapture", nullptr, StopScreenCaptureToStream, nullptr, nullptr, nullptr, napi\_default, nullptr},
    };
    napi\_define\_properties(env, exports, sizeof(desc) / sizeof(desc\[0\]), desc);
    return exports;
}
EXTERN\_C\_END

static napi\_module demoModule = {
    .nm\_version = 1,
    .nm\_flags = 0,
    .nm\_filename = nullptr,
    .nm\_register\_func = Init,
    .nm\_modname = "entry",
    .nm\_priv = ((void \*)0),
    .reserved = {0},
};

extern "C" \_\_attribute\_\_((constructor)) void RegisterEntryModule(void) { napi\_module\_register(&demoModule); }

SampleInfo.h:

_/\*_
 _\* Copyright (c) Huawei Technologies Co., Ltd. 2026-2026. All rights reserved._
 _\*/_
#ifndef AVCODEC\_SAMPLE\_INFO\_H
#define AVCODEC\_SAMPLE\_INFO\_H

#include "multimedia/player\_framework/native\_avbuffer.h"
#include "multimedia/player\_framework/native\_avcodec\_base.h"
#include <condition\_variable>
#include <cstdint>
#include <queue>
#include <string>

_/\*_
 _\* Screen recording configuration item_
 _\*/_
struct SampleInfo {
    int32\_t outputFd = -1;

    std::string audioCodecMime = "";
    int32\_t audioSampleFormat = 0;
    int32\_t audioSampleRate = 0;
    int32\_t audioChannelCount = 0;
    int32\_t audioBitRate = 0;
    int64\_t audioChannelLayout = 0;
    int32\_t audioMaxInputSize = 0;
};

_/\*_
 _\* Buffer Info_
 _\*/_
struct CodecBufferInfo {
    OH\_AVCodec \*codec;
    uint32\_t bufferIndex = 0;
    uintptr\_t \*buffer = nullptr;
    uint8\_t \*bufferAddr = nullptr;
    OH\_AVCodecBufferAttr attr = {0, 0, 0, AVCODEC\_BUFFER\_FLAGS\_NONE};

    CodecBufferInfo(uint32\_t argBufferIndex, OH\_AVBuffer \*argBuffer, OH\_AVCodec \*codec)
        : bufferIndex(argBufferIndex), buffer(reinterpret\_cast<uintptr\_t \*>(argBuffer)), codec(codec)
    {
        OH\_AVBuffer\_GetBufferAttr(argBuffer, &attr);
    };
};

struct BufferCache {
    std::vector<char> cache;
    int32\_t remainLen = 0;

    void ClearCache()
    {
        cache.clear();
        remainLen = 0;
    }

    void WriteCache(void \*buffer, int32\_t bufferLen)
    {
        if (bufferLen + remainLen > cache.size()) {
            cache.resize(remainLen + bufferLen);
        }
        for (int32\_t idx = 0; idx < bufferLen; ++idx) {
            cache.data()\[remainLen + idx\] = ((uint8\_t \*)buffer)\[idx\];
        }
        remainLen += bufferLen;
    }

    bool ReadCache(void \*buffer, int32\_t bufferLen)
    {
        if (remainLen < bufferLen) {
            return false;
        }
        for (int32\_t idx = 0; idx < bufferLen; ++idx) {
            ((uint8\_t \*)buffer)\[idx\] = cache.data()\[idx\];
        }
        remainLen = remainLen - bufferLen;
        if (remainLen > 0) {
            for (int32\_t idx = 0; idx < remainLen; ++idx) {
                cache.data()\[idx\] = cache.data()\[bufferLen + idx\];
            }
        }
        return true;
    }
};

_/\*_
 _\* Buffer Data_
 _\*/_
struct CodecUserData {
public:
    std::mutex inputMutex;
    std::condition\_variable inputCond;
    std::queue<CodecBufferInfo> inputBufferInfoQueue;

    std::mutex outputMutex;
    std::condition\_variable outputCond;
    std::queue<CodecBufferInfo> outputBufferInfoQueue;

    BufferCache innerCache;
    BufferCache micCache;
};

#endif

Muxer.h:

_/\*_
 _\* Copyright (c) Huawei Technologies Co., Ltd. 2026-2026. All rights reserved._
 _\*/_
#ifndef MUXER\_H
#define MUXER\_H

#include "SampleInfo.h"
#include "multimedia/player\_framework/native\_avmuxer.h"

class Muxer {
public:
    Muxer() = default;
    ~Muxer();

    int32\_t Create(int32\_t fd);
    int32\_t Config(SampleInfo &sampleInfo);
    int32\_t Start();
    int32\_t WriteSample(int32\_t trackId, OH\_AVBuffer \*buffer, OH\_AVCodecBufferAttr &attr);
    void Release();
    int32\_t GetInnerTrackId();
    int32\_t GetMicTrackId();

private:
    OH\_AVMuxer \*muxer\_ = nullptr;
    int32\_t innerTrackId\_ = -1;
    int32\_t micTrackId\_ = -1;
    std::mutex writeMutex\_;
};

#endif

Muxer.cpp:

_/\*_
 _\* Copyright (c) Huawei Technologies Co., Ltd. 2026-2026. All rights reserved._
 _\*/_
#include "Muxer.h"
#include <cstdint>
#include <hilog/log.h>

#undef LOG\_TAG
#define LOG\_TAG "Muxer"

Muxer::~Muxer() { Release(); }

_/\*_
 _\* Create an encapsulate instance object and set the encapsulation format to mp4_
 _\*/_
int32\_t Muxer::Create(int32\_t fd)
{
    muxer\_ = OH\_AVMuxer\_Create(fd, AV\_OUTPUT\_FORMAT\_M4A);
    if (muxer\_ == nullptr) {
        return -1;
    }
    return 0;
}

int32\_t Muxer::Config(SampleInfo &sampleInfo)
{
    OH\_AVFormat \*formatAudio = OH\_AVFormat\_CreateAudioFormat(sampleInfo.audioCodecMime.data(),
                                                             sampleInfo.audioSampleRate, sampleInfo.audioChannelCount);

    OH\_AVFormat\_SetIntValue(formatAudio, OH\_MD\_KEY\_PROFILE, AAC\_PROFILE\_LC);

  _  // 内录编码音频轨道_
    int32\_t ret = OH\_AVMuxer\_AddTrack(muxer\_, &innerTrackId\_, formatAudio);
    if (ret != AV\_ERR\_OK) {
        OH\_LOG\_ERROR(LOG\_APP, "AddTrack failed");
    }
   _ // 外录音频编码轨道_
    ret = OH\_AVMuxer\_AddTrack(muxer\_, &micTrackId\_, formatAudio);
    if (ret != AV\_ERR\_OK) {
        OH\_LOG\_ERROR(LOG\_APP, "AddTrack failed");
    }
    OH\_AVFormat\_Destroy(formatAudio);

    return ret;
}

int32\_t Muxer::Start()
{
    int ret = OH\_AVMuxer\_Start(muxer\_);
    return ret;
}

int32\_t Muxer::WriteSample(int32\_t trackId, OH\_AVBuffer \*buffer, OH\_AVCodecBufferAttr &attr)
{
    std::lock\_guard<std::mutex> lock(writeMutex\_);

    int32\_t ret = OH\_AVBuffer\_SetBufferAttr(buffer, &attr);

    ret = OH\_AVMuxer\_WriteSampleBuffer(muxer\_, trackId, buffer);
    return ret;
}

void Muxer::Release()
{
    if (muxer\_ != nullptr) {
        OH\_AVMuxer\_Destroy(muxer\_);
        muxer\_ = nullptr;
    }
}

int32\_t Muxer::GetInnerTrackId() { return innerTrackId\_; }

int32\_t Muxer::GetMicTrackId() { return micTrackId\_; }

AudioEncoder.h:

_/\*_
 _\* Copyright (c) Huawei Technologies Co., Ltd. 2026-2026. All rights reserved._
 _\*/_
_/\*_
 _\* Copyright (c) 2025 Huawei Device Co., Ltd._
 _\* Licensed under the Apache License, Version 2.0 (the "License");_
 _\* you may not use this file except in compliance with the License._
 _\* You may obtain a copy of the License at_
 _\*_
 _\*     http://www.apache.org/licenses/LICENSE-2.0_
 _\*_
 _\* Unless required by applicable law or agreed to in writing, software_
 _\* distributed under the License is distributed on an "AS IS" BASIS,_
 _\* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied._
 _\* See the License for the specific language governing permissions and_
 _\* limitations under the License._
 _\*/_

#ifndef AVSCREENCAPTURESCREENRECORD\_AUDIOENCODER\_H
#define AVSCREENCAPTURESCREENRECORD\_AUDIOENCODER\_H
#include "CAVScreenCaptureToStream/SampleInfo.h"
#include <multimedia/player\_framework/native\_avmuxer.h>
#include <string>

class AudioEncoder {
public:
    OH\_AVCodec \*encoder\_;
    int32\_t Create(const std::string &codecMime);
    int32\_t Config(SampleInfo &sampleInfo, CodecUserData \*codecUserData);
    void Stop();
    void Release();
    void Start();
    int32\_t PushInputData(CodecBufferInfo &info);
    int32\_t FreeOutputData(uint32\_t bufferIndex);

private:
    static void OnCodecError(OH\_AVCodec \*codec, int32\_t errorCode, void \*userData);
    static void OnCodecFormatChange(OH\_AVCodec \*codec, OH\_AVFormat \*format, void \*userData);
    static void OnNeedInputBuffer(OH\_AVCodec \*codec, uint32\_t index, OH\_AVBuffer \*buffer, void \*userData);
    static void OnNewOutputBuffer(OH\_AVCodec \*codec, uint32\_t index, OH\_AVBuffer \*buffer, void \*userData);
};
#endif

AudioEncoder.cpp:

_/\*_
 _\* Copyright (c) Huawei Technologies Co., Ltd. 2026-2026. All rights reserved._
 _\*/_
#include "AudioEncoder.h"
#include "hilog/log.h"
#include <multimedia/player\_framework/native\_avcodec\_audiocodec.h>
#include <mutex>

#undef LOG\_TAG
#define LOG\_TAG "AudioEncoder"

int32\_t AudioEncoder::Create(const std::string &codecMime)
{
    encoder\_ = OH\_AudioCodec\_CreateByMime(codecMime.c\_str(), true);
    return 0;
}

_/\*_
 _\* Configure audio encoder_
 _\*/_
int32\_t AudioEncoder::Config(SampleInfo &sampleInfo, CodecUserData \*codecUserData)
{
    OH\_AVFormat \*format = OH\_AVFormat\_Create();
    if (format == nullptr) {
        OH\_LOG\_ERROR(LOG\_APP, "AVFormat create failed");
    }

    OH\_AVFormat\_SetIntValue(format, OH\_MD\_KEY\_AUDIO\_SAMPLE\_FORMAT, sampleInfo.audioSampleFormat);
    OH\_AVFormat\_SetIntValue(format, OH\_MD\_KEY\_AUD\_CHANNEL\_COUNT, sampleInfo.audioChannelCount);
    OH\_AVFormat\_SetIntValue(format, OH\_MD\_KEY\_AUD\_SAMPLE\_RATE, sampleInfo.audioSampleRate);
    OH\_AVFormat\_SetLongValue(format, OH\_MD\_KEY\_BITRATE, sampleInfo.audioBitRate);
    OH\_AVFormat\_SetLongValue(format, OH\_MD\_KEY\_CHANNEL\_LAYOUT, sampleInfo.audioChannelLayout);
    OH\_AVFormat\_SetIntValue(format, OH\_MD\_KEY\_MAX\_INPUT\_SIZE, sampleInfo.audioMaxInputSize);

    int ret = OH\_AudioCodec\_Configure(encoder\_, format);
    if (ret != AV\_ERR\_OK) {
        OH\_LOG\_ERROR(LOG\_APP, "Config failed, ret: %{public}d", ret);
    }
    OH\_AVFormat\_Destroy(format);
    format = nullptr;

   _ // SetCallback for audio encoder_
    OH\_AudioCodec\_RegisterCallback(encoder\_,
                                   {AudioEncoder::OnCodecError, AudioEncoder::OnCodecFormatChange,
                                    AudioEncoder::OnNeedInputBuffer, AudioEncoder::OnNewOutputBuffer},
                                   codecUserData);

  _  // Prepare audio encoder_
    ret = OH\_AudioCodec\_Prepare(encoder\_);
    if (ret != AV\_ERR\_OK) {
        OH\_LOG\_ERROR(LOG\_APP, "Prepare failed, ret: %{public}d", ret);
    }
    return 0;
}

void AudioEncoder::Start()
{
    int ret = OH\_AudioCodec\_Start(encoder\_);
    if (ret != AV\_ERR\_OK) {
        OH\_LOG\_ERROR(LOG\_APP, "Start failed, ret: %{public}d", ret);
    }
}

int32\_t AudioEncoder::PushInputData(CodecBufferInfo &info)
{
    int32\_t ret = OH\_AVBuffer\_SetBufferAttr(reinterpret\_cast<OH\_AVBuffer \*>(info.buffer), &info.attr);
    if (ret != AV\_ERR\_OK) {
        OH\_LOG\_ERROR(LOG\_APP, "Set av buffer attr failed");
    }
    ret = OH\_AudioCodec\_PushInputBuffer(encoder\_, info.bufferIndex);
    if (ret != AV\_ERR\_OK) {
        OH\_LOG\_ERROR(LOG\_APP, "Push input data failed");
    }
    return ret;
}

int32\_t AudioEncoder::FreeOutputData(uint32\_t bufferIndex)
{
    int32\_t ret = OH\_AudioCodec\_FreeOutputBuffer(encoder\_, bufferIndex);
    if (ret != AV\_ERR\_OK) {
        OH\_LOG\_ERROR(LOG\_APP, "Free output data failed");
    }
    return ret;
}

void AudioEncoder::Stop()
{
    int ret = OH\_AudioCodec\_Flush(encoder\_);
    if (ret != AV\_ERR\_OK) {
        OH\_LOG\_ERROR(LOG\_APP, "Flush failed, ret: %{public}d", ret);
    }
    ret = OH\_AudioCodec\_Stop(encoder\_);
    if (ret != AV\_ERR\_OK) {
        OH\_LOG\_ERROR(LOG\_APP, "Stop failed, ret: %{public}d", ret);
    }
}

void AudioEncoder::Release()
{
    if (encoder\_ != nullptr) {
        OH\_AudioCodec\_Flush(encoder\_);
        OH\_AudioCodec\_Stop(encoder\_);
        OH\_AudioCodec\_Destroy(encoder\_);
        encoder\_ = nullptr;
    }
}

void AudioEncoder::OnCodecError(OH\_AVCodec \*codec, int32\_t errorCode, void \*userData)
{
    (void)codec;
    (void)errorCode;
    (void)userData;
}

void AudioEncoder::OnCodecFormatChange(OH\_AVCodec \*codec, OH\_AVFormat \*format, void \*userData)
{
    (void)codec;
    (void)format;
    (void)userData;
}

void AudioEncoder::OnNeedInputBuffer(OH\_AVCodec \*codec, uint32\_t index, OH\_AVBuffer \*buffer, void \*userData)
{
    if (userData == nullptr) {
        return;
    }
    CodecUserData \*codecUserData = static\_cast<CodecUserData \*>(userData);
    std::unique\_lock<std::mutex> lock(codecUserData->inputMutex);
    (void)lock;

    codecUserData->inputBufferInfoQueue.emplace(index, buffer, codec);
    codecUserData->inputCond.notify\_all();
}

void AudioEncoder::OnNewOutputBuffer(OH\_AVCodec \*codec, uint32\_t index, OH\_AVBuffer \*buffer, void \*userData)
{
    if (userData == nullptr) {
        return;
    }
    CodecUserData \*codecUserData = static\_cast<CodecUserData \*>(userData);
    std::unique\_lock<std::mutex> lock(codecUserData->outputMutex);
    (void)lock;

    codecUserData->outputBufferInfoQueue.emplace(index, buffer, codec);
    codecUserData->outputCond.notify\_all();
}

CAVScreenCaptureToStream.h:

_/\*_
 _\* Copyright (c) Huawei Technologies Co., Ltd. 2026-2026. All rights reserved._
 _\*/_
#ifndef AVSCREENCAPTURESCREENRECORD\_CAVSCREENCAPTURETOSTREAM\_H
#define AVSCREENCAPTURESCREENRECORD\_CAVSCREENCAPTURETOSTREAM\_H
#include "CAVScreenCaptureToStream/AudioEncoder.h"
#include "CAVScreenCaptureToStream/Muxer.h"
#include "CAVScreenCaptureToStream/SampleInfo.h"
#include <memory>
#include <multimedia/player\_framework/native\_avscreen\_capture\_base.h>
#include <thread>

class CAVScreenCaptureToStream {
private:
    static void StopScreenCaptureRecording(struct OH\_AVScreenCapture \*capture);
    void InitAVScreenCapture();

    static void OnErrorToStream(OH\_AVScreenCapture \*capture, int32\_t errorCode, void \*userData);
    static void OnBufferAvailable(OH\_AVScreenCapture \*capture, OH\_AVBuffer \*buffer,
                                  OH\_AVScreenCaptureBufferType bufferType, int64\_t timestamp, void \*userData);
    static void OnSurfaceStateChange(struct OH\_AVScreenCapture \*capture, OH\_AVScreenCaptureStateCode stateCode,
                                     void \*userData);

    void InitMuxerAndEncoder(int32\_t outputFd);
    void StartMuxerAndEncoder();
    void CreateEncoder();
    void Release();

    void AudioEncInputThread();
    void AudioEncOutputThread();

    std::unique\_ptr<std::thread> audioEncInputThread\_ = nullptr;
    std::unique\_ptr<std::thread> audioEncOutputThread\_ = nullptr;
    std::unique\_ptr<Muxer> muxer\_ = nullptr;

    SampleInfo sampleInfo\_;
    std::atomic<bool> isStarted\_{false};
    CodecUserData \*audioEncContext\_ = nullptr;

public:
    CAVScreenCaptureToStream(){};
    ~CAVScreenCaptureToStream();

    static CAVScreenCaptureToStream &GetInstance()
    {
        static CAVScreenCaptureToStream avScreenCapture;
        return avScreenCapture;
    }

    void StartScreenCapture(int32\_t outputFd);
    void StopScreenCapture();
};
#endif

CAVScreenCaptureToStream.cpp:

_/\*_
 _\* Copyright (c) Huawei Technologies Co., Ltd. 2026-2026. All rights reserved._
 _\*/_
#include "CAVScreenCaptureToStream.h"
#include "CAVScreenCaptureToStream/Muxer.h"
#include "SampleInfo.h"
#include "hilog/log.h"
#include <multimedia/player\_framework/native\_avcapability.h>
#include <multimedia/player\_framework/native\_avcodec\_base.h>
#include <multimedia/player\_framework/native\_avcodec\_videoencoder.h>
#include <multimedia/player\_framework/native\_avscreen\_capture.h>
#include <multimedia/player\_framework/native\_avscreen\_capture\_errors.h>
#include <multimedia/native\_audio\_channel\_layout.h>
#include <thread>

#undef LOG\_DOMAIN
#undef LOG\_TAG
#define LOG\_DOMAIN 0x3200
#define LOG\_TAG "CAVSCREENCAPTURETOSTREAM"

using namespace std;

bool g\_isRunning = false;

OH\_AVScreenCapture \*g\_avCapture = nullptr;
std::unique\_ptr<AudioEncoder> audioEncoderInner\_ = nullptr;
std::unique\_ptr<AudioEncoder> audioEncoderMic\_ = nullptr;

CAVScreenCaptureToStream::~CAVScreenCaptureToStream() { Release(); }

_/\*_
 _\* Screen recording Error callback_
 _\*/_
void CAVScreenCaptureToStream::OnErrorToStream(OH\_AVScreenCapture \*capture, int32\_t errorCode, void \*userData)
{
    (void)capture;
    (void)userData;
    OH\_LOG\_ERROR(LOG\_APP, "ScreenCapture OnError errorCode is %{public}d", errorCode);
}

void CAVScreenCaptureToStream::OnBufferAvailable(OH\_AVScreenCapture \*capture, OH\_AVBuffer \*buffer,
                                                 OH\_AVScreenCaptureBufferType bufferType, int64\_t timestamp,
                                                 void \*userData)
{
    (void)capture;
    (void)timestamp;
    if (g\_isRunning) {
        CodecUserData \*codecUserData = static\_cast<CodecUserData \*>(userData);
        std::unique\_lock<std::mutex> lock(codecUserData->inputMutex);
        if (bufferType == OH\_AVScreenCaptureBufferType::OH\_SCREEN\_CAPTURE\_BUFFERTYPE\_AUDIO\_INNER) {
         _   // 内录音频原始数据，写入数据缓冲区_
            OH\_AVCodecBufferAttr attr;
            OH\_AVBuffer\_GetBufferAttr(buffer, &attr);
            uint8\_t \*addr = OH\_AVBuffer\_GetAddr(buffer);
            codecUserData->innerCache.WriteCache(addr, attr.size);
            OH\_AVBuffer\_Destroy(buffer);
        } else if (bufferType == OH\_AVScreenCaptureBufferType::OH\_SCREEN\_CAPTURE\_BUFFERTYPE\_AUDIO\_MIC) {
          _  // 外录音频原始数据，写入数据缓冲区_
            OH\_AVCodecBufferAttr attr;
            OH\_AVBuffer\_GetBufferAttr(buffer, &attr);
            uint8\_t \*addr = OH\_AVBuffer\_GetAddr(buffer);
            codecUserData->micCache.WriteCache(addr, attr.size);
            OH\_AVBuffer\_Destroy(buffer);
        }
    }
}

_/\*_
 _\* Screen recording state change callback_
 _\*/_
void CAVScreenCaptureToStream::OnSurfaceStateChange(struct OH\_AVScreenCapture \*capture,
                                                    OH\_AVScreenCaptureStateCode stateCode, void \*userData)
{
    (void)userData;
    switch (stateCode) {
    case OH\_SCREEN\_CAPTURE\_STATE\_STOPPED\_BY\_USER: {
        OH\_LOG\_INFO(LOG\_APP, "ScreenCapture OnStateChange OH\_SCREEN\_CAPTURE\_STATE\_STOPPED\_BY\_USER");
        break;
    }
    case OH\_SCREEN\_CAPTURE\_STATE\_CANCELED: {
        OH\_LOG\_INFO(LOG\_APP, "ScreenCapture OnStateChange OH\_SCREEN\_CAPTURE\_STATE\_CANCELED ");
        StopScreenCaptureRecording(capture);
        break;
    }
    case OH\_SCREEN\_CAPTURE\_STATE\_STARTED: {
        int ret = OH\_AVScreenCapture\_SetMaxVideoFrameRate(g\_avCapture, 30);
        OH\_LOG\_INFO(LOG\_APP, "OH\_AVScreenCapture\_SetMaxVideoFrameRate result: %{public}d", ret);
        OH\_LOG\_INFO(LOG\_APP, "ScreenCapture OnStateChange started OH\_SCREEN\_CAPTURE\_STATE\_STARTED");
        break;
    }
    case OH\_SCREEN\_CAPTURE\_STATE\_STOPPED\_BY\_CALL: {
        OH\_LOG\_INFO(LOG\_APP, "ScreenCapture OH\_SCREEN\_CAPTURE\_STATE\_STOPPED\_BY\_CALL");
        break;
    }
    case OH\_SCREEN\_CAPTURE\_STATE\_INTERRUPTED\_BY\_OTHER: {
        OH\_LOG\_INFO(LOG\_APP, "ScreenCapture OH\_SCREEN\_CAPTURE\_STATE\_INTERRUPTED\_BY\_OTHER");
        break;
    }
    case OH\_SCREEN\_CAPTURE\_STATE\_MIC\_UNMUTED\_BY\_USER: {
        OH\_LOG\_INFO(LOG\_APP, "ScreenCapture OH\_SCREEN\_CAPTURE\_STATE\_MIC\_MUTED\_BY\_USER");
        break;
    }
    case OH\_SCREEN\_CAPTURE\_STATE\_ENTER\_PRIVATE\_SCENE: {
        OH\_LOG\_INFO(LOG\_APP, "ScreenCapture OnStateChange OH\_SCREEN\_CAPTURE\_STATE\_ENTER\_PRIVATE\_SCENE");
        break;
    }
    case OH\_SCREEN\_CAPTURE\_STATE\_EXIT\_PRIVATE\_SCENE: {
        OH\_LOG\_INFO(LOG\_APP, "ScreenCapture OH\_SCREEN\_CAPTURE\_STATE\_EXIT\_PRIVATE\_SCENE");
        break;
    }
    default:
        break;
    }
}

_/\*_
 _\* Configure screen recording parameters_
 _\*/_
void CAVScreenCaptureToStream::InitAVScreenCapture()
{
    if (g\_avCapture != nullptr) {
        StopScreenCaptureRecording(g\_avCapture);
    }

    g\_avCapture = OH\_AVScreenCapture\_Create();
    if (g\_avCapture == nullptr) {
        OH\_LOG\_ERROR(LOG\_APP, "create screen capture failed");
    }
    OH\_LOG\_INFO(LOG\_APP, "ScreenCapture after create sc");

   _ // Set callback_
    OH\_AVScreenCapture\_SetErrorCallback(g\_avCapture, OnErrorToStream, nullptr);
    OH\_AVScreenCapture\_SetStateCallback(g\_avCapture, OnSurfaceStateChange, nullptr);

    OH\_AVScreenCapture\_SetMicrophoneEnabled(g\_avCapture, true);
    OH\_AVScreenCapture\_SetDataCallback(g\_avCapture, OnBufferAvailable, audioEncContext\_);
    OH\_AVScreenCapture\_SetCanvasRotation(g\_avCapture, true);

    OH\_AVScreenCaptureConfig config;
   _ // 麦克风音频采集参数_
    OH\_AudioCaptureInfo micCapInfo = {.audioSampleRate = 48000, .audioChannels = 2, .audioSource = OH\_MIC};
  _  // 内录音频采集参数_
    OH\_AudioCaptureInfo innerCapInfo = {.audioSampleRate = 48000, .audioChannels = 2, .audioSource = OH\_ALL\_PLAYBACK};
  _  // 音频输出规格_
    OH\_AudioEncInfo audioEncInfo = {.audioBitrate = 96000, .audioCodecformat = OH\_AudioCodecFormat::OH\_AAC\_LC};
  _  // 配置音频采集参数_
    OH\_AudioInfo audioInfo = {.micCapInfo = micCapInfo, .innerCapInfo = innerCapInfo, .audioEncInfo = audioEncInfo};
   _ // 录屏配置信息_
    config = {
        .captureMode = OH\_CAPTURE\_HOME\_SCREEN,
        .dataType = OH\_ORIGINAL\_STREAM,
        .audioInfo = audioInfo,
    };

    int result = OH\_AVScreenCapture\_Init(g\_avCapture, config);
    if (result != AV\_SCREEN\_CAPTURE\_ERR\_OK) {
        OH\_LOG\_INFO(LOG\_APP, "ScreenCapture OH\_AVScreenCapture\_Init failed %{public}d", result);
    }
    OH\_LOG\_INFO(LOG\_APP, "ScreenCapture OH\_AVScreenCapture\_Init %{public}d", result);
}

void CAVScreenCaptureToStream::StopScreenCaptureRecording(struct OH\_AVScreenCapture \*capture)
{
    if (g\_isRunning && capture != nullptr) {
        OH\_AVScreenCapture\_StopScreenCapture(capture);
        g\_isRunning = false;
        OH\_LOG\_INFO(LOG\_APP, "CAVScreenCaptureToStream ScreenCapture StopScreenCapture");
    }
}

_/\*_
 _\* Create audio and video encoder_
 _\*/_
void CAVScreenCaptureToStream::CreateEncoder()
{
    audioEncContext\_ = new CodecUserData;
    audioEncoderInner\_->Create(sampleInfo\_.audioCodecMime);
    audioEncoderInner\_->Config(sampleInfo\_, audioEncContext\_);
    audioEncoderMic\_->Create(sampleInfo\_.audioCodecMime);
    audioEncoderMic\_->Config(sampleInfo\_, audioEncContext\_);
}

_/\*_
 _\* Initialize configuration information_
 _\*/_
void CAVScreenCaptureToStream::InitMuxerAndEncoder(int32\_t outputFd)
{
    sampleInfo\_.outputFd = outputFd;

   _ // 音频编码参数_
    sampleInfo\_.audioCodecMime = OH\_AVCODEC\_MIMETYPE\_AUDIO\_AAC;
    sampleInfo\_.audioSampleFormat = OH\_BitsPerSample::SAMPLE\_S16LE;
    sampleInfo\_.audioSampleRate = 48000;
    sampleInfo\_.audioChannelCount = 2;
    sampleInfo\_.audioBitRate = 96000;
    sampleInfo\_.audioChannelLayout = OH\_AudioChannelLayout::CH\_LAYOUT\_STEREO;
    sampleInfo\_.audioMaxInputSize = sampleInfo\_.audioSampleRate \* 0.02 \* sampleInfo\_.audioChannelCount \* sizeof(short);
   _ // 音频编码器_
    audioEncoderInner\_ = std::make\_unique<AudioEncoder>(); _// 内录音频编码器_
    audioEncoderMic\_ = std::make\_unique<AudioEncoder>();  _ // 外录音频编码器_
   _ // 媒体文件封装器_
    muxer\_ = std::make\_unique<Muxer>();
    muxer\_->Create(sampleInfo\_.outputFd);
    muxer\_->Config(sampleInfo\_);

    CreateEncoder();
}

void CAVScreenCaptureToStream::StartMuxerAndEncoder()
{
  _  // Start muxer_
    muxer\_->Start();

    isStarted\_ = true;

    if (audioEncContext\_) {
     _   // Start audio Encoder_
        audioEncoderInner\_->Start();
        audioEncoderMic\_->Start();
        isStarted\_ = true;
      _  // Start the audio input and output threads_
        audioEncInputThread\_ = std::make\_unique<std::thread>(&CAVScreenCaptureToStream::AudioEncInputThread, this);
        audioEncOutputThread\_ = std::make\_unique<std::thread>(&CAVScreenCaptureToStream::AudioEncOutputThread, this);
        if (audioEncInputThread\_ == nullptr || audioEncOutputThread\_ == nullptr) {
            Release();
            return;
        }

       _ // Empty the playback cache queue_
        if (audioEncContext\_ != nullptr) {
            audioEncContext\_->innerCache.ClearCache();
            audioEncContext\_->micCache.ClearCache();
        }
    }
}

void CAVScreenCaptureToStream::StartScreenCapture(int32\_t outputFd)
{
    if (g\_isRunning) {
        OH\_LOG\_ERROR(LOG\_APP, "Screen Capture is running");
        return;
    }

    InitMuxerAndEncoder(outputFd);

    InitAVScreenCapture();

    g\_isRunning = true;

    StartMuxerAndEncoder();

    int result = OH\_AVScreenCapture\_StartScreenCapture(g\_avCapture);
    OH\_LOG\_INFO(LOG\_APP, "OH\_VideoEncoder\_Start Started %{public}d", result);
    if (result != AV\_SCREEN\_CAPTURE\_ERR\_OK) {
        OH\_LOG\_INFO(LOG\_APP, "ScreenCapture Started failed %{public}d", result);
        OH\_AVScreenCapture\_Release(g\_avCapture);
    }
}

_/\*_
 _\* Stop screen recording_
 _\*/_
void CAVScreenCaptureToStream::StopScreenCapture()
{
    if (g\_isRunning) {
        int32\_t ret = OH\_AVScreenCapture\_StopScreenCapture(g\_avCapture);
        if (ret != AV\_SCREEN\_CAPTURE\_ERR\_BASE) {
            OH\_LOG\_ERROR(LOG\_APP, "StopScreenCapture OH\_AVScreenCapture\_StopScreenCapture Result: %{public}d", ret);
        } else {
            OH\_LOG\_INFO(LOG\_APP, "StopScreenCapture OH\_AVScreenCapture\_StopScreenCapture");
        }
        ret = OH\_AVScreenCapture\_Release(g\_avCapture);
        if (ret != AV\_SCREEN\_CAPTURE\_ERR\_BASE) {
            OH\_LOG\_ERROR(LOG\_APP, "StopScreenCapture OH\_AVScreenCapture\_Release: %{public}d", ret);
        } else {
            OH\_LOG\_INFO(LOG\_APP, "OH\_AVScreenCapture\_Release success");
        }
        g\_isRunning = false;
        g\_avCapture = nullptr;
        Release();
    }
}

_/\*_
 _\* Release_
 _\*/_
void CAVScreenCaptureToStream::Release()
{
    isStarted\_ = false;
    if (audioEncInputThread\_ && audioEncInputThread\_->joinable()) {
        audioEncContext\_->inputCond.notify\_all();
        audioEncInputThread\_->join();
        audioEncInputThread\_.reset();
    }
    if (audioEncOutputThread\_ && audioEncOutputThread\_->joinable()) {
        audioEncContext\_->outputCond.notify\_all();
        audioEncOutputThread\_->join();
        audioEncOutputThread\_.reset();
    }
    if (muxer\_ != nullptr) {
        muxer\_->Release();
        muxer\_.reset();
        OH\_LOG\_INFO(LOG\_APP, "Muxer release successful");
    }
    if (audioEncoderMic\_ != nullptr) {
        audioEncoderMic\_->Stop();
        audioEncoderMic\_->Release();
        audioEncoderMic\_.reset();
        OH\_LOG\_INFO(LOG\_APP, "Audio encoder release successful");
    }
    if (audioEncoderInner\_ != nullptr) {
        audioEncoderInner\_->Stop();
        audioEncoderInner\_->Release();
        audioEncoderInner\_.reset();
        OH\_LOG\_INFO(LOG\_APP, "Audio encoder release successful");
    }
    if (audioEncContext\_ != nullptr) {
        delete audioEncContext\_;
        audioEncContext\_ = nullptr;
    }
    OH\_LOG\_INFO(LOG\_APP, "Release successful");
}

void CAVScreenCaptureToStream::AudioEncInputThread()
{
    while (g\_isRunning) {
        if (!isStarted\_) {
            OH\_LOG\_INFO(LOG\_APP, "Audio encoder input thread out");
            break;
        }
        std::unique\_lock<std::mutex> lock(audioEncContext\_->inputMutex);
        audioEncContext\_->inputCond.wait\_for(lock, 1s);
        if (!isStarted\_) {
            OH\_LOG\_INFO(LOG\_APP, "Audio encoder input thread out");
            break;
        }
        if (audioEncContext\_->inputBufferInfoQueue.empty()) {
            OH\_LOG\_INFO(LOG\_APP, "Audio Buffer queue is empty");
            continue;
        }
        CodecBufferInfo bufferInfo = audioEncContext\_->inputBufferInfoQueue.front();
        if (bufferInfo.codec == audioEncoderInner\_->encoder\_) {
          _  // 从数据缓冲区中读取内录原始音频数据，写入内录音频编码器_
            if (audioEncContext\_->innerCache.remainLen < sampleInfo\_.audioMaxInputSize) {
                continue;
            }
            audioEncContext\_->inputBufferInfoQueue.pop();
            uint8\_t \*inputBufferAddr = OH\_AVBuffer\_GetAddr(reinterpret\_cast<OH\_AVBuffer \*>(bufferInfo.buffer));
            audioEncContext\_->innerCache.ReadCache(inputBufferAddr, sampleInfo\_.audioMaxInputSize);
            lock.unlock();
            bufferInfo.attr.size = sampleInfo\_.audioMaxInputSize;
            bufferInfo.attr.flags = AVCODEC\_BUFFER\_FLAGS\_NONE;
            int32\_t ret = audioEncoderInner\_->PushInputData(bufferInfo);
            if (ret) {
                OH\_LOG\_ERROR(LOG\_APP, "Push data failed, thread out");
                break;
            }
        } else {
         _   // 从数据缓冲区中读取外录原始音频数据，写入外录音频编码器_
            if (audioEncContext\_->micCache.remainLen < sampleInfo\_.audioMaxInputSize) {
                continue;
            }
            audioEncContext\_->inputBufferInfoQueue.pop();
            uint8\_t \*inputBufferAddr = OH\_AVBuffer\_GetAddr(reinterpret\_cast<OH\_AVBuffer \*>(bufferInfo.buffer));
            audioEncContext\_->micCache.ReadCache(inputBufferAddr, sampleInfo\_.audioMaxInputSize);
            lock.unlock();
            bufferInfo.attr.size = sampleInfo\_.audioMaxInputSize;
            bufferInfo.attr.flags = AVCODEC\_BUFFER\_FLAGS\_NONE;
            int32\_t ret = audioEncoderMic\_->PushInputData(bufferInfo);
            if (ret) {
                OH\_LOG\_ERROR(LOG\_APP, "Push data failed, thread out");
                break;
            }
        }
    }
}

void CAVScreenCaptureToStream::AudioEncOutputThread()
{
    while (g\_isRunning) {
        if (!isStarted\_) {
            OH\_LOG\_INFO(LOG\_APP, "Audio encoder output thread out");
            break;
        }
        std::unique\_lock<std::mutex> lock(audioEncContext\_->outputMutex);
        audioEncContext\_->outputCond.wait\_for(lock, 1s);

        if (!isStarted\_) {
            OH\_LOG\_INFO(LOG\_APP, "Audio encoder output thread out");
            break;
        }

        if (audioEncContext\_->outputBufferInfoQueue.empty()) {
            OH\_LOG\_ERROR(LOG\_APP, "Buffer queue is empty");
            continue;
        }

        CodecBufferInfo bufferInfo = audioEncContext\_->outputBufferInfoQueue.front();
        audioEncContext\_->outputBufferInfoQueue.pop();
        lock.unlock();
        if (bufferInfo.codec == audioEncoderInner\_->encoder\_) {
       _     // 内录编码音频写入内录音频轨道_
            muxer\_->WriteSample(muxer\_->GetInnerTrackId(), reinterpret\_cast<OH\_AVBuffer \*>(bufferInfo.buffer),
                                bufferInfo.attr);
            int32\_t ret = audioEncoderInner\_->FreeOutputData(bufferInfo.bufferIndex);
            if (ret) {
                OH\_LOG\_ERROR(LOG\_APP, "Encoder output thread out");
                break;
            }
        } else {
          _  // 外录编码音频流写入外录音频轨道_
            muxer\_->WriteSample(muxer\_->GetMicTrackId(), reinterpret\_cast<OH\_AVBuffer \*>(bufferInfo.buffer),
                                bufferInfo.attr);
            int32\_t ret = audioEncoderMic\_->FreeOutputData(bufferInfo.bufferIndex);
            if (ret) {
                OH\_LOG\_ERROR(LOG\_APP, "Encoder output thread out");
                break;
            }
        }
    }
}