/*
 * 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 {
// microsecond
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;
} // namespace

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
 */
// [Start InitAVScreenCapture]
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");

    // Set callback
    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);

    // Initialize configuration information
    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);
}
// [End InitAVScreenCapture]

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
 */
// [Start EncOutputThread]
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 first Frame, last frame info not init, Set pts directly to 0
            if (lastFrameTimestampPts_ == 0) {
                lastFrameTimestampPts_ = bufferInfo.attr.pts;
                bufferInfo.attr.pts = 0;
            } else {
                // calc cur frame pts and last pts diff, get cur frame encode pts
                lastFrameEncodePts_ += (bufferInfo.attr.pts - lastFrameTimestampPts_) / MICROSECOND;
                // record last frame pts info
                lastFrameTimestampPts_ = bufferInfo.attr.pts;
                // set cur frame encode 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);
}
// [End EncOutputThread]

/*
 * Initialize configuration information
 */
// [Start init_config2]
void CAVScreenCaptureToStream::InitMuxerAndEncoder(int32_t outputFd, int32_t videoWidth, int32_t videoHeight) {
    sampleInfo_.outputFd = outputFd;

    // Video encoding configuration
    sampleInfo_.videoWidth = videoWidth;
    sampleInfo_.videoHeight = videoHeight;
    sampleInfo_.frameRate = FRAME_RATE;
    sampleInfo_.bitrate = SAMPLE_BITRATE;
    sampleInfo_.videoCodecMime = "video/avc";

    // Audio encoding configuration
    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;
}
// [End init_config2]

// [Start start_capture_thread2]
void CAVScreenCaptureToStream::StartMuxerAndEncoder() {
    // Start muxer
    int32_t ret = muxer_->Start();
    // Start video Encoder
    ret = videoEncoder_->Start();

    isStarted_ = true;
    lastFrameTimestampPts_ = 0;
    lastFrameEncodePts_ = 0;
    // Start the video output threads
    encOutputThread_ = std::make_unique<std::thread>(&CAVScreenCaptureToStream::EncOutputThread, this);
    if (encOutputThread_ == nullptr) {
        StartRelease();
        return;
    }

    if (audioEncContext_) {
        // Start audio Encoder
        audioEncoder_->Start();
        micAudioEncoder_->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);
        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;
        }

        // Empty the playback cache queue
        if (audioData_ != nullptr) {
            audioData_->ClearCache();
            audioData_->ClearMicCache();
        }
    }
}
// [End start_capture_thread2]

// [Start init_Screen_capture2]
void CAVScreenCaptureToStream::StartScreenCapture(int32_t outputFd, int32_t videoWidth, int32_t videoHeight) {
    InitMuxerAndEncoder(outputFd, videoWidth, videoHeight);

    InitAVScreenCapture(videoWidth, videoHeight);
    
    isStreamRunning = true;

    StartMuxerAndEncoder();

    // [Start start_Screen_capture2]
    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);
    }
    // [End start_Screen_capture2]
}
// [End init_Screen_capture2]

/*
 * Stop screen recording
 */
// [Start stop_capture_record2]
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;
    }
}
// [End stop_capture_record2]

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
 */
// [Start release_resource2]
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();
    }
}
// [End release_resource2]

/*
 * 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;
        }
    }
}