* Copyright (c) 2026 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.
*/
#include "CAVScreenCaptureToStream.h"
#include "CAVScreenCaptureToStream/Muxer.h"
#include "SampleInfo.h"
#include "VideoEncoder.h"
#include "hilog/log.h"
#include <js_native_api.h>
#include <multimedia/native_audio_channel_layout.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 <node_api.h>
#include <node_api_types.h>
#include <thread>
#undef LOG_DOMAIN
#undef LOG_TAG
#define LOG_DOMAIN 0x3200
#define LOG_TAG "CAVSCREENCAPTURETOSTREAM"
using namespace std;
namespace {
constexpr int MICROSECOND = 1000;
constexpr int CHANNELS_COUNT = 2;
constexpr int SAMPLE_RATE = 48000;
constexpr int SAMPLE_BITRATE = 10000000;
constexpr int AUDIO_BITRATE = 96000;
constexpr float AUDIO_SAMPLE_RATE_NUM = 0.02;
constexpr int FRAME_RATE = 30;
}
bool isStreamRunning = false;
napi_threadsafe_function tsFnToStream;
napi_threadsafe_function cancelFnToStream;
OH_AVScreenCapture *g_avCapture = nullptr;
std::unique_ptr<VideoEncoder> videoEncoder_ = nullptr;
std::unique_ptr<AudioEncoder> audioEncoder_ = nullptr;
std::unique_ptr<AudioEncoder> micAudioEncoder_ = nullptr;
std::unique_ptr<std::thread> releaseThread_ = nullptr;
std::mutex mutex_;
std::condition_variable doneCond_;
CAVScreenCaptureToStream::~CAVScreenCaptureToStream() { StartRelease(); }
* Screen recording Error callback
*/
void CAVScreenCaptureToStream::OnErrorToStream(OH_AVScreenCapture *capture, int32_t errorCode, void *userData) {
OH_LOG_ERROR(LOG_APP, "ScreenCapture OnError errorCode is %{public}d", errorCode);
}
* Screen recording state change callback
*/
void CAVScreenCaptureToStream::OnSurfaceStateChangeToStream(struct OH_AVScreenCapture *capture,
OH_AVScreenCaptureStateCode stateCode, 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");
if (isStreamRunning) {
int32_t ret = videoEncoder_->NotifyEndOfStream();
std::unique_lock<std::mutex> lock(mutex_);
doneCond_.wait(lock);
if (releaseThread_ && releaseThread_->joinable()) {
releaseThread_->join();
releaseThread_.reset();
}
}
OH_AVScreenCapture_StopScreenCapture(g_avCapture);
if (tsFnToStream != nullptr) {
napi_acquire_threadsafe_function(tsFnToStream);
napi_call_threadsafe_function(tsFnToStream, nullptr, napi_tsfn_nonblocking);
napi_release_threadsafe_function(tsFnToStream, napi_tsfn_release);
tsFnToStream = nullptr;
}
break;
}
case OH_SCREEN_CAPTURE_STATE_CANCELED: {
OH_LOG_INFO(LOG_APP, "ScreenCapture OnStateChange OH_SCREEN_CAPTURE_STATE_CANCELED ");
if (isStreamRunning) {
int32_t ret = videoEncoder_->NotifyEndOfStream();
std::unique_lock<std::mutex> lock(mutex_);
doneCond_.wait(lock);
if (releaseThread_ && releaseThread_->joinable()) {
releaseThread_->join();
releaseThread_.reset();
}
}
OH_AVScreenCapture_StopScreenCapture(g_avCapture);
if (cancelFnToStream != nullptr) {
napi_acquire_threadsafe_function(cancelFnToStream);
napi_call_threadsafe_function(cancelFnToStream, nullptr, napi_tsfn_nonblocking);
napi_release_threadsafe_function(cancelFnToStream, napi_tsfn_release);
cancelFnToStream = nullptr;
}
break;
}
case OH_SCREEN_CAPTURE_STATE_STARTED: {
int ret = OH_AVScreenCapture_SetMaxVideoFrameRate(g_avCapture, FRAME_RATE);
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_UNMUTED_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(int32_t videoWidth,
int32_t videoHeight) {
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");
return;
}
OH_LOG_INFO(LOG_APP, "ScreenCapture after create sc");
OH_AVScreenCapture_SetErrorCallback(g_avCapture, OnErrorToStream, nullptr);
OH_AVScreenCapture_SetStateCallback(g_avCapture, OnSurfaceStateChangeToStream, nullptr);
OH_AVScreenCapture_SetDataCallback(g_avCapture, OnBufferAvailable, audioData_);
OH_AVScreenCapture_SetMicrophoneEnabled(g_avCapture, true);
OH_AVScreenCapture_SetCanvasRotation(g_avCapture, true);
OH_AVScreenCaptureConfig config;
OH_AudioCaptureInfo micCapInfo = {.audioSampleRate = SAMPLE_RATE, .audioChannels = CHANNELS_COUNT, .audioSource = OH_SOURCE_DEFAULT};
OH_AudioCaptureInfo innerCapInfo = {.audioSampleRate = SAMPLE_RATE, .audioChannels = CHANNELS_COUNT, .audioSource = OH_ALL_PLAYBACK};
OH_AudioEncInfo audioEncInfo = {.audioBitrate = AUDIO_BITRATE, .audioCodecformat = OH_AudioCodecFormat::OH_AAC_LC};
OH_AudioInfo audioInfo = {.micCapInfo = micCapInfo, .innerCapInfo = innerCapInfo, .audioEncInfo = audioEncInfo};
OH_VideoCaptureInfo videoCapInfo = {
.videoFrameWidth = videoWidth, .videoFrameHeight = videoHeight, .videoSource = OH_VIDEO_SOURCE_SURFACE_RGBA};
OH_VideoEncInfo videoEncInfo = {
.videoCodec = OH_VideoCodecFormat::OH_H264, .videoBitrate = SAMPLE_BITRATE, .videoFrameRate = FRAME_RATE};
OH_VideoInfo videoInfo = {.videoCapInfo = videoCapInfo, .videoEncInfo = videoEncInfo};
config = {
.captureMode = OH_CAPTURE_HOME_SCREEN,
.dataType = OH_ORIGINAL_STREAM,
.audioInfo = audioInfo,
.videoInfo = videoInfo,
};
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 (capture != nullptr) {
OH_AVScreenCapture_StopScreenCapture(capture);
OH_AVScreenCapture_Release(capture);
capture = nullptr;
isStreamRunning = false;
OH_LOG_INFO(LOG_APP, "CAVScreenCaptureToStream ScreenCapture StopScreenCapture");
}
}
void CAVScreenCaptureToStream::ReleaseSCWorkerToStream(struct OH_AVScreenCapture *capture) {
OH_LOG_INFO(LOG_APP, "CAVScreenCaptureToStream ScreenCapture ReleaseSCInstanceWorker S");
OH_AVScreenCapture_Release(capture);
isStreamRunning = false;
g_avCapture = nullptr;
OH_LOG_INFO(LOG_APP, "CAVScreenCaptureToStream ScreenCapture ReleaseSCInstanceWorker E");
}
* Create audio and video encoder
*/
void CAVScreenCaptureToStream::CreateEncoder() {
audioData_ = new AudioData;
audioEncoder_->Create(sampleInfo_.audioCodecMime);
audioEncContext_ = new CodecUserData;
audioEncoder_->Config(sampleInfo_, audioEncContext_);
micAudioEncoder_->Create(sampleInfo_.audioCodecMime);
micAudioEncContext_ = new CodecUserData;
micAudioEncoder_->Config(sampleInfo_, micAudioEncContext_);
videoEncoder_->Create(sampleInfo_.videoCodecMime);
videoEncContext_ = new CodecUserData;
videoEncoder_->Config(sampleInfo_, videoEncContext_);
}
void CAVScreenCaptureToStream::OnBufferAvailable(OH_AVScreenCapture *capture, OH_AVBuffer *buffer,
OH_AVScreenCaptureBufferType bufferType, int64_t timestamp,
void *userData)
{
(void)capture;
AudioData *audioData = static_cast<AudioData *>(userData);
int bufferLen = OH_AVBuffer_GetCapacity(buffer);
uint8_t *buf = OH_AVBuffer_GetAddr(buffer);
if (bufferType == OH_SCREEN_CAPTURE_BUFFERTYPE_AUDIO_INNER) {
if (audioData != nullptr) {
std::unique_lock<std::mutex> lock(audioData->inputMutex);
audioData->WriteCache(buf, bufferLen);
lock.unlock();
audioData->inputCond.notify_all();
}
} else if (bufferType == OH_SCREEN_CAPTURE_BUFFERTYPE_AUDIO_MIC) {
if (audioData != nullptr) {
std::unique_lock<std::mutex> lock(audioData->inputMicMutex);
audioData->WriteMicCache(buf, bufferLen);
lock.unlock();
audioData->inputMicCond.notify_all();
}
}
}
* Video encoder output thread
*/
void CAVScreenCaptureToStream::EncOutputThread() {
while (true) {
if (!isStarted_) {
OH_LOG_ERROR(LOG_APP, "Work done, thread out");
break;
}
std::unique_lock<std::mutex> lock(videoEncContext_->outputMutex);
bool condRet = videoEncContext_->outputCond.wait_for(
lock, 5s, [this]() { return !isStarted_ || !videoEncContext_->outputBufferInfoQueue.empty(); });
if (!isStarted_) {
OH_LOG_ERROR(LOG_APP, "Work done, thread out");
break;
}
if (videoEncContext_->outputBufferInfoQueue.empty()) {
OH_LOG_ERROR(LOG_APP, "Buffer queue is empty, continue, cond ret: %{public}d", condRet);
continue;
}
CodecBufferInfo bufferInfo = videoEncContext_->outputBufferInfoQueue.front();
videoEncContext_->outputBufferInfoQueue.pop();
if (bufferInfo.attr.flags & AVCODEC_BUFFER_FLAGS_EOS) {
OH_LOG_ERROR(LOG_APP, "Catch EOS, thread out");
break;
}
lock.unlock();
if ((bufferInfo.attr.flags & AVCODEC_BUFFER_FLAGS_SYNC_FRAME) ||
(bufferInfo.attr.flags == AVCODEC_BUFFER_FLAGS_NONE)) {
videoEncContext_->outputFrameCount++;
if (lastFrameTimestampPts_ == 0) {
lastFrameTimestampPts_ = bufferInfo.attr.pts;
bufferInfo.attr.pts = 0;
} else {
lastFrameEncodePts_ += (bufferInfo.attr.pts - lastFrameTimestampPts_) / MICROSECOND;
lastFrameTimestampPts_ = bufferInfo.attr.pts;
bufferInfo.attr.pts = lastFrameEncodePts_;
}
} else {
bufferInfo.attr.pts = 0;
}
muxer_->WriteSample(muxer_->GetVideoTrackId(), reinterpret_cast<OH_AVBuffer *>(bufferInfo.buffer),
bufferInfo.attr);
int32_t ret = videoEncoder_->FreeOutputBuffer(bufferInfo.bufferIndex);
if (ret) {
OH_LOG_ERROR(LOG_APP, "Encoder output thread out");
break;
}
}
StartRelease();
OH_LOG_INFO(LOG_APP, "Exit, frame count: %{public}u", videoEncContext_->outputFrameCount);
}
* Initialize configuration information
*/
void CAVScreenCaptureToStream::InitMuxerAndEncoder(int32_t outputFd, int32_t videoWidth, int32_t videoHeight) {
sampleInfo_.outputFd = outputFd;
sampleInfo_.videoWidth = videoWidth;
sampleInfo_.videoHeight = videoHeight;
sampleInfo_.frameRate = FRAME_RATE;
sampleInfo_.bitrate = SAMPLE_BITRATE;
sampleInfo_.videoCodecMime = "video/avc";
sampleInfo_.audioCodecMime = OH_AVCODEC_MIMETYPE_AUDIO_AAC;
sampleInfo_.audioSampleFormat = OH_BitsPerSample::SAMPLE_S16LE;
sampleInfo_.audioSampleRate = SAMPLE_RATE;
sampleInfo_.audioChannelCount = CHANNELS_COUNT;
sampleInfo_.audioBitRate = AUDIO_BITRATE;
sampleInfo_.audioChannelLayout = OH_AudioChannelLayout::CH_LAYOUT_STEREO;
sampleInfo_.audioMaxInputSize = sampleInfo_.audioSampleRate * AUDIO_SAMPLE_RATE_NUM * sampleInfo_.audioChannelCount * sizeof(short);
std::lock_guard<std::mutex> lock(mutex_);
audioEncoder_ = std::make_unique<AudioEncoder>();
micAudioEncoder_ = std::make_unique<AudioEncoder>();
videoEncoder_ = std::make_unique<VideoEncoder>();
muxer_ = std::make_unique<Muxer>();
muxer_->Create(sampleInfo_.outputFd);
muxer_->Config(sampleInfo_);
CreateEncoder();
releaseThread_ = nullptr;
}
void CAVScreenCaptureToStream::StartMuxerAndEncoder() {
int32_t ret = muxer_->Start();
ret = videoEncoder_->Start();
isStarted_ = true;
lastFrameTimestampPts_ = 0;
lastFrameEncodePts_ = 0;
encOutputThread_ = std::make_unique<std::thread>(&CAVScreenCaptureToStream::EncOutputThread, this);
if (encOutputThread_ == nullptr) {
StartRelease();
return;
}
if (audioEncContext_) {
audioEncoder_->Start();
micAudioEncoder_->Start();
isStarted_ = true;
audioEncInputThread_ = std::make_unique<std::thread>(&CAVScreenCaptureToStream::AudioEncInputThread, this);
audioEncOutputThread_ = std::make_unique<std::thread>(&CAVScreenCaptureToStream::AudioEncOutputThread, this);
micAudioEncInputThread_ =
std::make_unique<std::thread>(&CAVScreenCaptureToStream::MicAudioEncInputThread, this);
micAudioEncOutputThread_ =
std::make_unique<std::thread>(&CAVScreenCaptureToStream::MicAudioEncOutputThread, this);
if (audioEncInputThread_ == nullptr || audioEncOutputThread_ == nullptr || micAudioEncInputThread_ == nullptr ||
micAudioEncOutputThread_ == nullptr) {
StartRelease();
return;
}
if (audioData_ != nullptr) {
audioData_->ClearCache();
audioData_->ClearMicCache();
}
}
}
void CAVScreenCaptureToStream::StartScreenCapture(int32_t outputFd, int32_t videoWidth, int32_t videoHeight) {
InitMuxerAndEncoder(outputFd, videoWidth, videoHeight);
InitAVScreenCapture(videoWidth, videoHeight);
isStreamRunning = true;
StartMuxerAndEncoder();
int result = OH_AVScreenCapture_StartScreenCaptureWithSurface(g_avCapture, sampleInfo_.window);
OH_LOG_INFO(LOG_APP, "OH_AVScreenCapture_StartScreenCaptureWithSurface Started, result: %{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() {
int32_t ret = videoEncoder_->NotifyEndOfStream();
std::unique_lock<std::mutex> lock(mutex_);
doneCond_.wait(lock);
if (releaseThread_ && releaseThread_->joinable()) {
releaseThread_->join();
releaseThread_.reset();
}
if (isStreamRunning) {
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");
}
isStreamRunning = false;
g_avCapture = nullptr;
}
}
void CAVScreenCaptureToStream::SetStopFlagToStream(napi_env env, napi_value jsCb, void *context, void *data) {
if (env == nullptr) {
return;
}
napi_value res;
napi_call_function(env, nullptr, jsCb, 0, nullptr, &res);
OH_LOG_INFO(LOG_APP, "Callback result");
}
* Set the callback function after stopping
*/
napi_value CAVScreenCaptureToStream::SetStopCallbackToStream(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);
napi_value resourceName = nullptr;
napi_create_string_utf8(env, "ThreadSafeSetStopFlag", NAPI_AUTO_LENGTH, &resourceName);
napi_status result = napi_create_threadsafe_function(env, args[0], nullptr, resourceName, 0, 1, nullptr, nullptr,
nullptr, SetStopFlagToStream, &tsFnToStream);
if (result != napi_ok) {
OH_LOG_INFO(LOG_APP, "napi_create_threadsafe_function fail, result: %{public}d", result);
}
return nullptr;
}
napi_value CAVScreenCaptureToStream::SetCancelCallbackToStream(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);
napi_value resourceName = nullptr;
napi_create_string_utf8(env, "ThreadSafeSetStopFlag", NAPI_AUTO_LENGTH, &resourceName);
napi_status result = napi_create_threadsafe_function(env, args[0], nullptr, resourceName, 0, 1, nullptr, nullptr,
nullptr, SetStopFlagToStream, &cancelFnToStream);
if (result != napi_ok) {
OH_LOG_INFO(LOG_APP, "napi_create_threadsafe_function fail, result: %{public}d", result);
}
return nullptr;
}
void CAVScreenCaptureToStream::StartRelease() {
if (releaseThread_ == nullptr) {
releaseThread_ = std::make_unique<std::thread>(&CAVScreenCaptureToStream::Release, this);
}
}
* Release thread
*/
void CAVScreenCaptureToStream::Release() {
std::lock_guard<std::mutex> lock(mutex_);
isStarted_ = false;
ThreadRelease();
if (muxer_ != nullptr) {
muxer_->Release();
muxer_.reset();
OH_LOG_INFO(LOG_APP, "Muxer release successful");
}
if (videoEncoder_ != nullptr) {
videoEncoder_->Stop();
if (sampleInfo_.window != nullptr) {
OH_NativeWindow_DestroyNativeWindow(sampleInfo_.window);
sampleInfo_.window = nullptr;
}
videoEncoder_->Release();
videoEncoder_.reset();
OH_LOG_INFO(LOG_APP, "Video encoder release successful");
}
if (audioEncoder_ != nullptr) {
audioEncoder_->Release();
audioEncoder_.reset();
OH_LOG_INFO(LOG_APP, "Audio encoder release successful");
}
if (micAudioEncoder_ != nullptr) {
micAudioEncoder_->Release();
micAudioEncoder_.reset();
OH_LOG_INFO(LOG_APP, "Mic Audio encoder release successful");
}
if (audioEncContext_ != nullptr) {
delete audioEncContext_;
audioEncContext_ = nullptr;
}
if (micAudioEncContext_ != nullptr) {
delete micAudioEncContext_;
micAudioEncContext_ = nullptr;
}
if (videoEncContext_ != nullptr) {
delete videoEncContext_;
videoEncContext_ = nullptr;
}
if (audioData_ != nullptr) {
delete audioData_;
audioData_ = nullptr;
}
doneCond_.notify_all();
OH_LOG_INFO(LOG_APP, "Release successful");
}
void CAVScreenCaptureToStream::ThreadRelease() {
if (encOutputThread_ && encOutputThread_->joinable()) {
encOutputThread_->join();
encOutputThread_.reset();
}
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 (micAudioEncInputThread_ && micAudioEncInputThread_->joinable()) {
micAudioEncContext_->inputCond.notify_all();
micAudioEncInputThread_->join();
micAudioEncInputThread_.reset();
}
if (micAudioEncOutputThread_ && micAudioEncOutputThread_->joinable()) {
micAudioEncContext_->outputCond.notify_all();
micAudioEncOutputThread_->join();
micAudioEncOutputThread_.reset();
}
}
* Audio encoder input thread
*/
void CAVScreenCaptureToStream::AudioEncInputThread() {
while (true) {
if (!isStarted_) {
OH_LOG_ERROR(LOG_APP, "Encoder input thread out");
break;
}
OH_LOG_ERROR(LOG_APP, "AudioEncInputThread lock");
std::unique_lock<std::mutex> lock(audioEncContext_->inputMutex);
bool condRet = audioEncContext_->inputCond.wait_for(lock, 1s, [this]() {
return !isStarted_ || !audioEncContext_->inputBufferInfoQueue.empty();
});
if (audioEncContext_->inputBufferInfoQueue.empty()) {
OH_LOG_ERROR(LOG_APP, "Audio Buffer queue is empty, continue, cond ret: %{public}d", condRet);
continue;
}
if (isStarted_ && audioData_->remainlen < sampleInfo_.audioMaxInputSize) {
continue;
}
CodecBufferInfo bufferInfo = audioEncContext_->inputBufferInfoQueue.front();
audioEncContext_->inputBufferInfoQueue.pop();
audioEncContext_->inputFrameCount++;
uint8_t *inputBufferAddr = OH_AVBuffer_GetAddr(reinterpret_cast<OH_AVBuffer *>(bufferInfo.buffer));
std::unique_lock<std::mutex> lockAudio(audioData_->inputMutex);
audioData_->ReadCache(inputBufferAddr, sampleInfo_.audioMaxInputSize);
lockAudio.unlock();
lock.unlock();
bufferInfo.attr.size = sampleInfo_.audioMaxInputSize;
if (isFirstFrame_) {
bufferInfo.attr.flags = AVCODEC_BUFFER_FLAGS_CODEC_DATA;
isFirstFrame_ = false;
} else {
bufferInfo.attr.flags = AVCODEC_BUFFER_FLAGS_NONE;
}
int32_t ret = audioEncoder_->PushInputData(bufferInfo);
if (ret) {
OH_LOG_ERROR(LOG_APP, "Push data failed, thread out");
break;
}
}
}
* Audio encoder output thread
*/
void CAVScreenCaptureToStream::AudioEncOutputThread() {
while (true) {
if (!isStarted_) {
OH_LOG_ERROR(LOG_APP, "Work done, thread out");
break;
}
OH_LOG_ERROR(LOG_APP, "AudioEncOutputThread lock");
std::unique_lock<std::mutex> lock(audioEncContext_->outputMutex);
bool condRet = audioEncContext_->outputCond.wait_for(
lock, 1s, [this]() { return !isStarted_ || !audioEncContext_->outputBufferInfoQueue.empty(); });
if (!isStarted_) {
OH_LOG_ERROR(LOG_APP, "Work done, thread out");
break;
}
if (audioEncContext_->outputBufferInfoQueue.empty()) {
OH_LOG_ERROR(LOG_APP, "Buffer queue is empty, continue, cond ret: %{public}d", condRet);
continue;
}
CodecBufferInfo bufferInfo = audioEncContext_->outputBufferInfoQueue.front();
audioEncContext_->outputBufferInfoQueue.pop();
audioEncContext_->outputFrameCount++;
lock.unlock();
muxer_->WriteSample(muxer_->GetAudioTrackId(), reinterpret_cast<OH_AVBuffer *>(bufferInfo.buffer),
bufferInfo.attr);
int32_t ret = audioEncoder_->FreeOutputData(bufferInfo.bufferIndex);
if (ret) {
OH_LOG_ERROR(LOG_APP, "Encoder output thread out");
break;
}
}
OH_LOG_INFO(LOG_APP, "Exit, frame count: %{public}u", audioEncContext_->inputFrameCount);
StartRelease();
}
void CAVScreenCaptureToStream::MicAudioEncInputThread() {
while (true) {
if (!isStarted_) {
OH_LOG_ERROR(LOG_APP, "Mic Encoder input thread out");
break;
}
OH_LOG_ERROR(LOG_APP, "MicAudioEncInputThread lock");
std::unique_lock<std::mutex> lock(micAudioEncContext_->inputMutex);
bool condRet = micAudioEncContext_->inputCond.wait_for(lock, 1s, [this]() {
return !isStarted_ || (!micAudioEncContext_->inputBufferInfoQueue.empty());
});
if (micAudioEncContext_->inputBufferInfoQueue.empty()) {
OH_LOG_ERROR(LOG_APP, "Mic Audio Buffer queue is empty, continue, cond ret: %{public}d", condRet);
continue;
}
if (isStarted_ && audioData_->micRemainlen < sampleInfo_.audioMaxInputSize) {
continue;
}
CodecBufferInfo bufferInfo = micAudioEncContext_->inputBufferInfoQueue.front();
micAudioEncContext_->inputBufferInfoQueue.pop();
micAudioEncContext_->inputFrameCount++;
uint8_t *inputBufferAddr = OH_AVBuffer_GetAddr(reinterpret_cast<OH_AVBuffer *>(bufferInfo.buffer));
std::unique_lock<std::mutex> lockAudio(audioData_->inputMicMutex);
audioData_->ReadMicCache(inputBufferAddr, sampleInfo_.audioMaxInputSize);
lockAudio.unlock();
lock.unlock();
bufferInfo.attr.size = sampleInfo_.audioMaxInputSize;
if (isMicFirstFrame_) {
bufferInfo.attr.flags = AVCODEC_BUFFER_FLAGS_CODEC_DATA;
isMicFirstFrame_ = false;
} else {
bufferInfo.attr.flags = AVCODEC_BUFFER_FLAGS_NONE;
}
int32_t ret = micAudioEncoder_->PushInputData(bufferInfo);
if (ret) {
OH_LOG_ERROR(LOG_APP, "Push data failed, thread out");
break;
}
}
}
* Audio encoder output thread
*/
void CAVScreenCaptureToStream::MicAudioEncOutputThread() {
while (true) {
if (!isStarted_) {
OH_LOG_ERROR(LOG_APP, "Mic Work done, thread out");
break;
}
OH_LOG_ERROR(LOG_APP, "MicAudioEncOutputThread lock");
std::unique_lock<std::mutex> lock(micAudioEncContext_->outputMutex);
bool condRet = micAudioEncContext_->outputCond.wait_for(
lock, 1s, [this]() { return !isStarted_ || !micAudioEncContext_->outputBufferInfoQueue.empty(); });
if (!isStarted_) {
OH_LOG_ERROR(LOG_APP, "Mic Work done, thread out");
break;
}
if (micAudioEncContext_->outputBufferInfoQueue.empty()) {
OH_LOG_ERROR(LOG_APP, "Mic Buffer queue is empty, continue, cond ret: %{public}d", condRet);
continue;
}
CodecBufferInfo bufferInfo = micAudioEncContext_->outputBufferInfoQueue.front();
micAudioEncContext_->outputBufferInfoQueue.pop();
micAudioEncContext_->outputFrameCount++;
lock.unlock();
muxer_->WriteSample(muxer_->GetMicTrackId(), reinterpret_cast<OH_AVBuffer *>(bufferInfo.buffer),
bufferInfo.attr);
int32_t ret = micAudioEncoder_->FreeOutputData(bufferInfo.bufferIndex);
if (ret) {
OH_LOG_ERROR(LOG_APP, "MicEncoder output thread out");
break;
}
}
}