diff --git a/LICENSE b/LICENSE index 338e5b0bc22082e0ffcc7121c2ed3897a3ddccb0..18795a48d6b12fcdc1aa7bac9a9cb99f83815267 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ - Copyright (c) 2024 Huawei Device Co., Ltd. All rights reserved. + Copyright (c) 2025 Huawei Device Co., Ltd. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/README.en.md b/README.en.md index f47e380753f404bf436f41108b7dbcd8d438e062..5cce7ec2e7f5bfe6913fade6f8166b5e85639ab6 100644 --- a/README.en.md +++ b/README.en.md @@ -21,7 +21,6 @@ This sample demonstrates video playback and recording implemented on AVCodec. |---------------|------------------| | mp4 | H.264/H.265 | -This sample supports only video recording. It does not integrate the audio capability. ### Preview @@ -43,7 +42,7 @@ This sample supports only video recording. It does not integrate the audio capab #### Playback -1. Place the recorded video file in **gallery**, or click **Start** to record another video file (without audio). +1. Place the recorded video file in **gallery**, or click **Start** to record another video file. 2. Touch **Play** and select a file to play. @@ -53,7 +52,9 @@ This sample supports only video recording. It does not integrate the audio capab ├──entry/src/main/cpp // Native layer │ ├──capbilities // Capability interfaces and implementation │ │ ├──include // Capability interfaces +│ │ ├──AudioCapturer.cpp // Audio capturer implementation │ │ ├──AudioDecoder.cpp // Audio decoder implementation +│ │ ├──AudioEncoder.cpp // Audio encoder implementation │ │ ├──Demuxer.cpp // Demuxer implementation │ │ ├──Muxer.cpp // Muxer implementation │ │ ├──VideoDecoder.cpp // Video decoder implementation @@ -65,7 +66,6 @@ This sample supports only video recording. It does not integrate the audio capab │ │ └──SampleInfo.h // Common classes for function implementation │ ├──render // Interfaces and implementation of the display module │ │ ├──include // Display module interfaces -│ │ ├──EglCore.cpp // Display parameter settings │ │ ├──PluginManager.cpp // Display module management implementation │ │ └──PluginRender.cpp // Display logic implementation │ ├──sample // Native layer @@ -185,6 +185,7 @@ This sample supports only video recording. It does not integrate the audio capab ### Required Permissions **ohos.permission.CAMERA**: allows an app to use the camera. +**ohos.permission.MICROPHONE**: allows an app to use the microphone. ### Dependencies diff --git a/README.md b/README.md index 37c9a0210352ec23f2ff45cbd59ed4ee2572ae94..b8c32eb4ac78d4526adfb256d51df6c0abbedde9 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,6 @@ |------|-------------| | mp4 | H.264/H.265 | -注意,本示例仅支持视频录制,未集成音频能力,播放的视频仅支持横屏录制视频。 ### 效果预览 | 应用主界面 | 应用使用展示 | @@ -27,21 +26,21 @@ | ![AVCodec_Index.png](screenshots/device/AVCodec_Index.png) | ![AVCodecSample.gif](screenshots/device/AVCodecSample.gif) | ### 使用说明 -1. 弹出是否允许“AVCodecVideo”使用相机?点击“允许” +1. 在弹出是否允许“AVCodecVideo”使用相机后,点击“允许”。 #### 录制 -1. 点击“录制” +1. 点击“录制”。 -2. 确认允许录制文件保存到图库 +2. 确认允许录制文件保存到图库。 -3. 录制完成后点击“停止录制” +3. 录制完成后点击“停止录制”。 #### 播放 -1. 推送视频文件至图库下或点击下方“开始录制”,录制一个视频文件(无音频) +1. 推送视频文件至图库下或点击下方“开始录制”,录制一个视频文件。 -2. 点击播放按钮,选择文件,开始播放 +2. 点击播放按钮,选择文件,开始播放。 ### 工程目录 @@ -49,7 +48,9 @@ ├──entry/src/main/cpp // Native层 │ ├──capbilities // 能力接口和实现 │ │ ├──include // 能力接口 +│ │ ├──AudioCapturer.cpp // 音频采集实现 │ │ ├──AudioDecoder.cpp // 音频解码实现 +│ │ ├──AudioEncoder.cpp // 音频编码实现 │ │ ├──Demuxer.cpp // 解封装实现 │ │ ├──Muxer.cpp // 封装实现 │ │ ├──VideoDecoder.cpp // 视频解码实现 @@ -61,7 +62,6 @@ │ │ └──SampleInfo.h // 功能实现公共类 │ ├──render // 送显模块接口和实现 │ │ ├──include // 送显模块接口 -│ │ ├──EglCore.cpp // 送显参数设置 │ │ ├──PluginManager.cpp // 送显模块管理实现 │ │ └──PluginRender.cpp // 送显逻辑实现 │ ├──sample // Native层 @@ -149,6 +149,7 @@ ### 相关权限 - 允许应用使用相机: ohos.permission.CAMERA。 +- 允许应用使用麦克风: ohos.permission.MICROPHONE。 ### 依赖 diff --git a/build-profile.json5 b/build-profile.json5 index 01ef137f920b7763b51f7b10e0b7c752792fed62..408cc3421a62dc5207e9ee644b4fb3c43459669a 100644 --- a/build-profile.json5 +++ b/build-profile.json5 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2024 Huawei Device Co., Ltd. + * 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 diff --git a/entry/build-profile.json5 b/entry/build-profile.json5 index f869c0488ce6e97d09a002a4e4246f32769bfbdf..e825ce51f0a99668d420bac3838dca0969e0c132 100644 --- a/entry/build-profile.json5 +++ b/entry/build-profile.json5 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2024 Huawei Device Co., Ltd. + * 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 diff --git a/entry/oh-package.json5 b/entry/oh-package.json5 index 304cb2a400aebd7ba0485a3b235baa19149ca48e..72e73dbb49c01d07cd34448ee57e008260f57ae8 100644 --- a/entry/oh-package.json5 +++ b/entry/oh-package.json5 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2024 Huawei Device Co., Ltd. + * 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 diff --git a/entry/src/main/cpp/CMakeLists.txt b/entry/src/main/cpp/CMakeLists.txt index e498f02e07801619a12e1182c24ebf6615cf55bd..be7f6d366ef31557b31530fd344d2f32a2f24fad 100644 --- a/entry/src/main/cpp/CMakeLists.txt +++ b/entry/src/main/cpp/CMakeLists.txt @@ -25,7 +25,6 @@ add_library(player SHARED sample/player/PlayerNative.cpp capbilities/Demuxer.cpp capbilities/VideoDecoder.cpp capbilities/AudioDecoder.cpp - render/EglCore.cpp render/PluginRender.cpp render/PluginManager.cpp common/SampleCallback.cpp @@ -35,6 +34,8 @@ add_library(recorder SHARED sample/recorder/RecorderNative.cpp sample/recorder/Recorder.cpp capbilities/Muxer.cpp capbilities/VideoEncoder.cpp + capbilities/AudioCapturer.cpp + capbilities/AudioEncoder.cpp common/SampleCallback.cpp ) diff --git a/entry/src/main/cpp/capbilities/AudioCapturer.cpp b/entry/src/main/cpp/capbilities/AudioCapturer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..527c82d4284913b062156bf1e273d71e3e118f8f --- /dev/null +++ b/entry/src/main/cpp/capbilities/AudioCapturer.cpp @@ -0,0 +1,76 @@ +/* + * 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. + */ + +#include "AudioCapturer.h" +#include "SampleCallback.h" + + +AudioCapturer::~AudioCapturer() +{ + AudioCapturerRelease(); +} + +// AudioCapturer Callback +static int32_t AudioCapturerOnReadData(OH_AudioCapturer *capturer, void *userData, void *buffer, int32_t bufferLen) +{ + (void)capturer; + CodecUserData *codecUserData = static_cast(userData); + if (codecUserData != nullptr) { + std::unique_lock lock(codecUserData->inputMutex); + codecUserData->WriteCache(buffer, bufferLen); + codecUserData->inputCond.notify_all(); + } + return 0; +} + +void AudioCapturer::AudioCapturerInit(SampleInfo &sampleInfo, CodecUserData *audioEncContext) +{ + AudioCapturerRelease(); + + // Create builder + OH_AudioStream_Type type = AUDIOSTREAM_TYPE_CAPTURER; + OH_AudioStreamBuilder_Create(&builder_, type); + // set params and callbacks + OH_AudioStreamBuilder_SetSamplingRate(builder_, sampleInfo.audioSampleRate); + OH_AudioStreamBuilder_SetChannelCount(builder_, sampleInfo.audioChannelCount); + OH_AudioStreamBuilder_SetSampleFormat(builder_, AUDIOSTREAM_SAMPLE_S16LE); + OH_AudioStreamBuilder_SetLatencyMode(builder_, AUDIOSTREAM_LATENCY_MODE_NORMAL); + OH_AudioStreamBuilder_SetEncodingType(builder_, AUDIOSTREAM_ENCODING_TYPE_RAW); + OH_AudioCapturer_Callbacks callbacks; + callbacks.OH_AudioCapturer_OnReadData = AudioCapturerOnReadData; + OH_AudioStreamBuilder_SetCapturerCallback(builder_, callbacks, audioEncContext); + // create OH_AudioCapturer + OH_AudioStreamBuilder_GenerateCapturer(builder_, &audioCapturer_); +} + +void AudioCapturer::AudioCapturerStart() +{ + if (audioCapturer_ != nullptr) { + OH_AudioCapturer_Start(audioCapturer_); + } +} + +void AudioCapturer::AudioCapturerRelease() +{ + if (audioCapturer_ != nullptr) { + OH_AudioCapturer_Stop(audioCapturer_); + OH_AudioCapturer_Release(audioCapturer_); + audioCapturer_ = nullptr; + } + if (builder_ != nullptr) { + OH_AudioStreamBuilder_Destroy(builder_); + builder_ = nullptr; + } +} \ No newline at end of file diff --git a/entry/src/main/cpp/capbilities/AudioDecoder.cpp b/entry/src/main/cpp/capbilities/AudioDecoder.cpp index 0c77fa19a55ae1b0f673d1ce603a13c6a5d80e3b..b51bb7858038adc8dca3fcb3d617326e1fb4efe8 100644 --- a/entry/src/main/cpp/capbilities/AudioDecoder.cpp +++ b/entry/src/main/cpp/capbilities/AudioDecoder.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * 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 diff --git a/entry/src/main/cpp/capbilities/AudioEncoder.cpp b/entry/src/main/cpp/capbilities/AudioEncoder.cpp new file mode 100644 index 0000000000000000000000000000000000000000..06589ad8ac06bd592b1b246a6533287fc47c1f06 --- /dev/null +++ b/entry/src/main/cpp/capbilities/AudioEncoder.cpp @@ -0,0 +1,147 @@ +/* + * 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. + */ + +#include "AudioEncoder.h" + +#undef LOG_TAG +#define LOG_TAG "AudioEncoder" + +namespace { +constexpr int LIMIT_LOGD_FREQUENCY = 50; +} + +AudioEncoder::~AudioEncoder() +{ + Release(); +} + +int32_t AudioEncoder::Create(const std::string &codecMime) +{ + encoder_ = OH_AudioCodec_CreateByMime(codecMime.c_str(), true); + CHECK_AND_RETURN_RET_LOG(encoder_ != nullptr, AVCODEC_SAMPLE_ERR_ERROR, "Create failed"); + return AVCODEC_SAMPLE_ERR_OK; +} + +int32_t AudioEncoder::SetCallback(CodecUserData *codecUserData) +{ + int32_t ret = AV_ERR_OK; + ret = OH_AudioCodec_RegisterCallback(encoder_, + { SampleCallback::OnCodecError, SampleCallback::OnCodecFormatChange, + SampleCallback::OnNeedInputBuffer, SampleCallback::OnNewOutputBuffer }, + codecUserData); + CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK, AVCODEC_SAMPLE_ERR_ERROR, "Set callback failed, ret: %{public}d", ret); + AVCODEC_SAMPLE_LOGI("====== AudioEncoder SetCallback ======"); + + return AVCODEC_SAMPLE_ERR_OK; +} + +int32_t AudioEncoder::Configure(const SampleInfo &sampleInfo) +{ + OH_AVFormat *format = OH_AVFormat_Create(); + CHECK_AND_RETURN_RET_LOG(format != nullptr, AVCODEC_SAMPLE_ERR_ERROR, "AVFormat create failed"); + + OH_AVFormat_SetIntValue(format, OH_MD_KEY_AUDIO_SAMPLE_FORMAT, sampleInfo.audioSampleForamt); + 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); + AVCODEC_SAMPLE_LOGI("audioChannelCount:%{public}d audioSampleRate:%{public}d audioBitRate:%{public}d " + "audioChannelLayout:%{public}ld", + sampleInfo.audioChannelCount, sampleInfo.audioSampleRate, sampleInfo.audioBitRate, + sampleInfo.audioChannelLayout); + + int ret = OH_AudioCodec_Configure(encoder_, format); + CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK, AVCODEC_SAMPLE_ERR_ERROR, "Config failed, ret: %{public}d", ret); + OH_AVFormat_Destroy(format); + format = nullptr; + + return AVCODEC_SAMPLE_ERR_OK; +} + +int32_t AudioEncoder::Config(const SampleInfo &sampleInfo, CodecUserData *codecUserData) +{ + CHECK_AND_RETURN_RET_LOG(encoder_ != nullptr, AVCODEC_SAMPLE_ERR_ERROR, "Encoder is null"); + CHECK_AND_RETURN_RET_LOG(codecUserData != nullptr, AVCODEC_SAMPLE_ERR_ERROR, "Invalid param: codecUserData"); + + // Configure audio encoder + int32_t ret = Configure(sampleInfo); + CHECK_AND_RETURN_RET_LOG(ret == AVCODEC_SAMPLE_ERR_OK, AVCODEC_SAMPLE_ERR_ERROR, "Configure failed"); + + // SetCallback for audio encoder + ret = SetCallback(codecUserData); + CHECK_AND_RETURN_RET_LOG(ret == AVCODEC_SAMPLE_ERR_OK, AVCODEC_SAMPLE_ERR_ERROR, + "Set callback failed, ret: %{public}d", ret); + + // Prepare audio encoder + { + int ret = OH_AudioCodec_Prepare(encoder_); + CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK, AVCODEC_SAMPLE_ERR_ERROR, "Prepare failed, ret: %{public}d", ret); + } + + return AVCODEC_SAMPLE_ERR_OK; +} + +int32_t AudioEncoder::Start() +{ + CHECK_AND_RETURN_RET_LOG(encoder_ != nullptr, AVCODEC_SAMPLE_ERR_ERROR, "Encoder is null"); + + int ret = OH_AudioCodec_Start(encoder_); + CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK, AVCODEC_SAMPLE_ERR_ERROR, "Start failed, ret: %{public}d", ret); + return AVCODEC_SAMPLE_ERR_OK; +} + +int32_t AudioEncoder::PushInputData(CodecBufferInfo &info) +{ + CHECK_AND_RETURN_RET_LOG(encoder_ != nullptr, AVCODEC_SAMPLE_ERR_ERROR, "Encoder is null"); + int32_t ret = OH_AVBuffer_SetBufferAttr(reinterpret_cast(info.buffer), &info.attr); + CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK, AVCODEC_SAMPLE_ERR_ERROR, "Set avbuffer attr failed"); + ret = OH_AudioCodec_PushInputBuffer(encoder_, info.bufferIndex); + CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK, AVCODEC_SAMPLE_ERR_ERROR, "Push input data failed"); + return AVCODEC_SAMPLE_ERR_OK; +} + +int32_t AudioEncoder::FreeOutputData(uint32_t bufferIndex) +{ + CHECK_AND_RETURN_RET_LOG(encoder_ != nullptr, AVCODEC_SAMPLE_ERR_ERROR, "Encoder is null"); + + int32_t ret = AVCODEC_SAMPLE_ERR_OK; + ret = OH_AudioCodec_FreeOutputBuffer(encoder_, bufferIndex); + CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK, AVCODEC_SAMPLE_ERR_ERROR, "Free output data failed"); + return AVCODEC_SAMPLE_ERR_OK; +} + +int32_t AudioEncoder::Stop() +{ + CHECK_AND_RETURN_RET_LOG(encoder_ != nullptr, AVCODEC_SAMPLE_ERR_ERROR, "Encoder is null"); + + int ret = OH_AudioCodec_Flush(encoder_); + CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK, AVCODEC_SAMPLE_ERR_ERROR, "Flush failed, ret: %{public}d", ret); + + ret = OH_AudioCodec_Stop(encoder_); + CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK, AVCODEC_SAMPLE_ERR_ERROR, "Stop failed, ret: %{public}d", ret); + return AVCODEC_SAMPLE_ERR_OK; +} + +int32_t AudioEncoder::Release() +{ + if (encoder_ != nullptr) { + OH_AudioCodec_Flush(encoder_); + OH_AudioCodec_Stop(encoder_); + OH_AudioCodec_Destroy(encoder_); + encoder_ = nullptr; + } + return AVCODEC_SAMPLE_ERR_OK; +} diff --git a/entry/src/main/cpp/capbilities/Demuxer.cpp b/entry/src/main/cpp/capbilities/Demuxer.cpp index 094a9e949ac0e545a55497382477b9d5739138ed..93de06aadb12066f6a2bda61752739cb74106484 100644 --- a/entry/src/main/cpp/capbilities/Demuxer.cpp +++ b/entry/src/main/cpp/capbilities/Demuxer.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * 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 diff --git a/entry/src/main/cpp/capbilities/Muxer.cpp b/entry/src/main/cpp/capbilities/Muxer.cpp index 528fddb87d7cc95d21d6189d82795393e3e88cbb..380228b581786fe22ae0a239dbf7291b480439c8 100644 --- a/entry/src/main/cpp/capbilities/Muxer.cpp +++ b/entry/src/main/cpp/capbilities/Muxer.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * 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 @@ -15,13 +15,15 @@ #include "Muxer.h" #include "dfx/error/AVCodecSampleError.h" +#include #undef LOG_TAG #define LOG_TAG "Muxer" namespace { constexpr int32_t CAMERA_ANGLE = 90; -} +constexpr int32_t SAMPLE_RATE = 16000; +} // namespace Muxer::~Muxer() { Release(); } @@ -35,6 +37,13 @@ int32_t Muxer::Create(int32_t fd) { int32_t Muxer::Config(SampleInfo &sampleInfo) { CHECK_AND_RETURN_RET_LOG(muxer_ != nullptr, AVCODEC_SAMPLE_ERR_ERROR, "Muxer is null"); + + OH_AVFormat *formatAudio = OH_AVFormat_CreateAudioFormat(sampleInfo.audioCodecMime.data(), + sampleInfo.audioSampleRate, sampleInfo.audioChannelCount); + CHECK_AND_RETURN_RET_LOG(formatAudio != nullptr, AVCODEC_SAMPLE_ERR_ERROR, "Create audio format failed"); + OH_AVFormat_SetIntValue(formatAudio, OH_MD_KEY_PROFILE, AAC_PROFILE_LC); + int32_t ret = OH_AVMuxer_AddTrack(muxer_, &audioTrackId_, formatAudio); + OH_AVFormat_Destroy(formatAudio); OH_AVFormat *formatVideo = OH_AVFormat_CreateVideoFormat(sampleInfo.videoCodecMime.data(), sampleInfo.videoWidth, sampleInfo.videoHeight); @@ -52,7 +61,7 @@ int32_t Muxer::Config(SampleInfo &sampleInfo) { OH_AVFormat_SetIntValue(formatVideo, OH_MD_KEY_MATRIX_COEFFICIENTS, sampleInfo.matrix); } - int32_t ret = OH_AVMuxer_AddTrack(muxer_, &videoTrackId_, formatVideo); + ret = OH_AVMuxer_AddTrack(muxer_, &videoTrackId_, formatVideo); OH_AVFormat_Destroy(formatVideo); formatVideo = nullptr; OH_AVMuxer_SetRotation(muxer_, CAMERA_ANGLE); @@ -69,14 +78,16 @@ int32_t Muxer::Start() { return AVCODEC_SAMPLE_ERR_OK; } -int32_t Muxer::WriteSample(OH_AVBuffer *buffer, OH_AVCodecBufferAttr &attr) { +int32_t Muxer::WriteSample(int32_t trackId, OH_AVBuffer *buffer, OH_AVCodecBufferAttr &attr){ + std::lock_guard lock(writeMutex_); + CHECK_AND_RETURN_RET_LOG(muxer_ != nullptr, AVCODEC_SAMPLE_ERR_ERROR, "Muxer is null"); CHECK_AND_RETURN_RET_LOG(buffer != nullptr, AVCODEC_SAMPLE_ERR_ERROR, "Get a empty buffer"); int32_t ret = OH_AVBuffer_SetBufferAttr(buffer, &attr); CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK, AVCODEC_SAMPLE_ERR_ERROR, "SetBufferAttr failed"); - ret = OH_AVMuxer_WriteSampleBuffer(muxer_, videoTrackId_, buffer); + ret = OH_AVMuxer_WriteSampleBuffer(muxer_, trackId, buffer); CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK, AVCODEC_SAMPLE_ERR_ERROR, "Write sample failed"); return AVCODEC_SAMPLE_ERR_OK; } @@ -87,4 +98,7 @@ int32_t Muxer::Release() { muxer_ = nullptr; } return AVCODEC_SAMPLE_ERR_OK; -} \ No newline at end of file +} + +int32_t Muxer::GetVideoTrackId() { return videoTrackId_; } +int32_t Muxer::GetAudioTrackId() { return audioTrackId_; } \ No newline at end of file diff --git a/entry/src/main/cpp/capbilities/VideoDecoder.cpp b/entry/src/main/cpp/capbilities/VideoDecoder.cpp index bb3f060eeb8ddb9c7904492f47118e6cb2a50f70..8eea999d58138f571619b6627c2c728fd8668a61 100644 --- a/entry/src/main/cpp/capbilities/VideoDecoder.cpp +++ b/entry/src/main/cpp/capbilities/VideoDecoder.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * 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 diff --git a/entry/src/main/cpp/capbilities/VideoEncoder.cpp b/entry/src/main/cpp/capbilities/VideoEncoder.cpp index 982b2a360c99c7db7b42ab831a873714db08be62..2bd03828e2278cd5ab678d66e3dcfe68424a987a 100644 --- a/entry/src/main/cpp/capbilities/VideoEncoder.cpp +++ b/entry/src/main/cpp/capbilities/VideoEncoder.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * 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 diff --git a/entry/src/main/cpp/render/include/EglCore.h b/entry/src/main/cpp/capbilities/include/AudioCapturer.h similarity index 37% rename from entry/src/main/cpp/render/include/EglCore.h rename to entry/src/main/cpp/capbilities/include/AudioCapturer.h index fb7fcbb97d07a7d6b999adba3c7225988c6afb5d..1fd8898a78f34eca77383f01b55d99978c6ea731 100644 --- a/entry/src/main/cpp/render/include/EglCore.h +++ b/entry/src/main/cpp/capbilities/include/AudioCapturer.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * 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 @@ -13,38 +13,28 @@ * limitations under the License. */ -#ifndef NATIVE_XCOMPONENT_EGL_CORE_H -#define NATIVE_XCOMPONENT_EGL_CORE_H +#ifndef AVCODECVIDEO_AUDIOCAPTURER_H +#define AVCODECVIDEO_AUDIOCAPTURER_H -#include -#include -#include +#include +#include +#include -namespace NativeXComponentSample { -class EGLCore { +#include "SampleInfo.h" + +class AudioCapturer { public: - explicit EGLCore(){}; - ~EGLCore() {} - bool EglContextInit(void *window, int width, int height); - bool CreateEnvironment(); - void Release(); - void UpdateSize(int width, int height); + AudioCapturer() = default; + ~AudioCapturer(); -private: - GLuint LoadShader(GLenum type, const char *shaderSrc); - GLuint CreateProgram(const char *vertexShader, const char *fragShader); + void AudioCapturerInit(SampleInfo& sampleInfo, CodecUserData *audioEncContext); + void AudioCapturerStart(); + void AudioCapturerRelease(); private: - EGLNativeWindowType eglWindow_; - EGLDisplay eglDisplay_ = EGL_NO_DISPLAY; - EGLConfig eglConfig_ = EGL_NO_CONFIG_KHR; - EGLSurface eglSurface_ = EGL_NO_SURFACE; - EGLContext eglContext_ = EGL_NO_CONTEXT; - GLuint program_; - bool flag_ = false; - int width_; - int height_; - GLfloat widthPercent_; + OH_AudioCapturer *audioCapturer_ = nullptr; + OH_AudioStreamBuilder *builder_ = nullptr; }; -} // namespace NativeXComponentSample -#endif // NATIVE_XCOMPONENT_EGL_CORE_H \ No newline at end of file + + +#endif // AVCODECVIDEO_AUDIOCAPTURER_H diff --git a/entry/src/main/cpp/capbilities/include/AudioDecoder.h b/entry/src/main/cpp/capbilities/include/AudioDecoder.h index 8534042798b77aa2fe78687c1d6a0a82bcf0a504..5c776844978765cddcef92e5633df1ff6a1f63f9 100644 --- a/entry/src/main/cpp/capbilities/include/AudioDecoder.h +++ b/entry/src/main/cpp/capbilities/include/AudioDecoder.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * 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 diff --git a/entry/src/main/cpp/capbilities/include/AudioEncoder.h b/entry/src/main/cpp/capbilities/include/AudioEncoder.h new file mode 100644 index 0000000000000000000000000000000000000000..1261bf12e07162b548ce3253a70ab32008688691 --- /dev/null +++ b/entry/src/main/cpp/capbilities/include/AudioEncoder.h @@ -0,0 +1,48 @@ +/* + * 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 AVCODECVIDEO_AUDIOENCODER_H +#define AVCODECVIDEO_AUDIOENCODER_H + +#include "multimedia/player_framework/native_avcodec_audiocodec.h" +#include "multimedia/player_framework/native_avbuffer_info.h" +#include "multimedia/native_audio_channel_layout.h" +#include "SampleInfo.h" +#include "SampleCallback.h" +#include "dfx/error/AVCodecSampleError.h" +#include "dfx/log/AVCodecSampleLog.h" + +class AudioEncoder { +public: + AudioEncoder() = default; + ~AudioEncoder(); + + int32_t Create(const std::string &codecMime); + int32_t Config(const SampleInfo &sampleInfo, CodecUserData *codecUserData); + int32_t Start(); + int32_t PushInputData(CodecBufferInfo &info); + int32_t FreeOutputData(uint32_t bufferIndex); + int32_t Stop(); + int32_t Release(); + +private: + int32_t SetCallback(CodecUserData *codecUserData); + int32_t Configure(const SampleInfo &sampleInfo); + + bool isAVBufferMode_ = false; + OH_AVCodec *encoder_; +}; + +#endif // AVCODECVIDEO_AUDIOENCODER_H diff --git a/entry/src/main/cpp/capbilities/include/Demuxer.h b/entry/src/main/cpp/capbilities/include/Demuxer.h index e5d45b06f3706fd7643102a41cd963f7c58d6c8c..26fcd6c705195de062c080e48bf05196d73c8d34 100644 --- a/entry/src/main/cpp/capbilities/include/Demuxer.h +++ b/entry/src/main/cpp/capbilities/include/Demuxer.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * 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 diff --git a/entry/src/main/cpp/capbilities/include/Muxer.h b/entry/src/main/cpp/capbilities/include/Muxer.h index 93b5b4c208ec0c3040f53f0963d9b8b1591e7d97..e9a040a11c8ff85f2e71b1cd26f9ac687e422fff 100644 --- a/entry/src/main/cpp/capbilities/include/Muxer.h +++ b/entry/src/main/cpp/capbilities/include/Muxer.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * 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 @@ -16,10 +16,10 @@ #ifndef MUXER_H #define MUXER_H -#include -#include "multimedia/player_framework/native_avmuxer.h" -#include "SampleInfo.h" #include "AVCodecSampleLog.h" +#include "SampleInfo.h" +#include "multimedia/player_framework/native_avmuxer.h" +#include class Muxer { public: @@ -29,12 +29,16 @@ public: int32_t Create(int32_t fd); int32_t Config(SampleInfo &sampleInfo); int32_t Start(); - int32_t WriteSample(OH_AVBuffer *buffer, OH_AVCodecBufferAttr &attr); + int32_t WriteSample(int32_t trackId, OH_AVBuffer *buffer, OH_AVCodecBufferAttr &attr); int32_t Release(); + int32_t GetVideoTrackId(); + int32_t GetAudioTrackId(); private: OH_AVMuxer *muxer_ = nullptr; int32_t videoTrackId_ = -1; + int32_t audioTrackId_ = -1; + std::mutex writeMutex_; }; #endif // MUXER_H \ No newline at end of file diff --git a/entry/src/main/cpp/capbilities/include/VideoDecoder.h b/entry/src/main/cpp/capbilities/include/VideoDecoder.h index 295592764eb2a7d5029609df92ae70acdfad1149..45ee99029e06edbd70ea1cab6ef3a7363b22f92e 100644 --- a/entry/src/main/cpp/capbilities/include/VideoDecoder.h +++ b/entry/src/main/cpp/capbilities/include/VideoDecoder.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * 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 diff --git a/entry/src/main/cpp/capbilities/include/VideoEncoder.h b/entry/src/main/cpp/capbilities/include/VideoEncoder.h index 6941a1a909e89f1cc08ae857ae9b8b0195b2f0a8..23ba1826327ae4bbd07f3b4a603caa5ebb16d9c4 100644 --- a/entry/src/main/cpp/capbilities/include/VideoEncoder.h +++ b/entry/src/main/cpp/capbilities/include/VideoEncoder.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * 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 diff --git a/entry/src/main/cpp/common/SampleCallback.cpp b/entry/src/main/cpp/common/SampleCallback.cpp index a47b6ae623d15ff44d869323391b6788470b2cff..c8b8d62b735501afbd109a8e9bde480e9d85caab 100644 --- a/entry/src/main/cpp/common/SampleCallback.cpp +++ b/entry/src/main/cpp/common/SampleCallback.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * 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 diff --git a/entry/src/main/cpp/common/SampleCallback.h b/entry/src/main/cpp/common/SampleCallback.h index 351c3a7084233825097b7c31784353e6d40fb73a..073645817867b98027554cda014b8e9ea83a6a8d 100644 --- a/entry/src/main/cpp/common/SampleCallback.h +++ b/entry/src/main/cpp/common/SampleCallback.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * 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 diff --git a/entry/src/main/cpp/common/SampleInfo.h b/entry/src/main/cpp/common/SampleInfo.h index b08dbb6de5836e7a54dee78d4fe1d54ff2f67368..1d2bc8fa221c88cd23e119f86af20e7ba69d52e1 100644 --- a/entry/src/main/cpp/common/SampleInfo.h +++ b/entry/src/main/cpp/common/SampleInfo.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * 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 @@ -55,6 +55,11 @@ struct SampleInfo { int32_t audioSampleRate = 0; int32_t audioChannelCount = 0; int64_t audioChannelLayout = 0; + int32_t audioBitRate = 0; + uint8_t audioCodecConfig[100] = { 0 }; + size_t audioCodecSize = 0; + int32_t audioMaxInputSize = 0; + OH_AVFormat *audioFormat; int32_t isHDRVivid = 0; @@ -106,6 +111,38 @@ public: std::queue outputBufferInfoQueue; std::queue renderQueue; + + // Create cache + std::vector 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); + } + std::memcpy(cache.data() + remainlen, buffer, bufferLen); + remainlen += bufferLen; + } + + bool ReadCache(void *buffer, int32_t bufferLen) + { + if (remainlen < bufferLen) { + return false; + } + std::memcpy(buffer, cache.data(), bufferLen); + remainlen = remainlen - bufferLen; + if (remainlen > 0) { + std::memmove(cache.data(), cache.data() + bufferLen, remainlen); + } + return true; + } }; #endif // AVCODEC_SAMPLE_INFO_H \ No newline at end of file diff --git a/entry/src/main/cpp/common/dfx/error/AVCodecSampleError.h b/entry/src/main/cpp/common/dfx/error/AVCodecSampleError.h index 59f35de8137e629727beba5245a2b9f5187f0e8e..a219a31336f1b05d45867caa9f29ce36133698c9 100644 --- a/entry/src/main/cpp/common/dfx/error/AVCodecSampleError.h +++ b/entry/src/main/cpp/common/dfx/error/AVCodecSampleError.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * 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 diff --git a/entry/src/main/cpp/common/dfx/log/AVCodecSampleLog.h b/entry/src/main/cpp/common/dfx/log/AVCodecSampleLog.h index 05be70f3ee61df5e5721be800a48c043c5316d5f..d1b5add9cf7e75d966d7cc0fd06e3457ad9ff370 100644 --- a/entry/src/main/cpp/common/dfx/log/AVCodecSampleLog.h +++ b/entry/src/main/cpp/common/dfx/log/AVCodecSampleLog.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * 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 diff --git a/entry/src/main/cpp/render/EglCore.cpp b/entry/src/main/cpp/render/EglCore.cpp deleted file mode 100644 index 2f4cfa0b1eb02d9497f342bae3d6cb5f10b526ef..0000000000000000000000000000000000000000 --- a/entry/src/main/cpp/render/EglCore.cpp +++ /dev/null @@ -1,365 +0,0 @@ -/* - * Copyright (c) 2024 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 "EglCore.h" -#include -#include -#include -#include -#include -#include -#include "PluginRender.h" - -#undef LOG_TAG -#define LOG_TAG "EGLCORE" - -namespace NativeXComponentSample { -namespace { -constexpr uint32_t LOG_PRINT_DOMAIN = 0xFF00; -constexpr int32_t NUM_4 = 4; -/** - * Vertex shader. - */ -const char VERTEX_SHADER[] = "#version 300 es\n" - "layout(location = 0) in vec4 a_position;\n" - "layout(location = 1) in vec4 a_color; \n" - "out vec4 v_color; \n" - "void main() \n" - "{ \n" - " gl_Position = a_position; \n" - " v_color = a_color; \n" - "} \n"; - -/** - * Fragment shader. - */ -const char FRAGMENT_SHADER[] = "#version 300 es\n" - "precision mediump float; \n" - "in vec4 v_color; \n" - "out vec4 fragColor; \n" - "void main() \n" - "{ \n" - " fragColor = v_color; \n" - "} \n"; - -/** - * Background color #f4f4f4. - */ -const GLfloat BACKGROUND_COLOR[] = {244.0f / 255, 244.0f / 255, 244.0f / 255, 1.0f}; - -/** - * Draw color #7E8FFB. - */ -const GLfloat DRAW_COLOR[] = {126.0f / 255, 143.0f / 255, 251.0f / 255, 1.0f}; - -/** - * Change color #92D6CC. - */ -const GLfloat CHANGE_COLOR[] = {146.0f / 255, 214.0f / 255, 204.0f / 255, 1.0f}; - -/** - * Background area. - */ -const GLfloat BACKGROUND_RECTANGLE_VERTICES[] = {-1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f}; - -/** - * Get context parameter count. - */ -const size_t GET_CONTEXT_PARAM_CNT = 1; - -/** - * Fifty percent. - */ -const float FIFTY_PERCENT = 0.5; - -/** - * Pointer size. - */ -const GLint POINTER_SIZE = 2; - -/** - * Triangle fan size. - */ -const GLsizei TRIANGLE_FAN_SIZE = 4; - -/** - * Egl red size default. - */ -const int EGL_RED_SIZE_DEFAULT = 8; - -/** - * Egl green size default. - */ -const int EGL_GREEN_SIZE_DEFAULT = 8; - -/** - * Egl blue size default. - */ -const int EGL_BLUE_SIZE_DEFAULT = 8; - -/** - * Egl alpha size default. - */ -const int EGL_ALPHA_SIZE_DEFAULT = 8; - -/** - * Default x position. - */ -const int DEFAULT_X_POSITION = 0; - -/** - * Default y position. - */ -const int DEFAULT_Y_POSITION = 0; - -/** - * Gl red default. - */ -const GLfloat GL_RED_DEFAULT = 0.0; - -/** - * Gl green default. - */ -const GLfloat GL_GREEN_DEFAULT = 0.0; - -/** - * Gl blue default. - */ -const GLfloat GL_BLUE_DEFAULT = 0.0; - -/** - * Gl alpha default. - */ -const GLfloat GL_ALPHA_DEFAULT = 1.0; - -/** - * Program error. - */ -const GLuint PROGRAM_ERROR = 0; - -/** - * Shape vertices size. - */ -const int SHAPE_VERTICES_SIZE = 8; - -/** - * Position handle name. - */ -const char POSITION_NAME[] = "a_position"; - -/** - * Position error. - */ -const GLint POSITION_ERROR = -1; - -/** - * Config attribute list. - */ -const EGLint ATTRIB_LIST[] = { - // Key,value. - EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_RED_SIZE, EGL_RED_SIZE_DEFAULT, EGL_GREEN_SIZE, EGL_GREEN_SIZE_DEFAULT, - EGL_BLUE_SIZE, EGL_BLUE_SIZE_DEFAULT, EGL_ALPHA_SIZE, EGL_ALPHA_SIZE_DEFAULT, EGL_RENDERABLE_TYPE, - EGL_OPENGL_ES2_BIT, - // End. - EGL_NONE}; - -/** - * Context attributes. - */ -const EGLint CONTEXT_ATTRIBS[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE}; -} // namespace -bool EGLCore::EglContextInit(void *window, int width, int height) { - OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "EGLCore", "EglContextInit execute"); - if ((window == nullptr) || (width <= 0) || (height <= 0)) { - OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "EglContextInit: param error"); - return false; - } - - UpdateSize(width, height); - eglWindow_ = reinterpret_cast(window); - - // Init display. - eglDisplay_ = eglGetDisplay(EGL_DEFAULT_DISPLAY); - if (eglDisplay_ == EGL_NO_DISPLAY) { - OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglGetDisplay: unable to get EGL display"); - return false; - } - - EGLint majorVersion; - EGLint minorVersion; - if (!eglInitialize(eglDisplay_, &majorVersion, &minorVersion)) { - OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", - "eglInitialize: unable to get initialize EGL display"); - return false; - } - - // Select configuration. - const EGLint maxConfigSize = 1; - EGLint numConfigs; - if (!eglChooseConfig(eglDisplay_, ATTRIB_LIST, &eglConfig_, maxConfigSize, &numConfigs)) { - OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglChooseConfig: unable to choose configs"); - return false; - } - - return CreateEnvironment(); -} - -bool EGLCore::CreateEnvironment() { - // Create surface. - if (!eglWindow_) { - OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglWindow_ is null"); - return false; - } - eglSurface_ = eglCreateWindowSurface(eglDisplay_, eglConfig_, eglWindow_, NULL); - if (eglSurface_ == nullptr) { - OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", - "eglCreateWindowSurface: unable to create surface"); - return false; - } - // Create context. - eglContext_ = eglCreateContext(eglDisplay_, eglConfig_, EGL_NO_CONTEXT, CONTEXT_ATTRIBS); - if (!eglMakeCurrent(eglDisplay_, eglSurface_, eglSurface_, eglContext_)) { - OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglMakeCurrent failed"); - return false; - } - // Create program. - program_ = CreateProgram(VERTEX_SHADER, FRAGMENT_SHADER); - if (program_ == PROGRAM_ERROR) { - OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "CreateProgram: unable to create program"); - return false; - } - return true; -} - -GLuint EGLCore::LoadShader(GLenum type, const char *shaderSrc) { - if ((type <= 0) || (shaderSrc == nullptr)) { - OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "glCreateShader type or shaderSrc error"); - return PROGRAM_ERROR; - } - - GLuint shader = glCreateShader(type); - if (shader == 0) { - OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "glCreateShader unable to load shader"); - return PROGRAM_ERROR; - } - - // The gl function has no return value. - glShaderSource(shader, 1, &shaderSrc, nullptr); - glCompileShader(shader); - - GLint compiled; - glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); - if (compiled != 0) { - return shader; - } - - GLint infoLen = 0; - glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen); - if (infoLen <= 1) { - glDeleteShader(shader); - return PROGRAM_ERROR; - } - - char *infoLog = (char *)malloc(sizeof(char) * (infoLen + 1)); - if (infoLog != nullptr) { - memset(infoLog, 0, infoLen + 1); - glGetShaderInfoLog(shader, infoLen, nullptr, infoLog); - OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "glCompileShader error = %s", infoLog); - free(infoLog); - infoLog = nullptr; - } - glDeleteShader(shader); - return PROGRAM_ERROR; -} - -GLuint EGLCore::CreateProgram(const char *vertexShader, const char *fragShader) { - if ((vertexShader == nullptr) || (fragShader == nullptr)) { - OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", - "createProgram: vertexShader or fragShader is null"); - return PROGRAM_ERROR; - } - - GLuint vertex = LoadShader(GL_VERTEX_SHADER, vertexShader); - if (vertex == PROGRAM_ERROR) { - OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "createProgram vertex error"); - return PROGRAM_ERROR; - } - - GLuint fragment = LoadShader(GL_FRAGMENT_SHADER, fragShader); - if (fragment == PROGRAM_ERROR) { - OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "createProgram fragment error"); - return PROGRAM_ERROR; - } - - GLuint program = glCreateProgram(); - if (program == PROGRAM_ERROR) { - OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "createProgram program error"); - glDeleteShader(vertex); - glDeleteShader(fragment); - return PROGRAM_ERROR; - } - - // The gl function has no return value. - glAttachShader(program, vertex); - glAttachShader(program, fragment); - glLinkProgram(program); - - GLint linked; - glGetProgramiv(program, GL_LINK_STATUS, &linked); - if (linked != 0) { - glDeleteShader(vertex); - glDeleteShader(fragment); - return program; - } - - OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "createProgram linked error"); - GLint infoLen = 0; - glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLen); - if (infoLen > 1) { - char *infoLog = (char *)malloc(sizeof(char) * (infoLen + 1)); - memset(infoLog, 0, infoLen + 1); - glGetProgramInfoLog(program, infoLen, nullptr, infoLog); - OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "glLinkProgram error = %s", infoLog); - free(infoLog); - infoLog = nullptr; - } - glDeleteShader(vertex); - glDeleteShader(fragment); - glDeleteProgram(program); - return PROGRAM_ERROR; -} - -void EGLCore::UpdateSize(int width, int height) { - width_ = width; - height_ = height; - if (width_ > 0) { - widthPercent_ = FIFTY_PERCENT * height_ / width_; - } -} - -void EGLCore::Release() { - if ((eglDisplay_ == nullptr) || (eglSurface_ == nullptr) || (!eglDestroySurface(eglDisplay_, eglSurface_))) { - OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Release eglDestroySurface failed"); - } - - if ((eglDisplay_ == nullptr) || (eglContext_ == nullptr) || (!eglDestroyContext(eglDisplay_, eglContext_))) { - OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Release eglDestroyContext failed"); - } - - if ((eglDisplay_ == nullptr) || (!eglTerminate(eglDisplay_))) { - OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Release eglTerminate failed"); - } -} -} // namespace NativeXComponentSample \ No newline at end of file diff --git a/entry/src/main/cpp/render/PluginManager.cpp b/entry/src/main/cpp/render/PluginManager.cpp index 3649775d4caed9d50170e0eefd95a4f68f2519c7..3367417b857c1654cfa124c9a3b1ec46d7c3bd67 100644 --- a/entry/src/main/cpp/render/PluginManager.cpp +++ b/entry/src/main/cpp/render/PluginManager.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * 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 diff --git a/entry/src/main/cpp/render/PluginRender.cpp b/entry/src/main/cpp/render/PluginRender.cpp index 415003d78737082608f786da161d1048ee375ee0..1e796be20d6e0bf5ea594f55643556af313e7280 100644 --- a/entry/src/main/cpp/render/PluginRender.cpp +++ b/entry/src/main/cpp/render/PluginRender.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * 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 @@ -51,11 +51,9 @@ void OnSurfaceCreatedCB(OH_NativeXComponent *component, void *window) { uint64_t height; int32_t xSize = OH_NativeXComponent_GetXComponentSize(component, window, &width, &height); if ((xSize == OH_NATIVEXCOMPONENT_RESULT_SUCCESS) && (render != nullptr)) { - if (render->eglCore_->EglContextInit(window, width, height)) { - auto context = PluginManager::GetInstance(); - context->pluginWindow_ = (OHNativeWindow *)window; - OH_NativeWindow_NativeWindowSetScalingModeV2(context->pluginWindow_, OH_SCALING_MODE_SCALE_FIT_V2); - } + auto context = PluginManager::GetInstance(); + context->pluginWindow_ = (OHNativeWindow *)window; + OH_NativeWindow_NativeWindowSetScalingModeV2(context->pluginWindow_, OH_SCALING_MODE_SCALE_FIT_V2); } } @@ -133,7 +131,6 @@ int32_t PluginRender::hasChangeColor_ = 0; PluginRender::PluginRender(std::string &id) { this->id_ = id; - this->eglCore_ = new EGLCore(); } PluginRender *PluginRender::GetInstance(std::string &id) { @@ -161,9 +158,6 @@ void PluginRender::Export(napi_env env, napi_value exports) { void PluginRender::Release(std::string &id) { PluginRender *render = PluginRender::GetInstance(id); if (render != nullptr) { - render->eglCore_->Release(); - delete render->eglCore_; - render->eglCore_ = nullptr; delete render; render = nullptr; instance_.erase(instance_.find(id)); @@ -188,9 +182,6 @@ void PluginRender::OnSurfaceChanged(OH_NativeXComponent *component, void *window uint64_t width; uint64_t height; OH_NativeXComponent_GetXComponentSize(component, window, &width, &height); - if (render != nullptr) { - render->eglCore_->UpdateSize(width, height); - } } void PluginRender::OnTouchEvent(OH_NativeXComponent *component, void *window) { diff --git a/entry/src/main/cpp/render/include/PluginManager.h b/entry/src/main/cpp/render/include/PluginManager.h index d9dfc992485f1572de3a8c6a89dff0c15b7ef340..5ddc225bbf92d71b7884005a24b6d3daba7895fa 100644 --- a/entry/src/main/cpp/render/include/PluginManager.h +++ b/entry/src/main/cpp/render/include/PluginManager.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * 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 diff --git a/entry/src/main/cpp/render/include/PluginRender.h b/entry/src/main/cpp/render/include/PluginRender.h index 5e03083fa224561fd29f173e1d4e38dd1b027cf0..bccae2e1a6dd29323962db5f09d6528f79ccc8ee 100644 --- a/entry/src/main/cpp/render/include/PluginRender.h +++ b/entry/src/main/cpp/render/include/PluginRender.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * 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 @@ -21,18 +21,11 @@ #include #include -#include "EglCore.h" - namespace NativeXComponentSample { class PluginRender { public: explicit PluginRender(std::string &id); ~PluginRender() { - if (eglCore_ != nullptr) { - eglCore_->Release(); - delete eglCore_; - eglCore_ = nullptr; - } } static PluginRender *GetInstance(std::string &id); static void Release(std::string &id); @@ -43,7 +36,6 @@ public: public: static std::unordered_map instance_; - EGLCore *eglCore_; std::string id_; static int32_t hasDraw_; static int32_t hasChangeColor_; diff --git a/entry/src/main/cpp/sample/player/Player.cpp b/entry/src/main/cpp/sample/player/Player.cpp index 1e571c2b73957380103582c8d5fc87facf6a1dc2..dc21f2ef3b6d2689aeba60fc44b1484330abcc60 100644 --- a/entry/src/main/cpp/sample/player/Player.cpp +++ b/entry/src/main/cpp/sample/player/Player.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * 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 @@ -22,7 +22,7 @@ #define LOG_TAG "player" namespace { -constexpr int BALANCE_VALUE = 5; +constexpr int BALANCE_VALUE = 2; using namespace std::chrono_literals; } // namespace @@ -170,6 +170,8 @@ int32_t Player::Start() { void Player::StartRelease() { AVCODEC_SAMPLE_LOGI("start release"); + std::unique_lock lock(doneMutex); + doneCond_.wait(lock, [this]() {return isAudioDone.load();}); if (audioRenderer_) { OH_AudioRenderer_Stop(audioRenderer_); } @@ -266,6 +268,7 @@ void Player::VideoDecInputThread() { } void Player::VideoDecOutputThread() { + isVideoDone = false; sampleInfo_.frameInterval = MICROSECOND / sampleInfo_.frameRate; while (true) { thread_local auto lastPushTime = std::chrono::system_clock::now(); @@ -292,6 +295,7 @@ void Player::VideoDecOutputThread() { std::this_thread::sleep_until(lastPushTime + std::chrono::microseconds(sampleInfo_.frameInterval)); lastPushTime = std::chrono::system_clock::now(); } + isVideoDone = true; StartRelease(); } @@ -321,6 +325,7 @@ void Player::AudioDecInputThread() { } void Player::AudioDecOutputThread() { + isAudioDone = false; while (true) { CHECK_AND_BREAK_LOG(isStarted_, "Decoder output thread out"); std::unique_lock lock(audioDecContext_->outputMutex); @@ -356,6 +361,7 @@ void Player::AudioDecOutputThread() { audioDecContext_->renderCond.wait_for(lockRender, 500ms, [this](){ return audioDecContext_->renderQueue.size() < 1; }); + isAudioDone = true; AVCODEC_SAMPLE_LOGI("Out buffer end"); StartRelease(); } \ No newline at end of file diff --git a/entry/src/main/cpp/sample/player/Player.h b/entry/src/main/cpp/sample/player/Player.h index 1359093bd2c840bf5f12867dbe731635fd3d038d..90321c6676dc3c97b7f3adae9a0597e24ee8339a 100644 --- a/entry/src/main/cpp/sample/player/Player.h +++ b/entry/src/main/cpp/sample/player/Player.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * 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 @@ -61,8 +61,11 @@ private: std::unique_ptr demuxer_ = nullptr; std::mutex mutex_; + std::mutex doneMutex; std::atomic isStarted_{false}; std::atomic isReleased_{false}; + std::atomic isVideoDone{true}; + std::atomic isAudioDone{true}; std::unique_ptr videoDecInputThread_ = nullptr; std::unique_ptr videoDecOutputThread_ = nullptr; std::unique_ptr audioDecInputThread_ = nullptr; diff --git a/entry/src/main/cpp/sample/player/PlayerNative.cpp b/entry/src/main/cpp/sample/player/PlayerNative.cpp index ef2262c519cfd1b7eba2231a1602deac46d9a5d3..d20a20fac241f7a8727793714cad4340a3702545 100644 --- a/entry/src/main/cpp/sample/player/PlayerNative.cpp +++ b/entry/src/main/cpp/sample/player/PlayerNative.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * 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 @@ -53,16 +53,14 @@ napi_value PlayerNative::Play(napi_env env, napi_callback_info info) { size_t argc = 4; napi_value args[4] = {nullptr}; napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); - - int32_t two = 2; - int32_t three = 3; + napi_get_value_int32(env, args[0], &sampleInfo.inputFd); napi_get_value_int64(env, args[1], &sampleInfo.inputFileOffset); - napi_get_value_int64(env, args[two], &sampleInfo.inputFileSize); + napi_get_value_int64(env, args[2], &sampleInfo.inputFileSize); auto asyncContext = new CallbackContext(); asyncContext->env = env; - napi_create_reference(env, args[three], 1, &asyncContext->callbackRef); + napi_create_reference(env, args[3], 1, &asyncContext->callbackRef); sampleInfo.playDoneCallback = &Callback; sampleInfo.playDoneCallbackData = asyncContext; diff --git a/entry/src/main/cpp/sample/player/PlayerNative.h b/entry/src/main/cpp/sample/player/PlayerNative.h index 75dc35af559748e379a280aa94c699a76edc7d90..0a300b0a7671da6ca78dd70814fcc59820657368 100644 --- a/entry/src/main/cpp/sample/player/PlayerNative.h +++ b/entry/src/main/cpp/sample/player/PlayerNative.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * 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 diff --git a/entry/src/main/cpp/sample/recorder/Recorder.cpp b/entry/src/main/cpp/sample/recorder/Recorder.cpp index 2688b3fbf536b384e66de76e02a2424006fdc24b..396fd732a2d40fb4983a11ae31d96e11eb4ee33b 100644 --- a/entry/src/main/cpp/sample/recorder/Recorder.cpp +++ b/entry/src/main/cpp/sample/recorder/Recorder.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * 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 @@ -14,7 +14,6 @@ */ #include "Recorder.h" -#include #include "AVCodecSampleLog.h" #include "dfx/error/AVCodecSampleError.h" @@ -24,6 +23,7 @@ namespace { using namespace std::chrono_literals; constexpr int64_t MICROSECOND = 1000000; +constexpr int32_t INPUT_FRAME_BYTES = 2 * 1024; } // namespace Recorder::~Recorder() { StartRelease(); } @@ -35,6 +35,9 @@ int32_t Recorder::Init(SampleInfo &sampleInfo) { "Already started."); sampleInfo_ = sampleInfo; + // Audio Capturer Init + audioEncoder_ = std::make_unique(); + audioCapturer_ = std::make_unique(); videoEncoder_ = std::make_unique(); muxer_ = std::make_unique(); @@ -44,13 +47,16 @@ int32_t Recorder::Init(SampleInfo &sampleInfo) { ret = muxer_->Create(sampleInfo_.outputFd); CHECK_AND_RETURN_RET_LOG(ret == AVCODEC_SAMPLE_ERR_OK, ret, "Create muxer with fd(%{public}d) failed", sampleInfo_.outputFd); + ret = muxer_->Config(sampleInfo_); - encContext_ = new CodecUserData; - ret = videoEncoder_->Config(sampleInfo_, encContext_); - CHECK_AND_RETURN_RET_LOG(ret == AVCODEC_SAMPLE_ERR_OK, ret, "Encoder config failed"); + ret = CreateAudioEncoder(); + CHECK_AND_RETURN_RET_LOG(ret == AVCODEC_SAMPLE_ERR_OK, ret, "Create audio encoder failed"); - ret = muxer_->Config(sampleInfo_); - CHECK_AND_RETURN_RET_LOG(ret == AVCODEC_SAMPLE_ERR_OK, ret, "Recorder muxer config failed"); + // Init AudioCapturer + audioCapturer_->AudioCapturerInit(sampleInfo_, audioEncContext_); + + ret = CreateVideoEncoder(); + CHECK_AND_RETURN_RET_LOG(ret == AVCODEC_SAMPLE_ERR_OK, ret, "Create video encoder failed"); sampleInfo.window = sampleInfo_.window; @@ -79,6 +85,27 @@ int32_t Recorder::Start() { return AVCODEC_SAMPLE_ERR_ERROR; } + if (audioEncContext_) { + // Start AudioCapturer + audioCapturer_->AudioCapturerStart(); + + ret = audioEncoder_->Start(); + CHECK_AND_RETURN_RET_LOG(ret == AVCODEC_SAMPLE_ERR_OK, ret, "Audio Encoder start failed"); + isStarted_ = true; + audioEncInputThread_ = std::make_unique(&Recorder::AudioEncInputThread, this); + audioEncOutputThread_ = std::make_unique(&Recorder::AudioEncOutputThread, this); + if (audioEncInputThread_ == nullptr || audioEncOutputThread_ == nullptr) { + AVCODEC_SAMPLE_LOGE("Create thread failed"); + StartRelease(); + return AVCODEC_SAMPLE_ERR_ERROR; + } + + // 清空播放缓存队列 + if (audioEncContext_ != nullptr) { + audioEncContext_->ClearCache(); + } + } + AVCODEC_SAMPLE_LOGI("Succeed"); return AVCODEC_SAMPLE_ERR_OK; } @@ -108,7 +135,8 @@ void Recorder::EncOutputThread() { encContext_->outputFrameCount, bufferInfo.attr.size, bufferInfo.attr.flags, bufferInfo.attr.pts); - muxer_->WriteSample(reinterpret_cast(bufferInfo.buffer), bufferInfo.attr); + muxer_->WriteSample(muxer_->GetVideoTrackId(), reinterpret_cast(bufferInfo.buffer), + bufferInfo.attr); int32_t ret = videoEncoder_->FreeOutputBuffer(bufferInfo.bufferIndex); CHECK_AND_BREAK_LOG(ret == AVCODEC_SAMPLE_ERR_OK, "Encoder output thread out"); } @@ -130,6 +158,16 @@ void Recorder::Release() { 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 (muxer_ != nullptr) { muxer_->Release(); muxer_.reset(); @@ -145,6 +183,21 @@ void Recorder::Release() { videoEncoder_.reset(); AVCODEC_SAMPLE_LOGI("Video encoder release successful"); } + if (audioEncoder_ != nullptr) { + audioEncoder_->Stop(); + audioEncoder_->Release(); + audioEncoder_.reset(); + AVCODEC_SAMPLE_LOGI("Audio encoder release successful"); + } + if (audioCapturer_ != nullptr) { + audioCapturer_->AudioCapturerRelease(); + audioCapturer_.reset(); + AVCODEC_SAMPLE_LOGI("Audio Capturer release successful"); + } + if (audioEncContext_ != nullptr) { + delete audioEncContext_; + audioEncContext_ = nullptr; + } if (encContext_ != nullptr) { delete encContext_; encContext_ = nullptr; @@ -169,4 +222,90 @@ int32_t Recorder::Stop() { int32_t ret = videoEncoder_->NotifyEndOfStream(); CHECK_AND_RETURN_RET_LOG(ret == AVCODEC_SAMPLE_ERR_OK, ret, "Encoder notifyEndOfStream failed"); return WaitForDone(); +} + +int32_t Recorder::CreateVideoEncoder() { + int32_t ret = videoEncoder_->Create(sampleInfo_.videoCodecMime); + CHECK_AND_RETURN_RET_LOG(ret == AVCODEC_SAMPLE_ERR_OK, ret, "Create video encoder failed"); + + encContext_ = new CodecUserData; + ret = videoEncoder_->Config(sampleInfo_, encContext_); + CHECK_AND_RETURN_RET_LOG(ret == AVCODEC_SAMPLE_ERR_OK, ret, "Encoder config failed"); + + return AVCODEC_SAMPLE_ERR_OK; +} + +int32_t Recorder::CreateAudioEncoder() { + int32_t ret = audioEncoder_->Create(sampleInfo_.audioCodecMime); + CHECK_AND_RETURN_RET_LOG(ret == AVCODEC_SAMPLE_ERR_OK, ret, "Create audio encoder(%{public}s) failed", + sampleInfo_.audioCodecMime.c_str()); + AVCODEC_SAMPLE_LOGI("Create audio encoder(%{public}s)", sampleInfo_.audioCodecMime.c_str()); + + audioEncContext_ = new CodecUserData; + ret = audioEncoder_->Config(sampleInfo_, audioEncContext_); + CHECK_AND_RETURN_RET_LOG(ret == AVCODEC_SAMPLE_ERR_OK, ret, "Encoder config failed"); + + return AVCODEC_SAMPLE_ERR_OK; +} + +void Recorder::AudioEncInputThread() { + while (true) { + CHECK_AND_BREAK_LOG(isStarted_, "Encoder input thread out"); + std::unique_lock lock(audioEncContext_->inputMutex); + bool condRet = audioEncContext_->inputCond.wait_for(lock, 5s, [this]() { + return !isStarted_ || (!audioEncContext_->inputBufferInfoQueue.empty() && + (audioEncContext_->remainlen >= sampleInfo_.audioMaxInputSize)); + }); + + CHECK_AND_CONTINUE_LOG(!audioEncContext_->inputBufferInfoQueue.empty(), + "Audio Buffer queue is empty, continue, cond ret: %{public}d", condRet); + + if (isStarted_ && audioEncContext_->remainlen < sampleInfo_.audioMaxInputSize) { + continue; + } + + CodecBufferInfo bufferInfo = audioEncContext_->inputBufferInfoQueue.front(); + audioEncContext_->inputBufferInfoQueue.pop(); + audioEncContext_->inputFrameCount++; + + uint8_t *inputBufferAddr = OH_AVBuffer_GetAddr(reinterpret_cast(bufferInfo.buffer)); + audioEncContext_->ReadCache(inputBufferAddr, sampleInfo_.audioMaxInputSize); + 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); + CHECK_AND_BREAK_LOG(ret == AVCODEC_SAMPLE_ERR_OK, "Push data failed, thread out"); + } +} + +void Recorder::AudioEncOutputThread() { + while (true) { + CHECK_AND_BREAK_LOG(isStarted_, "Work done, thread out"); + std::unique_lock lock(audioEncContext_->outputMutex); + bool condRet = audioEncContext_->outputCond.wait_for( + lock, 5s, [this]() { return !isStarted_ || !audioEncContext_->outputBufferInfoQueue.empty(); }); + CHECK_AND_BREAK_LOG(isStarted_, "Work done, thread out"); + CHECK_AND_CONTINUE_LOG(!audioEncContext_->outputBufferInfoQueue.empty(), + "Buffer queue is empty, continue, cond ret: %{public}d", condRet); + + CodecBufferInfo bufferInfo = audioEncContext_->outputBufferInfoQueue.front(); + audioEncContext_->outputBufferInfoQueue.pop(); + audioEncContext_->outputFrameCount++; + AVCODEC_SAMPLE_LOGW( + "Audio Out buffer count: %{public}u, size: %{public}d, flag: %{public}u, pts: %{public}" PRId64, + audioEncContext_->outputFrameCount, bufferInfo.attr.size, bufferInfo.attr.flags, bufferInfo.attr.pts); +// lock.unlock(); + muxer_->WriteSample(muxer_->GetAudioTrackId(), reinterpret_cast(bufferInfo.buffer), + bufferInfo.attr); + int32_t ret = audioEncoder_->FreeOutputData(bufferInfo.bufferIndex); + CHECK_AND_BREAK_LOG(ret == AVCODEC_SAMPLE_ERR_OK, "Encoder output thread out"); + } + AVCODEC_SAMPLE_LOGI("Exit, frame count: %{public}u", audioEncContext_->inputFrameCount); + StartRelease(); } \ No newline at end of file diff --git a/entry/src/main/cpp/sample/recorder/Recorder.h b/entry/src/main/cpp/sample/recorder/Recorder.h index 5fd7b2863bb8f4b635f57539e16dda571a351446..13a46e25db4860b3053878de58a520dc369ce891 100644 --- a/entry/src/main/cpp/sample/recorder/Recorder.h +++ b/entry/src/main/cpp/sample/recorder/Recorder.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * 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 @@ -23,8 +23,10 @@ #include #include #include "VideoEncoder.h" +#include "AudioEncoder.h" #include "Muxer.h" #include "SampleInfo.h" +#include class Recorder { public: @@ -42,20 +44,32 @@ public: private: void EncOutputThread(); + void AudioEncInputThread(); + void AudioEncOutputThread(); void Release(); void StartRelease(); int32_t WaitForDone(); + + int32_t CreateAudioEncoder(); + int32_t CreateVideoEncoder(); std::unique_ptr videoEncoder_ = nullptr; + std::unique_ptr audioEncoder_ = nullptr; std::unique_ptr muxer_ = nullptr; std::mutex mutex_; std::atomic isStarted_{false}; + int32_t isFirstFrame_ = true; std::unique_ptr encOutputThread_ = nullptr; + std::unique_ptr audioEncInputThread_ = nullptr; + std::unique_ptr audioEncOutputThread_ = nullptr; std::unique_ptr releaseThread_ = nullptr; std::condition_variable doneCond_; SampleInfo sampleInfo_; CodecUserData *encContext_ = nullptr; + CodecUserData *audioEncContext_ = nullptr; + + std::unique_ptr audioCapturer_ = nullptr; }; #endif // VIDEO_CODEC_SAMPLE_RECODER_H \ No newline at end of file diff --git a/entry/src/main/cpp/sample/recorder/RecorderNative.cpp b/entry/src/main/cpp/sample/recorder/RecorderNative.cpp index fe404a9f78b8bed5b28f2be2c9f192ee574c1934..91894ea9e06f930b84c1f6db57a7afa47a27466b 100644 --- a/entry/src/main/cpp/sample/recorder/RecorderNative.cpp +++ b/entry/src/main/cpp/sample/recorder/RecorderNative.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * 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 @@ -69,11 +69,6 @@ void NativeInit(napi_env env, void *data) { } napi_value RecorderNative::Init(napi_env env, napi_callback_info info) { - int32_t two = 2; - int32_t three = 3; - int32_t four = 4; - int32_t five = 5; - int32_t six = 6; SampleInfo sampleInfo; size_t argc = 7; napi_value args[7] = {nullptr}; @@ -84,16 +79,25 @@ napi_value RecorderNative::Init(napi_env env, napi_callback_info info) { size_t videoCodecMimeStrlen = 0; size_t len = 20; napi_get_value_string_utf8(env, args[1], videoCodecMime, len, &videoCodecMimeStrlen); - napi_get_value_int32(env, args[two], &sampleInfo.videoWidth); - napi_get_value_int32(env, args[three], &sampleInfo.videoHeight); - napi_get_value_double(env, args[four], &sampleInfo.frameRate); - napi_get_value_int32(env, args[five], &sampleInfo.isHDRVivid); - napi_get_value_int64(env, args[six], &sampleInfo.bitrate); + napi_get_value_int32(env, args[2], &sampleInfo.videoWidth); + napi_get_value_int32(env, args[3], &sampleInfo.videoHeight); + napi_get_value_double(env, args[4], &sampleInfo.frameRate); + napi_get_value_int32(env, args[5], &sampleInfo.isHDRVivid); + napi_get_value_int64(env, args[6], &sampleInfo.bitrate); sampleInfo.videoCodecMime = videoCodecMime; if (sampleInfo.isHDRVivid) { sampleInfo.hevcProfile = HEVC_PROFILE_MAIN_10; } + + sampleInfo.audioCodecMime = OH_AVCODEC_MIMETYPE_AUDIO_AAC; + sampleInfo.audioSampleForamt = OH_BitsPerSample::SAMPLE_S16LE; + sampleInfo.audioSampleRate = 48000; + sampleInfo.audioChannelCount = 2; + sampleInfo.audioBitRate = 32000; + sampleInfo.audioChannelLayout = OH_AudioChannelLayout::CH_LAYOUT_STEREO; + sampleInfo.audioMaxInputSize = sampleInfo.audioSampleRate * 0.02 * sampleInfo.audioChannelCount * sizeof(short); + napi_value promise; napi_deferred deferred; diff --git a/entry/src/main/cpp/sample/recorder/RecorderNative.h b/entry/src/main/cpp/sample/recorder/RecorderNative.h index adad4cb98ae6f6239ef1c9a44661cba7fa503f2b..a344c8a170930990df81350bd71735012b2c5de0 100644 --- a/entry/src/main/cpp/sample/recorder/RecorderNative.h +++ b/entry/src/main/cpp/sample/recorder/RecorderNative.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * 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 diff --git a/entry/src/main/cpp/types/libplayer/index.d.ts b/entry/src/main/cpp/types/libplayer/index.d.ts index 35995e8d521657b9271f0e24c2d3336229a7b4b4..ddfbc13ebc9d32ec0dbd90dbd5d6940599edf3f6 100644 --- a/entry/src/main/cpp/types/libplayer/index.d.ts +++ b/entry/src/main/cpp/types/libplayer/index.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * 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 diff --git a/entry/src/main/cpp/types/libplayer/oh-package.json5 b/entry/src/main/cpp/types/libplayer/oh-package.json5 index 9b895e04180f589cb0f7ff26ce3a4631dc439236..d8dedda613c9474e7e40ecc691dafacc4bdca9a0 100644 --- a/entry/src/main/cpp/types/libplayer/oh-package.json5 +++ b/entry/src/main/cpp/types/libplayer/oh-package.json5 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * 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 diff --git a/entry/src/main/cpp/types/librecorder/index.d.ts b/entry/src/main/cpp/types/librecorder/index.d.ts index 7bd6fcfe9195eea4d3f5f7ba8800200d261c91a0..880ad56d92b48d415fa60062f874b5ccc9f864ee 100644 --- a/entry/src/main/cpp/types/librecorder/index.d.ts +++ b/entry/src/main/cpp/types/librecorder/index.d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * 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 diff --git a/entry/src/main/cpp/types/librecorder/oh-package.json5 b/entry/src/main/cpp/types/librecorder/oh-package.json5 index bb79f73394101892d4c1e736062bef7740bd06cb..2b35556f4feee9d2a8f4044459c3cec6d9059535 100644 --- a/entry/src/main/cpp/types/librecorder/oh-package.json5 +++ b/entry/src/main/cpp/types/librecorder/oh-package.json5 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * 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 diff --git a/entry/src/main/ets/common/CommonConstants.ets b/entry/src/main/ets/common/CommonConstants.ets index e6e1af7bf7254d66f1e03a43dbc58dd3e5ba1b68..c9cc1c1bc27e6584b249603f883d9542457a9d6e 100644 --- a/entry/src/main/ets/common/CommonConstants.ets +++ b/entry/src/main/ets/common/CommonConstants.ets @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * 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 diff --git a/entry/src/main/ets/common/utils/CameraCheck.ets b/entry/src/main/ets/common/utils/CameraCheck.ets index 8320765db763d7438defb36c9153e40f83b5c429..30324ff88399ee771952a70398b2ccf9407ebac7 100644 --- a/entry/src/main/ets/common/utils/CameraCheck.ets +++ b/entry/src/main/ets/common/utils/CameraCheck.ets @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * 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 @@ -31,7 +31,7 @@ function getPreviewProfile(previewProfiles: Array, profile.size.width === size.width && profile.size.height == size.height } }); - Logger.error(TAG, `profile.format: ${previewProfile!.size.width}`); + Logger.info(TAG, `profile.format: ${previewProfile!.size.width}`); return previewProfile; } diff --git a/entry/src/main/ets/common/utils/DateTimeUtils.ets b/entry/src/main/ets/common/utils/DateTimeUtils.ets index c956b711445ea0fc2ec6087fece0ce06f6f25d64..77de33fb45d626db23b502d927c89aadd6a8d84c 100644 --- a/entry/src/main/ets/common/utils/DateTimeUtils.ets +++ b/entry/src/main/ets/common/utils/DateTimeUtils.ets @@ -1,5 +1,5 @@ /* - * Copyright (C) 2024 Huawei Device Co., Ltd. + * 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 diff --git a/entry/src/main/ets/common/utils/Logger.ets b/entry/src/main/ets/common/utils/Logger.ets index 7293357f7995f70603422ec46b42103123e47623..82525da9ada62154d39805784a9fa05750ac19ce 100644 --- a/entry/src/main/ets/common/utils/Logger.ets +++ b/entry/src/main/ets/common/utils/Logger.ets @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * 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 diff --git a/entry/src/main/ets/entryability/EntryAbility.ets b/entry/src/main/ets/entryability/EntryAbility.ets index 7796bdab54077c986e51c0f5b93b9666d87e11c6..61c02a3d4d43e2852381516046bb0581aee805e1 100644 --- a/entry/src/main/ets/entryability/EntryAbility.ets +++ b/entry/src/main/ets/entryability/EntryAbility.ets @@ -1,5 +1,5 @@ /* - * Copyright (C) 2024 Huawei Device Co., Ltd. + * 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 @@ -34,7 +34,7 @@ export default class EntryAbility extends UIAbility { try { let atManager = abilityAccessCtrl.createAtManager(); - atManager.requestPermissionsFromUser(this.context, ['ohos.permission.CAMERA']) + atManager.requestPermissionsFromUser(this.context, ['ohos.permission.CAMERA', 'ohos.permission.MICROPHONE']) .then((data) => { Logger.info('Entry', 'requestPre() data: ' + JSON.stringify(data)); }).catch((err: BusinessError) => { diff --git a/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets b/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets index 62cb15a36f79397d9ac3c07fefb163a0087e1ca5..7b815b479cc363e7c9f0db13131d879fa9b790f9 100644 --- a/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets +++ b/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets @@ -1,5 +1,5 @@ /* - * Copyright (C) 2024 Huawei Device Co., Ltd. + * 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 diff --git a/entry/src/main/ets/model/CameraDateModel.ets b/entry/src/main/ets/model/CameraDateModel.ets index e17383bd1a8f013174f054da48e0c8313c621a90..6b7d62dfd4ce497cecf73b85580ed0b6c930e7b5 100644 --- a/entry/src/main/ets/model/CameraDateModel.ets +++ b/entry/src/main/ets/model/CameraDateModel.ets @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * 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 diff --git a/entry/src/main/ets/pages/Index.ets b/entry/src/main/ets/pages/Index.ets index 6bcd909f5d06bda4609fca0efd6a79955054c1ee..33085dcd7f3d8ebbd85d67c9a076c9d8d12c62dd 100644 --- a/entry/src/main/ets/pages/Index.ets +++ b/entry/src/main/ets/pages/Index.ets @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * 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 diff --git a/entry/src/main/ets/pages/Recorder.ets b/entry/src/main/ets/pages/Recorder.ets index 79c030d2990bd48560afa249598a6bc1a13caf1d..2d1264082b10a4793cc47cfe7f2500b1f75bb0ae 100644 --- a/entry/src/main/ets/pages/Recorder.ets +++ b/entry/src/main/ets/pages/Recorder.ets @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * 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 @@ -26,7 +26,7 @@ import { CameraDataModel } from '../model/CameraDateModel'; import { previewProfileCameraCheck, videoProfileCheck } from '../common/utils/CameraCheck'; const TAG: string = Const.RECORDER_TAG; -const context = AppStorage.get("context") as UIContext; +const context = AppStorage.get('context') as UIContext; const params: CameraDataModel = context.getRouter().getParams() as CameraDataModel; let cameraInput: camera.CameraInput; diff --git a/entry/src/main/module.json5 b/entry/src/main/module.json5 index 8bd98e4a1f52a0fd9ff1566cbb38779a2112cb45..188c5bd69bc658a7cf344b3bff5f00b244ab36bf 100644 --- a/entry/src/main/module.json5 +++ b/entry/src/main/module.json5 @@ -49,11 +49,19 @@ "requestPermissions": [ { "name": "ohos.permission.CAMERA", - "reason": "$string:EntryAbility_desc", + "reason": "$string:camera_reason", "usedScene": { "abilities": ["EntryAbility"], "when": "always" } + }, + { + "name": "ohos.permission.MICROPHONE", + "reason": "$string:reason", + "usedScene": { + "abilities": ["FormAbility"], + "when": "inuse" + } } ] } diff --git a/entry/src/main/resources/base/element/string.json b/entry/src/main/resources/base/element/string.json index 6be5677fe5b289fa6016e306a0667098c35ad31c..f273e844e5a38b177040e273e6c2caa3068f9b1f 100644 --- a/entry/src/main/resources/base/element/string.json +++ b/entry/src/main/resources/base/element/string.json @@ -26,7 +26,11 @@ }, { "name": "reason", - "value": "label" + "value": "allows an app to use the microphone" + }, + { + "name": "camera_reason", + "value": "allows an app to use the camera" }, { "name": "playing", diff --git a/entry/src/main/resources/en_US/element/string.json b/entry/src/main/resources/en_US/element/string.json index 6be5677fe5b289fa6016e306a0667098c35ad31c..f273e844e5a38b177040e273e6c2caa3068f9b1f 100644 --- a/entry/src/main/resources/en_US/element/string.json +++ b/entry/src/main/resources/en_US/element/string.json @@ -26,7 +26,11 @@ }, { "name": "reason", - "value": "label" + "value": "allows an app to use the microphone" + }, + { + "name": "camera_reason", + "value": "allows an app to use the camera" }, { "name": "playing", diff --git a/entry/src/main/resources/zh_CN/element/string.json b/entry/src/main/resources/zh_CN/element/string.json index 91621817b7d2729e91d7fe6fb5ee12f4fd9768e1..a187edd53cd06470073572f5e3450d0abafd8299 100644 --- a/entry/src/main/resources/zh_CN/element/string.json +++ b/entry/src/main/resources/zh_CN/element/string.json @@ -26,7 +26,11 @@ }, { "name": "reason", - "value": "label" + "value": "允许应用使用麦克风" + }, + { + "name": "camera_reason", + "value": "允许应用使用相机" }, { "name": "playing", diff --git a/oh-package.json5 b/oh-package.json5 index 490e08c08b4db775f0c5887aabe564c0e36e2b44..c87d8717cbe065f51d70bdc5b427792508b9d92b 100644 --- a/oh-package.json5 +++ b/oh-package.json5 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2024 Huawei Device Co., Ltd. + * 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 diff --git a/screenshots/device/AVCodecSample.en.gif b/screenshots/device/AVCodecSample.en.gif index d4374f9bd14b32e50ca732999b6432373cb48741..4612428d35328cb37b3f21ab25bc5337e8d854b3 100644 Binary files a/screenshots/device/AVCodecSample.en.gif and b/screenshots/device/AVCodecSample.en.gif differ diff --git a/screenshots/device/AVCodecSample.gif b/screenshots/device/AVCodecSample.gif index 04e0f77b71a2c786ab86caf3d727a92ed13c0af4..b819db40469bdc097d14e5a93223627ce236d673 100644 Binary files a/screenshots/device/AVCodecSample.gif and b/screenshots/device/AVCodecSample.gif differ diff --git a/screenshots/device/AVCodec_Index.en.png b/screenshots/device/AVCodec_Index.en.png index 4d04a4c8b6f0474107f745144473fe457ade61f3..b0817c678a8cf1058f769a9d687f97eedb5aa485 100644 Binary files a/screenshots/device/AVCodec_Index.en.png and b/screenshots/device/AVCodec_Index.en.png differ diff --git a/screenshots/device/AVCodec_Index.png b/screenshots/device/AVCodec_Index.png index 2be1928bded5808dbda7f5d4e710bac60b6ce662..a3cdf31d6a86c8b7dfa45d2a8970a24aa871817b 100644 Binary files a/screenshots/device/AVCodec_Index.png and b/screenshots/device/AVCodec_Index.png differ