diff --git a/.gitignore b/.gitignore deleted file mode 100644 index b66e151593388f1d07634527242d4edfebb8a85f..0000000000000000000000000000000000000000 --- a/.gitignore +++ /dev/null @@ -1,14 +0,0 @@ -/node_modules -/oh_modules -/local.properties -/.idea -**/build -/.hvigor -.cxx -/.clangd -/.clang-format -/.clang-tidy -**/.test -/.appanalyzer -log.txt -.DS_Store \ No newline at end of file diff --git a/README.en.md b/README.en.md index f02e763c5853457998c25e1c246fa1bf9ad00189..6e39a28d5e18d425fa899483880140bfa65bda32 100644 --- a/README.en.md +++ b/README.en.md @@ -7,13 +7,11 @@ and convert it to SDR by leveraging hardware acceleration and color-space optimi ## Effect -| Home Page | Recording Page | -|--------------------------------------------------------|------------------------------------------------------| -| | | - -| Transcoding Page | Save Dialog | -|------------------------------------------------------------|---------------------------------------------------------| -| | | +| 首页 | 播放页 | +|-----------------------------------------------------------|--------------------------------------------------------------| +| | | +| 录制页 | 转码页 | +| | | ## How to Use diff --git a/README.md b/README.md index 78f89a635a9248b0a1f417fd4aef9a4d9d42f37a..476a19fac80c704aba7a7181ae90d12d69f5776d 100644 --- a/README.md +++ b/README.md @@ -6,68 +6,80 @@ ## 效果图预览 -| 首页 | 录制页 | -|-----------------------------------------------------|---------------------------------------------| -| | | - -| 转码页 | 保存弹窗 | -|---------------------------------------------------------|------------------------------------------------------| -| | | +| 首页 | 播放页 | +|----------------------------------------------------------|--------------------------------------------------------------| +| | | +| 录制页 | 转码页 | +| | | ## 使用说明 -1. 打开应用,点击HDR视频录制,选择Recorder或AVCodec封装方法。 +1. 打开应用,点击AVRecorder或AVCodec进行HDR视频录制。 2. 点击录制视频,完成录制后返回,点击视频播放或暂停视频。 3. 点击保存视频,确认保存。 4. 返回首页,点击HDR视频转码。 5. 点击屏幕选择视频,点击AVCodec或Video_Processing进行SDR转码并送显。 +> **注意**: +> 1. AVRecorder录制不支持设置AVMetadata音视频元数据的HDR类型。 +> 2. 在使用转码的功能时,请选择HDR类型的视频,HDR视频在相册中有HDR标识。 + ## 工程目录 ``` ├──entry/src/main/cpp // Native层 -│ ├──CMakeLists.txt // 编译入口 │ ├──capbilities // 接口能力实现 -│ ├──AudioCapturer.cpp // 音频采集实现 -│ ├──AudioEncoder.cpp // 音频编码实现 -│ ├──Demuxer.cpp // 解封装实现 -│ ├──Muxer.cpp // 封装实现 -│ ├──AudioDecoder.cpp // 转码音频解码实现 -│ ├──Demuxer.cpp // 转码解封装实现 +│ │ ├──include // 编解码模块接口定义 +│ │ ├──AudioCapturer.cpp // 音频采集实现 +│ │ ├──AudioDecoder.cpp // 转码音频解码实现 +│ │ ├──AudioEncoder.cpp // 音频编码实现 +│ │ ├──Demuxer.cpp // 解封装实现 +│ │ ├──Muxer.cpp // 封装实现 │ │ ├──VideoDecoder.cpp // 转码视频解码实现 │ │ ├──VideoEncoder.cpp // 视频编码实现 -│ │ └──include // 接口能力定义 +│ │ └──VideoProcessing.cpp // 视频转换实现 │ ├──common // 公共模块 +│ │ ├──dfx // 日志实现 │ │ ├──SampleCallback.cpp // 编解码回调实现 │ │ ├──SampleCallback.h // 编解码回调定义 -│ │ ├──SampleInfo.h // 公共类 -│ │ └──dfx // 日志实现 +│ │ └──SampleInfo.h // 公共类 │ ├──render // 送显模块 +│ │ ├──include // 送显模块接口定义 │ │ ├──PluginManager.cpp // 送显模块管理实现 -│ │ ├──PluginRender.cpp // 送显模块逻辑实现 -│ │ └──include // 送显模块接口定义 +│ │ └──PluginRender.cpp // 送显模块逻辑实现 │ ├──sample │ │ ├──player // 转码播放接口 │ │ │ ├──Player.cpp // 转码播放接口实现 │ │ │ ├──Player.h // 转码播放接口定义 │ │ │ ├──PlayerNative.cpp // 转码接口调用入口 │ │ │ └──PlayerNative.h // 调用入口定义 -│ │ └──recorder // 录制接口 -│ │ ├──Recorder.cpp // 录制功能接口实现 -│ │ ├──Recorder.h // 录制功能接口定义 -│ │ ├──RecorderNative.cpp // 录制接口调用入口 -│ │ └──RecorderNative.h // 调用入口定义 -│ └──types // Native层暴露上来的接口 -│ ├──libplayer // 转码播放模块暴露给UI层的接口 -│ └──librecorder // 录制模块暴露给UI层的接口 +│ │ ├──recorder // 录制接口 +│ │ │ ├──Recorder.cpp // 录制功能接口实现 +│ │ │ ├──Recorder.h // 录制功能接口定义 +│ │ │ ├──RecorderNative.cpp // 录制接口调用入口 +│ │ │ └──RecorderNative.h // 调用入口定义 +│ │ └──transcoder // 转码接口 +│ │ ├──AVCodecTranscoder.cpp // AVCodec转码功能接口实现 +│ │ ├──AVCodecTranscoder.h // AVCodec转码功能接口定义 +│ │ ├──AVTranscoder.cpp // AVTranscoder转码功能接口实现 +│ │ ├──AVTranscoder.h // AVTranscoder转码功能接口定义 +│ │ ├──TranscoderNative.cpp // 转码接口调用入口 +│ │ └──TranscoderNative.h // 转码调用入口定义 +│ ├──types // Native层暴露上来的接口 +│ │ ├──libplayer // 转码播放模块暴露给UI层的接口 +│ │ ├──librecorder // 录制模块暴露给UI层的接口 +│ │ └──libtranscoder // 转码模块暴露给UI层的接口 +│ └──CMakeLists.txt // 编译入口 ├──ets // UI层 │ ├──common // 公共模块 +│ │ ├──utils // 公共工具类 +│ │ │ ├──DateTimeUtils.ets // 时间工具类 +│ │ │ ├──FileUtil.ets // 文件工具类 +│ │ │ ├──Logger.ets // 日志类 +│ │ │ ├──PermissionsUtils.ets // 申请权限类 +│ │ │ └──VideoOperationUtils.ets // 保存文件类 │ │ ├──CommonConstants.ets // 参数常量 -│ │ └──utils // 公共工具类 -│ │ ├──DateTimeUtils.ets // 时间工具类 -│ │ ├──FileUtil.ets // 文件工具类 -│ │ ├──PermissionsUtils.ets // 申请权限类 -│ │ └──VideoOperationUtils.ets // 保存文件类 +│ │ └──CommonEnum.ets // 枚举类 │ ├──controller // 控制模块 │ │ ├──AVCodecController.ets // AVcodec控制类 │ │ ├──AvPlayerController.ets // AVplayer播放类 @@ -79,10 +91,13 @@ │ ├──model │ │ └──CameraDateModel.ets // 相机参数数据类 │ ├──pages +│ │ ├──AVCodecPage.ets // 视频转码页 +│ │ ├──AVCodecPage.ets // 视频AVCodec播放页 +│ │ ├──AVPlayerPage.ets // 视频AVPlayer播放页 +│ │ ├──AVTranscoderPage.ets // 视频AVTranscoder转码页 │ │ ├──CameraPage.ets // 相机录制页 │ │ ├──Index.ets // 首页 -│ │ ├──Recording.ets // 视频录制页 -│ │ └──Transcode.ets // 视频转码页面 +│ │ └──Recording.ets // 视频录制页 │ └──view // 组件模块 │ ├──MultiStatusButton.ets // 多选框组件 │ └──SaveDialog.ets // 保存弹窗 @@ -101,7 +116,7 @@ 视频转码: -1. 用户成功选择文件后,调用playNative转码的接口。 +1. 用户成功选择文件后,调用转码的接口。 2. 开始转码前,调用init函数初始化解封装器、封装器、编码器、解码器。同时保存上下文参数。 3. 调用Start函数开始解码,开启解码线程包括输入子线程和输出子线程对视频数据进行解码。 4. 在输入子线程中,使用解封装后的bufferInfo,调用解码的PushInputData接口将帧buffer、index存入输入队列中。在输出子线程中,把上一步的帧信息储存为bufferInfo后,pop出队,调用FreeOutputData接口后,就会送显并释放buffer。 diff --git a/entry/build-profile.json5 b/entry/build-profile.json5 index 7a38cc838fbeffc2de2ee08dfed997628a3d9e74..ca8d2eb8b157342c338ef11995c83f7b9a0f4086 100644 --- a/entry/build-profile.json5 +++ b/entry/build-profile.json5 @@ -32,9 +32,6 @@ "targets": [ { "name": "default" - }, - { - "name": "ohosTest", } ] } \ No newline at end of file diff --git a/entry/oh-package.json5 b/entry/oh-package.json5 index ec7d872486ca227b261d1fa73532c3fb82cf8cd0..e94da9f95e8eade042bca00c3378a42a0360f022 100644 --- a/entry/oh-package.json5 +++ b/entry/oh-package.json5 @@ -7,6 +7,7 @@ "license": "", "dependencies": { "librecorder.so": "file:./src/main/cpp/types/librecorder", - "libplayer.so": "file:./src/main/cpp/types/libplayer" + "libplayer.so": "file:./src/main/cpp/types/libplayer", + "libtranscoder.so": "file:./src/main/cpp/types/libtranscoder" } } \ No newline at end of file diff --git a/entry/src/main/cpp/CMakeLists.txt b/entry/src/main/cpp/CMakeLists.txt index e1f0111004185edb0e8694bb4327fb5603853702..ea97eb2354a9a7efcd376c7f588ba65188fdc452 100644 --- a/entry/src/main/cpp/CMakeLists.txt +++ b/entry/src/main/cpp/CMakeLists.txt @@ -25,6 +25,7 @@ include_directories(${NATIVERENDER_ROOT_PATH} ${NATIVERENDER_ROOT_PATH}/render/include ${NATIVERENDER_ROOT_PATH}/sample/player ${NATIVERENDER_ROOT_PATH}/sample/recorder + ${NATIVERENDER_ROOT_PATH}/sample/transcoder ) set(BASE_LIBRARY @@ -32,6 +33,7 @@ set(BASE_LIBRARY libnative_media_codecbase.so libnative_media_core.so libnative_media_vdec.so libnative_window.so libnative_media_venc.so libnative_media_acodec.so libnative_media_avdemuxer.so libnative_media_avsource.so libnative_media_avmuxer.so libohaudio.so + libvideo_processing.so ) set(VIDEO_BASE_LIBRARY libpixelmap_ndk.z.so @@ -56,6 +58,34 @@ set(VIDEO_BASE_LIBRARY libnative_image.so libohaudio.so libpixelmap.so + libvideo_processing.so +) + +set(TRANSCODER_BASE_LIBRARY + libpixelmap_ndk.z.so + libace_napi.z.so + libEGL.so + libGLESv3.so + libace_ndk.z.so + libuv.so + libhilog_ndk.z.so + libvideo_processing.so + libnative_media_codecbase.so + libnative_media_core.so + libnative_media_vdec.so + libnative_window.so + libnative_media_venc.so + libnative_media_acodec.so + libnative_media_avdemuxer.so + libnative_media_avsource.so + libnative_media_avmuxer.so + libnative_buffer.so + libnative_vsync.so + libnative_image.so + libohaudio.so + libpixelmap.so + libavtranscoder.so + libvideo_processing.so ) add_library(recorder SHARED sample/recorder/RecorderNative.cpp @@ -65,7 +95,7 @@ add_library(recorder SHARED sample/recorder/RecorderNative.cpp capbilities/AudioCapturer.cpp capbilities/AudioEncoder.cpp common/SampleCallback.cpp -) + capbilities/VideoProcessing.cpp) add_library(player SHARED sample/player/PlayerNative.cpp sample/player/Player.cpp @@ -77,5 +107,21 @@ add_library(player SHARED sample/player/PlayerNative.cpp common/SampleCallback.cpp ) +add_library(transcoder SHARED sample/transcoder/TranscoderNative.cpp + sample/transcoder/AVTranscoder.cpp + sample/transcoder/AVCodecTranscoder.cpp + capbilities/Muxer.cpp + capbilities/Demuxer.cpp + capbilities/VideoEncoder.cpp + capbilities/VideoDecoder.cpp + capbilities/AudioEncoder.cpp + capbilities/AudioDecoder.cpp + capbilities/VideoProcessing.cpp + render/PluginRender.cpp + render/PluginManager.cpp + common/SampleCallback.cpp +) + target_link_libraries(recorder PUBLIC ${BASE_LIBRARY}) -target_link_libraries(player PUBLIC ${VIDEO_BASE_LIBRARY}) \ No newline at end of file +target_link_libraries(player PUBLIC ${VIDEO_BASE_LIBRARY}) +target_link_libraries(transcoder PUBLIC ${TRANSCODER_BASE_LIBRARY}) \ No newline at end of file diff --git a/entry/src/main/cpp/capbilities/AudioCapturer.cpp b/entry/src/main/cpp/capbilities/AudioCapturer.cpp index 0dee5a5bf35b46c334c7d33ea34278dc2e550ef8..c811fc6bdd275afc927e02e528c9c43ed0252305 100644 --- a/entry/src/main/cpp/capbilities/AudioCapturer.cpp +++ b/entry/src/main/cpp/capbilities/AudioCapturer.cpp @@ -16,14 +16,10 @@ #include "AudioCapturer.h" #include "SampleCallback.h" -AudioCapturer::~AudioCapturer() -{ - AudioCapturerRelease(); -} +AudioCapturer::~AudioCapturer() { AudioCapturerRelease(); } // AudioCapturer Callback -static int32_t AudioCapturerOnReadData(OH_AudioCapturer *capturer, void *userData, void *buffer, int32_t bufferLen) -{ +static int32_t AudioCapturerOnReadData(OH_AudioCapturer *capturer, void *userData, void *buffer, int32_t bufferLen) { (void)capturer; CodecUserData *codecUserData = static_cast(userData); if (codecUserData != nullptr) { @@ -33,8 +29,7 @@ static int32_t AudioCapturerOnReadData(OH_AudioCapturer *capturer, void *userDat return 0; } -void AudioCapturer::AudioCapturerInit(SampleInfo &sampleInfo, CodecUserData *audioEncContext) -{ +void AudioCapturer::AudioCapturerInit(SampleInfo &sampleInfo, CodecUserData *audioEncContext) { AudioCapturerRelease(); // Create builder @@ -53,15 +48,13 @@ void AudioCapturer::AudioCapturerInit(SampleInfo &sampleInfo, CodecUserData *aud OH_AudioStreamBuilder_GenerateCapturer(builder_, &audioCapturer_); } -void AudioCapturer::AudioCapturerStart() -{ +void AudioCapturer::AudioCapturerStart() { if (audioCapturer_ != nullptr) { OH_AudioCapturer_Start(audioCapturer_); } } -void AudioCapturer::AudioCapturerRelease() -{ +void AudioCapturer::AudioCapturerRelease() { if (audioCapturer_ != nullptr) { OH_AudioCapturer_Stop(audioCapturer_); OH_AudioCapturer_Release(audioCapturer_); diff --git a/entry/src/main/cpp/capbilities/AudioEncoder.cpp b/entry/src/main/cpp/capbilities/AudioEncoder.cpp index 4391fecfc58c6206d4d981b78c480d7dc08e8298..95a8a86e8d192ee229073c8eaed8900129ef6f9c 100644 --- a/entry/src/main/cpp/capbilities/AudioEncoder.cpp +++ b/entry/src/main/cpp/capbilities/AudioEncoder.cpp @@ -24,24 +24,19 @@ namespace { constexpr int LIMIT_LOGD_FREQUENCY = 50; } -AudioEncoder::~AudioEncoder() -{ - Release(); -} +AudioEncoder::~AudioEncoder() { Release(); } -int32_t AudioEncoder::Create(const std::string &codecMime) -{ +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_ERROR, "Create failed"); return AVCODEC_SAMPLE_OK; } -int32_t AudioEncoder::SetCallback(CodecUserData *codecUserData) -{ +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 }, + {SampleCallback::OnCodecError, SampleCallback::OnCodecFormatChange, + SampleCallback::OnNeedInputBuffer, SampleCallback::OnNewOutputBuffer}, codecUserData); CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK, AVCODEC_SAMPLE_ERROR, "Set callback failed, ret: %{public}d", ret); AVCODEC_SAMPLE_LOGI("====== AudioEncoder SetCallback ======"); @@ -49,8 +44,7 @@ int32_t AudioEncoder::SetCallback(CodecUserData *codecUserData) return AVCODEC_SAMPLE_OK; } -int32_t AudioEncoder::Configure(const SampleInfo &sampleInfo) -{ +int32_t AudioEncoder::Configure(const SampleInfo &sampleInfo) { OH_AVFormat *format = OH_AVFormat_Create(); CHECK_AND_RETURN_RET_LOG(format != nullptr, AVCODEC_SAMPLE_ERROR, "AVFormat create failed"); @@ -72,8 +66,7 @@ int32_t AudioEncoder::Configure(const SampleInfo &sampleInfo) return AVCODEC_SAMPLE_OK; } -int32_t AudioEncoder::Config(const SampleInfo &sampleInfo, CodecUserData *codecUserData) -{ +int32_t AudioEncoder::Config(const SampleInfo &sampleInfo, CodecUserData *codecUserData) { CHECK_AND_RETURN_RET_LOG(encoder_ != nullptr, AVCODEC_SAMPLE_ERROR, "Encoder is null"); CHECK_AND_RETURN_RET_LOG(codecUserData != nullptr, AVCODEC_SAMPLE_ERROR, "Invalid param: codecUserData"); @@ -83,8 +76,8 @@ int32_t AudioEncoder::Config(const SampleInfo &sampleInfo, CodecUserData *codecU // SetCallback for audio encoder ret = SetCallback(codecUserData); - CHECK_AND_RETURN_RET_LOG(ret == AVCODEC_SAMPLE_OK, AVCODEC_SAMPLE_ERROR, - "Set callback failed, ret: %{public}d", ret); + CHECK_AND_RETURN_RET_LOG(ret == AVCODEC_SAMPLE_OK, AVCODEC_SAMPLE_ERROR, "Set callback failed, ret: %{public}d", + ret); // Prepare audio encoder { @@ -95,8 +88,7 @@ int32_t AudioEncoder::Config(const SampleInfo &sampleInfo, CodecUserData *codecU return AVCODEC_SAMPLE_OK; } -int32_t AudioEncoder::Start() -{ +int32_t AudioEncoder::Start() { CHECK_AND_RETURN_RET_LOG(encoder_ != nullptr, AVCODEC_SAMPLE_ERROR, "Encoder is null"); int ret = OH_AudioCodec_Start(encoder_); @@ -104,8 +96,7 @@ int32_t AudioEncoder::Start() return AVCODEC_SAMPLE_OK; } -int32_t AudioEncoder::PushInputData(CodecBufferInfo &info) -{ +int32_t AudioEncoder::PushInputData(CodecBufferInfo &info) { CHECK_AND_RETURN_RET_LOG(encoder_ != nullptr, AVCODEC_SAMPLE_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_ERROR, "Set avbuffer attr failed"); @@ -114,8 +105,7 @@ int32_t AudioEncoder::PushInputData(CodecBufferInfo &info) return AVCODEC_SAMPLE_OK; } -int32_t AudioEncoder::FreeOutputData(uint32_t bufferIndex) -{ +int32_t AudioEncoder::FreeOutputData(uint32_t bufferIndex) { CHECK_AND_RETURN_RET_LOG(encoder_ != nullptr, AVCODEC_SAMPLE_ERROR, "Encoder is null"); int32_t ret = AVCODEC_SAMPLE_OK; @@ -124,23 +114,15 @@ int32_t AudioEncoder::FreeOutputData(uint32_t bufferIndex) return AVCODEC_SAMPLE_OK; } -int32_t AudioEncoder::Stop() -{ +int32_t AudioEncoder::Stop() { CHECK_AND_RETURN_RET_LOG(encoder_ != nullptr, AVCODEC_SAMPLE_ERROR, "Encoder is null"); - - int ret = OH_AudioCodec_Flush(encoder_); - CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK, AVCODEC_SAMPLE_ERROR, "Flush failed, ret: %{public}d", ret); - - ret = OH_AudioCodec_Stop(encoder_); + int ret = OH_AudioCodec_Stop(encoder_); CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK, AVCODEC_SAMPLE_ERROR, "Stop failed, ret: %{public}d", ret); return AVCODEC_SAMPLE_OK; } -int32_t AudioEncoder::Release() -{ +int32_t AudioEncoder::Release() { if (encoder_ != nullptr) { - OH_AudioCodec_Flush(encoder_); - OH_AudioCodec_Stop(encoder_); OH_AudioCodec_Destroy(encoder_); encoder_ = nullptr; } diff --git a/entry/src/main/cpp/capbilities/Demuxer.cpp b/entry/src/main/cpp/capbilities/Demuxer.cpp index efd38a78c64efbd1fa1f2b42f473be75191c9bbf..cd456996e64e09e9719670f8d91a3ae90e1ec830 100644 --- a/entry/src/main/cpp/capbilities/Demuxer.cpp +++ b/entry/src/main/cpp/capbilities/Demuxer.cpp @@ -1,17 +1,17 @@ /* -* 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. -*/ + * 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 "Demuxer.h" #include "dfx/error/AVCodecSampleError.h" @@ -23,7 +23,7 @@ Demuxer::~Demuxer() { Release(); } -int32_t Demuxer::Create(SampleInfo &info) { +int32_t Demuxer::Create(SampleInfo &info) { source_ = OH_AVSource_CreateWithFD(info.inputFd, info.inputFileOffset, info.inputFileSize); CHECK_AND_RETURN_RET_LOG(source_ != nullptr, AVCODEC_SAMPLE_ERROR, "Create AVSource failed"); demuxer_ = OH_AVDemuxer_CreateWithSource(source_); @@ -35,16 +35,15 @@ int32_t Demuxer::Create(SampleInfo &info) { return AVCODEC_SAMPLE_OK; } -int32_t Demuxer::ReadSample(int32_t trackId, OH_AVBuffer *buffer, OH_AVCodecBufferAttr &attr) { +int32_t Demuxer::ReadSample(int32_t trackId, OH_AVBuffer *buffer, OH_AVCodecBufferAttr &attr) { int32_t ret = OH_AVDemuxer_ReadSampleBuffer(demuxer_, trackId, buffer); - CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK, AVCODEC_SAMPLE_ERROR, "" - "ReadSampleBuffer failed"); + CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK, AVCODEC_SAMPLE_ERROR, "ReadSampleBuffer failed"); ret = OH_AVBuffer_GetBufferAttr(buffer, &attr); CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK, AVCODEC_SAMPLE_ERROR, "GetBufferAttr failed"); return AVCODEC_SAMPLE_OK; } -int32_t Demuxer::Release() { +int32_t Demuxer::Release() { if (demuxer_ != nullptr) { OH_AVDemuxer_Destroy(demuxer_); demuxer_ = nullptr; @@ -56,20 +55,17 @@ int32_t Demuxer::Release() { return AVCODEC_SAMPLE_OK; } -int32_t Demuxer::GetVideoTrackId() { - return videoTrackId_; -} +int32_t Demuxer::GetVideoTrackId() { return videoTrackId_; } -int32_t Demuxer::GetAudioTrackId() { - return audioTrackId_; -} +int32_t Demuxer::GetAudioTrackId() { return audioTrackId_; } -int32_t Demuxer::GetTrackInfo(std::shared_ptr sourceFormat, SampleInfo &info) { +int32_t Demuxer::GetTrackInfo(std::shared_ptr sourceFormat, SampleInfo &info) { int32_t trackCount = 0; OH_AVFormat_GetIntValue(sourceFormat.get(), OH_MD_KEY_TRACK_COUNT, &trackCount); - for(int32_t index = 0; index < trackCount; index++) { + for (int32_t index = 0; index < trackCount; index++) { int trackType = -1; - auto trackFormat = std::shared_ptr(OH_AVSource_GetTrackFormat(source_, index), OH_AVFormat_Destroy); + auto trackFormat = + std::shared_ptr(OH_AVSource_GetTrackFormat(source_, index), OH_AVFormat_Destroy); if (trackFormat == nullptr) { OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN, "Demuxer", "Failed to get track format for index %d", index); return AVCODEC_SAMPLE_ERROR; @@ -82,12 +78,13 @@ int32_t Demuxer::GetTrackInfo(std::shared_ptr sourceFormat, SampleI OH_AVFormat_GetDoubleValue(trackFormat.get(), OH_MD_KEY_FRAME_RATE, &info.frameRate); int32_t pixelFormat = 0; OH_AVFormat_GetIntValue(trackFormat.get(), OH_MD_KEY_PIXEL_FORMAT, &pixelFormat); - info.pixelFormat = ConvertPixelFormat(info.pixelFormat); + info.pixelFormat = ConvertPixelFormat(pixelFormat); OH_AVFormat_GetLongValue(trackFormat.get(), OH_MD_KEY_BITRATE, &info.bitrate); OH_AVFormat_GetIntValue(trackFormat.get(), OH_MD_KEY_ROTATION, &info.rotation); - + OH_AVFormat_GetIntValue(trackFormat.get(), OH_MD_KEY_VIDEO_IS_HDR_VIVID, &info.isHDRVivid); char *videoCodecMime; - OH_AVFormat_GetStringValue(trackFormat.get(), OH_MD_KEY_CODEC_MIME, const_cast(&videoCodecMime)); + OH_AVFormat_GetStringValue(trackFormat.get(), OH_MD_KEY_CODEC_MIME, + const_cast(&videoCodecMime)); info.videoCodecMime = videoCodecMime; AVCODEC_SAMPLE_LOGI("Video Mime: %{public}s", videoCodecMime); OH_AVFormat_GetIntValue(trackFormat.get(), OH_MD_KEY_PROFILE, &info.hevcProfile); @@ -99,7 +96,8 @@ int32_t Demuxer::GetTrackInfo(std::shared_ptr sourceFormat, SampleI OH_AVFormat_GetLongValue(trackFormat.get(), OH_MD_KEY_CHANNEL_LAYOUT, &info.audioChannelLayout); OH_AVFormat_GetIntValue(trackFormat.get(), OH_MD_KEY_AUD_SAMPLE_RATE, &info.audioSampleRate); char *audioCodecMime; - OH_AVFormat_GetStringValue(trackFormat.get(), OH_MD_KEY_CODEC_MIME, const_cast(&audioCodecMime)); + OH_AVFormat_GetStringValue(trackFormat.get(), OH_MD_KEY_CODEC_MIME, + const_cast(&audioCodecMime)); info.audioCodecMime = audioCodecMime; AVCODEC_SAMPLE_LOGI("Audio Mime: %{public}s", audioCodecMime); audioTrackId_ = index; @@ -108,19 +106,19 @@ int32_t Demuxer::GetTrackInfo(std::shared_ptr sourceFormat, SampleI return AVCODEC_SAMPLE_OK; } -OH_AVPixelFormat Demuxer::ConvertPixelFormat(int32_t pixelFormat) { +OH_AVPixelFormat Demuxer::ConvertPixelFormat(int32_t pixelFormat) { switch (pixelFormat) { - case 1: - return AV_PIXEL_FORMAT_YUVI420; - case 2: - return AV_PIXEL_FORMAT_NV12; - case 3: - return AV_PIXEL_FORMAT_NV21; - case 4: - return AV_PIXEL_FORMAT_SURFACE_FORMAT; - case 5: - return AV_PIXEL_FORMAT_RGBA; - default: - return AV_PIXEL_FORMAT_NV12; + case 1: + return AV_PIXEL_FORMAT_YUVI420; + case 2: + return AV_PIXEL_FORMAT_NV12; + case 3: + return AV_PIXEL_FORMAT_NV21; + case 4: + return AV_PIXEL_FORMAT_SURFACE_FORMAT; + case 5: + return AV_PIXEL_FORMAT_RGBA; + default: + return AV_PIXEL_FORMAT_NV12; } } \ No newline at end of file diff --git a/entry/src/main/cpp/capbilities/Muxer.cpp b/entry/src/main/cpp/capbilities/Muxer.cpp index bc7c7f13843a95a103048094f07883b1b825ec23..74f14660411ec064e2d2c5ee7ba99c9c5f6e40f7 100644 --- a/entry/src/main/cpp/capbilities/Muxer.cpp +++ b/entry/src/main/cpp/capbilities/Muxer.cpp @@ -38,13 +38,13 @@ int32_t Muxer::Create(int32_t fd) { int32_t Muxer::Config(SampleInfo &sampleInfo) { CHECK_AND_RETURN_RET_LOG(muxer_ != nullptr, AVCODEC_SAMPLE_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_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_Destroy(formatAudio); OH_AVFormat *formatVideo = OH_AVFormat_CreateVideoFormat(sampleInfo.videoCodecMime.data(), sampleInfo.videoWidth, sampleInfo.videoHeight); @@ -53,13 +53,15 @@ int32_t Muxer::Config(SampleInfo &sampleInfo) { OH_AVFormat_SetDoubleValue(formatVideo, OH_MD_KEY_FRAME_RATE, sampleInfo.frameRate); OH_AVFormat_SetIntValue(formatVideo, OH_MD_KEY_WIDTH, sampleInfo.videoWidth); OH_AVFormat_SetIntValue(formatVideo, OH_MD_KEY_HEIGHT, sampleInfo.videoHeight); - OH_AVFormat_SetStringValue(formatVideo, OH_MD_KEY_CODEC_MIME, sampleInfo.videoCodecMime.data()); - if (sampleInfo.isHDRVivid) { + if (sampleInfo.isRecordHDRVivid == 1) { + OH_AVFormat_SetStringValue(formatVideo, OH_MD_KEY_CODEC_MIME, OH_AVCODEC_MIMETYPE_VIDEO_HEVC); OH_AVFormat_SetIntValue(formatVideo, OH_MD_KEY_VIDEO_IS_HDR_VIVID, 1); OH_AVFormat_SetIntValue(formatVideo, OH_MD_KEY_RANGE_FLAG, sampleInfo.rangFlag); OH_AVFormat_SetIntValue(formatVideo, OH_MD_KEY_COLOR_PRIMARIES, sampleInfo.primary); OH_AVFormat_SetIntValue(formatVideo, OH_MD_KEY_TRANSFER_CHARACTERISTICS, sampleInfo.transfer); OH_AVFormat_SetIntValue(formatVideo, OH_MD_KEY_MATRIX_COEFFICIENTS, sampleInfo.matrix); + } else { + OH_AVFormat_SetStringValue(formatVideo, OH_MD_KEY_CODEC_MIME, OH_AVCODEC_MIMETYPE_VIDEO_AVC); } ret = OH_AVMuxer_AddTrack(muxer_, &videoTrackId_, formatVideo); @@ -79,7 +81,7 @@ int32_t Muxer::Start() { return AVCODEC_SAMPLE_OK; } -int32_t Muxer::WriteSample(int32_t trackId, OH_AVBuffer *buffer, OH_AVCodecBufferAttr &attr){ +int32_t Muxer::WriteSample(int32_t trackId, OH_AVBuffer *buffer, OH_AVCodecBufferAttr &attr) { CHECK_AND_RETURN_RET_LOG(muxer_ != nullptr, AVCODEC_SAMPLE_ERROR, "Muxer is null"); CHECK_AND_RETURN_RET_LOG(buffer != nullptr, AVCODEC_SAMPLE_ERROR, "Get a empty buffer"); @@ -93,6 +95,7 @@ int32_t Muxer::WriteSample(int32_t trackId, OH_AVBuffer *buffer, OH_AVCodecBuffe int32_t Muxer::Release() { if (muxer_ != nullptr) { + OH_AVMuxer_Stop(muxer_); OH_AVMuxer_Destroy(muxer_); muxer_ = nullptr; } diff --git a/entry/src/main/cpp/capbilities/VideoDecoder.cpp b/entry/src/main/cpp/capbilities/VideoDecoder.cpp index c9f25346e7b6ed2e4eee0d0440e4203c68cae58f..a2f4b6eb83b09b0df69b1a1a9fe367ae8a0fbeb1 100644 --- a/entry/src/main/cpp/capbilities/VideoDecoder.cpp +++ b/entry/src/main/cpp/capbilities/VideoDecoder.cpp @@ -27,22 +27,24 @@ namespace { constexpr int LIMIT_LOGD_FREQUENCY = 50; constexpr int ROTATION_ANGLE = 90; -} +} // namespace VideoDecoder::~VideoDecoder() { Release(); } // [Start VideoDecoderCreate] int32_t VideoDecoder::Create(SampleInfo &sampleInfo) { // [StartExclude VideoDecoderCreate] - if(sampleInfo.processType == 2){ - // [EndExclude VideoDecoderCreate] - OH_AVCapability *capability = OH_AVCodec_GetCapabilityByCategory(OH_AVCODEC_MIMETYPE_VIDEO_HEVC, false, HARDWARE); - CHECK_AND_RETURN_RET_LOG(capability != nullptr, AVCODEC_SAMPLE_ERROR, "OH_AVCodec_GetCapabilityByCategory failed"); - const char *name = OH_AVCapability_GetName(capability); - decoder_ = OH_VideoDecoder_CreateByName(name); - // [StartExclude VideoDecoderCreate] + if (sampleInfo.isHDRVivid == 1) { + // [EndExclude VideoDecoderCreate] + OH_AVCapability *capability = + OH_AVCodec_GetCapabilityByCategory(OH_AVCODEC_MIMETYPE_VIDEO_HEVC, false, HARDWARE); + CHECK_AND_RETURN_RET_LOG(capability != nullptr, AVCODEC_SAMPLE_ERROR, + "OH_AVCodec_GetCapabilityByCategory failed"); + const char *name = OH_AVCapability_GetName(capability); + decoder_ = OH_VideoDecoder_CreateByName(name); + // [StartExclude VideoDecoderCreate] } else { - decoder_ = OH_VideoDecoder_CreateByMime(sampleInfo.videoCodecMime.c_str()); + decoder_ = OH_VideoDecoder_CreateByMime(MIME_VIDEO_AVC.data()); } // [EndExclude VideoDecoderCreate] CHECK_AND_RETURN_RET_LOG(decoder_ != nullptr, AVCODEC_SAMPLE_ERROR, "Create VideoDecoder failed"); @@ -53,37 +55,27 @@ int32_t VideoDecoder::Create(SampleInfo &sampleInfo) { int32_t VideoDecoder::Config(SampleInfo &sampleInfo, CodecUserData *codecUserData) { int32_t ret = Configure(sampleInfo); CHECK_AND_RETURN_RET_LOG(ret == AVCODEC_SAMPLE_OK, AVCODEC_SAMPLE_ERROR, "Configure failed"); - if (sampleInfo.videoIndex == 0) { - ret = OH_VideoDecoder_SetSurface(decoder_, NativeXComponentSample::PluginManager::GetInstance()->renderWindow); - CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK && NativeXComponentSample::PluginManager::GetInstance()->renderWindow, - AVCODEC_SAMPLE_ERROR, "VideoDecoder SetSurface failed"); - } else if (sampleInfo.videoIndex == 1) { - if (sampleInfo.processType == 1) { - NativeXComponentSample::PluginManager::GetInstance()->PrepareSurface(sampleInfo); - if (NativeXComponentSample::PluginManager::GetInstance()->pluginWindow_ != nullptr) { - ret = OH_VideoDecoder_SetSurface(decoder_, NativeXComponentSample::PluginManager::GetInstance()->pluginWindow_); - CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK && NativeXComponentSample::PluginManager::GetInstance()->pluginWindow_, - AVCODEC_SAMPLE_ERROR, "VideoDecoder SetSurface failed"); - } - } else { - ret = OH_VideoDecoder_SetSurface(decoder_, NativeXComponentSample::PluginManager::GetInstance()->windowOut); - CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK && NativeXComponentSample::PluginManager::GetInstance()->windowOut, - AVCODEC_SAMPLE_ERROR, "VideoDecoder SetSurface failed"); - } + if (sampleInfo.processType > 1) { + ret = OH_VideoDecoder_SetSurface(decoder_, sampleInfo.inWindow); + } else { + ret = OH_VideoDecoder_SetSurface(decoder_, sampleInfo.window); } + CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK, AVCODEC_SAMPLE_ERROR, "VideoDecoder SetSurface failed"); ret = SetCallback(codecUserData); CHECK_AND_RETURN_RET_LOG(ret == AVCODEC_SAMPLE_OK, AVCODEC_SAMPLE_ERROR, "VideoDecoder SetCallback failed"); { ret = OH_VideoDecoder_Prepare(decoder_); CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK, AVCODEC_SAMPLE_ERROR, "VideoDecoder Prepare failed"); } - if (sampleInfo.videoIndex == 1 && sampleInfo.processType == 1) { - NativeXComponentSample::PluginManager::GetInstance()->InitProcessing(sampleInfo); - int32_t rotation = sampleInfo.rotation == 0 ? 0 : (360 - sampleInfo.rotation) / 90; - AVCODEC_SAMPLE_LOGI("Set Surface Rotation: %{public}d", rotation); - OH_NativeWindow_NativeWindowHandleOpt(NativeXComponentSample::PluginManager::GetInstance()->windowOut, SET_TRANSFORM, rotation); - OH_NativeWindow_NativeWindowHandleOpt(NativeXComponentSample::PluginManager::GetInstance()->pluginWindow_, SET_USAGE, NATIVEBUFFER_USAGE_CPU_READ); - } +// if (sampleInfo.videoIndex == 1 && sampleInfo.processType == 1) { +// NativeXComponentSample::PluginManager::GetInstance()->InitProcessing(sampleInfo); +// int32_t rotation = sampleInfo.rotation == 0 ? 0 : (360 - sampleInfo.rotation) / 90; +// AVCODEC_SAMPLE_LOGI("Set Surface Rotation: %{public}d", rotation); +// OH_NativeWindow_NativeWindowHandleOpt(NativeXComponentSample::PluginManager::GetInstance()->windowOut, +// SET_TRANSFORM, rotation); +// OH_NativeWindow_NativeWindowHandleOpt(NativeXComponentSample::PluginManager::GetInstance()->pluginWindow_, +// SET_USAGE, NATIVEBUFFER_USAGE_CPU_READ); +// } return AVCODEC_SAMPLE_OK; } @@ -122,11 +114,12 @@ int32_t VideoDecoder::Release() { } // [Start VideoDecoderSetCallback] -int32_t VideoDecoder::SetCallback(CodecUserData *codecUserData){ - int32_t ret = OH_VideoDecoder_RegisterCallback(decoder_, - {SampleCallback::OnCodecError, SampleCallback::OnCodecFormatChange, - SampleCallback::OnNeedInputBuffer, SampleCallback::OnNewOutputBuffer}, - codecUserData); +int32_t VideoDecoder::SetCallback(CodecUserData *codecUserData) { + int32_t ret = + OH_VideoDecoder_RegisterCallback(decoder_, + {SampleCallback::OnCodecError, SampleCallback::OnCodecFormatChange, + SampleCallback::OnNeedInputBuffer, SampleCallback::OnNewOutputBuffer}, + codecUserData); CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK, AVCODEC_SAMPLE_ERROR, "Create SetCallback failed"); return AVCODEC_SAMPLE_OK; } @@ -142,7 +135,7 @@ int32_t VideoDecoder::Configure(const SampleInfo &sampleInfo) { OH_AVFormat_SetIntValue(format, OH_MD_KEY_PIXEL_FORMAT, sampleInfo.pixelFormat); OH_AVFormat_SetIntValue(format, OH_MD_KEY_ROTATION, sampleInfo.rotation); // [StartExclude VideoDecoderConfigure] - if(sampleInfo.processType == 2){ + if (sampleInfo.processType == 1) { // [EndExclude VideoDecoderConfigure] // Key configuration: HDR to SDR conversion OH_AVFormat_SetIntValue(format, OH_MD_KEY_VIDEO_DECODER_OUTPUT_COLOR_SPACE, OH_COLORSPACE_BT709_LIMIT); diff --git a/entry/src/main/cpp/capbilities/VideoEncoder.cpp b/entry/src/main/cpp/capbilities/VideoEncoder.cpp index 9c1c622e37afbc7671b3374d46ce9bba45591664..2f2043659bab0b3a023a35fe4a31e42df54bc787 100644 --- a/entry/src/main/cpp/capbilities/VideoEncoder.cpp +++ b/entry/src/main/cpp/capbilities/VideoEncoder.cpp @@ -14,8 +14,10 @@ */ #include "VideoEncoder.h" +#include "VideoProcessing.h" #include "dfx/error/AVCodecSampleError.h" #include "AVCodecSampleLog.h" +#include #undef LOG_TAG #define LOG_TAG "VideoEncoder" @@ -45,8 +47,8 @@ int32_t VideoEncoder::Config(SampleInfo &sampleInfo, CodecUserData *codecUserDat // SetCallback for video encoder ret = SetCallback(codecUserData); - CHECK_AND_RETURN_RET_LOG(ret == AVCODEC_SAMPLE_OK, AVCODEC_SAMPLE_ERROR, - "Set callback failed, ret: %{public}d", ret); + CHECK_AND_RETURN_RET_LOG(ret == AVCODEC_SAMPLE_OK, AVCODEC_SAMPLE_ERROR, "Set callback failed, ret: %{public}d", + ret); // Prepare video encoder ret = OH_VideoEncoder_Prepare(encoder_); @@ -70,8 +72,7 @@ int32_t VideoEncoder::FreeOutputBuffer(uint32_t bufferIndex) { CHECK_AND_RETURN_RET_LOG(encoder_ != nullptr, AVCODEC_SAMPLE_ERROR, "Encoder is null"); int32_t ret = OH_VideoEncoder_FreeOutputBuffer(encoder_, bufferIndex); - CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK, AVCODEC_SAMPLE_ERROR, "Free output data failed, ret: %{public}d", - ret); + CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK, AVCODEC_SAMPLE_ERROR, "Free output data failed, ret: %{public}d", ret); return AVCODEC_SAMPLE_OK; } @@ -128,15 +129,17 @@ int32_t VideoEncoder::Configure(const SampleInfo &sampleInfo) { OH_AVFormat_SetIntValue(format, OH_MD_KEY_PIXEL_FORMAT, sampleInfo.pixelFormat); OH_AVFormat_SetIntValue(format, OH_MD_KEY_VIDEO_ENCODE_BITRATE_MODE, sampleInfo.bitrateMode); OH_AVFormat_SetLongValue(format, OH_MD_KEY_BITRATE, sampleInfo.bitrate); - OH_AVFormat_SetIntValue(format, OH_MD_KEY_PROFILE, sampleInfo.hevcProfile); // [EndExclude camera_AVCodec] // Setting HDRVivid-related parameters - if (sampleInfo.isHDRVivid) { + if (sampleInfo.isRecordHDRVivid == 1) { + OH_AVFormat_SetIntValue(format, OH_MD_KEY_PROFILE, sampleInfo.hevcProfile); OH_AVFormat_SetIntValue(format, OH_MD_KEY_I_FRAME_INTERVAL, sampleInfo.iFrameInterval); OH_AVFormat_SetIntValue(format, OH_MD_KEY_RANGE_FLAG, sampleInfo.rangFlag); OH_AVFormat_SetIntValue(format, OH_MD_KEY_COLOR_PRIMARIES, sampleInfo.primary); OH_AVFormat_SetIntValue(format, OH_MD_KEY_TRANSFER_CHARACTERISTICS, sampleInfo.transfer); OH_AVFormat_SetIntValue(format, OH_MD_KEY_MATRIX_COEFFICIENTS, sampleInfo.matrix); + } else { + OH_AVFormat_SetIntValue(format, OH_MD_KEY_PROFILE, OH_AVCProfile::AVC_PROFILE_HIGH); } // [StartExclude video_encoder_faq] // [StartExclude camera_AVCodec] @@ -163,8 +166,12 @@ int32_t VideoEncoder::Configure(const SampleInfo &sampleInfo) { // [End camera_AVCodec] int32_t VideoEncoder::GetSurface(SampleInfo &sampleInfo) { - int32_t ret = OH_VideoEncoder_GetSurface(encoder_, &sampleInfo.window); - CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK && sampleInfo.window, AVCODEC_SAMPLE_ERROR, - "Get surface failed, ret: %{public}d", ret); + int32_t ret; + if (sampleInfo.processType > 1) { + ret = OH_VideoEncoder_GetSurface(encoder_, &sampleInfo.outWindow); + } else { + ret = OH_VideoEncoder_GetSurface(encoder_, &sampleInfo.window); + } + CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK, AVCODEC_SAMPLE_ERROR, "Get surface failed, ret: %{public}d", ret); return AVCODEC_SAMPLE_OK; -} \ No newline at end of file +} diff --git a/entry/src/main/cpp/capbilities/VideoProcessing.cpp b/entry/src/main/cpp/capbilities/VideoProcessing.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e9cc05eb27595a84c0350ace43d519c40c582a69 --- /dev/null +++ b/entry/src/main/cpp/capbilities/VideoProcessing.cpp @@ -0,0 +1,120 @@ +/* + * 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 "include/VideoProcessing.h" +#include "AVCodecSampleLog.h" +#include +#include +#include + +constexpr uint32_t LOG_PRINT_DOMAIN = 0xFF00; + +// [Start set_callback] +void OnError(OH_VideoProcessing *videoProcessor, VideoProcessing_ErrorCode error, void *userData) { + (void)videoProcessor; + (void)error; + (void)userData; + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, LOG_TAG, "OnError: %{public}d", error); +} + +void OnState(OH_VideoProcessing *videoProcessor, VideoProcessing_State state, void *userData) { + (void)videoProcessor; + (void)state; + (void)userData; +} + +void OnNewOutputBuffer(OH_VideoProcessing *videoProcessor, uint32_t index, void *userData) { + + OH_VideoProcessing_RenderOutputBuffer(videoProcessor, index); + (void)userData; +} +// [End set_callback] + +void VideoProcessing::InitProcessing(SampleInfo &sampleInfo) { + VideoProcessing_ErrorCode ret = VIDEO_PROCESSING_ERROR_INVALID_VALUE; + // [Start SetSurface] + int32_t err = 0; + err = OH_NativeWindow_SetMetadataValue(sampleInfo.outWindow, OH_HDR_METADATA_TYPE, sizeof(uint8_t), + (uint8_t *)&sampleInfo.outputFormat.metadataType); + CHECK_AND_RETURN_LOG(err == 0, "SetMetadataValue BT2020_PQ_LIMIT failed"); + err = OH_NativeWindow_NativeWindowHandleOpt(sampleInfo.outWindow, SET_FORMAT, sampleInfo.outputFormat.pixelFormat); + CHECK_AND_RETURN_LOG(err == 0, "NativeWindowHandleOpt BT2020_PQ_LIMIT failed"); + err = OH_NativeWindow_SetColorSpace(sampleInfo.outWindow, + OH_NativeBuffer_ColorSpace(sampleInfo.outputFormat.colorSpace)); + CHECK_AND_RETURN_LOG(err == 0, "SetColorSpace failed"); + + ret = OH_VideoProcessing_SetSurface(processor, sampleInfo.outWindow); + CHECK_AND_RETURN_LOG(ret == VIDEO_PROCESSING_SUCCESS, "SetSurface failed"); + // [End SetSurface] + // [Start VideoProcessingRegisterCallback] + ret = OH_VideoProcessingCallback_Create(&callback); + CHECK_AND_RETURN_LOG(ret == VIDEO_PROCESSING_SUCCESS, "OH_VideoProcessingCallback_Create failed"); + ret = OH_VideoProcessingCallback_BindOnError(callback, OnError); + CHECK_AND_RETURN_LOG(ret == VIDEO_PROCESSING_SUCCESS, "OH_VideoProcessingCallback_BindOnError failed"); + ret = OH_VideoProcessingCallback_BindOnState(callback, OnState); + CHECK_AND_RETURN_LOG(ret == VIDEO_PROCESSING_SUCCESS, "OH_VideoProcessingCallback_BindOnState failed"); + ret = OH_VideoProcessingCallback_BindOnNewOutputBuffer(callback, OnNewOutputBuffer); + CHECK_AND_RETURN_LOG(ret == VIDEO_PROCESSING_SUCCESS, "OH_VideoProcessingCallback_BindOnNewOutputBuffer failed"); + ret = OH_VideoProcessing_RegisterCallback(processor, callback, nullptr); + CHECK_AND_RETURN_LOG(ret == VIDEO_PROCESSING_SUCCESS, "OH_VideoProcessing_RegisterCallback failed"); + // [End VideoProcessingRegisterCallback] + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, LOG_TAG, "InitProcessing succeed"); +} + +// [Start SetProcessingSurface] +void VideoProcessing::SetProcessingSurface(SampleInfo &sampleInfo) { + VideoProcessing_ErrorCode ret = OH_VideoProcessing_Create(&processor, VIDEO_PROCESSING_TYPE_COLOR_SPACE_CONVERSION); + ret = OH_VideoProcessing_GetSurface(processor, &sampleInfo.inWindow); + CHECK_AND_RETURN_LOG(ret == VIDEO_PROCESSING_SUCCESS, "OH_VideoProcessing_GetSurface failed"); +} +// [End SetProcessingSurface] + +// [Start IsColorSpaceConversionSupported] +bool VideoProcessing::IsColorSpaceConversionSupported(SampleInfo &sampleInfo) { + OH_VideoProcessing_InitializeEnvironment(); + return OH_VideoProcessing_IsColorSpaceConversionSupported(&sampleInfo.inputFormat, &sampleInfo.outputFormat); +} +// [End IsColorSpaceConversionSupported] + +// [Start StartProcessing] +void VideoProcessing::StartProcessing() { + VideoProcessing_ErrorCode ret = OH_VideoProcessing_Start(processor); + CHECK_AND_RETURN_LOG(ret == VIDEO_PROCESSING_SUCCESS, "OH_VideoProcessing_Start failed"); +} +// [End StartProcessing] + +// [Start DestroyProcessing] +void VideoProcessing::DestroyProcessing() { + CHECK_AND_RETURN_LOG(processor != nullptr, "processor is nullptr"); + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, LOG_TAG, "start DestroyProcessing"); + VideoProcessing_ErrorCode ret = OH_VideoProcessing_Destroy(processor); + CHECK_AND_RETURN_LOG(ret == VIDEO_PROCESSING_SUCCESS, "OH_VideoProcessing_Destroy failed"); + processor = nullptr; + CHECK_AND_RETURN_LOG(callback != nullptr, "callback is nullptr"); + ret = OH_VideoProcessingCallback_Destroy(callback); + CHECK_AND_RETURN_LOG(ret == VIDEO_PROCESSING_SUCCESS, "OH_VideoProcessingCallback_Destroy failed"); + callback = nullptr; + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, LOG_TAG, "Destroy And Callback_Destroy succeed"); + OH_VideoProcessing_DeinitializeEnvironment(); +} +// [End DestroyProcessing] + +// [Start StopProcessing] +void VideoProcessing::StopProcessing() { + VideoProcessing_ErrorCode ret = OH_VideoProcessing_Stop(processor); + CHECK_AND_RETURN_LOG(ret == VIDEO_PROCESSING_SUCCESS, "OH_VideoProcessing_Stop failed"); + DestroyProcessing(); +} +// [End StopProcessing] \ No newline at end of file diff --git a/entry/src/main/cpp/capbilities/include/AudioCapturer.h b/entry/src/main/cpp/capbilities/include/AudioCapturer.h index 1fd8898a78f34eca77383f01b55d99978c6ea731..ec5f8f59dd6f3468117810ed27cc1023926753e4 100644 --- a/entry/src/main/cpp/capbilities/include/AudioCapturer.h +++ b/entry/src/main/cpp/capbilities/include/AudioCapturer.h @@ -27,7 +27,7 @@ public: AudioCapturer() = default; ~AudioCapturer(); - void AudioCapturerInit(SampleInfo& sampleInfo, CodecUserData *audioEncContext); + void AudioCapturerInit(SampleInfo &sampleInfo, CodecUserData *audioEncContext); void AudioCapturerStart(); void AudioCapturerRelease(); @@ -36,5 +36,4 @@ private: OH_AudioStreamBuilder *builder_ = nullptr; }; - #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 69e5486400b097a5c33ef3d6e79b83dc35268b50..39095bce52ff9b580f2a7de364d4efa9f15c9a8e 100644 --- a/entry/src/main/cpp/capbilities/include/AudioDecoder.h +++ b/entry/src/main/cpp/capbilities/include/AudioDecoder.h @@ -36,6 +36,6 @@ private: int32_t Configure(const SampleInfo &sampleInfo); bool isAVBufferMode_ = false; - OH_AVCodec *decoder_; + OH_AVCodec *decoder_ = nullptr; }; #endif // AUDIODECODER_H \ No newline at end of file diff --git a/entry/src/main/cpp/capbilities/include/AudioEncoder.h b/entry/src/main/cpp/capbilities/include/AudioEncoder.h index 2d365d8bf3a5b57753a93e5c9ff5461df3d424d5..5ab4f95da89f03dd827ac2be149c8f2de92bf23e 100644 --- a/entry/src/main/cpp/capbilities/include/AudioEncoder.h +++ b/entry/src/main/cpp/capbilities/include/AudioEncoder.h @@ -32,13 +32,13 @@ public: 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_; + OH_AVCodec *encoder_ = nullptr; }; #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 ed94788820bd491f0cf0c2ef85f252cd4df00a77..fe5d212bfceb105558451f2da807eae43f6375b8 100644 --- a/entry/src/main/cpp/capbilities/include/Demuxer.h +++ b/entry/src/main/cpp/capbilities/include/Demuxer.h @@ -28,15 +28,15 @@ public: int32_t Release(); int32_t GetVideoTrackId(); int32_t GetAudioTrackId(); - + private: int32_t GetTrackInfo(std::shared_ptr sourceFormat, SampleInfo &info); OH_AVPixelFormat ConvertPixelFormat(int32_t pixelFormat); - - OH_AVSource *source_; - OH_AVDemuxer *demuxer_; - int32_t videoTrackId_; - int32_t audioTrackId_; + + OH_AVSource *source_ = nullptr; + OH_AVDemuxer *demuxer_ = nullptr; + int32_t videoTrackId_ = -1; + int32_t audioTrackId_ = -1; }; #endif // DEMUXER_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 73681fa3cccfa9fe9762845ccc6d515471f849e4..d713ab2b4a85666a93c48ad12e393c799a28ffa3 100644 --- a/entry/src/main/cpp/capbilities/include/VideoDecoder.h +++ b/entry/src/main/cpp/capbilities/include/VideoDecoder.h @@ -40,7 +40,7 @@ private: int32_t Configure(const SampleInfo &sampleInfo); bool isAVBufferMode_ = false; // [EndExclude VideoDecoderDecoder] - OH_AVCodec *decoder_; + OH_AVCodec *decoder_ = nullptr; }; // [End VideoDecoderDecoder] #endif // VIDEODECODER_H \ No newline at end of file diff --git a/entry/src/main/cpp/capbilities/include/VideoProcessing.h b/entry/src/main/cpp/capbilities/include/VideoProcessing.h new file mode 100644 index 0000000000000000000000000000000000000000..034a70fcf0c91efaa52ad7c6e45b8efb8c1f6b08 --- /dev/null +++ b/entry/src/main/cpp/capbilities/include/VideoProcessing.h @@ -0,0 +1,36 @@ +/* + * 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 HDR2SDR_VIDEOPROCESSING_H +#define HDR2SDR_VIDEOPROCESSING_H + +#include "SampleInfo.h" +#include + +class VideoProcessing { +public: + OH_VideoProcessing *processor = nullptr; + VideoProcessing_ColorSpaceInfo inputFormat; + VideoProcessing_ColorSpaceInfo outputFormat; + VideoProcessing_Callback *callback = nullptr; + void InitProcessing(SampleInfo &sampleInfo); + void DestroyProcessing(); + void StopProcessing(); + void SetProcessingSurface(SampleInfo &sampleInfo); + bool IsColorSpaceConversionSupported(SampleInfo &sampleInfo); + void StartProcessing(); +}; + +#endif // HDR2SDR_VIDEOPROCESSING_H diff --git a/entry/src/main/cpp/common/SampleInfo.h b/entry/src/main/cpp/common/SampleInfo.h index e1fd1cda2860d49f94e7d996bdb3f8c03ffea6f9..e6ab68143c3be3c532e92dfc3fbb0f192741e7c6 100644 --- a/entry/src/main/cpp/common/SampleInfo.h +++ b/entry/src/main/cpp/common/SampleInfo.h @@ -17,7 +17,10 @@ #define AVCODEC_SAMPLE_INFO_H #include +#include +#include #include +#include #include #include #include @@ -64,15 +67,14 @@ struct SampleInfo { int32_t audioChannelCount = 0; int64_t audioChannelLayout = 0; int32_t audioBitRate = 0; - uint8_t audioCodecConfig[100] = { 0 }; + uint8_t audioCodecConfig[100] = {0}; size_t audioCodecSize = 0; int32_t audioMaxInputSize = 0; OH_AVFormat *audioFormat; int32_t videoIndex = 0; - int8_t processType = 0; // 0none,1color,2avcodec - int32_t isHDRVivid = 0; + int32_t isRecordHDRVivid = 0; int32_t hevcProfile = HEVC_PROFILE_MAIN; OH_ColorPrimary primary = COLOR_PRIMARY_BT2020; OH_TransferCharacteristic transfer = TRANSFER_CHARACTERISTIC_PQ; @@ -80,9 +82,15 @@ struct SampleInfo { int32_t rotation = 0; OHNativeWindow *window = nullptr; + + int8_t processType = 0; // 0none,1HDR,2Color int8_t metaData = OH_VIDEO_NONE; OH_NativeBuffer_ColorSpace colorSpace = OH_COLORSPACE_BT709_LIMIT; - + VideoProcessing_ColorSpaceInfo inputFormat; + VideoProcessing_ColorSpaceInfo outputFormat; + OHNativeWindow *inWindow = nullptr; + OHNativeWindow *outWindow = nullptr; + int8_t vpErrorCode = 0; void (*playDoneCallback)(void *context) = nullptr; void *playDoneCallbackData = nullptr; @@ -101,8 +109,7 @@ struct CodecBufferInfo { CodecBufferInfo(uint8_t *addr, int32_t bufferSize) : bufferAddr(addr), attr({0, bufferSize, 0, AVCODEC_BUFFER_FLAGS_NONE}){}; CodecBufferInfo(uint32_t argBufferIndex, OH_AVBuffer *argBuffer) - : bufferIndex(argBufferIndex), buffer(reinterpret_cast(argBuffer)) - { + : bufferIndex(argBufferIndex), buffer(reinterpret_cast(argBuffer)) { OH_AVBuffer_GetBufferAttr(argBuffer, &attr); }; }; @@ -127,16 +134,14 @@ public: // Create cache std::vector cache; - int32_t remainlen = 0; + int32_t remainlen = 0; - void ClearCache() - { + void ClearCache() { cache.clear(); remainlen = 0; } - void WriteCache(void *buffer, int32_t bufferLen) - { + void WriteCache(void *buffer, int32_t bufferLen) { if (bufferLen + remainlen > cache.size()) { cache.resize(remainlen + bufferLen); } @@ -144,8 +149,7 @@ public: remainlen += bufferLen; } - bool ReadCache(void *buffer, int32_t bufferLen) - { + bool ReadCache(void *buffer, int32_t bufferLen) { if (remainlen < bufferLen) { return false; } diff --git a/entry/src/main/cpp/common/dfx/log/AVCodecSampleLog.h b/entry/src/main/cpp/common/dfx/log/AVCodecSampleLog.h index d1b5add9cf7e75d966d7cc0fd06e3457ad9ff370..88d5babdab6630c948f5cc0f6b2d0b0ea7b530b5 100644 --- a/entry/src/main/cpp/common/dfx/log/AVCodecSampleLog.h +++ b/entry/src/main/cpp/common/dfx/log/AVCodecSampleLog.h @@ -79,5 +79,11 @@ } \ } else \ void(0) - +#define CHECK_AND_CONTINUE(cond) \ + if (1) { \ + if (!(cond)) { \ + continue; \ + } \ + } else \ + void(0) #endif // AVCODEC_SAMPLE_LOG_H \ 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 d3b3c5de81f6413df4f451341fd31efe5a2e70b0..87ffc1487d6a4289e8077462ba4e2340dca8deb6 100644 --- a/entry/src/main/cpp/render/PluginManager.cpp +++ b/entry/src/main/cpp/render/PluginManager.cpp @@ -16,57 +16,18 @@ #include "AVCodecSampleLog.h" #include "PluginManager.h" #include "SampleInfo.h" -#include "multimedia/video_processing_engine/video_processing.h" -#include "multimedia/video_processing_engine/video_processing_types.h" - #include -#include #include #include #include -#include #include -#include -#include -#include - #undef LOG_TAG #define LOG_TAG "PluginManager" namespace NativeXComponentSample { constexpr uint32_t LOG_PRINT_DOMAIN = 0xFF00; PluginManager PluginManager::pluginManager_; -std::condition_variable g_SyncCond; -std::mutex g_SyncLock; -uint32_t g_FrameCount = 0; -uint32_t g_Index = -1; - -void OnError(OH_VideoProcessing *videoProcessor, VideoProcessing_ErrorCode error, void *userData) { - (void)videoProcessor; - (void)error; - (void)userData; - OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, LOG_TAG, "OnError: %{public}d", error); - if ((error == VIDEO_PROCESSING_ERROR_UNSUPPORTED_PROCESSING) && - (PluginManager::GetInstance()->sampleInfo_->vpErrorCode == 0)) { - PluginManager::GetInstance()->ResetVP(5, 1); - } -} - -void OnState(OH_VideoProcessing *videoProcessor, VideoProcessing_State state, void *userData) { - (void)videoProcessor; - (void)state; - (void)userData; -} - -void OnNewOutputBuffer(OH_VideoProcessing *videoProcessor, uint32_t index, void *userData) { - if (g_Index != index) { - g_Index = index; - g_FrameCount++; - } - OH_VideoProcessing_RenderOutputBuffer(videoProcessor, index); - (void)userData; -} PluginManager::~PluginManager() { for (auto iter = nativeXComponentMap_.begin(); iter != nativeXComponentMap_.end(); ++iter) { @@ -141,236 +102,7 @@ void PluginManager::Export(napi_env env, napi_value exports) { render->RegisterCallback(nativeXComponent); render->Export(env, exports); OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, LOG_TAG, "SetNativeXComponent123"); - - } - } -} - -// [Start PluginManagerInitProcessing] -void PluginManager::InitProcessing(SampleInfo &sampleInfo) { - VideoProcessing_ErrorCode ret = VIDEO_PROCESSING_ERROR_INVALID_VALUE; - int32_t processType = sampleInfo.processType; - - if (processType) { - int32_t err = 0; - if (sampleInfo.colorSpace == OH_COLORSPACE_BT709_LIMIT) { - err = OH_NativeWindow_NativeWindowHandleOpt(PluginManager::GetInstance()->windowOut, SET_FORMAT, - NATIVEBUFFER_PIXEL_FMT_YCBCR_420_SP); - int8_t metaData = OH_VIDEO_NONE; - err = OH_NativeWindow_SetMetadataValue(PluginManager::GetInstance()->windowOut, OH_HDR_METADATA_TYPE, - sizeof(uint8_t), (uint8_t *)&metaData); - CHECK_AND_RETURN_LOG(err == 0, "NativeWindowHandleOpt BT709_LIMIT failed"); - } else if (sampleInfo.colorSpace == OH_COLORSPACE_BT2020_HLG_LIMIT) { - err = OH_NativeWindow_SetMetadataValue(PluginManager::GetInstance()->windowOut, OH_HDR_METADATA_TYPE, - sizeof(uint8_t), (uint8_t *)&sampleInfo.metaData); - CHECK_AND_RETURN_LOG(err == 0, "SetMetadataValue BT2020_HLG_LIMIT failed"); - err = OH_NativeWindow_NativeWindowHandleOpt(PluginManager::GetInstance()->windowOut, SET_FORMAT, - NATIVEBUFFER_PIXEL_FMT_RGBA_1010102); - CHECK_AND_RETURN_LOG(err == 0, "NativeWindowHandleOpt BT2020_HLG_LIMIT failed"); - } else if (sampleInfo.colorSpace == OH_COLORSPACE_BT2020_PQ_LIMIT) { - err = OH_NativeWindow_SetMetadataValue(PluginManager::GetInstance()->windowOut, OH_HDR_METADATA_TYPE, - sizeof(uint8_t), (uint8_t *)&sampleInfo.metaData); - CHECK_AND_RETURN_LOG(err == 0, "SetMetadataValue BT2020_PQ_LIMIT failed"); - err = OH_NativeWindow_NativeWindowHandleOpt(PluginManager::GetInstance()->windowOut, SET_FORMAT, - NATIVEBUFFER_PIXEL_FMT_RGBA_1010102); - CHECK_AND_RETURN_LOG(err == 0, "NativeWindowHandleOpt BT2020_PQ_LIMIT failed"); } - err = OH_NativeWindow_SetColorSpace(PluginManager::GetInstance()->windowOut, sampleInfo.colorSpace); - CHECK_AND_RETURN_LOG(err == 0, "SetColorSpace failed"); - } - if (sampleInfo.window != nullptr) { - ret = OH_VideoProcessing_SetSurface(processor, sampleInfo.window); - } else { - ret = OH_VideoProcessing_SetSurface(processor, PluginManager::GetInstance()->windowOut); - } - CHECK_AND_RETURN_LOG(ret == VIDEO_PROCESSING_SUCCESS, "SetSurface failed"); - // [Start VideoProcessingRegisterCallback] - ret = OH_VideoProcessingCallback_Create(&callback); - CHECK_AND_RETURN_LOG(ret == VIDEO_PROCESSING_SUCCESS, "OH_VideoProcessingCallback_Create failed"); - ret = OH_VideoProcessingCallback_BindOnError(callback, OnError); - CHECK_AND_RETURN_LOG(ret == VIDEO_PROCESSING_SUCCESS, "OH_VideoProcessingCallback_BindOnError failed"); - ret = OH_VideoProcessingCallback_BindOnState(callback, OnState); - CHECK_AND_RETURN_LOG(ret == VIDEO_PROCESSING_SUCCESS, "OH_VideoProcessingCallback_BindOnState failed"); - ret = OH_VideoProcessingCallback_BindOnNewOutputBuffer(callback, OnNewOutputBuffer); - CHECK_AND_RETURN_LOG(ret == VIDEO_PROCESSING_SUCCESS, "OH_VideoProcessingCallback_BindOnNewOutputBuffer failed"); - ret = OH_VideoProcessing_RegisterCallback(processor, callback, nullptr); - CHECK_AND_RETURN_LOG(ret == VIDEO_PROCESSING_SUCCESS, "OH_VideoProcessing_RegisterCallback failed"); - // [End VideoProcessingRegisterCallback] - OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, LOG_TAG, "InitProcessing succeed"); -} -// [End PluginManagerInitProcessing] - -// [Start PluginManagerPrepareSurface] -void PluginManager::PrepareSurface(SampleInfo &sampleInfo) { - VideoProcessing_ErrorCode ret = VIDEO_PROCESSING_ERROR_INVALID_VALUE; - if (!isEnvironmentInited) { - ret = OH_VideoProcessing_InitializeEnvironment(); - CHECK_AND_RETURN_LOG(ret == VIDEO_PROCESSING_SUCCESS, "OH_VideoProcessing_InitializeEnvironment failed"); - isEnvironmentInited = true; - } - if (isVPRunning) { - StopProcessing(); - } - int32_t metaData = sampleInfo.metaData; - if (metaData == 2) { // metadata only supports generating OH_VIDEO_HDR_VIVID format - ret = OH_VideoProcessing_Create(&processor, VIDEO_PROCESSING_TYPE_METADATA_GENERATION); - } else { - ret = OH_VideoProcessing_Create(&processor, VIDEO_PROCESSING_TYPE_COLOR_SPACE_CONVERSION); - } - CHECK_AND_RETURN_LOG(ret == VIDEO_PROCESSING_SUCCESS, "OH_VideoProcessing_Create failed"); - ret = OH_VideoProcessing_GetSurface(processor, &PluginManager::GetInstance()->pluginWindow_); - CHECK_AND_RETURN_LOG(ret == VIDEO_PROCESSING_SUCCESS, "OH_VideoProcessing_GetSurface failed"); - OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, LOG_TAG, "PrepareSurface succeed"); -} -// [End PluginManagerPrepareSurface] - -// [Start PluginManagerStartProcessing] -void PluginManager::StartProcessing() { - PluginManager::GetInstance()->sampleInfo_->vpErrorCode = 0; - VideoProcessing_ErrorCode ret = OH_VideoProcessing_Start(processor); - if (ret != VIDEO_PROCESSING_SUCCESS) { - // [StartExclude PluginManagerStartProcessing] - ResetVP(5, 0); - // [EndExclude PluginManagerStartProcessing] - } - CHECK_AND_RETURN_LOG(ret == VIDEO_PROCESSING_SUCCESS, "OH_VideoProcessing_Start failed, ret: %{public}d", ret); - // [StartExclude PluginManagerStartProcessing] - isVPRunning = true; - // [EndExclude PluginManagerStartProcessing] - OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, LOG_TAG, "StartProcessing succeed"); - -} -// [End PluginManagerStartProcessing] - -void PluginManager::SetSampleInfo(SampleInfo *sampleInfo) { sampleInfo_ = sampleInfo; } - -// [Start PluginManagerStopProcessing] -void PluginManager::StopProcessing() { - OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, LOG_TAG, "StartProcessing stop :%{public}d, %{public}d", - g_FrameCount, PluginManager::GetInstance()->frameCount); - if (isVPRunning) { - VideoProcessing_ErrorCode ret = OH_VideoProcessing_Stop(processor); - CHECK_AND_RETURN_LOG(ret == VIDEO_PROCESSING_SUCCESS, "OH_VideoProcessing_Stop failed"); - isVPRunning = false; - } - g_FrameCount = 0; - PluginManager::GetInstance()->frameCount = 0; - OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, LOG_TAG, "StartProcessing Stop succeed"); - DestroyProcessing(); -} -// [End PluginManagerStopProcessing] - -// [Start PluginManagerDestroyProcessing] -void PluginManager::DestroyProcessing() { - CHECK_AND_RETURN_LOG(processor != nullptr, "processor is nullptr"); - OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, LOG_TAG, "start DestroyProcessing"); - VideoProcessing_ErrorCode ret = OH_VideoProcessing_Destroy(processor); - CHECK_AND_RETURN_LOG(ret == VIDEO_PROCESSING_SUCCESS, "OH_VideoProcessing_Destroy failed"); - processor = nullptr; - CHECK_AND_RETURN_LOG(callback != nullptr, "callback is nullptr"); - ret = OH_VideoProcessingCallback_Destroy(callback); - CHECK_AND_RETURN_LOG(ret == VIDEO_PROCESSING_SUCCESS, "OH_VideoProcessingCallback_Destroy failed"); - callback = nullptr; - OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, LOG_TAG, "Destroy And Callback_Destroy succeed"); - - if (OnErrorThread_ && OnErrorThread_->joinable()) { - OnErrorThread_->detach(); - OnErrorThread_.reset(); - OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, LOG_TAG, "Release OnError Thread succeed"); - } -} -// [End PluginManagerDestroyProcessing] - -// [Start PluginManagerRelease] -void PluginManager::Release() { - if (isVPRunning) { - // [StartExclude PluginManagerRelease] - StopProcessing(); - // [EndExclude PluginManagerRelease] - DestroyProcessing(); - } - if (isEnvironmentInited) { - VideoProcessing_ErrorCode ret = OH_VideoProcessing_DeinitializeEnvironment(); - CHECK_AND_RETURN_LOG(ret == VIDEO_PROCESSING_SUCCESS, "OH_VideoProcessing_DeinitializeEnvironment failed"); - isEnvironmentInited = false; - OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, LOG_TAG, "DeinitializeEnvironment Succeed"); - } -} -// [End PluginManagerRelease] - -void PluginManager::ResetVP(int32_t ret, int8_t from) { - PluginManager::GetInstance()->sampleInfo_->vpErrorCode = ret; - CallbackContext *context = (CallbackContext *)PluginManager::GetInstance()->sampleInfo_->playDoneCallbackData; - context->code = ret; - PluginManager::GetInstance()->sampleInfo_->playDoneCallback( - PluginManager::GetInstance()->sampleInfo_->playDoneCallbackData); - errorCode = ret; - if (from == 0) { - StopProcessing(); - } else { - OnErrorThread_ = std::make_unique(&PluginManager::VPOnErrorThread, this); - } -} - -void PluginManager::VPOnErrorThread() { StopProcessing(); } - -int32_t PluginManager::BlackLastFrame() { - int32_t format = 0; - int32_t ret = OH_NativeWindow_NativeWindowHandleOpt(windowOut, GET_FORMAT, &format); - if (ret != 0) { - OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, LOG_TAG, "StartProcessing succeed"); - } - if (format != NATIVEBUFFER_PIXEL_FMT_RGBA_8888) { - ret = OH_NativeWindow_NativeWindowHandleOpt(windowOut, SET_FORMAT, NATIVEBUFFER_PIXEL_FMT_RGBA_8888); - if (ret != 0) { - OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, LOG_TAG, "StartProcessing succeed"); - } - } - OHNativeWindowBuffer *buffer = nullptr; - int releaseFenceFd = -1; - ret = OH_NativeWindow_NativeWindowRequestBuffer(windowOut, &buffer, &releaseFenceFd); - if (ret != 0 || buffer == nullptr) { - OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, LOG_TAG, "StartProcessing succeed"); - return -1; - } - BufferHandle *bufferHandle = OH_NativeWindow_GetBufferHandleFromNative(buffer); - void *mappedAddr = - mmap(bufferHandle->virAddr, bufferHandle->size, PROT_READ | PROT_WRITE, MAP_SHARED, bufferHandle->fd, 0); - if (mappedAddr == MAP_FAILED) { - OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, LOG_TAG, "StartProcessing succeed"); - return -1; - } - OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, LOG_TAG, "StartProcessing succeed"); - int retCode = -1; - uint32_t timeout = 3000; - if (releaseFenceFd != -1) { - struct pollfd pollfds = {0}; - pollfds.fd = releaseFenceFd; - pollfds.events = POLL_IN; - do { - retCode = poll(&pollfds, 1, timeout); - } while (retCode == -1 && (errno == EINTR || errno == EAGAIN)); - close(releaseFenceFd); - } -// memset_s(mappedAddr, bufferHandle->stride * bufferHandle->height, 0, bufferHandle->stride * - // bufferHandle->height); - static uint32_t value = 0x00; - value++; - uint32_t *pixel = static_cast(mappedAddr); - for (uint32_t x = 0; x < bufferHandle->width; x++) { - for (uint32_t y = 0; y < bufferHandle->height; y++) { - *pixel++ = value; - } - } - Region region{nullptr, 0}; - int acquireFenceFd = -1; - ret = OH_NativeWindow_NativeWindowFlushBuffer(windowOut, buffer, acquireFenceFd, region); - OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, LOG_TAG, "StartProcessing succeed"); - int result = munmap(mappedAddr, bufferHandle->size); - if (result == -1) { - OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, LOG_TAG, "StartProcessing succeed"); - return -1; } - return 0; } } // namespace NativeXComponentSample diff --git a/entry/src/main/cpp/render/PluginRender.cpp b/entry/src/main/cpp/render/PluginRender.cpp index b2a35c302f784f32cd38ef5e3f4141affaea3594..866b5b696734e02236ae162370eb6009b764ced1 100644 --- a/entry/src/main/cpp/render/PluginRender.cpp +++ b/entry/src/main/cpp/render/PluginRender.cpp @@ -42,22 +42,12 @@ void OnSurfaceCreatedCB(OH_NativeXComponent *component, void *window) { std::string id(idStr); uint64_t width; uint64_t height; - int32_t xSize = OH_NativeXComponent_GetXComponentSize(component, window, &width, &height); OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, LOG_TAG, "OnSurfaceCreatedCB id: %{public}s", id.c_str()); - if ((xSize == OH_NATIVEXCOMPONENT_RESULT_SUCCESS) && (id == "srcPlayer")) { - auto context = PluginManager::GetInstance(); - context->renderWindow = (OHNativeWindow *)window; - OH_NativeWindow_NativeWindowHandleOpt(context->renderWindow, SET_BUFFER_GEOMETRY, width, height); - OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, LOG_TAG, - "OnSurfaceCreatedCB create windowOut: w:%{public}ld h:%{public}ld", width, height); - } - if ((xSize == OH_NATIVEXCOMPONENT_RESULT_SUCCESS) && (id == "dstPlayer")) { - auto context = PluginManager::GetInstance(); - context->windowOut = (OHNativeWindow *)window; - OH_NativeWindow_NativeWindowHandleOpt(context->windowOut, SET_BUFFER_GEOMETRY, width, height); - OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, LOG_TAG, - "OnSurfaceCreatedCB create windowOut: w:%{public}ld h:%{public}ld", width, height); - } + auto context = PluginManager::GetInstance(); + context->pluginWindow_ = (OHNativeWindow *)window; + OH_NativeWindow_NativeWindowHandleOpt(context->pluginWindow_, SET_BUFFER_GEOMETRY, width, height); + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, LOG_TAG, + "OnSurfaceCreatedCB create windowOut: w:%{public}ld h:%{public}ld", width, height); } void OnSurfaceChangedCB(OH_NativeXComponent *component, void *window) { @@ -148,21 +138,14 @@ void PluginRender::Export(napi_env env, napi_value exports) { } void PluginRender::OnSurfaceChanged(OH_NativeXComponent *component, void *window) { - char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {'\0'}; - uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; - if (OH_NativeXComponent_GetXComponentId(component, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { - OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, LOG_TAG, "OnSurfaceChanged unable to get XComponent id"); - return; - } - std::string id(idStr); - PluginRender *render = PluginRender::GetInstance(id); double offsetX; double offsetY; OH_NativeXComponent_GetXComponentOffset(component, window, &offsetX, &offsetY); - OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, LOG_TAG, - "OnSurfaceChanged offsetX:%{public}lf, " - "offsetY:%{public}lf", - offsetX, offsetY); + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "OH_NativeXComponent_GetXComponentOffset", + "offsetX = %{public}lf, offsetY = %{public}lf", offsetX, offsetY); + uint64_t width; + uint64_t height; + OH_NativeXComponent_GetXComponentSize(component, window, &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 3e38d5b21a60cc3d611cf2e8790e0ff630a33341..2a2cbe520d3195d4eb4b9835f110934dbb74106c 100644 --- a/entry/src/main/cpp/render/include/PluginManager.h +++ b/entry/src/main/cpp/render/include/PluginManager.h @@ -32,39 +32,16 @@ namespace NativeXComponentSample { class PluginManager { public: ~PluginManager(); - - static PluginManager *GetInstance() {return &PluginManager::pluginManager_;} - + OHNativeWindow *pluginWindow_ = nullptr; + static PluginManager *GetInstance() { return &PluginManager::pluginManager_; } void SetNativeXComponent(std::string &id, OH_NativeXComponent *nativeXComponent); PluginRender *GetRender(std::string &id); void Export(napi_env env, napi_value exports); - OHNativeWindow *pluginWindow_ = nullptr; //decoder ->pluginWindow_ - OHNativeWindow *windowOut = nullptr; - OHNativeWindow *renderWindow = nullptr; - OH_VideoProcessing* processor = nullptr; - VideoProcessing_Callback* callback = nullptr; - uint32_t *processCount = nullptr; - SampleInfo *sampleInfo_ = nullptr; - bool isVPRunning = false; - int32_t frameCount = 0; - std::unique_ptr OnErrorThread_ = nullptr; - int32_t errorCode = 0; - void InitProcessing(SampleInfo &sampleInfo); - void PrepareSurface(SampleInfo &sampleInfo); - void StartProcessing(); - void StopProcessing(); - void SetSampleInfo(SampleInfo *sampleInfo); - void DestroyProcessing(); - void Release(); - void ResetVP(int32_t ret, int8_t from); - int32_t BlackLastFrame(); private: static PluginManager pluginManager_; - bool isEnvironmentInited = false; - void VPOnErrorThread(); std::unordered_map nativeXComponentMap_; std::unordered_map pluginRenderMap_; }; -} -#endif //VIDEOPROCESSING_PLUGINMANAGER_H +} // namespace NativeXComponentSample +#endif // VIDEOPROCESSING_PLUGINMANAGER_H diff --git a/entry/src/main/cpp/render/include/PluginRender.h b/entry/src/main/cpp/render/include/PluginRender.h index bccae2e1a6dd29323962db5f09d6528f79ccc8ee..2cd5a5d2d73c094639e30d79f41c35fa11c8782d 100644 --- a/entry/src/main/cpp/render/include/PluginRender.h +++ b/entry/src/main/cpp/render/include/PluginRender.h @@ -25,8 +25,7 @@ namespace NativeXComponentSample { class PluginRender { public: explicit PluginRender(std::string &id); - ~PluginRender() { - } + ~PluginRender() {} static PluginRender *GetInstance(std::string &id); static void Release(std::string &id); void Export(napi_env env, napi_value exports); diff --git a/entry/src/main/cpp/sample/player/Player.cpp b/entry/src/main/cpp/sample/player/Player.cpp index 7a4a98f149e0e6f62082df3ed73349541274dbd8..fc7a752a44b4192b6a01b3cb75e9e9fdb9275068 100644 --- a/entry/src/main/cpp/sample/player/Player.cpp +++ b/entry/src/main/cpp/sample/player/Player.cpp @@ -58,6 +58,7 @@ int32_t Player::Start() { return AVCODEC_SAMPLE_ERROR; } isStarted_ = true; + isPause_ = false; videoDecInputThread_ = std::make_unique(&Player::VideoDecInputThread, this); videoDecOutputThread_ = std::make_unique(&Player::VideoDecOutputThread, this); if (videoDecInputThread_ == nullptr || videoDecOutputThread_ == nullptr) { @@ -94,7 +95,37 @@ int32_t Player::Start() { } doneCond_.notify_all(); AVCODEC_SAMPLE_LOGI("Player Start Succeed"); - isPlaying = true; + return AVCODEC_SAMPLE_OK; +} + +int32_t Player::Pause() { + CHECK_AND_RETURN_RET_LOG(isStarted_, AVCODEC_SAMPLE_ERROR, "player do not start!"); + // Set pause state. + isPause_.store(true); + // if the audio render, pause it. + if (audioRender_) { + OH_AudioRenderer_Pause(audioRender_); + } + return AVCODEC_SAMPLE_OK; +} + +int32_t Player::Resume() { + CHECK_AND_RETURN_RET_LOG(isStarted_, AVCODEC_SAMPLE_ERROR, "player do not start!"); + // Cancel the pause state. + isPause_.store(false); + // Notify the thread to continue work. + if (videoDecContext_) { + videoDecContext_->inputCond.notify_all(); + videoDecContext_->outputCond.notify_all(); + } + if (audioDecContext_) { + audioDecContext_->inputCond.notify_all(); + audioDecContext_->outputCond.notify_all(); + } + // if need audio to play, continue. + if (audioRender_) { + OH_AudioRenderer_Start(audioRender_); + } return AVCODEC_SAMPLE_OK; } @@ -102,9 +133,11 @@ void Player::VideoDecInputThread() { while (true) { CHECK_AND_BREAK_LOG(isStarted_, "VideoDecInputThread in"); std::unique_lock lock(videoDecContext_->inputMutex); - bool condRet = videoDecContext_->inputCond.wait_for( - lock, 5s, [this]() { return !isStarted_ || !videoDecContext_->inputBufferInfoQueue.empty(); }); + bool condRet = videoDecContext_->inputCond.wait_for(lock, 5s, [this]() { + return !isPause_ && (!isStarted_ || !videoDecContext_->inputBufferInfoQueue.empty()); + }); CHECK_AND_BREAK_LOG(isStarted_, "VideoDecInputThread Work Done, thread out"); + CHECK_AND_CONTINUE(!isPause_); CHECK_AND_CONTINUE_LOG(!videoDecContext_->inputBufferInfoQueue.empty(), "VideoDecInputThread Buffer queue is empty,cond ret:%{public}d", condRet); CodecBufferInfo bufferInfo = videoDecContext_->inputBufferInfoQueue.front(); @@ -112,8 +145,8 @@ void Player::VideoDecInputThread() { videoDecContext_->inputFrameCount++; lock.unlock(); - int32_t ret = demuxer_->ReadSample(demuxer_->GetVideoTrackId(), reinterpret_cast(bufferInfo.buffer), - bufferInfo.attr); + int32_t ret = demuxer_->ReadSample(demuxer_->GetVideoTrackId(), + reinterpret_cast(bufferInfo.buffer), bufferInfo.attr); CHECK_AND_BREAK_LOG(ret == AVCODEC_SAMPLE_OK, "VideoDecInputThread ReadSample data failed"); ret = videoDecoder_->PushInputBuffer(bufferInfo); @@ -129,9 +162,11 @@ void Player::VideoDecOutputThread() { thread_local auto lastPushTime = std::chrono::system_clock::now(); CHECK_AND_BREAK_LOG(isStarted_, "VideoDecOutputThread in"); std::unique_lock lock(videoDecContext_->outputMutex); - bool condRet = videoDecContext_->outputCond.wait_for( - lock, 5s, [this]() { return !isStarted_ || !videoDecContext_->outputBufferInfoQueue.empty(); }); + bool condRet = videoDecContext_->outputCond.wait_for(lock, 5s, [this]() { + return !isPause_ && (!isStarted_ || !videoDecContext_->outputBufferInfoQueue.empty()); + }); CHECK_AND_BREAK_LOG(isStarted_, "VideoDecOutputThread Work Done, thread out"); + CHECK_AND_CONTINUE(!isPause_); CHECK_AND_CONTINUE_LOG(!videoDecContext_->outputBufferInfoQueue.empty(), "VideoDecOutputThread Buffer queue is empty, cond ret:%{public}d", condRet); CodecBufferInfo bufferInfo = videoDecContext_->outputBufferInfoQueue.front(); @@ -152,10 +187,22 @@ void Player::VideoDecOutputThread() { void Player::Release() { std::lock_guard lock(mutex_); isStarted_ = false; + isPause_ = false; + + if (videoDecContext_) { + videoDecContext_->inputCond.notify_all(); + videoDecContext_->outputCond.notify_all(); + } + if (audioDecContext_) { + audioDecContext_->inputCond.notify_all(); + audioDecContext_->outputCond.notify_all(); + } + while (audioDecContext_ && !audioDecContext_->renderQueue.empty()) { audioDecContext_->renderQueue.pop(); } if (audioRender_ != nullptr) { + OH_AudioRenderer_Stop(audioRender_); OH_AudioRenderer_Release(audioRender_); audioRender_ = nullptr; } @@ -168,9 +215,8 @@ void Player::Release() { videoDecoder_->Release(); videoDecoder_.reset(); } - int32_t decodeFrameCount = 0; + if (videoDecContext_ != nullptr) { - decodeFrameCount = videoDecContext_->outputFrameCount; delete videoDecContext_; videoDecContext_ = nullptr; } @@ -182,26 +228,15 @@ void Player::Release() { delete audioDecContext_; audioDecContext_ = nullptr; } - OH_AudioStreamBuilder_Destroy(builder_); doneCond_.notify_all(); - sampleInfo_.playDoneCallback(sampleInfo_.playDoneCallbackData); - - if (sampleInfo_.processType == 1) { - NativeXComponentSample::PluginManager().GetInstance()->frameCount = decodeFrameCount; - NativeXComponentSample::PluginManager().GetInstance()->StopProcessing(); - } - NativeXComponentSample::PluginManager().GetInstance()->BlackLastFrame(); } void Player::StartRelease() { - if (audioRender_) { - OH_AudioRenderer_Stop(audioRender_); - } - if (!isReleased_) { - isReleased_ = true; - Release(); + if (isReleased_) { + return; } - isPlaying = false; + isReleased_ = true; + Release(); } void Player::ReleaseThread() { @@ -236,17 +271,15 @@ int32_t Player::CreateAudioDecoder() { OH_AudioStreamBuilder_SetEncodingType(builder_, AUDIOSTREAM_ENCODING_TYPE_RAW); OH_AudioStreamBuilder_SetRendererInfo(builder_, AUDIOSTREAM_USAGE_MUSIC); OH_AudioRenderer_Callbacks callbacks; -#ifndef DEBUG_DECODE callbacks.OH_AudioRenderer_OnWriteData = SampleCallback::OnRenderWriteData; -#else - callbacks.OH_AudioRenderer_OnWriteData = null; -#endif callbacks.OH_AudioRenderer_OnStreamEvent = SampleCallback::OnRenderStreamEvent; callbacks.OH_AudioRenderer_OnInterruptEvent = SampleCallback::OnRenderInterruptEvent; callbacks.OH_AudioRenderer_OnError = SampleCallback::OnRenderError; OH_AudioStreamBuilder_SetRendererCallback(builder_, callbacks, audioDecContext_); OH_AudioStreamBuilder_GenerateRenderer(builder_, &audioRender_); + OH_AudioStreamBuilder_Destroy(builder_); + builder_ = nullptr; CHECK_AND_RETURN_RET_LOG(ret == AVCODEC_SAMPLE_OK, ret, "AudioDecoder Config failed"); return AVCODEC_SAMPLE_OK; } @@ -268,9 +301,11 @@ void Player::AudioDecInputThread() { while (true) { CHECK_AND_BREAK_LOG(isStarted_, "Decoder input thread out"); std::unique_lock lock(audioDecContext_->inputMutex); - bool condRet = audioDecContext_->inputCond.wait_for( - lock, 5s, [this]() { return !isStarted_ || !audioDecContext_->inputBufferInfoQueue.empty(); }); + bool condRet = audioDecContext_->inputCond.wait_for(lock, 5s, [this]() { + return !isPause_ && (!isStarted_ || !audioDecContext_->inputBufferInfoQueue.empty()); + }); CHECK_AND_BREAK_LOG(isStarted_, "Work done, thread out"); + CHECK_AND_CONTINUE(!isPause_); CHECK_AND_CONTINUE_LOG(!audioDecContext_->inputBufferInfoQueue.empty(), "Buffer queue is empty, continue, cond ret: %{public}d", condRet); @@ -279,8 +314,8 @@ void Player::AudioDecInputThread() { audioDecContext_->inputFrameCount++; lock.unlock(); - int32_t ret = demuxer_->ReadSample(demuxer_->GetAudioTrackId(), reinterpret_cast(bufferInfo.buffer), - bufferInfo.attr); + int32_t ret = demuxer_->ReadSample(demuxer_->GetAudioTrackId(), + reinterpret_cast(bufferInfo.buffer), bufferInfo.attr); CHECK_AND_BREAK_LOG(ret == AVCODEC_SAMPLE_OK, "VideoDecInputThread ReadSample data failed"); ret = audioDecoder_->PushInputBuffer(bufferInfo); @@ -294,9 +329,11 @@ void Player::AudioDecOutputThread() { while (true) { CHECK_AND_BREAK_LOG(isStarted_, "Decoder output thread out"); std::unique_lock lock(audioDecContext_->outputMutex); - bool condRet = audioDecContext_->outputCond.wait_for( - lock, 5s, [this]() { return !isStarted_ || !audioDecContext_->outputBufferInfoQueue.empty(); }); + bool condRet = audioDecContext_->outputCond.wait_for(lock, 5s, [this]() { + return !isPause_ && (!isStarted_ || !audioDecContext_->outputBufferInfoQueue.empty()); + }); CHECK_AND_BREAK_LOG(isStarted_, "Decoder output thread out"); + CHECK_AND_CONTINUE(!isPause_); CHECK_AND_CONTINUE_LOG(!audioDecContext_->outputBufferInfoQueue.empty(), "Buffer queue is empty, continue, cond ret: %{public}d", condRet); @@ -322,9 +359,6 @@ void Player::AudioDecOutputThread() { return audioDecContext_->renderQueue.size() < BALANCE_VALUE * bufferInfo.attr.size; }); } - std::unique_lock lockRender(audioDecContext_->renderMutex); - audioDecContext_->renderCond.wait_for(lockRender, 500ms, - [this]() { return audioDecContext_->renderQueue.size() < 1; }); 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 2dbf821fc416430e92be539000eb9c8fb1d5ff8c..564ffc23d42f720188834f74699994060e1f620a 100644 --- a/entry/src/main/cpp/sample/player/Player.h +++ b/entry/src/main/cpp/sample/player/Player.h @@ -32,27 +32,35 @@ class Player { public: Player(){}; ~Player(); - + + static Player &GetInstance() { + static Player player; + return player; + } + int32_t Init(SampleInfo &sampleInfo); int32_t Start(); - bool isPlaying = false; + int32_t Pause(); + int32_t Resume(); + void StartRelease(); + private: void VideoDecInputThread(); void VideoDecOutputThread(); void AudioDecInputThread(); void AudioDecOutputThread(); void Release(); - void StartRelease(); void ReleaseThread(); int32_t CreateAudioDecoder(); int32_t CreateVideoDecoder(); std::unique_ptr videoDecoder_ = nullptr; std::shared_ptr audioDecoder_ = nullptr; std::unique_ptr demuxer_ = nullptr; - + std::mutex mutex_; std::atomic isStarted_{false}; std::atomic isReleased_{false}; + std::atomic isPause_{false}; std::unique_ptr videoDecInputThread_ = nullptr; std::unique_ptr videoDecOutputThread_ = nullptr; std::unique_ptr audioDecInputThread_ = nullptr; @@ -63,8 +71,8 @@ private: CodecUserData *audioDecContext_ = nullptr; OH_AudioStreamBuilder *builder_ = nullptr; OH_AudioRenderer *audioRender_ = nullptr; - + static constexpr int64_t MICROSECOND = 1000000; }; -#endif //VIDEOPROCESSING_PLAYER_H +#endif // VIDEOPROCESSING_PLAYER_H diff --git a/entry/src/main/cpp/sample/player/PlayerNative.cpp b/entry/src/main/cpp/sample/player/PlayerNative.cpp index a163abfb9a630e63876859df62af2b24e276ea5f..6739b49a82568d7dc802896f32d65e51a031ba76 100644 --- a/entry/src/main/cpp/sample/player/PlayerNative.cpp +++ b/entry/src/main/cpp/sample/player/PlayerNative.cpp @@ -15,223 +15,73 @@ #include "PlayerNative.h" #include "AVCodecSampleLog.h" +#include "dfx/error/AVCodecSampleError.h" #include #include -#include #include #undef LOG_DOMAIN #undef LOG_TAG #define LOG_DOMAIN 0xFF00 #define LOG_TAG "Player" -Player *player[2]; -SampleInfo *p_sampleInfo[2]; - -void Callback(void *asyncContext) { - uv_loop_s *loop = nullptr; - CallbackContext *context = (CallbackContext *)asyncContext; - napi_get_uv_event_loop(context->env, &loop); - if (loop == nullptr) { - delete context; - OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN, "playerNative", "callback::loop is nullptr"); - return; - } - uv_work_t *work = new uv_work_t; - work->data = context; - uv_queue_work( - loop, work, [](uv_work_t *work) {}, - [](uv_work_t *work, int status) { - CallbackContext *context = (CallbackContext *)work->data; - napi_handle_scope scope = nullptr; - napi_open_handle_scope(context->env, &scope); - napi_value callbck = nullptr; - napi_get_reference_value(context->env, context->callbackRef, &callbck); - size_t argc = 1; - napi_value arg; - napi_create_int32(context->env, context->code, &arg); - napi_value *argv = &arg; - napi_call_function(context->env, nullptr, callbck, argc, argv, nullptr); - napi_close_handle_scope(context->env, scope); - delete context; - delete work; - }); -} - -napi_value Preload(napi_env env, napi_callback_info info) { - - size_t argc = 2; - napi_value args[2] = {nullptr}; +napi_value PlayerNative::PlayHdr(napi_env env, napi_callback_info info) { + SampleInfo sampleInfo; + size_t argc = 3; + napi_value args[3] = {nullptr}; napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); - int32_t inputFD = 0; - int64_t offset = 0; - int64_t size = 0; - napi_get_value_int32(env, args[0], &inputFD); - napi_get_value_int64(env, args[1], &size); - - auto source = OH_AVSource_CreateWithFD(inputFD, offset, size); - auto sourceFormat = OH_AVSource_GetSourceFormat(source); - if (sourceFormat == nullptr || source == nullptr) { - OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN, "playerNative", "source is nullptr"); - return nullptr; - } - int32_t trackCount = 0; - OH_AVFormat_GetIntValue(sourceFormat, OH_MD_KEY_TRACK_COUNT, &trackCount); - OH_LOG_Print(LOG_APP, LOG_INFO, LOG_DOMAIN, "playerNative", "playerNative::Preload track count: %{public}d", - trackCount); - std::string msg = ""; - for (int i = 0; i < trackCount; i++) { - OH_AVFormat *trackFormat = OH_AVSource_GetTrackFormat(source, i); - CHECK_AND_BREAK_LOG(trackFormat != nullptr, "GetTrackFormat failed for track %d", i); - int32_t trackType = -1; - OH_AVFormat_GetIntValue(trackFormat, OH_MD_KEY_TRACK_TYPE, &trackType); - if (trackType == OH_MediaType::MEDIA_TYPE_VID) { - int32_t isVivid = 0; - int32_t profile = 0; - int32_t transfer = 0; - OH_AVFormat_GetIntValue(trackFormat, OH_MD_KEY_VIDEO_IS_HDR_VIVID, &isVivid); - if (isVivid) { - msg = "HDRVivid"; - } else { - OH_AVFormat_GetIntValue(trackFormat, OH_MD_KEY_PROFILE, &profile); - if (profile == HEVC_PROFILE_MAIN_10) { - msg = "HDR"; - } else { - msg = "SDR"; - } - } - OH_AVFormat_GetIntValue(trackFormat, OH_MD_KEY_TRANSFER_CHARACTERISTICS, &transfer); - switch (transfer) { - case TRANSFER_CHARACTERISTIC_PQ: - msg += " PQ"; - break; - case TRANSFER_CHARACTERISTIC_HLG: - msg += " HLG"; - break; - default: - msg += " UNKNOWN"; - } - int32_t width = 0; - int32_t height = 0; - OH_AVFormat_GetIntValue(trackFormat, OH_MD_KEY_WIDTH, &width); - OH_AVFormat_GetIntValue(trackFormat, OH_MD_KEY_HEIGHT, &height); + napi_get_value_int32(env, args[0], &sampleInfo.inputFd); + napi_get_value_int64(env, args[1], &sampleInfo.inputFileOffset); + napi_get_value_int64(env, args[2], &sampleInfo.inputFileSize); - int32_t pixelFormat = 0; - OH_AVFormat_GetIntValue(trackFormat, OH_MD_KEY_PIXEL_FORMAT, &pixelFormat); - msg += "@" + std::to_string(width) + "x" + std::to_string(height) + "@" + std::to_string(pixelFormat); - } - OH_AVFormat_Destroy(trackFormat); + sampleInfo.window = NativeXComponentSample::PluginManager::GetInstance()->pluginWindow_; + int32_t ret = Player::GetInstance().Init(sampleInfo); + if (ret == AVCODEC_SAMPLE_OK) { + Player::GetInstance().Start(); } - OH_AVFormat_Destroy(sourceFormat); - OH_AVSource_Destroy(source); - napi_value retVal; - napi_create_string_utf8(env, msg.c_str(), msg.size(), &retVal); - return retVal; + return nullptr; } -napi_value StartPlay(napi_env env, napi_callback_info info) { - size_t argc = 9; - napi_value args[9] = {nullptr}; - napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); - int32_t outputFd = -1; - int32_t color = 0; - int32_t metaData = 0; - int32_t videoIndex = 0; - int32_t processType = 0; - napi_get_value_int32(env, args[7], &processType); - napi_get_value_int32(env, args[3], &videoIndex); - if (p_sampleInfo[videoIndex] == nullptr) { - p_sampleInfo[videoIndex] = new SampleInfo(); +napi_value PlayerNative::Pause(napi_env env, napi_callback_info info) { + CHECK_AND_RETURN_RET_LOG(env != nullptr && info != nullptr, nullptr, "env or info is null"); + int32_t ret = Player::GetInstance().Pause(); + if (ret != AVCODEC_SAMPLE_OK) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN, "Pause", "Player pause failed."); } - p_sampleInfo[videoIndex]->videoIndex = videoIndex; - p_sampleInfo[videoIndex]->processType = processType; - napi_get_value_int32(env, args[0], &p_sampleInfo[videoIndex]->inputFd); - napi_get_value_int64(env, args[1], &p_sampleInfo[videoIndex]->inputFileOffset); - napi_get_value_int64(env, args[2], &p_sampleInfo[videoIndex]->inputFileSize); - - auto asyncContext = new CallbackContext(); - asyncContext->env = env; - napi_create_reference(env, args[8], 1, &asyncContext->callbackRef); - p_sampleInfo[videoIndex]->playDoneCallback = &Callback; - p_sampleInfo[videoIndex]->playDoneCallbackData = asyncContext; - OH_LOG_Print(LOG_APP, LOG_INFO, LOG_DOMAIN, "playerNative", "StartPlay: index:%{public}d, type:%{public}d", - videoIndex, processType); - if (p_sampleInfo[videoIndex]->processType == 1) { - napi_get_value_int32(env, args[4], &outputFd); - p_sampleInfo[videoIndex]->outputFd = outputFd; - napi_get_value_int32(env, args[5], &color); - p_sampleInfo[videoIndex]->colorSpace = (OH_NativeBuffer_ColorSpace)color; - napi_get_value_int32(env, args[6], &metaData); - p_sampleInfo[videoIndex]->metaData = metaData; - OH_LOG_Print(LOG_APP, LOG_INFO, LOG_DOMAIN, "playerNative", - "StartPlay: fd:%{public}d, color:%{public}d,metadata:%{public}d", outputFd, color, metaData); - player[videoIndex] = new Player(); - player[videoIndex]->Init(*p_sampleInfo[videoIndex]); - player[videoIndex]->Start(); - NativeXComponentSample::PluginManager::GetInstance()->SetSampleInfo(p_sampleInfo[videoIndex]); - NativeXComponentSample::PluginManager::GetInstance()->StartProcessing(); - } else if (p_sampleInfo[videoIndex]->processType == 2) { - // AVcodec Transcoding is only supported for HDR Vivid. - auto source = - OH_AVSource_CreateWithFD(p_sampleInfo[videoIndex]->inputFd, p_sampleInfo[videoIndex]->inputFileOffset, - p_sampleInfo[videoIndex]->inputFileSize); - auto sourceFormat = OH_AVSource_GetSourceFormat(source); - if (sourceFormat == nullptr || source == nullptr) { - OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN, "playerNative", "source is nullptr"); - return nullptr; - } - int32_t trackCount = 0; - OH_AVFormat_GetIntValue(sourceFormat, OH_MD_KEY_TRACK_COUNT, &trackCount); - OH_LOG_Print(LOG_APP, LOG_INFO, LOG_DOMAIN, "playerNative", "playerNative::Preload track count: %{public}d", - trackCount); - for (int i = 0; i < trackCount; i++) { - OH_AVFormat *trackFormat = OH_AVSource_GetTrackFormat(source, i); - CHECK_AND_BREAK_LOG(trackFormat != nullptr, "GetTrackFormat failed for track %d", i); - int32_t trackType = -1; - OH_AVFormat_GetIntValue(trackFormat, OH_MD_KEY_TRACK_TYPE, &trackType); - if (trackType == OH_MediaType::MEDIA_TYPE_VID) { - int32_t isHDRVivid = 0; - bool getHDRRet = OH_AVFormat_GetIntValue(trackFormat, OH_MD_KEY_VIDEO_IS_HDR_VIVID, &isHDRVivid); + return nullptr; +} - OH_LOG_Print(LOG_APP, LOG_INFO, LOG_DOMAIN, "playerNative", "getHDRRet status: %{public}d", getHDRRet); - if (getHDRRet == false || isHDRVivid == 0) { - OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN, "playerNative", "is not HDRVivid"); - OH_AVFormat_Destroy(trackFormat); - return 0; - } - } - } - player[videoIndex] = new Player(); - player[videoIndex]->Init(*p_sampleInfo[videoIndex]); - player[videoIndex]->Start(); - } else { - player[videoIndex] = new Player(); - player[videoIndex]->Init(*p_sampleInfo[videoIndex]); - player[videoIndex]->Start(); +napi_value PlayerNative::Resume(napi_env env, napi_callback_info info) { + CHECK_AND_RETURN_RET_LOG(env != nullptr && info != nullptr, nullptr, "env or info is null"); + int32_t ret = Player::GetInstance().Resume(); + if (ret != AVCODEC_SAMPLE_OK) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN, "Resume", "Player resume failed."); } return nullptr; } -napi_value Destroy(napi_env env, napi_callback_info info) { - OH_LOG_Print(LOG_APP, LOG_INFO, LOG_DOMAIN, "playerNative", "playerNative::Destroy"); - if (p_sampleInfo[0] != nullptr) { - delete p_sampleInfo[0]; - p_sampleInfo[0] = nullptr; - } - if (p_sampleInfo[1] != nullptr) { - delete p_sampleInfo[1]; - p_sampleInfo[1] = nullptr; - } - NativeXComponentSample::PluginManager::GetInstance()->Release(); +napi_value PlayerNative::SwitchVideo(napi_env env, napi_callback_info info) { + CHECK_AND_RETURN_RET_LOG(env != nullptr && info != nullptr, nullptr, "env or info is null"); + // delete all resource + Player::GetInstance().StartRelease(); + return PlayHdr(env, info); +} + +napi_value PlayerNative::StartRelease(napi_env env, napi_callback_info info) { + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_DOMAIN, "playerNative", "StartRelease123"); + // delete all resource + Player::GetInstance().StartRelease(); return nullptr; } EXTERN_C_START static napi_value Init(napi_env env, napi_value exports) { napi_property_descriptor classProp[] = { - {"preload", nullptr, Preload, nullptr, nullptr, nullptr, napi_default, nullptr}, - {"playNative", nullptr, StartPlay, nullptr, nullptr, nullptr, napi_default, nullptr}, - {"destroy", nullptr, Destroy, nullptr, nullptr, nullptr, napi_default, nullptr}}; + {"playHdr", nullptr, PlayerNative::PlayHdr, nullptr, nullptr, nullptr, napi_default, nullptr}, + {"pause", nullptr, PlayerNative::Pause, nullptr, nullptr, nullptr, napi_default, nullptr}, + {"resume", nullptr, PlayerNative::Resume, nullptr, nullptr, nullptr, napi_default, nullptr}, + {"switchVideo", nullptr, PlayerNative::SwitchVideo, nullptr, nullptr, nullptr, napi_default, nullptr}, + {"startRelease", nullptr, PlayerNative::StartRelease, nullptr, nullptr, nullptr, napi_default, nullptr}}; napi_define_properties(env, exports, sizeof(classProp) / sizeof(classProp[0]), classProp); NativeXComponentSample::PluginManager::GetInstance()->Export(env, exports); return exports; diff --git a/entry/src/main/cpp/sample/player/PlayerNative.h b/entry/src/main/cpp/sample/player/PlayerNative.h index 077e6e848b07e80c1c3fc90fd9af16585055faf6..cbbad3669139117081bc2ba2660bd47ad6c5cd75 100644 --- a/entry/src/main/cpp/sample/player/PlayerNative.h +++ b/entry/src/main/cpp/sample/player/PlayerNative.h @@ -21,9 +21,12 @@ #include #include "Player.h" #include "PluginManager.h" - -static napi_value Preload(napi_env env, napi_callback_info info, napi_value exports); -static napi_value StartPlay(napi_env env, napi_callback_info info); -static napi_value Destroy(napi_env env, napi_callback_info info); - -#endif //VIDEOPROCESSING_PLAYERNATIVE_H +class PlayerNative { +public: + static napi_value PlayHdr(napi_env env, napi_callback_info info); + static napi_value Pause(napi_env env, napi_callback_info info); + static napi_value Resume(napi_env env, napi_callback_info info); + static napi_value SwitchVideo(napi_env env, napi_callback_info info); + static napi_value StartRelease(napi_env env, napi_callback_info info); +}; +#endif // VIDEOPROCESSING_PLAYERNATIVE_H diff --git a/entry/src/main/cpp/sample/recorder/Recorder.cpp b/entry/src/main/cpp/sample/recorder/Recorder.cpp index 6397f1bf92280e85ea3a98a08ef2336d7a8ef579..d3f7cadeed1d22b9f353d90d53cfd262928cb7c4 100644 --- a/entry/src/main/cpp/sample/recorder/Recorder.cpp +++ b/entry/src/main/cpp/sample/recorder/Recorder.cpp @@ -31,8 +31,7 @@ Recorder::~Recorder() { StartRelease(); } int32_t Recorder::Init(SampleInfo &sampleInfo) { std::lock_guard lock(mutex_); CHECK_AND_RETURN_RET_LOG(!isStarted_, AVCODEC_SAMPLE_ERROR, "Already started."); - CHECK_AND_RETURN_RET_LOG(videoEncoder_ == nullptr && muxer_ == nullptr, AVCODEC_SAMPLE_ERROR, - "Already started."); + CHECK_AND_RETURN_RET_LOG(videoEncoder_ == nullptr && muxer_ == nullptr, AVCODEC_SAMPLE_ERROR, "Already started."); sampleInfo_ = sampleInfo; // Audio Capturer Init @@ -67,8 +66,7 @@ int32_t Recorder::Start() { std::lock_guard lock(mutex_); CHECK_AND_RETURN_RET_LOG(!isStarted_, AVCODEC_SAMPLE_ERROR, "Already started."); CHECK_AND_RETURN_RET_LOG(encContext_ != nullptr, AVCODEC_SAMPLE_ERROR, "Already started."); - CHECK_AND_RETURN_RET_LOG(videoEncoder_ != nullptr && muxer_ != nullptr, AVCODEC_SAMPLE_ERROR, - "Already started."); + CHECK_AND_RETURN_RET_LOG(videoEncoder_ != nullptr && muxer_ != nullptr, AVCODEC_SAMPLE_ERROR, "Already started."); int32_t ret = muxer_->Start(); CHECK_AND_RETURN_RET_LOG(ret == AVCODEC_SAMPLE_OK, ret, "Muxer start failed"); @@ -217,8 +215,11 @@ int32_t Recorder::WaitForDone() { } int32_t Recorder::Stop() { - int32_t ret = videoEncoder_->NotifyEndOfStream(); - CHECK_AND_RETURN_RET_LOG(ret == AVCODEC_SAMPLE_OK, ret, "Encoder notifyEndOfStream failed"); + if (isStarted_) { + int32_t ret = videoEncoder_->NotifyEndOfStream(); + CHECK_AND_RETURN_RET_LOG(ret == AVCODEC_SAMPLE_OK, ret, "Encoder notifyEndOfStream failed"); + } + StartRelease(); return WaitForDone(); } @@ -265,7 +266,7 @@ void Recorder::AudioEncInputThread() { 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(); diff --git a/entry/src/main/cpp/sample/recorder/Recorder.h b/entry/src/main/cpp/sample/recorder/Recorder.h index 92cf0f16f8802ab572d6ff01e13f5b7560a8425e..9567d014f67d1d9ea7703bd9d4165af253a2b57f 100644 --- a/entry/src/main/cpp/sample/recorder/Recorder.h +++ b/entry/src/main/cpp/sample/recorder/Recorder.h @@ -47,7 +47,7 @@ private: void Release(); void StartRelease(); int32_t WaitForDone(); - + int32_t CreateAudioEncoder(); int32_t CreateVideoEncoder(); diff --git a/entry/src/main/cpp/sample/recorder/RecorderNative.cpp b/entry/src/main/cpp/sample/recorder/RecorderNative.cpp index 5f9d8206c932817471d15b0b530133dbbf709c20..57bda883f5980d674e8784b0a733b24b0ab8ddaf 100644 --- a/entry/src/main/cpp/sample/recorder/RecorderNative.cpp +++ b/entry/src/main/cpp/sample/recorder/RecorderNative.cpp @@ -87,11 +87,11 @@ napi_value RecorderNative::Init(napi_env env, napi_callback_info info) { 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_int32(env, args[5], &sampleInfo.isRecordHDRVivid); napi_get_value_int64(env, args[6], &sampleInfo.bitrate); sampleInfo.videoCodecMime = videoCodecMime; - if (sampleInfo.isHDRVivid) { + if (sampleInfo.isRecordHDRVivid == 1) { sampleInfo.hevcProfile = HEVC_PROFILE_MAIN_10; } diff --git a/entry/src/main/cpp/sample/recorder/RecorderNative.h b/entry/src/main/cpp/sample/recorder/RecorderNative.h index f518e75634fd4e703be02ce3ecfb1051e64e7a63..da81ed501ca8cbe4fe7a3b6c11f3c73ac6aa6ae0 100644 --- a/entry/src/main/cpp/sample/recorder/RecorderNative.h +++ b/entry/src/main/cpp/sample/recorder/RecorderNative.h @@ -21,7 +21,6 @@ #include "napi/native_api.h" #include "Recorder.h" - class RecorderNative { public: static napi_value Init(napi_env env, napi_callback_info info); diff --git a/entry/src/main/cpp/sample/transcoder/AVCodecTranscoder.cpp b/entry/src/main/cpp/sample/transcoder/AVCodecTranscoder.cpp new file mode 100644 index 0000000000000000000000000000000000000000..745e40e6d741de3b265933da16449860a64dce05 --- /dev/null +++ b/entry/src/main/cpp/sample/transcoder/AVCodecTranscoder.cpp @@ -0,0 +1,336 @@ +/* + * 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 "AVCodecTranscoder.h" +#include "AVCodecSampleLog.h" +#include "dfx/error/AVCodecSampleError.h" +#include + +#undef LOG_TAG +#define LOG_TAG "AVCodecTranscoder" + +namespace { +constexpr int BALANCE_VALUE = 5; +using namespace std::chrono_literals; +static const int MS_TO_S = 1000; +constexpr int64_t WAIT_TIME_US_THRESHOLD_WARNING = -1 * 40 * 1000; // warning threshold 40ms +constexpr int64_t WAIT_TIME_US_THRESHOLD = 1 * 1000 * 1000; // max sleep time 1s +constexpr int64_t SINK_TIME_US_THRESHOLD = 100000; // max sink time 100ms +constexpr int32_t BYTES_PER_SAMPLE_2 = 2; // 2 bytes per sample +constexpr double VSYNC_TIME = 1000 / 60; // frame time +constexpr double LIP_SYNC_BALANCE_VALUE = 2; // the balance value of sync sound and picture +} // namespace + +AVCodecTranscoder::~AVCodecTranscoder() { + AVCodecTranscoder::StartDecRelease(); + AVCodecTranscoder::StartEncRelease(); +} + +void AVCodecTranscoder::NotifyEndOfStream() { videoEncoder_->NotifyEndOfStream(); } + +bool AVCodecTranscoder::IsHDR(const SampleInfo &sampleInfo) { + sampleInfo_ = sampleInfo; + demuxer_ = std::make_unique(); + demuxer_->Create(sampleInfo_); + demuxer_->Release(); + demuxer_ = nullptr; + return sampleInfo_.isHDRVivid; +} + +int32_t AVCodecTranscoder::PreInit(const SampleInfo &sampleInfo) { + CHECK_AND_RETURN_RET_LOG(!isStarted_, AVCODEC_SAMPLE_ERROR, "Already started."); + CHECK_AND_RETURN_RET_LOG(demuxer_ == nullptr && videoDecoder_ == nullptr, AVCODEC_SAMPLE_ERROR, "Already started."); + + sampleInfo_ = sampleInfo; + bool isSupport = videoProcessing->IsColorSpaceConversionSupported(sampleInfo_); + if (isSupport && sampleInfo.processType > 1) { + videoProcessing = std::make_unique(); + videoProcessing->SetProcessingSurface(sampleInfo_); + } + int32_t ret = MuxerAndDemuxerInit(sampleInfo_); + CHECK_AND_RETURN_RET_LOG(ret == AVCODEC_SAMPLE_OK, ret, "Create MuxerAndDemuxerInit failed"); + ret = EncoderInit(sampleInfo_); + CHECK_AND_RETURN_RET_LOG(ret == AVCODEC_SAMPLE_OK, ret, "Create EncoderInit failed"); + if (isSupport && sampleInfo.processType > 1) { + videoProcessing->InitProcessing(sampleInfo_); + videoProcessing->StartProcessing(); + } + isEncReleased_ = false; + isDecReleased_ = false; + AVCODEC_SAMPLE_LOGI("Succeed"); + return AVCODEC_SAMPLE_OK; +} + +int32_t AVCodecTranscoder::EncoderInit(SampleInfo &sampleInfo) { + videoEncoder_ = std::make_unique(); + int32_t ret = sampleInfo.isRecordHDRVivid ? videoEncoder_->Create(MIME_VIDEO_HEVC.data()) + : videoEncoder_->Create(MIME_VIDEO_AVC.data()); + + CHECK_AND_RETURN_RET_LOG(ret == AVCODEC_SAMPLE_OK, ret, "Create videoEncoder_ failed"); + videoEncContext_ = new CodecUserData(); + ret = videoEncoder_->Config(sampleInfo_, videoEncContext_); + CHECK_AND_RETURN_RET_LOG(ret == AVCODEC_SAMPLE_OK, ret, "Create videoEncContext_ failed"); + return AVCODEC_SAMPLE_OK; +} + +int32_t AVCodecTranscoder::MuxerAndDemuxerInit(SampleInfo &sampleInfo) { + demuxer_ = std::make_unique(); + int32_t ret = demuxer_->Create(sampleInfo); + CHECK_AND_RETURN_RET_LOG(ret == AVCODEC_SAMPLE_OK, ret, "Create demuxer failed"); + + muxer_ = std::make_unique(); + ret = muxer_->Create(sampleInfo.outputFd); + CHECK_AND_RETURN_RET_LOG(ret == AVCODEC_SAMPLE_OK, ret, "Create muxer failed"); + + muxer_->Config(sampleInfo); + AVCODEC_SAMPLE_LOGI("Succeed"); + return AVCODEC_SAMPLE_OK; +} + +int32_t AVCodecTranscoder::AfterInit() { + int32_t ret = CreateVideoDecoder(); + CHECK_AND_RETURN_RET_LOG(ret == AVCODEC_SAMPLE_OK, ret, "Decoder config failed"); + + AVCODEC_SAMPLE_LOGI("Succeed"); + return AVCODEC_SAMPLE_OK; +} + + +int32_t AVCodecTranscoder::Start() { + std::unique_lock lock(mutex_); + CHECK_AND_RETURN_RET_LOG(!isStarted_, AVCODEC_SAMPLE_ERROR, "Already started."); + CHECK_AND_RETURN_RET_LOG(demuxer_ != nullptr && videoDecoder_ != nullptr, AVCODEC_SAMPLE_ERROR, "Already started."); + int32_t ret; + if (videoDecContext_) { + ret = videoDecoder_->Start(); + CHECK_AND_RETURN_RET_LOG(ret == AVCODEC_SAMPLE_OK, ret, "Decoder start failed"); + isDecStarted_ = true; + videoDecInputThread_ = std::make_unique(&AVCodecTranscoder::VideoDecInputThread, this); + videoDecOutputThread_ = std::make_unique(&AVCodecTranscoder::VideoDecOutputThread, this); + if (videoDecInputThread_ == nullptr || videoDecOutputThread_ == nullptr) { + AVCODEC_SAMPLE_LOGE("Create thread failed"); + StartDecRelease(); + return AVCODEC_SAMPLE_ERROR; + } + } + + if (videoEncContext_) { + ret = muxer_->Start(); + ret = videoEncoder_->Start(); + CHECK_AND_RETURN_RET_LOG(ret == AVCODEC_SAMPLE_OK, ret, "Encoder start failed"); + isEncStarted_ = true; + videoEncOutputThread_ = std::make_unique(&AVCodecTranscoder::VideoEncOutputThread, this); + if (videoEncOutputThread_ == nullptr) { + AVCODEC_SAMPLE_LOGE("Create thread failed"); + StartEncRelease(); + return AVCODEC_SAMPLE_ERROR; + } + } + + AVCODEC_SAMPLE_LOGI("Succeed"); + doneCond_.notify_all(); + return AVCODEC_SAMPLE_OK; +} + +void AVCodecTranscoder::StartDecRelease() { + if (!isDecReleased_) { + isDecReleased_ = true; + DecRelease(); + NotifyEndOfStream(); + } +} + +void AVCodecTranscoder::StartRelease() { + if (!isDecReleased_) { + isDecReleased_ = true; + DecRelease(); + } + if (!isEncReleased_) { + isEncReleased_ = true; + EncRelease(); + } +} + +void AVCodecTranscoder::DecRelease() { + isDecStarted_ = false; + AVCODEC_SAMPLE_LOGI("DecRelease Succeed: %{public}d, %{public}d", isDecStarted_.load(), isEncStarted_.load()); + if (videoDecInputThread_ && videoDecInputThread_->joinable()) { + videoDecContext_->inputCond.notify_all(); + videoDecInputThread_->join(); + videoDecInputThread_.reset(); + } + if (videoDecOutputThread_ && videoDecOutputThread_->joinable()) { + videoDecContext_->outputCond.notify_all(); + videoDecOutputThread_->detach(); + videoDecOutputThread_.reset(); + } + if (demuxer_ != nullptr) { + demuxer_->Release(); + demuxer_.reset(); + } + if (videoDecoder_ != nullptr) { + videoDecoder_->Release(); + videoDecoder_.reset(); + } + if (videoDecContext_ != nullptr) { + delete videoDecContext_; + videoDecContext_ = nullptr; + } + + AVCODEC_SAMPLE_LOGI("DecRelease Succeed"); +} + +void AVCodecTranscoder::StartEncRelease() { + if (!isEncReleased_) { + isEncReleased_ = true; + EncRelease(); + } +} + +void AVCodecTranscoder::EncRelease() { + isEncStarted_ = false; + if (videoEncOutputThread_ && videoEncOutputThread_->joinable()) { + videoEncOutputThread_->detach(); + videoEncOutputThread_.reset(); + } + if (muxer_ != nullptr) { + muxer_->Release(); + muxer_.reset(); + AVCODEC_SAMPLE_LOGI("Muxer release successful"); + } + if (videoEncoder_ != nullptr) { + videoEncoder_->Stop(); + videoEncoder_->Release(); + videoEncoder_.reset(); + } + if (videoEncContext_ != nullptr) { + delete videoEncContext_; + videoEncContext_ = nullptr; + } + if (videoProcessing != nullptr) { + videoProcessing->StopProcessing(); + } + std::string id("AVCodecTranscoder"); + AVCODEC_SAMPLE_LOGI("EncRelease Succeed"); + if (sampleInfo_.playDoneCallback != nullptr) { + sampleInfo_.playDoneCallback(sampleInfo_.playDoneCallbackData); + } +} + +void AVCodecTranscoder::VideoDecInputThread() { + while (true) { + CHECK_AND_BREAK_LOG(isDecStarted_, "Decoder input thread out"); + std::unique_lock lock(videoDecContext_->inputMutex); + bool condRet = videoDecContext_->inputCond.wait_for( + lock, 5s, [this]() { return !isDecStarted_ || !videoDecContext_->inputBufferInfoQueue.empty(); }); + CHECK_AND_BREAK_LOG(isDecStarted_, "Work done, thread out"); + CHECK_AND_CONTINUE_LOG(!videoDecContext_->inputBufferInfoQueue.empty(), + "Buffer queue is empty, continue, cond ret: %{public}d", condRet); + + CodecBufferInfo bufferInfo = videoDecContext_->inputBufferInfoQueue.front(); + videoDecContext_->inputBufferInfoQueue.pop(); + videoDecContext_->inputFrameCount++; + lock.unlock(); + + demuxer_->ReadSample(demuxer_->GetVideoTrackId(), reinterpret_cast(bufferInfo.buffer), + bufferInfo.attr); + + int32_t ret = videoDecoder_->PushInputBuffer(bufferInfo); + AVCODEC_SAMPLE_LOGW("Out buffer count: %{public}u, size: %{public}d, flag: %{public}u, pts: %{public}" PRId64, + videoDecContext_->inputFrameCount, bufferInfo.attr.size, bufferInfo.attr.flags, + bufferInfo.attr.pts); + CHECK_AND_BREAK_LOG(ret == AVCODEC_SAMPLE_OK, "Push data failed, thread out"); + CHECK_AND_BREAK_LOG(!(bufferInfo.attr.flags & AVCODEC_BUFFER_FLAGS_EOS), + "VideoDecInputThread Catch EOS, thread out"); + } +} + +void AVCodecTranscoder::VideoDecOutputThread() { + sampleInfo_.frameInterval = MICROSECOND_TO_S / sampleInfo_.frameRate; + while (true) { + CHECK_AND_BREAK_LOG(isDecStarted_, "Decoder output thread out"); + std::unique_lock lock(videoDecContext_->outputMutex); + bool condRet = videoDecContext_->outputCond.wait_for( + lock, 5s, [this]() { return !isDecStarted_ || !videoDecContext_->outputBufferInfoQueue.empty(); }); + CHECK_AND_BREAK_LOG(isDecStarted_, "Decoder output thread out"); + CHECK_AND_CONTINUE_LOG(!videoDecContext_->outputBufferInfoQueue.empty(), + "Buffer queue is empty, continue, cond ret: %{public}d", condRet); + + CodecBufferInfo bufferInfo = videoDecContext_->outputBufferInfoQueue.front(); + videoDecContext_->outputBufferInfoQueue.pop(); + CHECK_AND_BREAK_LOG(!(bufferInfo.attr.flags & AVCODEC_BUFFER_FLAGS_EOS), "Catch EOS, thread out"); + videoDecContext_->outputFrameCount++; + AVCODEC_SAMPLE_LOGW("Out buffer count: %{public}u, size: %{public}d, flag: %{public}u, pts: %{public}" PRId64, + videoDecContext_->outputFrameCount, bufferInfo.attr.size, bufferInfo.attr.flags, + bufferInfo.attr.pts); + lock.unlock(); + + + if (bufferInfo.attr.flags & AVCODEC_BUFFER_FLAGS_EOS) { + AVCODEC_SAMPLE_LOGW("VideoDecOutputThread Catch EOS, thread out" PRId64); + videoDecoder_->FreeOutputBuffer(bufferInfo.bufferIndex, false); + break; + } else { + int32_t ret = videoDecoder_->FreeOutputBuffer(bufferInfo.bufferIndex, true); + CHECK_AND_BREAK_LOG(ret == AVCODEC_SAMPLE_OK, "Decoder output thread"); + } + } + StartDecRelease(); +} + +void AVCodecTranscoder::VideoEncOutputThread() { + while (true) { + CHECK_AND_BREAK_LOG(isEncStarted_, "Work done, thread out"); + std::unique_lock lock(videoEncContext_->outputMutex); + bool condRet = videoEncContext_->outputCond.wait_for( + lock, 5s, [this]() { return !isEncStarted_ || !videoEncContext_->outputBufferInfoQueue.empty(); }); + CHECK_AND_BREAK_LOG(isEncStarted_, "Work done, thread out"); + CHECK_AND_CONTINUE_LOG(!videoEncContext_->outputBufferInfoQueue.empty(), + "Buffer queue is empty, continue, cond ret: %{public}d", condRet); + + CodecBufferInfo bufferInfo = videoEncContext_->outputBufferInfoQueue.front(); + videoEncContext_->outputBufferInfoQueue.pop(); + CHECK_AND_BREAK_LOG(!(bufferInfo.attr.flags & AVCODEC_BUFFER_FLAGS_EOS), "Catch EOS, thread out"); + lock.unlock(); + + if ((bufferInfo.attr.flags & AVCODEC_BUFFER_FLAGS_SYNC_FRAME) || + (bufferInfo.attr.flags == AVCODEC_BUFFER_FLAGS_NONE)) { + videoEncContext_->outputFrameCount++; + bufferInfo.attr.pts = videoEncContext_->outputFrameCount * 1000000 / sampleInfo_.frameRate; + } else { + bufferInfo.attr.pts = 0; + } + AVCODEC_SAMPLE_LOGD("Out buffer count: %{public}u, size: %{public}d, flag: %{public}u, pts: %{public}" PRId64, + videoEncContext_->outputFrameCount, bufferInfo.attr.size, bufferInfo.attr.flags, + bufferInfo.attr.pts); + + muxer_->WriteSample(muxer_->GetVideoTrackId(), reinterpret_cast(bufferInfo.buffer), + bufferInfo.attr); + int32_t ret = videoEncoder_->FreeOutputBuffer(bufferInfo.bufferIndex); + CHECK_AND_BREAK_LOG(ret == AVCODEC_SAMPLE_OK, "Encoder output thread out"); + } + StartEncRelease(); +} + +int32_t AVCodecTranscoder::CreateVideoDecoder() { + videoDecoder_ = std::make_unique(); + int ret = videoDecoder_->Create(sampleInfo_); + CHECK_AND_RETURN_RET_LOG(ret == AVCODEC_SAMPLE_OK, ret, "Create video decoder failed"); + + videoDecContext_ = new CodecUserData; + ret = videoDecoder_->Config(sampleInfo_, videoDecContext_); + CHECK_AND_RETURN_RET_LOG(ret == AVCODEC_SAMPLE_OK, ret, "Decoder config failed"); + return AVCODEC_SAMPLE_OK; +} diff --git a/entry/src/main/cpp/sample/transcoder/AVCodecTranscoder.h b/entry/src/main/cpp/sample/transcoder/AVCodecTranscoder.h new file mode 100644 index 0000000000000000000000000000000000000000..3fa5a11b96d10e1aaa3a948b76c58f9296ea8d49 --- /dev/null +++ b/entry/src/main/cpp/sample/transcoder/AVCodecTranscoder.h @@ -0,0 +1,92 @@ +/* + * 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 HDR2SDR_AVCODECTRANSCODER_H +#define HDR2SDR_AVCODECTRANSCODER_H + +#include "AudioDecoder.h" +#include "AudioEncoder.h" +#include "Demuxer.h" +#include "Muxer.h" +#include "SampleInfo.h" +#include "VideoDecoder.h" +#include "VideoEncoder.h" +#include "VideoProcessing.h" +#include + +class AVCodecTranscoder { +public: + AVCodecTranscoder(){}; + ~AVCodecTranscoder(); + + static AVCodecTranscoder &GetInstance() { + static AVCodecTranscoder avCodecTranscoder; + return avCodecTranscoder; + } + + void NotifyEndOfStream(); + bool IsHDR(const SampleInfo &sampleInfo); + int32_t PreInit(const SampleInfo &sampleInfo); + int32_t MuxerAndDemuxerInit(SampleInfo &sampleInfo); + int32_t EncoderInit(SampleInfo &sampleInfo); + int32_t AfterInit(); + int32_t Start(); + void StartDecRelease(); + void StartEncRelease(); + void StartRelease(); + +private: + void VideoDecInputThread(); + void VideoDecOutputThread(); + void VideoEncOutputThread(); + void DecRelease(); + void EncRelease(); + + int32_t CreateVideoDecoder(); + + std::unique_ptr muxer_ = nullptr; + std::unique_ptr demuxer_ = nullptr; + std::unique_ptr videoProcessing = nullptr; + std::unique_ptr videoDecoder_ = nullptr; + std::shared_ptr audioDecoder_ = nullptr; + std::unique_ptr videoEncoder_ = nullptr; + std::shared_ptr audioEncoder_ = nullptr; + + std::mutex mutex_; + std::atomic isStarted_{false}; + std::atomic isDecStarted_{false}; + std::atomic isEncStarted_{false}; + std::atomic isEncReleased_{false}; + std::atomic isDecReleased_{false}; + std::unique_ptr videoDecInputThread_ = nullptr; + std::unique_ptr videoDecOutputThread_ = nullptr; + std::unique_ptr videoEncOutputThread_ = nullptr; + std::unique_ptr audioDecInputThread_ = nullptr; + std::unique_ptr audioDecOutputThread_ = nullptr; + std::condition_variable doneCond_; + SampleInfo sampleInfo_; + CodecUserData *videoDecContext_ = nullptr; + CodecUserData *videoEncContext_ = nullptr; + CodecUserData *audioDecContext_ = nullptr; + + int64_t nowTimeStamp = 0; + int64_t audioTimeStamp = 0; + int64_t writtenSampleCnt = 0; + int64_t audioBufferPts = 0; + static constexpr int64_t MICROSECOND_TO_S = 1000000; + static constexpr int64_t NANO_TO_S = 1000000000; +}; + +#endif // HDR2SDR_AVCODECTRANSCODER_H diff --git a/entry/src/main/cpp/sample/transcoder/AVTranscoder.cpp b/entry/src/main/cpp/sample/transcoder/AVTranscoder.cpp new file mode 100644 index 0000000000000000000000000000000000000000..07048315f27d3eea614b849e3348f093d166d638 --- /dev/null +++ b/entry/src/main/cpp/sample/transcoder/AVTranscoder.cpp @@ -0,0 +1,109 @@ +/* + * 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 "multimedia/player_framework/avtranscoder.h" +#include "AVCodecSampleLog.h" +#include "multimedia/player_framework/avtranscoder_base.h" +#include "AVTranscoder.h" + +// [Start StartAVTranscoder] +int32_t AVTranscoder::StartAVTranscoder(const SampleInfo &sampleInfo) { + result = 0; + sampleInfo_ = sampleInfo; + transcoder = OH_AVTranscoder_Create(); + CreateDefaultTransCoderConfig(sampleInfo.outputFd); + // [Start SetStateCallback] + OH_AVTranscoder_SetStateCallback(transcoder, AvTranscoderStateChangeCb, nullptr); + // [End SetStateCallback] + OH_AVTranscoder_SetErrorCallback(transcoder, OnErrorCb, nullptr); + OH_AVErrCode result = + OH_AVTranscoderConfig_SetSrcFD(config, sampleInfo.inputFd, 0, sampleInfo.inputFileSize); + if (result != AV_ERR_OK) { + AVCODEC_SAMPLE_LOGI("Transcoder setSrcFD failed, ret %{public}d", result); + } + result = OH_AVTranscoder_Prepare(transcoder, config); + if (result != AV_ERR_OK) { + AVCODEC_SAMPLE_LOGI("Transcoder prepare failed, ret %{public}d", result); + } + result = OH_AVTranscoder_Start(transcoder); + return result; +} +// [End StartAVTranscoder] + +// [Start CreateDefaultTransCoderConfig] +void AVTranscoder::CreateDefaultTransCoderConfig(int32_t dstFd) { + config = OH_AVTranscoderConfig_Create(); + OH_AVTranscoderConfig_SetDstFD(config, dstFd); + OH_AVTranscoderConfig_SetDstFileType(config, AV_OUTPUT_FORMAT_MPEG_4); + // [Start SetDstVideoType] + OH_AVTranscoderConfig_SetDstVideoType(config, "video/avc"); + // [End SetDstVideoType] + OH_AVTranscoderConfig_SetDstAudioType(config, "audio/mp4a-latm"); + OH_AVTranscoderConfig_SetDstAudioBitrate(config, 200000); + OH_AVTranscoderConfig_SetDstVideoBitrate(config, 3000000); +} +// [End CreateDefaultTransCoderConfig] + +// [Start AvTranscoderStateChangeCb] +void AVTranscoder::AvTranscoderStateChangeCb(OH_AVTranscoder *transcoder, OH_AVTranscoder_State state, void *userData) { + if (transcoder == nullptr) { + return; + } + switch (state) { + case AVTRANSCODER_COMPLETED: { + AVTranscoder::GetInstance().result = 1; + AVTranscoder::GetInstance().ReleaseAVTranscoder(); + break; + } + default: + break; + } +} +// [End AvTranscoderStateChangeCb] + +void AVTranscoder::OnErrorCb(OH_AVTranscoder *transcoder, int32_t errorCode, const char *errorMsg, void *userData) { + AVCODEC_SAMPLE_LOGI("Transcoder failed, ret %s", errorMsg); + AVTranscoder::GetInstance().result = -1; + AVTranscoder::GetInstance().ReleaseAVTranscoder(); +} + +int32_t AVTranscoder::StopAVTranscoder() { + OH_AVErrCode result = AV_ERR_OK; + if (transcoder != nullptr) { + result = OH_AVTranscoder_Cancel(transcoder); + if (result != AV_ERR_OK) { + AVCODEC_SAMPLE_LOGI("Transcoder prepare failed, ret %{public}d", result); + } + } + return result; +} + +// [Start ReleaseAVTranscoder] +int32_t AVTranscoder::ReleaseAVTranscoder() { + int ret = 0; + AVCODEC_SAMPLE_LOGI("OH_AVTranscoder_Release ret:%{public}d", ret); + if (transcoder != nullptr) { + ret = OH_AVTranscoder_Release(transcoder); + AVCODEC_SAMPLE_LOGI("OH_AVTranscoder_Release ret:%{public}d", ret); + transcoder = nullptr; + } + if (config != nullptr) { + ret = OH_AVTranscoderConfig_Release(config); + AVCODEC_SAMPLE_LOGI("OH_AVTranscoderConfig_Release ret:%{public}d", ret); + config = nullptr; + } + return ret; +} +// [End ReleaseAVTranscoder] \ No newline at end of file diff --git a/entry/src/main/cpp/sample/transcoder/AVTranscoder.h b/entry/src/main/cpp/sample/transcoder/AVTranscoder.h new file mode 100644 index 0000000000000000000000000000000000000000..6ff4eb53ab2ec55aaaead7f4f12800489fe446eb --- /dev/null +++ b/entry/src/main/cpp/sample/transcoder/AVTranscoder.h @@ -0,0 +1,43 @@ +/* + * 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 HDR2SDR_AVTRANSCODER_H +#define HDR2SDR_AVTRANSCODER_H + +#include "SampleInfo.h" +#include "stdint.h" +#include +#include + +class AVTranscoder { +public: + static AVTranscoder &GetInstance() { + static AVTranscoder avTranscoder; + return avTranscoder; + } + + OH_AVTranscoder *transcoder = nullptr; + OH_AVTranscoder_Config *config = nullptr; + int32_t result = 0; + SampleInfo sampleInfo_; + int32_t StartAVTranscoder(const SampleInfo &sampleInfo); + void CreateDefaultTransCoderConfig(int32_t dstFd); + static void AvTranscoderStateChangeCb(OH_AVTranscoder *transcoder, OH_AVTranscoder_State state, void *userData); + static void OnErrorCb(OH_AVTranscoder *transcoder, int32_t errorCode, const char *errorMsg, void *userData); + int32_t StopAVTranscoder(); + int32_t ReleaseAVTranscoder(); +}; + +#endif // HDR2SDR_AVTRANSCODER_H diff --git a/entry/src/main/cpp/sample/transcoder/TranscoderNative.cpp b/entry/src/main/cpp/sample/transcoder/TranscoderNative.cpp new file mode 100644 index 0000000000000000000000000000000000000000..31abf5e458f0bd860f961fd745d77958a80a79fd --- /dev/null +++ b/entry/src/main/cpp/sample/transcoder/TranscoderNative.cpp @@ -0,0 +1,208 @@ +/* + * 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 "TranscoderNative.h" +#include "SampleInfo.h" +#include +#include "AVTranscoder.h" +#include "AVCodecTranscoder.h" + + +#undef LOG_DOMAIN +#undef LOG_TAG +#define LOG_DOMAIN 0xFF00 +#define LOG_TAG "transcoder" + +void Callback(void *asyncContext) { + uv_loop_s *loop = nullptr; + CallbackContext *context = (CallbackContext *)asyncContext; + napi_get_uv_event_loop(context->env, &loop); + uv_work_t *work = new uv_work_t; + work->data = context; + uv_queue_work( + loop, work, [](uv_work_t *work) {}, + [](uv_work_t *work, int status) { + CallbackContext *context = (CallbackContext *)work->data; + napi_handle_scope scope = nullptr; + // Manage the lifecycle of napi_value to prevent memory leaks. + napi_open_handle_scope(context->env, &scope); + napi_value callback = nullptr; + napi_get_reference_value(context->env, context->callbackRef, &callback); + // Callback to UI side. + napi_call_function(context->env, nullptr, callback, 0, nullptr, nullptr); + napi_close_handle_scope(context->env, scope); + delete context; + delete work; + }); +} + +napi_value TranscoderNative::IsHdr(napi_env env, napi_callback_info info) { + SampleInfo sampleInfo; + size_t argc = 2; + napi_value args[2] = {nullptr}; + napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + + napi_get_value_int32(env, args[0], &sampleInfo.inputFd); + napi_get_value_int64(env, args[1], &sampleInfo.inputFileSize); + + napi_value value; + bool isHdr = AVCodecTranscoder::GetInstance().IsHDR(sampleInfo); + napi_get_boolean(env, isHdr, &value); + return value; +} + +napi_value TranscoderNative::StartAVTranscoder(napi_env env, napi_callback_info info) { + SampleInfo sampleInfo; + size_t argc = 3; + napi_value args[3] = {nullptr}; + napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + + napi_get_value_int32(env, args[0], &sampleInfo.inputFd); + napi_get_value_int64(env, args[1], &sampleInfo.inputFileSize); + napi_get_value_int32(env, args[2], &sampleInfo.outputFd); + + AVTranscoder::GetInstance().StartAVTranscoder(sampleInfo); + return nullptr; +} + +napi_value TranscoderNative::GetAVTranscoderState(napi_env env, napi_callback_info info) { + napi_value value; + napi_create_int32(env, AVTranscoder::GetInstance().result, &value); + return value; +} + +napi_value TranscoderNative::StartAVCodecTranscoder(napi_env env, napi_callback_info info) { + SampleInfo sampleInfo; + size_t argc = 4; + napi_value args[4] = {nullptr}; + napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + + napi_get_value_int32(env, args[0], &sampleInfo.inputFd); + napi_get_value_int64(env, args[1], &sampleInfo.inputFileSize); + napi_get_value_int32(env, args[2], &sampleInfo.outputFd); + auto asyncContext = new CallbackContext(); + asyncContext->env = env; + napi_create_reference(env, args[3], 1, &asyncContext->callbackRef); + + sampleInfo.playDoneCallback = &Callback; + sampleInfo.playDoneCallbackData = asyncContext; + sampleInfo.processType = 1; + int32_t ret = AVCodecTranscoder::GetInstance().PreInit(sampleInfo); + ret = AVCodecTranscoder::GetInstance().AfterInit(); + AVCodecTranscoder::GetInstance().Start(); + return nullptr; +} + +napi_value TranscoderNative::ReleaseAVCodecTranscoder(napi_env env, napi_callback_info info) { + AVCodecTranscoder::GetInstance().StartRelease(); + return nullptr; +} + +napi_value TranscoderNative::StartVideoProcessingTranscoder(napi_env env, napi_callback_info info) { + SampleInfo sampleInfo; + size_t argc = 4; + napi_value args[4] = {nullptr}; + napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + + napi_get_value_int32(env, args[0], &sampleInfo.inputFd); + napi_get_value_int64(env, args[1], &sampleInfo.inputFileSize); + napi_get_value_int32(env, args[2], &sampleInfo.outputFd); + auto asyncContext = new CallbackContext(); + asyncContext->env = env; + napi_create_reference(env, args[3], 1, &asyncContext->callbackRef); + + sampleInfo.playDoneCallback = &Callback; + sampleInfo.playDoneCallbackData = asyncContext; + sampleInfo.processType = 2; + // [Start set_value] + sampleInfo.inputFormat.metadataType = OH_VIDEO_HDR_VIVID; + sampleInfo.inputFormat.colorSpace = OH_COLORSPACE_BT2020_HLG_LIMIT; + sampleInfo.inputFormat.pixelFormat = NATIVEBUFFER_PIXEL_FMT_YCRCB_P010; + sampleInfo.outputFormat.metadataType = OH_VIDEO_NONE; + sampleInfo.outputFormat.colorSpace = OH_COLORSPACE_BT709_LIMIT; + sampleInfo.outputFormat.pixelFormat = NATIVEBUFFER_PIXEL_FMT_YCBCR_420_SP; + // [End set_value] + + int32_t ret = AVCodecTranscoder::GetInstance().PreInit(sampleInfo); + ret = AVCodecTranscoder::GetInstance().AfterInit(); + AVCodecTranscoder::GetInstance().Start(); + return nullptr; +} + +napi_value TranscoderNative::StartTransformColorspace(napi_env env, napi_callback_info info) { + SampleInfo sampleInfo; + size_t argc = 4; + napi_value args[4] = {nullptr}; + napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + + napi_get_value_int32(env, args[0], &sampleInfo.inputFd); + napi_get_value_int64(env, args[1], &sampleInfo.inputFileSize); + napi_get_value_int32(env, args[2], &sampleInfo.outputFd); + auto asyncContext = new CallbackContext(); + asyncContext->env = env; + napi_create_reference(env, args[3], 1, &asyncContext->callbackRef); + + sampleInfo.playDoneCallback = &Callback; + sampleInfo.playDoneCallbackData = asyncContext; + sampleInfo.processType = 2; + sampleInfo.isRecordHDRVivid = 1; + sampleInfo.hevcProfile = HEVC_PROFILE_MAIN_10; + sampleInfo.inputFormat.metadataType = OH_VIDEO_HDR_VIVID; + sampleInfo.inputFormat.colorSpace = OH_COLORSPACE_BT2020_HLG_LIMIT; + sampleInfo.inputFormat.pixelFormat = NATIVEBUFFER_PIXEL_FMT_YCRCB_P010; + sampleInfo.outputFormat.metadataType = OH_VIDEO_HDR_VIVID; + sampleInfo.outputFormat.colorSpace = OH_COLORSPACE_BT2020_PQ_LIMIT; + sampleInfo.outputFormat.pixelFormat = NATIVEBUFFER_PIXEL_FMT_YCRCB_P010; + + int32_t ret = AVCodecTranscoder::GetInstance().PreInit(sampleInfo); + ret = AVCodecTranscoder::GetInstance().AfterInit(); + AVCodecTranscoder::GetInstance().Start(); + return nullptr; +} + + +EXTERN_C_START +static napi_value Init(napi_env env, napi_value exports) { + napi_property_descriptor desc[] = { + {"isHdr", nullptr, TranscoderNative::IsHdr, nullptr, nullptr, nullptr, napi_default, nullptr}, + {"startAVTranscoder", nullptr, TranscoderNative::StartAVTranscoder, nullptr, nullptr, nullptr, napi_default, + nullptr}, + {"startAVCodecTranscoder", nullptr, TranscoderNative::StartAVCodecTranscoder, nullptr, nullptr, nullptr, + napi_default, nullptr}, + {"startVideoProcessingTranscoder", nullptr, TranscoderNative::StartVideoProcessingTranscoder, nullptr, nullptr, + nullptr, napi_default, nullptr}, + {"startTransformColorspace", nullptr, TranscoderNative::StartTransformColorspace, nullptr, nullptr, nullptr, + napi_default, nullptr}, + {"getAVTranscoderState", nullptr, TranscoderNative::GetAVTranscoderState, nullptr, nullptr, nullptr, + napi_default, nullptr}, + {"releaseAVCodecTranscoder", nullptr, TranscoderNative::ReleaseAVCodecTranscoder, nullptr, nullptr, nullptr, + napi_default, nullptr}, + }; + napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc); + return exports; +} +EXTERN_C_END + +static napi_module RecorderModule = { + .nm_version = 1, + .nm_flags = 0, + .nm_filename = nullptr, + .nm_register_func = Init, + .nm_modname = "transcoder", + .nm_priv = ((void *)0), + .reserved = {0}, +}; + +extern "C" __attribute__((constructor)) void RegisterRecorderModule(void) { napi_module_register(&RecorderModule); } \ No newline at end of file diff --git a/entry/src/main/cpp/sample/transcoder/TranscoderNative.h b/entry/src/main/cpp/sample/transcoder/TranscoderNative.h new file mode 100644 index 0000000000000000000000000000000000000000..c8fca8299c896448ff4118e1f9a6acf2ac616485 --- /dev/null +++ b/entry/src/main/cpp/sample/transcoder/TranscoderNative.h @@ -0,0 +1,34 @@ +/* + * 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 VIDEO_CODEC_SAMPLE_TRANSCODER_NATIVE_H +#define VIDEO_CODEC_SAMPLE_TRANSCODER_NATIVE_H + +#include +#include +#include "napi/native_api.h" + +class TranscoderNative { +public: + static napi_value IsHdr(napi_env env, napi_callback_info info); + static napi_value StartAVTranscoder(napi_env env, napi_callback_info info); + static napi_value StartAVCodecTranscoder(napi_env env, napi_callback_info info); + static napi_value StartVideoProcessingTranscoder(napi_env env, napi_callback_info info); + static napi_value GetAVTranscoderState(napi_env env, napi_callback_info info); + static napi_value StartTransformColorspace(napi_env env, napi_callback_info info); + static napi_value ReleaseAVCodecTranscoder(napi_env env, napi_callback_info info); +}; + +#endif // VIDEO_CODEC_SAMPLE_TRANSCODER_NATIVE_H \ No newline at end of file diff --git a/entry/src/main/cpp/types/libplayer/index.d.ts b/entry/src/main/cpp/types/libplayer/index.d.ts index 6e8b009852daa4be5bdecc0897bdc8014a93c5ba..87e09fc16859519e0f63e5aa3435c51c678ec45a 100644 --- a/entry/src/main/cpp/types/libplayer/index.d.ts +++ b/entry/src/main/cpp/types/libplayer/index.d.ts @@ -13,21 +13,20 @@ * limitations under the License. */ -export const preload: ( +export const playHdr: ( inputFileFd: number, + inputFileOffset: number, inputFileSize: number -) => string; +) => void; + +export const pause: () => void; -export const playNative: ( - intputFIleFd: number, +export const resume: () => void; + +export const switchVideo: ( + inputFileFd: number, inputFileOffset: number, - inputFileSize: number, - needProcess: number, - outputFileFd: number, - outputColorSpace: number, - outputMetaData: number, - processType: number, - cbFn: (ret: number) => void + inputFileSize: number ) => void; -export const destroy: () => void; \ No newline at end of file +export const startRelease: () => void; \ No newline at end of file diff --git a/entry/src/main/cpp/types/libtranscoder/index.d.ts b/entry/src/main/cpp/types/libtranscoder/index.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..e14a210c3d1af380f129c7ee32933d25e7a7ae59 --- /dev/null +++ b/entry/src/main/cpp/types/libtranscoder/index.d.ts @@ -0,0 +1,62 @@ +/* + * 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. + */ + +// export const avTranscoder: ( +// inputFileFd: number, +// inputFileOffset: number, +// inputFileSize: number, +// cbFn: () => void +// ) => void; + +export const isHdr: ( + inputFileFd: number, + inputFileSize: number +) => boolean; + +export const startAVTranscoder: ( + inputFileFd: number, + inputFileSize: number, + outputFile: number +) => void; + +export const startAVCodecTranscoder: ( + inputFileFd: number, + inputFileSize: number, + outputFile: number, + cbFn: () => void +) => void; + +export const startVideoProcessingTranscoder: ( + inputFileFd: number, + inputFileSize: number, + outputFile: number, + cbFn: () => void +) => void; + +export const startTransformColorspace: ( + inputFileFd: number, + inputFileSize: number, + outputFile: number, + cbFn: () => void +) => void; + +export const getAVTranscoderState: () => number; + +export const releaseAVCodecTranscoder: () => void; + +export class Response { + code: number + surfaceId: string +} \ No newline at end of file diff --git a/entry/src/main/cpp/types/libtranscoder/oh-package.json5 b/entry/src/main/cpp/types/libtranscoder/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..aef35d87c09b3710452eb574784a479036120a4b --- /dev/null +++ b/entry/src/main/cpp/types/libtranscoder/oh-package.json5 @@ -0,0 +1,21 @@ +/* + * 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. + */ + +{ + "name": "libtranscoder.so", + "types": "./index.d.ts", + "version": "1.0.0", + "description": "Transcoder interface." +} \ No newline at end of file diff --git a/entry/src/main/ets/common/CommonEnum.ets b/entry/src/main/ets/common/CommonEnum.ets new file mode 100644 index 0000000000000000000000000000000000000000..3fcbe1fe4b47ee09dcdb4e4446e7aeb6e7827184 --- /dev/null +++ b/entry/src/main/ets/common/CommonEnum.ets @@ -0,0 +1,27 @@ +/* + * 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. + */ + +export enum VideoStatus { + NO_VIDEO = 0, // No videos + SELECTED_VIDEOS = 1, // Selected videos + OPTIMIZE_VIDEOS = 2, // Optimize videos + TRANSCODER_VIDEOS = 3 // Optimize videos +} + +export enum StatusEnum { + UNAVAILABLE = 0, // unavailable + AVAILABLE = 1, // available + SELECTED = 2 // selected +} \ No newline at end of file diff --git a/entry/src/main/ets/common/utils/Logger.ets b/entry/src/main/ets/common/utils/Logger.ets new file mode 100644 index 0000000000000000000000000000000000000000..168e877a46c77fa835f9624e6c2dd0d02499cb5c --- /dev/null +++ b/entry/src/main/ets/common/utils/Logger.ets @@ -0,0 +1,38 @@ +/* + * 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. + */ + +import { hilog } from '@kit.PerformanceAnalysisKit'; + +export class Logger { + private static domain: number = 0xFF00; + private static prefix: string = 'hdr2sdr'; + private static format: string = '%{public}s, %{public}s'; + + static debug(...args: string[]): void { + hilog.debug(Logger.domain, Logger.prefix, Logger.format, args); + } + + static info(...args: string[]): void { + hilog.info(Logger.domain, Logger.prefix, Logger.format, args); + } + + static warn(...args: string[]): void { + hilog.warn(Logger.domain, Logger.prefix, Logger.format, args); + } + + static error(...args: string[]): void { + hilog.error(Logger.domain, Logger.prefix, Logger.format, args); + } +} \ No newline at end of file diff --git a/entry/src/main/ets/common/utils/PermissionsUtils.ets b/entry/src/main/ets/common/utils/PermissionsUtils.ets index 04c75c1fbb18dcd0e88e27e5958e757dbc18d7ef..246ce875a68011a927024f96695d2c137f79702f 100644 --- a/entry/src/main/ets/common/utils/PermissionsUtils.ets +++ b/entry/src/main/ets/common/utils/PermissionsUtils.ets @@ -13,9 +13,10 @@ * limitations under the License. */ -import { abilityAccessCtrl, common, Permissions } from '@kit.AbilityKit'; +import { abilityAccessCtrl, common, Permissions, bundleManager } from '@kit.AbilityKit'; import { hilog } from '@kit.PerformanceAnalysisKit'; import { BusinessError } from '@kit.BasicServicesKit'; +import { Logger } from './Logger'; const context = (AppStorage.get('uiContext') as UIContext).getHostContext() as common.UIAbilityContext; const TAG = '[permissions]' @@ -25,6 +26,28 @@ const permissions: Array = [ 'ohos.permission.WRITE_MEDIA' ]; +export function checkPermission(permission: Permissions): abilityAccessCtrl.GrantStatus { + let grantStatus: abilityAccessCtrl.GrantStatus = abilityAccessCtrl.GrantStatus.PERMISSION_DENIED; + try { + let bundleInfo: bundleManager.BundleInfo = + bundleManager.getBundleInfoForSelfSync(bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION); + let tokenId: number = bundleInfo.appInfo.accessTokenId; + grantStatus = abilityAccessCtrl.createAtManager().checkAccessTokenSync(tokenId, permission); + } catch (e) { + Logger.error(TAG, `Get bundle info failed, ${JSON.stringify(e)}`); + } + return grantStatus; +} + +export function checkPermissions(): boolean { + let permissionGranted: boolean = true; + for (let i = 0; i < permissions.length; i++) { + permissionGranted = + permissionGranted && checkPermission(permissions[i]) === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED; + } + return permissionGranted; +} + export function PermissionsFromUser(): void { let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager(); atManager.requestPermissionsFromUser(context, permissions).then((data) => { @@ -32,13 +55,13 @@ export function PermissionsFromUser(): void { let length: number = grantStatus.length; for (let i = 0; i < length; i++) { if (grantStatus[i] === 0) { - hilog.info(0x0000, TAG, 'User authorized.'); + Logger.info(TAG, 'User authorized.'); } else { - hilog.info(0x0000, TAG, 'User denied authorization.'); + Logger.info(TAG, 'User denied authorization.'); return; } } }).catch((err: BusinessError) => { - hilog.error(0x0000, TAG, `Failed to request permissions from user. Code is ${err.code}, message is ${err.message}`); + Logger.error(TAG, `Failed to request permissions from user. Code is ${err.code}, message is ${err.message}`); }) } \ No newline at end of file diff --git a/entry/src/main/ets/common/utils/VideoOperationUtils.ets b/entry/src/main/ets/common/utils/VideoOperationUtils.ets index 3081342d49692fbced404a18b0a7d9840fb4d3ee..9c38a3eafd4441914008adf528bc0ac1fafa001d 100644 --- a/entry/src/main/ets/common/utils/VideoOperationUtils.ets +++ b/entry/src/main/ets/common/utils/VideoOperationUtils.ets @@ -28,10 +28,10 @@ export async function saveFile(context: Context) { const srcUri: string = fileUri.getUriFromPath(AppStorage.get('path')); // Create album resources try { - const asset = await helper.createAsset(photoAccessHelper.PhotoType.VIDEO, 'mp4', { - title: `Video_${DATETIME.getDate()}_${DATETIME.getTime()}` - }); - // Write sandbox file to album + const asset = await helper.createAsset(photoAccessHelper.PhotoType.VIDEO, 'mp4', { + title: `Video_${DATETIME.getDate()}_${DATETIME.getTime()}` + }); + // Write sandbox file to album const srcFd = await fileIo.open(srcUri, fileIo.OpenMode.READ_ONLY); const destFd = await fileIo.open(asset, fileIo.OpenMode.WRITE_ONLY); await fileIo.copyFile(srcFd.fd, destFd.fd); @@ -55,7 +55,8 @@ export function setVideoStabilizationMode(session: camera.VideoSession): boolean let activeVideoStabilizationMode = session.getActiveVideoStabilizationMode(); hilog.info(0x0000, TAG, `activeVideoStabilizationMode: ${activeVideoStabilizationMode}`); } catch (error) { - hilog.error(0x0000, TAG, `setVideoStabilizationMode failed. code is =${error.code}, message is = ${error.message}`); + hilog.error(0x0000, TAG, + `setVideoStabilizationMode failed. code is =${error.code}, message is = ${error.message}`); } } else { hilog.info(0x0000, TAG, `videoStabilizationMode: ${mode} is not support`); @@ -70,7 +71,8 @@ export function isVideoStabilizationModeSupported(session: camera.VideoSession, isSupported = session.isVideoStabilizationModeSupported(mode); } catch (error) { // Failed to return error code error. code and handle it. - hilog.error(0x0000, TAG, `The isVideoStabilizationModeSupported call failed. code is =${error.code}, message is = ${error.message}`); + hilog.error(0x0000, TAG, + `The isVideoStabilizationModeSupported call failed. code is =${error.code}, message is = ${error.message}`); } return isSupported; } @@ -99,7 +101,8 @@ export function getSupportedColorSpaces(session: camera.VideoSession): Array { + curWindow.setWindowKeepScreenOn(isKeepScreenOn, (err: BusinessError) => { + if (err.code) { + Logger.error(`Failed to set the screen. Cause code: ${err.code}, message: ${err.message}`); + return; + } + Logger.info(`Succeeded in setting the screen ${isKeepScreenOn}.`); + }); + }).catch((err: string) => { + Logger.error(`Failed to obtain the top window.. Cause code:: ${err}`); + }); +} \ No newline at end of file diff --git a/entry/src/main/ets/controller/AVCodecController.ets b/entry/src/main/ets/controller/AVCodecController.ets index fc924b2d67ab959d53803dcbf136a786434b7654..b2c848a621f987e45df7b74392a85fdf029d76dd 100644 --- a/entry/src/main/ets/controller/AVCodecController.ets +++ b/entry/src/main/ets/controller/AVCodecController.ets @@ -15,16 +15,14 @@ import { common } from '@kit.AbilityKit'; import { camera } from '@kit.CameraKit'; -import { hilog } from '@kit.PerformanceAnalysisKit'; import { BusinessError } from '@kit.BasicServicesKit'; import { FileUtil } from '../common/utils/FileUtil'; -import DateTimeUtil from '../common/utils/DateTimeUtils'; import { CameraDataModel } from '../model/CameraDateModel'; import recorder from 'librecorder.so'; import { setVideoStabilizationMode, setColorSpaceBeforeCommitConfig } from '../common/utils/VideoOperationUtils'; import { fileIo } from '@kit.CoreFileKit'; +import { Logger } from '../common/utils/Logger'; -const DATETIME: DateTimeUtil = new DateTimeUtil(); const TAG: string = '[AVCodecController]'; export class AVCodecController { @@ -32,26 +30,23 @@ export class AVCodecController { private cameraManager: camera.CameraManager | undefined = undefined; private cameraInput: camera.CameraInput | undefined = undefined; private videoSession: camera.VideoSession | undefined = undefined; - private cameraData: CameraDataModel = AppStorage.get('cameraData') as CameraDataModel; + private cameraData: CameraDataModel = new CameraDataModel(); private encoderVideoOutput: camera.VideoOutput | undefined = undefined; private XComponentPreviewOutput: camera.PreviewOutput | undefined = undefined; - public async createRecorder(context: common.Context, XComponentSurfaceId: string): Promise { - this.releaseCamera().catch((error:BusinessError) => { - hilog.error(0x0000, TAG, `releaseCamera fail. code is =${error.code}, message is = ${error.message}`); - }); + public async createRecorder(context: common.Context, XComponentSurfaceId: string, filePath: string): Promise { // Create the CameraManager object. try { this.cameraManager = camera.getCameraManager(context); } catch (error) { - hilog.error(0x0000, TAG, `error code is =${error.code}, getCameraManager fail, message is = ${error.message}`); + Logger.error(TAG, `error code is =${error.code}, getCameraManager fail, message is = ${error.message}`); } if (!this.cameraManager) { - hilog.error(0x0000, TAG, 'camera.getCameraManager error'); + Logger.error(TAG, 'camera.getCameraManager error'); return; } - this.path = context.filesDir + `/VIDEO_${DATETIME.getDate()}_${DATETIME.getTime()}.mp4`; + this.path = filePath; let file = FileUtil.createOrOpen(this.path); if (file) { this.cameraData.outputfd = file.fd; @@ -67,43 +62,45 @@ export class AVCodecController { }) } + this.createVideoOutput(XComponentSurfaceId); + // [EndExclude camera_conversation] + this.videoSessionConfig(); + this.startCamera(); + } + + public createVideoOutput(XComponentSurfaceId: string) { + console.log('createVideoOutput: ' + XComponentSurfaceId); // Get supported camera devices. + if (!this.cameraManager) { + return; + } let cameraArray: Array = this.cameraManager.getSupportedCameras(); if (cameraArray !== undefined && cameraArray.length <= 0) { - hilog.error(0x0000, TAG, 'cameraManager.getSupportedCameras error!'); + Logger.error(TAG, 'cameraManager.getSupportedCameras error!'); return; } - - let cameraOutputCap: camera.CameraOutputCapability = - this.cameraManager.getSupportedOutputCapability(cameraArray[0], camera.SceneMode.NORMAL_VIDEO); + let cameraOutputCap: camera.CameraOutputCapability | undefined = + this.cameraManager?.getSupportedOutputCapability(cameraArray[0], camera.SceneMode.NORMAL_VIDEO); if (!cameraOutputCap) { - hilog.error(0x0000, TAG, 'cameraManager.getSupportedOutputCapability error'); + Logger.error(TAG, 'cameraManager.getSupportedOutputCapability error'); return; } - let previewProfilesArray: Array = cameraOutputCap.previewProfiles; if (!previewProfilesArray) { - hilog.error(0x0000, TAG, 'createOutput previewProfilesArray === null || undefined'); - } - - let photoProfilesArray: Array = cameraOutputCap.photoProfiles; - if (!photoProfilesArray) { - hilog.error(0x0000, TAG, 'createOutput photoProfilesArray === null || undefined'); + Logger.error(TAG, 'createOutput previewProfilesArray === null || undefined'); } - let videoProfilesArray: Array = cameraOutputCap.videoProfiles; if (!videoProfilesArray) { - hilog.error(0x0000, TAG, 'createOutput videoProfilesArray === null || undefined'); + Logger.error(TAG, 'createOutput videoProfilesArray === null || undefined'); } - // [Start create_video_output3] let videoProfile: undefined | camera.VideoProfile = videoProfilesArray.find((profile: camera.VideoProfile) => { - return profile.format === camera.CameraFormat.CAMERA_FORMAT_YCBCR_P010 && + return profile.format === camera.CameraFormat.CAMERA_FORMAT_YCRCB_P010 && profile.size.width === this.cameraData.cameraWidth && profile.size.height === this.cameraData.cameraHeight; }); if (!videoProfile) { - hilog.error(0x0000, TAG, 'videoProfile is not found!'); + Logger.error(TAG, 'videoProfile is not found!'); return; } @@ -113,26 +110,26 @@ export class AVCodecController { }) // Create the encoder output object try { - this.encoderVideoOutput = this.cameraManager.createVideoOutput(videoProfile, this.cameraData.surfaceId); + this.encoderVideoOutput = this.cameraManager?.createVideoOutput(videoProfile, this.cameraData.surfaceId); } catch (error) { - hilog.error(0x0000, TAG, `error code is =${error.code}, createVideoOutput fail, message is = ${error.message}`); + Logger.error(TAG, `error code is =${error.code}, createVideoOutput fail, message is = ${error.message}`); } if (this.encoderVideoOutput === undefined) { - hilog.error(0x0000, TAG, 'encoderVideoOutput is undefined'); + Logger.error(TAG, 'encoderVideoOutput is undefined'); return; } - // [End create_video_output3] + // Create a preview stream output object try { this.XComponentPreviewOutput = this.cameraManager.createPreviewOutput(XComponentPreviewProfile, XComponentSurfaceId); } catch (error) { - hilog.error(0x0000, TAG, `error code is =${error.code}, createPreviewOutput fail, message is = ${error.message}`); + Logger.error(TAG, `error code is =${error.code}, createPreviewOutput fail, message is = ${error.message}`); } if (this.XComponentPreviewOutput === undefined) { - hilog.error(0x0000, TAG, 'XComponentPreviewOutput is undefined'); + Logger.error(TAG, 'XComponentPreviewOutput is undefined'); return; } @@ -140,31 +137,32 @@ export class AVCodecController { try { this.cameraInput = this.cameraManager.createCameraInput(cameraArray[0]); } catch (error) { - hilog.error(0x0000, TAG, `Failed to createCameraInput. code is =${error.code}, message is = ${error.message}`); + Logger.error(TAG, `Failed to createCameraInput. code is =${error.code}, message is = ${error.message}`); } if (this.cameraInput === undefined) { - hilog.error(0x0000, TAG, 'cameraInput is undefined'); + Logger.error(TAG, 'cameraInput is undefined'); return; } // Turn on the camera. try { - await this.cameraInput.open(); + this.cameraInput.open(); } catch (error) { - hilog.error(0x0000, TAG, `Failed to open cameraInput. code is =${error.code}, message is = ${error.message}`); - + Logger.error(TAG, `Failed to open cameraInput. code is =${error.code}, message is = ${error.message}`); } - // [EndExclude camera_conversation] + // [End create_video_output3] + } + public async videoSessionConfig(): Promise { // Create a session flow try { - this.videoSession = this.cameraManager.createSession(camera.SceneMode.NORMAL_VIDEO) as camera.VideoSession; + this.videoSession = this.cameraManager?.createSession(camera.SceneMode.NORMAL_VIDEO) as camera.VideoSession; } catch (error) { - hilog.error(0x0000, TAG, `Failed to create the session instance. code is =${error.code}, message is = ${error.message}`); + Logger.error(TAG, `Failed to create the session instance. code is =${error.code}, message is = ${error.message}`); } // [StartExclude camera_conversation] if (this.videoSession === undefined) { - hilog.error(0x0000, TAG, 'videoSession is undefined'); + Logger.error(TAG, 'videoSession is undefined'); return; } // [EndExclude camera_conversation] @@ -174,7 +172,7 @@ export class AVCodecController { this.videoSession.beginConfig(); } catch (error) { // [StartExclude camera_conversation] - hilog.error(0x0000, TAG, `Failed to beginConfig. code is =${error.code}, message is = ${error.message}`); + Logger.error(TAG, `Failed to beginConfig. code is =${error.code}, message is = ${error.message}`); // [EndExclude camera_conversation] } // [StartExclude camera_conversation] @@ -183,7 +181,7 @@ export class AVCodecController { try { this.videoSession.addInput(this.cameraInput); } catch (error) { - hilog.error(0x0000, TAG, `Failed to add cameraInput. code is =${error.code}, message is = ${error.message}`); + Logger.error(TAG, `Failed to add cameraInput. code is =${error.code}, message is = ${error.message}`); } // [EndExclude camera_conversation] // Add the XComponent preview stream to the session. @@ -191,7 +189,7 @@ export class AVCodecController { this.videoSession.addOutput(this.XComponentPreviewOutput); } catch (error) { // [StartExclude camera_conversation] - hilog.error(0x0000, TAG, `Failed to add XcomponentPreviewOutput. code is =${error.code}, message is = ${error.message}`); + Logger.error(TAG, `Failed to add XcomponentPreviewOutput. code is =${error.code}, message is = ${error.message}`); // [EndExclude camera_conversation] } @@ -201,44 +199,47 @@ export class AVCodecController { } catch (error) { // [StartExclude camera_conversation] let err = error as BusinessError; - hilog.error(0x0000, TAG, `Failed to add encoderVideoOutput. code is =${error.code}, message is = ${error.message}`); + Logger.error(TAG, `Failed to add encoderVideoOutput. code is =${err.code}, message is = ${err.message}`); // [EndExclude camera_conversation] } + // Set video stabilization. + if (setVideoStabilizationMode(this.videoSession)) { + // Set color space. + setColorSpaceBeforeCommitConfig(this.videoSession, this.cameraData.isHDRVivid); + } + // Submit configuration information. try { await this.videoSession.commitConfig(); } catch (error) { // [StartExclude camera_conversation] let err = error as BusinessError; - hilog.error(0x0000, TAG, `videoSession commitConfig code is =${error.code}, message is = ${error.message}`); + Logger.error(TAG, `videoSession commitConfig code is =${err.code}, message is = ${err.message}`); // [EndExclude camera_conversation] } + } - // Set video stabilization. - if (setVideoStabilizationMode(this.videoSession)) { - // Set color space. - setColorSpaceBeforeCommitConfig(this.videoSession, this.cameraData.isHDRVivid); - } - + public async startCamera(): Promise { // Session start. try { - await this.videoSession.start(); + await this.videoSession?.start(); } catch (error) { // [StartExclude camera_conversation] let err = error as BusinessError; - hilog.error(0x0000, TAG, `videoSession start. code is =${error.code}, message is = ${error.message}`); + Logger.error(TAG, `videoSession start. code is =${err.code}, message is = ${err.message}`); // [EndExclude camera_conversation] } // Start the video output stream - this.encoderVideoOutput.start((error: BusinessError) => { + this.encoderVideoOutput?.start((error: BusinessError) => { // [StartExclude camera_conversation] if (error) { - hilog.error(0x0000, TAG, `Failed to start the encoder video output. code is =${error.code}, message is = ${error.message}`); + Logger.error(TAG, + `Failed to start the encoder video output. code is =${error.code}, message is = ${error.message}`); return; } - hilog.info(0x0000, TAG, 'Callback invoked to indicate the encoder video output start success.'); + Logger.info(TAG, 'Callback invoked to indicate the encoder video output start success.'); // [EndExclude camera_conversation] }); // [End camera_conversation] @@ -249,33 +250,33 @@ export class AVCodecController { */ public async releaseCamera(): Promise { // Stop the video output stream - if (this.encoderVideoOutput) { - this.encoderVideoOutput.stop((error: BusinessError) => { - if (error) { - hilog.error(0x0000, TAG, `Failed to stop the encoder video output. code is =${error.code}, message is = ${error.message}`); - return; - } - hilog.info(0x0000, TAG, 'Callback invoked to indicate the encoder video output stop success.'); - }); - } + this.encoderVideoOutput?.stop((error: BusinessError) => { + if (error) { + Logger.error(TAG, + `Failed to stop the encoder video output. code is =${error.code}, message is = ${error.message}`); + return; + } + Logger.info(TAG, 'Callback invoked to indicate the encoder video output stop success.'); + }); try { // Stop the Session. - this.videoSession?.stop(); + await this.videoSession?.stop(); // Close camera input stream. - this.cameraInput?.close(); + await this.cameraInput?.close(); // Close file fd. fileIo?.close(this.cameraData.outputfd).catch(() => { - hilog.error(0x0000, TAG, 'Failed close file'); + Logger.error(TAG, 'Failed close file'); }); // Release preview output stream. - this.XComponentPreviewOutput?.release(); + await this.XComponentPreviewOutput?.release(); // Release the video output stream. - this.encoderVideoOutput?.release(); - hilog.info(0x0000, TAG, 'encoderVideoOutput release'); + await this.encoderVideoOutput?.release(); + Logger.info(TAG, 'encoderVideoOutput release'); // Release session. - this.videoSession?.release(); + await this.videoSession?.release(); + this.videoSession = undefined; } catch (error) { - hilog.error(0x0000, TAG, + Logger.error(TAG, `error code is =${error.code}, release camera fail, message is = ${error.message}`); } } @@ -285,7 +286,9 @@ export class AVCodecController { } public async stopRecord(): Promise { + await this.releaseCamera().catch((error: BusinessError) => { + Logger.error(TAG, `releaseCamera fail. code is =${error.code}, message is = ${error.message}`); + }); recorder.stopNative(); - AppStorage.set('path', this.path); } } \ No newline at end of file diff --git a/entry/src/main/ets/controller/AVPlayerController.ets b/entry/src/main/ets/controller/AVPlayerController.ets index be4bb4c45c10939d127871c0aba727215382f694..2043992499dd5a7d5dbb0e55164b83c26260b5c1 100644 --- a/entry/src/main/ets/controller/AVPlayerController.ets +++ b/entry/src/main/ets/controller/AVPlayerController.ets @@ -15,9 +15,9 @@ import { common } from '@kit.AbilityKit'; import { media } from '@kit.MediaKit'; -import { hilog } from '@kit.PerformanceAnalysisKit'; import { BusinessError } from '@kit.BasicServicesKit'; import { fileIo, fileUri } from '@kit.CoreFileKit'; +import { Logger } from '../common/utils/Logger'; const TAG = '[AVPlayerController]'; @@ -26,13 +26,14 @@ export class AVPlayerController { @Track isPlaying: boolean = false; private avPlayer?: media.AVPlayer; private context: common.UIAbilityContext | undefined = AppStorage.get('uiContext'); + // [Start create_instance] // Create an AVPlayer instance public async initAVPlayer(surfaceId: string, path: string) { try { this.avPlayer = await media.createAVPlayer(); } catch (error) { - hilog.error(0x0000, TAG, `AVPlayer error, code is ${error.code}, message is ${error.message}`); + Logger.error(TAG, `AVPlayer error, code is ${error.code}, message is ${error.message}`); } if (!this.context || !this.avPlayer) { return @@ -44,12 +45,32 @@ export class AVPlayerController { let fdUrl = `fd://${srcFd.fd}`; this.avPlayer.url = fdUrl; } catch (error) { - hilog.error(0x0000, TAG, `file open failed. error is: ${JSON.stringify(error)}`); + Logger.error(TAG, `file open failed. error is: ${JSON.stringify(error)}`); } this.setAVPlayerCallback(); } // [End create_instance] + + public async initAVPlayerByFd(surfaceId: string, fd: number) { + try { + this.avPlayer = await media.createAVPlayer(); + } catch (error) { + Logger.error(TAG, `AVPlayer error, code is ${error.code}, message is ${error.message}`); + } + if (!this.context || !this.avPlayer) { + return + } + this.surfaceID = surfaceId; + try { + let fdUrl = `fd://${fd}`; + this.avPlayer.url = fdUrl; + } catch (error) { + Logger.error(TAG, `file open failed. error is: ${JSON.stringify(error)}`); + } + this.setAVPlayerCallback(); + } + private setAVPlayerCallback() { if (!this.avPlayer) { return; @@ -60,9 +81,9 @@ export class AVPlayerController { if (!this.avPlayer) { return; } - hilog.error(0x0000, TAG, `AVPlayer error, code is ${error.code}, message is ${error.message}`); + Logger.error(TAG, `AVPlayer error, code is ${error.code}, message is ${error.message}`); this.avPlayer.reset().catch((error: BusinessError) => { - hilog.error(0x0000, TAG, `avPlayer reset fail. code is =${error.code}, message is = ${error.message}`); + Logger.error(TAG, `avPlayer reset fail. code is =${error.code}, message is = ${error.message}`); }); // resets the resources and triggers the idle state }) this.setStateChangeCallback(); @@ -94,15 +115,17 @@ export class AVPlayerController { showFirstFrameOnPrepare: true }); } catch (error) { - hilog.error(0x0000, TAG, + Logger.error(TAG, `AVPlayer setPlaybackStrategy error, code is ${error.code}, message is ${error.message}`); } this.avPlayer.prepare().catch((error: BusinessError) => { - hilog.error(0x0000, TAG, `avPlayer prepare fail. code is =${error.code}, message is = ${error.message}`); + Logger.error(TAG, `avPlayer prepare fail. code is =${error.code}, message is = ${error.message}`); }); break; case 'prepared': // This state machine is reported after the prepare interface is successfully invoked. - //this.avPlayer.play(); // Invoke the playback interface to start playback. + this.avPlayer.play().catch((error: BusinessError) => { + Logger.error(TAG, `avPlayer prepare play. code is =${error.code}, message is = ${error.message}`); + }); // Invoke the playback interface to start playback. break; case 'playing': // After the play interface is successfully invoked, the state machine is reported. this.isPlaying = true; @@ -110,7 +133,7 @@ export class AVPlayerController { case 'completed': // This state machine is triggered to report when the playback ends. this.avPlayer.seek(0); this.avPlayer.play().catch((error: BusinessError) => { - hilog.error(0x0000, TAG, `avPlayer prepare play. code is =${error.code}, message is = ${error.message}`); + Logger.error(TAG, `avPlayer prepare play. code is =${error.code}, message is = ${error.message}`); }); break; default: @@ -123,7 +146,7 @@ export class AVPlayerController { videoPlay(): void { if (this.avPlayer) { this.avPlayer.play().catch((error: BusinessError) => { - hilog.error(0x0000, TAG, `avPlayer play fail. code is =${error.code}, message is = ${error.message}`); + Logger.error(TAG, `avPlayer play fail. code is =${error.code}, message is = ${error.message}`); }); this.isPlaying = true; } @@ -132,7 +155,7 @@ export class AVPlayerController { videoPause(): void { if (this.avPlayer) { this.avPlayer.pause().catch((error: BusinessError) => { - hilog.error(0x0000, TAG, `avPlayer prepare fail. code is =${error.code}, message is = ${error.message}`); + Logger.error(TAG, `avPlayer prepare fail. code is =${error.code}, message is = ${error.message}`); }); this.isPlaying = false; } @@ -142,7 +165,7 @@ export class AVPlayerController { videoStop(): void { if (this.avPlayer) { this.avPlayer.stop().catch((error: BusinessError) => { - hilog.error(0x0000, TAG, `avPlayer stop fail. code is =${error.code}, message is = ${error.message}`); + Logger.error(TAG, `avPlayer stop fail. code is =${error.code}, message is = ${error.message}`); }); this.isPlaying = false; } @@ -154,9 +177,9 @@ export class AVPlayerController { } this.avPlayer.release((error) => { if (!error) { - hilog.info(0x0000, TAG, 'videoRelease release success'); + Logger.info(TAG, 'videoRelease release success'); } else { - hilog.error(0x0000, TAG, `videoRelease release fail. code is =${error.code}, message is = ${error.message}`); + Logger.error(TAG, `videoRelease release fail. code is =${error.code}, message is = ${error.message}`); } }); } diff --git a/entry/src/main/ets/controller/RecordController.ets b/entry/src/main/ets/controller/RecordController.ets index 2ed75c0ae87db081796ce82fe18442f0218cd8c3..60a5fdad484aae1eab3c5a6234d0e9bef1c7bbd3 100644 --- a/entry/src/main/ets/controller/RecordController.ets +++ b/entry/src/main/ets/controller/RecordController.ets @@ -23,7 +23,6 @@ import DateTimeUtil from '../common/utils/DateTimeUtils'; import { setVideoStabilizationMode, setColorSpaceBeforeCommitConfig } from '../common/utils/VideoOperationUtils'; import { CameraDataModel } from '../model/CameraDateModel'; - const DATETIME: DateTimeUtil = new DateTimeUtil(); const TAG: string = '[RecordController]'; @@ -33,12 +32,12 @@ export class RecordController { private cameraInput: camera.CameraInput | undefined = undefined; private previewOutput: camera.PreviewOutput | undefined = undefined; private videoOutput: camera.VideoOutput | undefined = undefined; - private cameraData: CameraDataModel = AppStorage.get('cameraData') as CameraDataModel; + private cameraData: CameraDataModel = new CameraDataModel(); private avRecorder: media.AVRecorder | undefined = undefined; private captureSession: camera.VideoSession | undefined = undefined; private url: string = ''; - public async initCamera(context: common.Context, surfaceId: string) { + public async initCamera(context: common.Context, surfaceId: string, filePath: string) { try { this.cameraManager = camera.getCameraManager(context); } catch (error) { @@ -48,7 +47,7 @@ export class RecordController { hilog.error(0x0000, TAG, 'camera.getCameraManager error'); return; } - this.path = context.filesDir + `/VIDEO_${DATETIME.getDate()}_${DATETIME.getTime()}.mp4`; + this.path = filePath; let file = FileUtil.createOrOpen(this.path); if (file) { this.url = 'fd://' + file.fd; @@ -58,7 +57,8 @@ export class RecordController { try { cameraArray = this.cameraManager.getSupportedCameras(); } catch (error) { - hilog.error(0x0000, TAG, `getSupportedCameras call failed. code is =${error.code}, message is = ${error.message}`); + hilog.error(0x0000, TAG, + `getSupportedCameras call failed. code is =${error.code}, message is = ${error.message}`); } if (cameraArray.length <= 0) { @@ -181,7 +181,8 @@ export class RecordController { try { this.videoOutput = this.cameraManager.createVideoOutput(videoProfile, videoSurfaceId); } catch (error) { - hilog.error(0x0000, TAG, `Failed to create the videoOutput instance. code is =${error.code}, message is = ${error.message}`); + hilog.error(0x0000, TAG, + `Failed to create the videoOutput instance. code is =${error.code}, message is = ${error.message}`); } // [End create_video_output] @@ -203,7 +204,8 @@ export class RecordController { this.captureSession = this.cameraManager.createSession(camera.SceneMode.NORMAL_VIDEO) as camera.VideoSession; } catch (error) { // [StartExclude set_session] - hilog.error(0x0000, TAG, `Failed to create the CaptureSession instance. code is =${error.code}, message is = ${error.message}`); + hilog.error(0x0000, TAG, + `Failed to create the CaptureSession instance. code is =${error.code}, message is = ${error.message}`); // [EndExclude set_session] } // [StartExclude set_session] @@ -251,7 +253,8 @@ export class RecordController { try { this.previewOutput = this.cameraManager.createPreviewOutput(previewProfile, surfaceId); } catch (error) { - hilog.error(0x0000, TAG, `Failed to create the PreviewOutput instance. code is =${error.code}, message is = ${error.message}`); + hilog.error(0x0000, TAG, + `Failed to create the PreviewOutput instance. code is =${error.code}, message is = ${error.message}`); } if (this.previewOutput === undefined) { @@ -271,6 +274,12 @@ export class RecordController { } // [EndExclude set_session] + // Set video stabilization. + if (setVideoStabilizationMode(this.captureSession)) { + // Set color space. + setColorSpaceBeforeCommitConfig(this.captureSession, this.cameraData.isHDRVivid); + } + try { await this.captureSession.commitConfig(); } catch (error) { @@ -279,12 +288,6 @@ export class RecordController { // [EndExclude set_session] } - // Set video stabilization. - if (setVideoStabilizationMode(this.captureSession)) { - // Set color space. - setColorSpaceBeforeCommitConfig(this.captureSession, this.cameraData.isHDRVivid); - } - try { await this.captureSession.start(); } catch (error) { @@ -295,7 +298,8 @@ export class RecordController { // [Start start_fn] this.videoOutput.start((error: BusinessError) => { if (error) { - hilog.error(0x0000, TAG, `Failed to start the video output. code is =${error.code}, message is = ${error.message}`); + hilog.error(0x0000, TAG, + `Failed to start the video output. code is =${error.code}, message is = ${error.message}`); return; } hilog.info(0x0000, TAG, 'Callback invoked to indicate the video output start success.'); @@ -312,7 +316,8 @@ export class RecordController { if (this.videoOutput) { this.videoOutput.stop((error: BusinessError) => { if (error) { - hilog.error(0x0000, TAG, `Failed to stop the video output. code is =${error.code}, message is = ${error.message}`); + hilog.error(0x0000, TAG, + `Failed to stop the video output. code is =${error.code}, message is = ${error.message}`); return; } hilog.info(0x0000, TAG, 'Callback invoked to indicate the video output stop success.'); @@ -349,7 +354,6 @@ export class RecordController { } catch (error) { hilog.error(0x0000, TAG, `avRecorder stop code is =${error.code}, message is = ${error.message}`); } - AppStorage.set('path', this.path); } } diff --git a/entry/src/main/ets/entryability/EntryAbility.ets b/entry/src/main/ets/entryability/EntryAbility.ets index ace480800f6239a014e7a08e852faf0f99174e86..c03563dbbacd87580b4cae41d4cbde405d310d0e 100644 --- a/entry/src/main/ets/entryability/EntryAbility.ets +++ b/entry/src/main/ets/entryability/EntryAbility.ets @@ -17,7 +17,7 @@ import { ConfigurationConstant, UIAbility } from '@kit.AbilityKit'; import { hilog } from '@kit.PerformanceAnalysisKit'; import { window } from '@kit.ArkUI'; -const DOMAIN = 0x0000; +const DOMAIN = 0xFF00; const TAG = '[EntryAbility]'; export default class EntryAbility extends UIAbility { diff --git a/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets b/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets index 88c815fcef74887a2dd23317abe5cf0ca5bf1e12..0ef621ca482188abaae39214c10edcbe1073e459 100644 --- a/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets +++ b/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets @@ -16,7 +16,7 @@ import { hilog } from '@kit.PerformanceAnalysisKit'; import { BackupExtensionAbility, BundleVersion } from '@kit.CoreFileKit'; -const DOMAIN = 0x0000; +const DOMAIN = 0xFF00; export default class EntryBackupAbility extends BackupExtensionAbility { async onBackup() { diff --git a/entry/src/main/ets/pages/AVCodecPage.ets b/entry/src/main/ets/pages/AVCodecPage.ets new file mode 100644 index 0000000000000000000000000000000000000000..8473eb3e5a9ffb34279f15c8c54a0d455d3bf40f --- /dev/null +++ b/entry/src/main/ets/pages/AVCodecPage.ets @@ -0,0 +1,252 @@ +/* + * 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. + */ + +import { photoAccessHelper } from '@kit.MediaLibraryKit'; +import { BusinessError } from '@kit.BasicServicesKit'; +import { LoadingDialog } from '@kit.ArkUI'; +import { fileIo } from '@kit.CoreFileKit'; +import { AVPlayerController } from '../controller/AVPlayerController'; +import { VideoStatus } from '../common/CommonEnum'; +import { Logger } from '../common/utils/Logger'; +import transcoder from 'libtranscoder.so'; +import { setScreenState } from '../common/utils/WindowUtils'; + +const TAG = '[AVCodecPage]'; + +@Builder +export function AVCodecPageBuilder() { + AVCodecPage(); +} + +@Component +struct AVCodecPage { + @Consume('navPathStack') PathStack: NavPathStack; + @State mode: number = 0; + @State viewState: number = VideoStatus.NO_VIDEO; + @State selectFilePath: string | undefined = undefined; + @State avPlayerController: AVPlayerController = new AVPlayerController(); + private XComponentSurfaceId: string = ''; + private XComponentController: XComponentController = new XComponentController(); + private outputFile?: fileIo.File; + private isTranscoder: boolean = false; + + dialogController: CustomDialogController = new CustomDialogController({ + builder: LoadingDialog({ + content: $r('app.string.Video_reminder_message') + }), + autoCancel: false, + onWillDismiss: (() => { + Logger.info(TAG, 'onWillDismiss'); + }) + }); + + aboutToAppear(): void { + this.mode = this.PathStack.getParamByName('AVCodecPage').pop() as number; + setScreenState(true, this.getUIContext().getHostContext()); + } + + aboutToDisappear(): void { + this.XComponentSurfaceId = ''; + this.avPlayerController.videoRelease(); + setScreenState(false, this.getUIContext().getHostContext()); + } + + selectFile() { + let photoPicker = new photoAccessHelper.PhotoViewPicker(); + photoPicker.select({ + MIMEType: photoAccessHelper.PhotoViewMIMETypes.VIDEO_TYPE, + maxSelectNumber: 1 + }) + .then(async (photoSelectResult) => { + this.selectFilePath = photoSelectResult.photoUris[0]; + if (this.selectFilePath === undefined) { + try { + this.getUIContext().getPromptAction().showToast({ + message: $r('app.string.alert'), + duration: 2000, + bottom: 280 + }); + } catch (error) { + Logger.error(TAG, `showToast fail, error code is =${error.code}, message is = ${error.message}`); + } + } else { + this.viewState = VideoStatus.OPTIMIZE_VIDEOS; + Logger.info(TAG, 'documentViewPicker.select to file succeed and URI is:' + this.selectFilePath); + } + }) + .catch((error: BusinessError) => { + Logger.error(TAG, + `photoPicker select fail, error code is =${error.code}, message is = ${error.message}`); + }) + } + + transcoder() { + try { + let inputFile = fileIo.openSync(this.selectFilePath, fileIo.OpenMode.READ_ONLY); + if (!inputFile) { + Logger.error(TAG, 'player inputFile is null'); + } + let inputFileState = fileIo.statSync(inputFile.fd); + if (inputFileState.size <= 0) { + Logger.error(TAG, 'player inputFile size is 0'); + } + + let isHdrVivid: boolean = transcoder.isHdr(inputFile.fd, inputFileState.size); + if (!isHdrVivid) { + try { + this.getUIContext().getPromptAction().showToast({ + message: $r('app.string.video_is_not_hdr'), + duration: 3000, + bottom: 280 + }); + } catch (error) { + Logger.error(TAG, `showToast fail, error code is =${error.code}, message is = ${error.message}`); + } + return; + } + this.outputFile = fileIo.openSync(this.getUIContext().getHostContext()?.cacheDir + '/transform.mp4', + fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE); + this.dialogController.open(); + this.isTranscoder = true; + if (this.mode === 0) { + transcoder.startAVCodecTranscoder(inputFile.fd, inputFileState.size, this.outputFile.fd, () => { + this.dialogController.close(); + if (this.outputFile) { + this.avPlayerController.initAVPlayerByFd(this.XComponentSurfaceId, this.outputFile.fd); + } + }); + } else if (this.mode === 1) { + transcoder.startVideoProcessingTranscoder(inputFile.fd, inputFileState.size, this.outputFile.fd, () => { + this.dialogController.close(); + if (this.outputFile) { + this.avPlayerController.initAVPlayerByFd(this.XComponentSurfaceId, this.outputFile.fd); + } + }); + } else { + transcoder.startTransformColorspace(inputFile.fd, inputFileState.size, this.outputFile.fd, () => { + this.dialogController.close(); + if (this.outputFile) { + this.avPlayerController.initAVPlayerByFd(this.XComponentSurfaceId, this.outputFile.fd); + } + }); + } + + } catch (error) { + Logger.error(TAG, `play catch error, code: ${error.code}, message: ${error.message}`); + return; + } + } + + build() { + NavDestination() { + Column() { + Stack({ alignContent: Alignment.Center }) { + Text() { + SymbolSpan($r('sys.symbol.ohos_folder_badge_plus')) + .fontWeight(FontWeight.Normal) + .fontSize(40) + Span($r('app.string.video_processing_before_are')) + } + .fontSize(14) + .fontColor($r('app.color.video_processing_after_area_hint_font_color')) + .textAlign(TextAlign.Center) + .backgroundColor($r('app.color.video_processing_after_area_bg_color')) + .width('100%') + .height('100%') + .visibility(this.viewState === VideoStatus.NO_VIDEO ? Visibility.Visible : Visibility.Hidden) + + XComponent({ + type: XComponentType.SURFACE, + controller: this.XComponentController, + }) + .width('100%') + .height('100%') + .visibility(this.viewState === VideoStatus.OPTIMIZE_VIDEOS ? Visibility.Visible : Visibility.Hidden) + .onLoad(() => { + this.XComponentSurfaceId = this.XComponentController.getXComponentSurfaceId(); + }) + if (!this.avPlayerController.isPlaying && this.viewState === VideoStatus.OPTIMIZE_VIDEOS) { + Image($r('app.media.icon_pause')) + .size({ width: 50, height: 50 }) + } + } + .onClick(() => { + if (this.viewState === VideoStatus.NO_VIDEO) { + this.selectFile(); + } else { + this.avPlayerController.isPlaying === true ? this.avPlayerController.videoPause() : + this.avPlayerController.videoPlay() + } + + }) + .width('100%') + .layoutWeight(1) + + Button(this.mode === 0 ? $r('app.string.transcoder_base_on_avcodec') : + this.mode === 1 ? $r('app.string.transcoder_base_on_videoprocess') : $r('app.string.transform_colorspace')) + .width('100%') + .constraintSize({ maxWidth: '100%' }) + .margin({ + left: 16, + top: 12, + right: 16 + }) + .fontWeight(500) + .backgroundColor($r('app.color.button_bg_color')) + .fontColor($r('app.color.button_text_color')) + .enabled(this.viewState !== VideoStatus.NO_VIDEO) + .onClick(() => { + this.transcoder(); + }) + + Button($r('app.string.video_select_file')) + .width('100%') + .constraintSize({ maxWidth: '100%' }) + .backgroundColor($r('app.color.button_bg_color')) + .fontColor($r('app.color.button_text_color')) + .fontWeight(500) + .onClick(() => { + this.selectFile(); + }) + .margin({ + left: 16, + top: 12, + right: 16 + }) + } + .height('100%') + .justifyContent(FlexAlign.Center) + .margin({ + bottom: 12 + }) + } + .title(this.mode === 0 ? $r('app.string.transcoder_base_on_avcodec') : + this.mode === 1 ? $r('app.string.transcoder_base_on_videoprocess') : $r('app.string.transform_colorspace')) + .hideToolBar(true) + .onHidden(() => { + if (this.isTranscoder) { + this.dialogController.close(); + transcoder.releaseAVCodecTranscoder(); + this.viewState = VideoStatus.NO_VIDEO; + this.isTranscoder = false; + this.XComponentSurfaceId = ''; + this.avPlayerController.videoRelease(); + } + }) + .onReady((context: NavDestinationContext) => { + this.PathStack = context.pathStack; + }) + } +} \ No newline at end of file diff --git a/entry/src/main/ets/pages/AVCodecPlayPage.ets b/entry/src/main/ets/pages/AVCodecPlayPage.ets new file mode 100644 index 0000000000000000000000000000000000000000..904de515c44e654e3ae11b852eb1ad64980c68dc --- /dev/null +++ b/entry/src/main/ets/pages/AVCodecPlayPage.ets @@ -0,0 +1,188 @@ +/* + * 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. + */ + +import { photoAccessHelper } from '@kit.MediaLibraryKit'; +import { image } from '@kit.ImageKit'; +import { fileIo } from '@kit.CoreFileKit'; +import player from 'libplayer.so'; +import { BusinessError } from '@kit.BasicServicesKit'; +import { VideoStatus } from '../common/CommonEnum'; +import { Logger } from '../common/utils/Logger'; +import { setScreenState } from '../common/utils/WindowUtils'; + +const TAG = '[AVCodecPlayPage]'; + +@Builder +export function AVCodecPlayPageBuilder() { + AVCodecPlayPage(); +} + +@Component +export struct AVCodecPlayPage { + @State viewState: number = VideoStatus.NO_VIDEO; + @State selectFilePath: string | undefined = undefined; + @State colorSpaceIsShow: boolean = false; + @State targetType: string = ''; + @State isHDRVivid: boolean = false; // video type + @State pixelMap: image.PixelMap | undefined = undefined; + @State isPlaying: boolean = false; + @State buttonEnabled: boolean = true; + private XComponentController: XComponentController = new XComponentController(); + private inputFile: fileIo.File | undefined; + + aboutToAppear(): void { + setScreenState(true, this.getUIContext().getHostContext()); + } + + aboutToDisappear(): void { + if (this.inputFile) { + fileIo.close(this.inputFile.fd).catch((error: BusinessError) => { + Logger.error(TAG, + `fileIo close fail, error code is =${error.code}, message is = ${error.message}`); + }); + } + player.startRelease(); + setScreenState(false, this.getUIContext().getHostContext()); + } + + selectFile() { + let photoPicker: photoAccessHelper.PhotoViewPicker = new photoAccessHelper.PhotoViewPicker(); + photoPicker.select({ + MIMEType: photoAccessHelper.PhotoViewMIMETypes.VIDEO_TYPE, + maxSelectNumber: 1 + }).then(async (photoSelectResult) => { + try { + this.selectFilePath = photoSelectResult.photoUris[0]; + if (this.selectFilePath === undefined) { + this.getUIContext().getPromptAction().showToast({ + message: $r('app.string.alert'), + duration: 2000, + bottom: 280 + }); + } else { + this.play(); + Logger.info(TAG, 'documentViewPicker.select to file succeed and URI is:' + this.selectFilePath); + } + } catch (error) { + Logger.error(TAG, `showToast fail, error code is =${error.code}, message is = ${error.message}`); + } + }) + .catch((error: BusinessError) => { + Logger.error(TAG, `photoPicker select fail, error code is =${error.code}, message is = ${error.message}`); + }) + } + + play() { + try { + if (this.inputFile) { + fileIo.close(this.inputFile).catch((error: BusinessError) => { + Logger.error(TAG, + `fileIo close fail, error code is =${error.code}, message is = ${error.message}`); + }); + } + this.inputFile = fileIo.openSync(this.selectFilePath, fileIo.OpenMode.READ_ONLY); + if (!this.inputFile) { + Logger.error(TAG, 'player inputFile is null'); + } + let inputFileState = fileIo.statSync(this.inputFile.fd); + if (inputFileState.size <= 0) { + Logger.error(TAG, 'player inputFile size is 0'); + } + this.buttonEnabled = false; + this.isPlaying = true; + this.buttonEnabled = true; + if (this.viewState === VideoStatus.OPTIMIZE_VIDEOS) { + player.switchVideo(this.inputFile.fd, 0, inputFileState.size); + } else { + this.viewState = VideoStatus.OPTIMIZE_VIDEOS; + player.playHdr(this.inputFile.fd, 0, inputFileState.size); + } + } catch (error) { + Logger.error(TAG, `play catch error, code: ${error.code}, message: ${error.message}`); + return; + } + } + + build() { + NavDestination() { + Column() { + Stack({ alignContent: Alignment.Center }) { + Text() { + SymbolSpan($r('sys.symbol.ohos_folder_badge_plus')) + .fontWeight(FontWeight.Normal) + .fontSize(40) + Span($r('app.string.video_processing_before_are')) + } + .fontSize(14) + .fontColor($r('app.color.video_processing_after_area_hint_font_color')) + .textAlign(TextAlign.Center) + .backgroundColor($r('app.color.video_processing_after_area_bg_color')) + .width('100%') + .height('100%') + .visibility(this.viewState === VideoStatus.NO_VIDEO ? Visibility.Visible : Visibility.Hidden) + + XComponent({ + id: 'dstPlayer', + type: XComponentType.SURFACE, + controller: this.XComponentController, + libraryname: 'player' + }) + .width('100%') + .height('100%') + .visibility(this.viewState === VideoStatus.OPTIMIZE_VIDEOS ? Visibility.Visible : Visibility.Hidden) + + if (!this.isPlaying && this.viewState === VideoStatus.OPTIMIZE_VIDEOS) { + Image($r('app.media.icon_pause')) + .size({ width: 50, height: 50 }) + } + } + .onClick(() => { + if (this.viewState === VideoStatus.NO_VIDEO) { + this.selectFile(); + } else { + this.isPlaying ? player.pause() : player.resume(); + this.isPlaying = !this.isPlaying; + } + }) + .width('100%') + .layoutWeight(1) + + Row() { + Button($r('app.string.video_select_file')) + .layoutWeight(1) + .backgroundColor($r('app.color.button_bg_color')) + .fontColor($r('app.color.button_text_color')) + .fontWeight(500) + .onClick(() => { + this.selectFile(); + }) + } + .justifyContent(FlexAlign.Center) + .margin({ + left: 16, + top: 12, + right: 16 + }) + } + .height('100%') + .justifyContent(FlexAlign.Center) + .margin({ + bottom: 12 + }) + } + .title($r('app.string.play_video_base_on_avcodec')) + .hideToolBar(true) + } +} \ No newline at end of file diff --git a/entry/src/main/ets/pages/AVPlayerPage.ets b/entry/src/main/ets/pages/AVPlayerPage.ets new file mode 100644 index 0000000000000000000000000000000000000000..a9efa08a630418c6b7db5b7ff6de370fe264fe55 --- /dev/null +++ b/entry/src/main/ets/pages/AVPlayerPage.ets @@ -0,0 +1,149 @@ +/* + * 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. + */ + +import { BusinessError } from '@kit.BasicServicesKit'; +import { photoAccessHelper } from '@kit.MediaLibraryKit'; +import { VideoStatus } from '../common/CommonEnum'; +import { AVPlayerController } from '../controller/AVPlayerController'; +import { Logger } from '../common/utils/Logger'; +import { setScreenState } from '../common/utils/WindowUtils'; + +@Builder +export function AVPlayerPageBuilder() { + AVPlayerPage(); +} + +const TAG = '[AVPlayerPage]'; + +@Component +export struct AVPlayerPage { + @State viewState: number = VideoStatus.NO_VIDEO; + @State selectFilePath: string | undefined = undefined; + @State avPlayerController: AVPlayerController = new AVPlayerController(); + private XComponentSurfaceId: string = ''; + private XComponentController: XComponentController = new XComponentController(); + + aboutToAppear(): void { + setScreenState(true, this.getUIContext().getHostContext()); + } + + aboutToDisappear(): void { + this.XComponentSurfaceId = ''; + this.avPlayerController.videoRelease(); + setScreenState(false, this.getUIContext().getHostContext()); + } + + selectFile() { + let photoPicker = new photoAccessHelper.PhotoViewPicker(); + photoPicker.select({ + MIMEType: photoAccessHelper.PhotoViewMIMETypes.VIDEO_TYPE, + maxSelectNumber: 1 + }) + .then(async (photoSelectResult) => { + this.selectFilePath = photoSelectResult.photoUris[0]; + if (this.selectFilePath === undefined) { + try { + this.getUIContext().getPromptAction().showToast({ + message: $r('app.string.alert'), + duration: 2000, + bottom: 280 + }); + } catch (error) { + Logger.error(TAG, `showToast fail, error code is =${error.code}, message is = ${error.message}`); + } + } else { + this.viewState = VideoStatus.OPTIMIZE_VIDEOS; + await this.avPlayerController.initAVPlayer(this.XComponentSurfaceId, this.selectFilePath); + this.avPlayerController.isPlaying = false; + Logger.info(TAG, 'documentViewPicker.select to file succeed and URI is:' + this.selectFilePath); + } + }) + .catch((error: BusinessError) => { + Logger.error(TAG, + `photoPicker select fail, error code is =${error.code}, message is = ${error.message}`); + }) + } + + build() { + NavDestination() { + Column() { + Stack({ alignContent: Alignment.Center }) { + Text() { + SymbolSpan($r('sys.symbol.ohos_folder_badge_plus')) + .fontWeight(FontWeight.Normal) + .fontSize(40) + Span($r('app.string.video_processing_before_are')) + } + .fontSize(14) + .fontColor($r('app.color.video_processing_after_area_hint_font_color')) + .textAlign(TextAlign.Center) + .backgroundColor($r('app.color.video_processing_after_area_bg_color')) + .width('100%') + .height('100%') + .visibility(this.viewState === VideoStatus.NO_VIDEO ? Visibility.Visible : Visibility.Hidden) + + XComponent({ + type: XComponentType.SURFACE, + controller: this.XComponentController, + }) + .width('100%') + .height('100%') + .visibility(this.viewState === VideoStatus.OPTIMIZE_VIDEOS ? Visibility.Visible : Visibility.Hidden) + .onLoad(() => { + this.XComponentSurfaceId = this.XComponentController.getXComponentSurfaceId(); + }) + if (!this.avPlayerController.isPlaying && this.viewState === VideoStatus.OPTIMIZE_VIDEOS) { + Image($r('app.media.icon_pause')) + .size({ width: 50, height: 50 }) + } + } + .onClick(() => { + if (this.viewState === VideoStatus.NO_VIDEO) { + this.selectFile(); + } else { + this.avPlayerController.isPlaying === true ? this.avPlayerController.videoPause() : + this.avPlayerController.videoPlay() + } + }) + .width('100%') + .layoutWeight(1) + + Row() { + Button($r('app.string.video_select_file')) + .layoutWeight(1) + .backgroundColor($r('app.color.button_bg_color')) + .fontColor($r('app.color.button_text_color')) + .fontWeight(500) + .onClick(() => { + this.selectFile(); + }) + } + .justifyContent(FlexAlign.Center) + .margin({ + left: 16, + top: 12, + right: 16 + }) + } + .height('100%') + .justifyContent(FlexAlign.Center) + .margin({ + bottom: 12 + }) + } + .title($r('app.string.play_video_base_on_avplayer')) + .hideToolBar(true) + } +} \ No newline at end of file diff --git a/entry/src/main/ets/pages/Transcode.ets b/entry/src/main/ets/pages/AVTranscoderPage.ets similarity index 41% rename from entry/src/main/ets/pages/Transcode.ets rename to entry/src/main/ets/pages/AVTranscoderPage.ets index 644c5769d7ed4790e8d36942fe80fdc5b0354ca9..31602d79a0eb731ad5c6488804788bf4c4e2541a 100644 --- a/entry/src/main/ets/pages/Transcode.ets +++ b/entry/src/main/ets/pages/AVTranscoderPage.ets @@ -14,123 +14,78 @@ */ import { photoAccessHelper } from '@kit.MediaLibraryKit'; -import { hilog } from '@kit.PerformanceAnalysisKit'; -import { image } from '@kit.ImageKit'; import { fileIo } from '@kit.CoreFileKit'; -import { AVPlayerController } from '../controller/AVPlayerController'; -import { MultiStatusButton } from '../view/MultiStatusButton'; -import player from 'libplayer.so'; import { BusinessError } from '@kit.BasicServicesKit'; +import { LoadingDialog } from '@kit.ArkUI'; +import { AVPlayerController } from '../controller/AVPlayerController'; +import { VideoStatus } from '../common/CommonEnum'; +import { Logger } from '../common/utils/Logger'; +import transcoder from 'libtranscoder.so'; +import { setScreenState } from '../common/utils/WindowUtils'; -const TAG = '[Transcode]'; - -enum VideoStatus { - NO_VIDEO = 0, // No videos - SELECTED_VIDEOS = 1, // Selected videos - OPTIMIZE_VIDEOS = 2 // Optimize videos -} +const TAG = '[AVTranscoderPage]'; @Builder -export function TranscodeBuilder() { - Transcode(); +export function AVTranscoderPageBuilder() { + AVTranscoderPage(); } @Component -struct Transcode { +struct AVTranscoderPage { @State viewState: number = VideoStatus.NO_VIDEO; @State selectFilePath: string | undefined = undefined; - @State colorSpaceIsShow: boolean = false; - @State targetType: string = ''; - @State isHDRVivid: boolean = false; // video type - @State pixelMap: image.PixelMap | undefined = undefined; @State avPlayerController: AVPlayerController = new AVPlayerController(); + @State @Watch('closeTimer') transferResult: number = 0; private XComponentSurfaceId: string = ''; private XComponentController: XComponentController = new XComponentController(); - private isProcessing: boolean = false; - private targetColorSpace: number = 0; //color space - private targetMetaData: number = 0; //Metadata Type - - preload() { - try { - let inputFile = fileIo.openSync(this.selectFilePath, fileIo.OpenMode.READ_ONLY); - let inputFileState = fileIo.statSync(inputFile.fd); - let info: string = player.preload(inputFile.fd, inputFileState.size); - fileIo.close(inputFile).catch((error: BusinessError) => { - hilog.error(0x0000, TAG, `file close fail, error code is =${error.code}, message is = ${error.message}`); - }); - hilog.info(0x0000, TAG, `Video info: ${info}}`); - let arr: string[] = info.split('@'); - let msg = arr[0] + ' ' + arr[1]; - if (msg.includes('HDRVivid ')) { - this.isHDRVivid = true; - } else { - this.getUIContext().getPromptAction().showToast({ - message: $r('app.string.video_processing_not_support'), - duration: 2000 - }); - this.isHDRVivid = false; - } - } catch (error) { - hilog.error(0x0000, TAG, `preload fail, error code is =${error.code}, message is = ${error.message}`); - } - } - - async processVideo(option: number): Promise { - this.avPlayerController.videoPause(); - try { - this.viewState = VideoStatus.SELECTED_VIDEOS; - let inputFile = fileIo.openSync(this.selectFilePath, fileIo.OpenMode.READ_ONLY); - let inputFileState = fileIo.statSync(inputFile.fd); - this.isProcessing = true; - player.playNative(inputFile.fd, 0, inputFileState.size, 1, -1, this.targetColorSpace, - this.targetMetaData, option, (ret: number) => { - fileIo.close(inputFile).catch((error: BusinessError) => { - hilog.error(0x0000, TAG, `file close fail, error code is =${error.code}, message is = ${error.message}`); - }); - if (ret && ret === 5) { - this.getUIContext().getPromptAction().showToast({ message: $r('app.string.video_processing_not_support') }); - } else { - hilog.info(0x0000, TAG, 'The video transcoding ' + this.targetType + ' succeed'); - } - this.isProcessing = false; - this.viewState = VideoStatus.OPTIMIZE_VIDEOS; - }); - } catch (error) { - hilog.error(0x0000, TAG, `processVideo fail, error code is =${error.code}, message is = ${error.message}`); - } + private outputFile?: fileIo.File; + private intervalID: number = 0; + dialogController: CustomDialogController = new CustomDialogController({ + builder: LoadingDialog({ + content: $r('app.string.Video_reminder_message') + }), + autoCancel: false + }); + + aboutToAppear(): void { + setScreenState(true, this.getUIContext().getHostContext()); } aboutToDisappear(): void { - player.destroy(); this.XComponentSurfaceId = ''; this.avPlayerController.videoRelease(); + setScreenState(false, this.getUIContext().getHostContext()); } - @Builder - ColorSpaceMenu() { - Column() { - MultiStatusButton({ text: 'SDR-BT709', status: this.isHDRVivid ? 1 : 0 }) - .onClick(() => { - this.colorSpaceIsShow = false; - this.targetType = 'SDR BT709'; - this.targetColorSpace = 8; //OH_COLORSPACE_BT709_LIMIT - this.targetMetaData = -1; //OH_VIDEO_NONE - this.processVideo(1); - }) - MultiStatusButton({ text: 'HDR-HLG', status: this.isHDRVivid ? 1 : 0 }) - .onClick(() => { - this.colorSpaceIsShow = false; - this.targetType = 'HDRVivid HLG'; - this.targetColorSpace = 9; //OH_COLORSPACE_BT2020_HLG_LIMIT - this.targetMetaData = 0; //OH_VIDEO_HDR_HLG - this.processVideo(1); - }) + closeDialog(): void { + this.intervalID = setInterval(() => { + this.transferResult = transcoder.getAVTranscoderState(); + if (transcoder.getAVTranscoderState() === 1) { + this.dialogController.close(); + if (this.outputFile) { + this.avPlayerController.initAVPlayerByFd(this.XComponentSurfaceId, this.outputFile.fd); + } + } else if (transcoder.getAVTranscoderState() === -1) { + try { + this.dialogController.close(); + this.getUIContext().getPromptAction().showToast({ + message: $r('app.string.transcoder_error'), + duration: 2000, + bottom: 280 + }); + } catch (error) { + Logger.error(TAG, `showToast fail, error code is =${error.code}, message is = ${error.message}`); + } + } + }, 100); + } + + closeTimer(): void { + if (this.transferResult !== 0) { + this.dialogController.close(); + clearInterval(this.intervalID); + this.transferResult = 0; } - .margin({ - left: 16, - right: 16, - bottom: 30 - }) } selectFile() { @@ -141,7 +96,6 @@ struct Transcode { }) .then(async (photoSelectResult) => { this.selectFilePath = photoSelectResult.photoUris[0]; - this.preload(); if (this.selectFilePath === undefined) { try { this.getUIContext().getPromptAction().showToast({ @@ -150,37 +104,58 @@ struct Transcode { bottom: 280 }); } catch (error) { - hilog.error(0x0000, TAG, `showToast fail, error code is =${error.code}, message is = ${error.message}`); + Logger.error(TAG, `showToast fail, error code is =${error.code}, message is = ${error.message}`); } } else { this.viewState = VideoStatus.OPTIMIZE_VIDEOS; - await this.avPlayerController.initAVPlayer(this.XComponentSurfaceId, this.selectFilePath); - this.avPlayerController.isPlaying = false; - hilog.info(0x0000, TAG, 'documentViewPicker.select to file succeed and URI is:' + this.selectFilePath); + Logger.info(TAG, 'documentViewPicker.select to file succeed and URI is:' + this.selectFilePath); } }) .catch((error: BusinessError) => { - hilog.error(0x0000, TAG, + Logger.error(TAG, `photoPicker select fail, error code is =${error.code}, message is = ${error.message}`); }) } + transcoder() { + try { + let inputFile = fileIo.openSync(this.selectFilePath, fileIo.OpenMode.READ_ONLY); + if (!inputFile) { + Logger.error(TAG, 'player inputFile is null'); + } + let inputFileState = fileIo.statSync(inputFile.fd); + if (inputFileState.size <= 0) { + Logger.error(TAG, 'player inputFile size is 0'); + } + + + this.outputFile = fileIo.openSync(this.getUIContext().getHostContext()?.cacheDir + '/transform.mp4', + fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE); + let isHdrVivid: boolean = transcoder.isHdr(inputFile.fd, inputFileState.size); + if (!isHdrVivid) { + try { + this.getUIContext().getPromptAction().showToast({ + message: $r('app.string.video_is_not_hdr'), + duration: 2000, + bottom: 280 + }); + } catch (error) { + Logger.error(TAG, `showToast fail, error code is =${error.code}, message is = ${error.message}`); + } + return; + } + this.dialogController.open(); + transcoder.startAVTranscoder(inputFile.fd, inputFileState.size, this.outputFile.fd); + this.closeDialog(); + } catch (error) { + Logger.error(TAG, `play catch error, code: ${error.code}, message: ${error.message}`); + return; + } + } + build() { NavDestination() { Column() { - Row() { - Text(this.viewState === VideoStatus.OPTIMIZE_VIDEOS ? $r('app.string.original_video') : - $r('app.string.Optimize_video')) - .fontWeight(700) - .fontSize(18) - .margin({ - left: 16, - bottom: 5 - }) - } - .width('100%') - .justifyContent(FlexAlign.SpaceBetween) - Stack({ alignContent: Alignment.Center }) { Text() { SymbolSpan($r('sys.symbol.ohos_folder_badge_plus')) @@ -196,16 +171,6 @@ struct Transcode { .height('100%') .visibility(this.viewState === VideoStatus.NO_VIDEO ? Visibility.Visible : Visibility.Hidden) - XComponent({ - id: 'dstPlayer', - type: XComponentType.SURFACE, - controller: this.XComponentController, - libraryname: 'player' - }) - .width('100%') - .height('100%') - .visibility(this.viewState === VideoStatus.SELECTED_VIDEOS ? Visibility.Visible : Visibility.Hidden) - XComponent({ type: XComponentType.SURFACE, controller: this.XComponentController, @@ -233,42 +198,7 @@ struct Transcode { .width('100%') .layoutWeight(1) - Button($r('app.string.AVCodec_transcode')) - .enabled(this.isHDRVivid) - .width('100%') - .constraintSize({ maxWidth: '100%' }) - .margin({ - left: 16, - top: 12, - right: 16 - }) - .fontWeight(500) - .backgroundColor($r('app.color.button_bg_color')) - .fontColor($r('app.color.button_text_color')) - .onClick(() => { - try { - if (this.isProcessing) { - this.getUIContext() - .getPromptAction() - .showToast({ message: $r('app.string.video_is_processing_try_later') }); - return; - } - if (this.selectFilePath === undefined) { - this.getUIContext().getPromptAction().showToast({ - message: $r('app.string.alert'), - duration: 2000, - bottom: 280 - }) - } else { - this.targetType = 'SDR BT709'; - this.targetColorSpace = 8; - this.processVideo(2); - } - } catch (error) { - hilog.error(0x0000, TAG, `showToast fail, error code is =${error.code}, message is = ${error.message}`); - } - }) - Button($r('app.string.color_space_conversion')) + Button($r('app.string.transcoder_base_on_avtranscoder')) .width('100%') .constraintSize({ maxWidth: '100%' }) .margin({ @@ -279,33 +209,9 @@ struct Transcode { .fontWeight(500) .backgroundColor($r('app.color.button_bg_color')) .fontColor($r('app.color.button_text_color')) - .bindSheet($$this.colorSpaceIsShow, this.ColorSpaceMenu(), { - detents: [SheetSize.FIT_CONTENT], - backgroundColor: Color.White, - blurStyle: BlurStyle.NONE, - showClose: true, - title: { title: $r('app.string.color_space_list') } - }) + .enabled(this.viewState !== VideoStatus.NO_VIDEO ? true : false) .onClick(() => { - try { - if (this.isProcessing) { - this.getUIContext() - .getPromptAction() - .showToast({ message: $r('app.string.video_is_processing_try_later') }); - return; - } - if (this.selectFilePath === undefined) { - this.getUIContext().getPromptAction().showToast({ - message: $r('app.string.alert'), - duration: 2000, - bottom: 280 - }) - } else { - this.colorSpaceIsShow = true; - } - } catch (error) { - hilog.error(0x0000, TAG, `showToast fail, error code is =${error.code}, message is = ${error.message}`); - } + this.transcoder(); }) Row() { Button($r('app.string.video_select_file')) @@ -314,7 +220,6 @@ struct Transcode { .fontColor($r('app.color.button_text_color')) .fontWeight(500) .margin({ right: 6 }) - .enabled(this.viewState !== VideoStatus.NO_VIDEO ? true : false) .onClick(() => { this.selectFile(); }) @@ -332,7 +237,7 @@ struct Transcode { bottom: '12vp' }) } - .hideTitleBar(true) + .title($r('app.string.transcoder_base_on_avtranscoder')) .hideToolBar(true) } } \ No newline at end of file diff --git a/entry/src/main/ets/pages/CameraPage.ets b/entry/src/main/ets/pages/CameraPage.ets index 926fd31599726c8522626ae6cfb4d1cb7efd0332..957027af67b6d7ab9b3c0ad128f7438e207d1625 100644 --- a/entry/src/main/ets/pages/CameraPage.ets +++ b/entry/src/main/ets/pages/CameraPage.ets @@ -13,23 +13,26 @@ * limitations under the License. */ -import { display } from '@kit.ArkUI'; +import { display, window } from '@kit.ArkUI'; import { CommonConstants as Const } from '../common/CommonConstants'; import { RecordController } from '../controller/RecordController' import { AVCodecController } from '../controller/AVCodecController' -import { dateTime } from '../common/utils/DateTimeUtils'; -import { hilog } from '@kit.PerformanceAnalysisKit'; +import DateTimeUtil, { dateTime } from '../common/utils/DateTimeUtils'; import { BusinessError } from '@kit.BasicServicesKit'; - -interface CameraParams { - mode: number; -} +import { Logger } from '../common/utils/Logger'; +import { setScreenState } from '../common/utils/WindowUtils'; const TAG = '[CameraPage]'; +const DATETIME: DateTimeUtil = new DateTimeUtil(); + +@Builder +export function CameraPageBuilder() { + CameraPage(); +} -@Entry @Component -struct CameraPage { +export struct CameraPage { + @Consume('navPathStack') PathStack: NavPathStack; @State mode: number = 0; //0 avRecord 1 AVCodec @State recording: boolean = false; @State isEnable: boolean = true; @@ -45,15 +48,11 @@ struct CameraPage { private widthPx = Const.DEFAULT_ID; private timer: number = Const.DEFAULT_VALUE; private seconds: number = Const.DEFAULT_VALUE; - - getRecordTime(): void { - this.timer = setInterval(() => { - this.seconds += 1; - this.videoRecorderTimeText = dateTime(this.seconds); - }, 1000); - } + private filePath: string = + this.getUIContext().getHostContext()?.filesDir + `/VIDEO_${DATETIME.getDate()}_${DATETIME.getTime()}.mp4`; aboutToAppear(): void { + this.mode = this.PathStack.getParamByName('CameraPage').pop() as number; try { this.display = display.getDefaultDisplaySync(); if (!this.display) { @@ -62,83 +61,114 @@ struct CameraPage { this.heightPx = (this.display.width * this.cameraWidth / this.cameraHeight) + Const.PX; this.widthPx = this.display.width + Const.PX; } catch (error) { - hilog.error(0x0000, TAG, + Logger.error(TAG, `getSupportedCameras call failed. code is =${error.code}, message is = ${error.message}`); } - const params = this.getUIContext().getRouter().getParams() as CameraParams; - if (params.mode) { - this.mode = params.mode; + setScreenState(true, this.getUIContext().getHostContext()); + } + + aboutToDisappear(): void { + if (!this.recording) { + this.mode === 0 ? this.recordController.stopRecord().catch((error: BusinessError) => { + Logger.error(TAG, `stopRecord failed. code is =${error.code}, message is = ${error.message}`); + }) : this.aVCodecController.stopRecord(); + this.recording = false; } + setScreenState(false, this.getUIContext().getHostContext()); + clearInterval(this.timer); + } + + getRecordTime(): void { + this.timer = setInterval(() => { + this.seconds += 1; + this.videoRecorderTimeText = dateTime(this.seconds); + }, 1000); } build() { - Stack({ alignContent: Alignment.Top }) { - XComponent({ - id: 'recorderXComponent', - type: XComponentType.SURFACE, - controller: this.XComponentController - }) - .onLoad(async () => { - this.XComponentSurfaceId = this.XComponentController.getXComponentSurfaceId(); - if (this.mode === 0) { - await this.recordController.initCamera(this.getUIContext().getHostContext()!, this.XComponentSurfaceId); - } else { - await this.aVCodecController.createRecorder(this.getUIContext().getHostContext()!, this.XComponentSurfaceId) - } + NavDestination() { + Stack({ alignContent: Alignment.Top }) { + XComponent({ + id: 'recorderXComponent', + type: XComponentType.SURFACE, + controller: this.XComponentController }) - .width(this.widthPx) - .height(this.heightPx) - .position({ x: '50%', y: '50%' }) - .translate({ x: '-50%', y: '-50%' }) - + .onLoad(async () => { + this.XComponentSurfaceId = this.XComponentController.getXComponentSurfaceId(); + if (this.mode === 0) { + await this.recordController.initCamera(this.getUIContext().getHostContext()!, this.XComponentSurfaceId, + this.filePath); + } else { + await this.aVCodecController.createRecorder(this.getUIContext().getHostContext()!, + this.XComponentSurfaceId, this.filePath) + } + }) + .width(this.widthPx) + .height(this.heightPx) + .position({ x: '50%', y: '50%' }) + .translate({ x: '-50%', y: '-50%' }) - if (this.videoRecorderTimeText !== Const.DEFAULT_TIME) { - Row() { - Circle() - .width(8) - .height(8) - .fill(Color.Red) - .margin({ right: 10 }) - Text(this.videoRecorderTimeText) + if (this.recording) { + Row() { + Circle() + .width(8) + .height(8) + .fill(Color.Red) + .margin({ right: 10 }) + Text(this.videoRecorderTimeText) + .fontFamily('HarmonyHeilTi-Light') + .fontSize(20) + .fontColor(Color.White) + } + .align(Alignment.Top) + .margin({ top: 20 }) + } else { + Text(Const.DEFAULT_TIME) .fontFamily('HarmonyHeilTi-Light') - .fontSize(20) + .fontSize(35) .fontColor(Color.White) + .align(Alignment.Top) + .margin({ top: 80 }) } - .align(Alignment.Top) - .margin({ top: 20 }) - } - if (this.videoRecorderTimeText == Const.DEFAULT_TIME) { - Text(Const.DEFAULT_TIME) - .fontFamily('HarmonyHeilTi-Light') - .fontSize(35) - .fontColor(Color.White) - .align(Alignment.Top) - .margin({ top: 80 }) + Image(this.recording ? $r('app.media.camera_pause_video_4x') : $r('app.media.camera_take_video_4x')) + .width(60) + .height(60) + .position({ x: '50%', y: '90%' }) + .translate({ x: '-50%', y: '-100%' }) + .onClick(async () => { + if (this.recording) { + this.isEnable = false; + await (this.mode === 0 ? this.recordController.stopRecord().catch((error: BusinessError) => { + Logger.error(TAG, `stopRecord failed. code is =${error.code}, message is = ${error.message}`); + }) : this.aVCodecController.stopRecord()); + AppStorage.set('path', this.filePath); + this.PathStack.pop(); + return; + } + this.getRecordTime(); + await (this.mode === 0 ? this.recordController.startRecord() : this.aVCodecController.startRecord()); + this.recording = !this.recording; + }) + .enabled(this.isEnable) } - Image(this.recording ? $r('app.media.camera_pause_video_4x') : $r('app.media.camera_take_video_4x')) - .width(60) - .height(60) - .position({ x: '50%', y: '90%' }) - .translate({ x: '-50%', y: '-100%' }) - .onClick(async () => { - if (this.recording) { - this.isEnable = false; - clearInterval(this.timer); - await (this.mode === 0 ? this.recordController.stopRecord().catch((error: BusinessError) => { - hilog.error(0x0000, TAG, `stopRecord failed. code is =${error.code}, message is = ${error.message}`); - }) : this.aVCodecController.stopRecord()); - this.getUIContext().getRouter().back(); - return; - } - this.getRecordTime(); - await (this.mode === 0 ? this.recordController.startRecord() : this.aVCodecController.startRecord()); - this.recording = !this.recording; - }) - .enabled(this.isEnable) + .width('100%') + .height('100%') + .backgroundColor(Color.Black) + .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM]) } - .width('100%') - .height('100%') - .backgroundColor(Color.Black) - .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM]) + .onReady((context: NavDestinationContext) => { + this.PathStack = context.pathStack; + }) + .onBackPressed(() => { + if (this.recording) { + this.isEnable = false; + this.mode === 0 ? this.recordController.stopRecord().catch((error: BusinessError) => { + Logger.error(TAG, `stopRecord failed. code is =${error.code}, message is = ${error.message}`); + }) : this.aVCodecController.stopRecord(); + } + return false; + }) + .hideTitleBar(true) + .hideToolBar(true) } } \ No newline at end of file diff --git a/entry/src/main/ets/pages/Index.ets b/entry/src/main/ets/pages/Index.ets index 6bf3605def95cdeb3b4fa394a0f0a92ea0ab0895..3addc7df2cc52b073a951dee421d79a08ca3f153 100644 --- a/entry/src/main/ets/pages/Index.ets +++ b/entry/src/main/ets/pages/Index.ets @@ -20,81 +20,64 @@ AppStorage.setOrCreate('path', ''); @Component struct Index { @Provide navPathStack: NavPathStack = new NavPathStack(); - @State isShow: boolean = false; - @State selectedMethod: number = 0; //0 avRecord 1 AVCodec - - @Builder - encapsulationMethod() { - Column() { - Row() { - Text($r('app.string.recorder_video_encoding_encapsulation')) - .fontSize(15) - .fontWeight(FontWeight.Bold) - .padding(10) - .fontColor(Color.Black) - Blank() - Image(this.selectedMethod == 0 ? $r('app.media.ic_radio_selected') : - $r('app.media.ic_radio')) - .width(25) - .height(25) - .objectFit(ImageFit.Contain) - } - .width('95%') - .margin(5) - .borderRadius(20) - .justifyContent(FlexAlign.SpaceBetween) - .onClick(() => { - this.selectedMethod = 0; - this.navPathStack.pushPathByName('Recording', this.selectedMethod); - this.isShow = false; - }) - - Row() { - Text($r('app.string.AVCodec_encoding_encapsulation')) - .fontSize(15) - .fontWeight(FontWeight.Bold) - .padding(10) - .fontColor(Color.Black) - Blank() - Image(this.selectedMethod == 1 ? $r('app.media.ic_radio_selected') : - $r('app.media.ic_radio')) - .width(25) - .height(25) - .objectFit(ImageFit.Contain) - } - .width('95%') - .margin(5) - .borderRadius(20) - .justifyContent(FlexAlign.SpaceBetween) - .onClick(() => { - this.selectedMethod = 1; - this.navPathStack.pushPathByName('Recording', this.selectedMethod); - this.isShow = false; - }) - } - } build() { Navigation(this.navPathStack) { Column({ space: 16 }) { - Button($r('app.string.HDR_video_recording')) + Button($r('app.string.play_video_base_on_avplayer')) + .width('100%') + .height(40) + .onClick(() => { + this.navPathStack.pushPathByName('AVPlayerPage', null); + }) + + Button($r('app.string.play_video_base_on_avcodec')) + .width('100%') + .height(40) + .onClick(() => { + this.navPathStack.pushPathByName('AVCodecPlayPage', null); + }) + + Button($r('app.string.record_base_on_avrecorder')) + .width('100%') + .height(40) + .onClick(() => { + this.navPathStack.pushPathByName('Recording', 0); + }) + + Button($r('app.string.record_base_on_avcodec')) .width('100%') - .height('40vp') - .bindSheet($$this.isShow, this.encapsulationMethod(), { - backgroundColor: Color.White, - blurStyle: BlurStyle.NONE, - showClose: true, - height: '180vp', - title: { title: $r('app.string.video_encapsulation_list') } + .height(40) + .onClick(() => { + this.navPathStack.pushPathByName('Recording', 1); }) + + Button($r('app.string.transcoder_base_on_avtranscoder')) + .width('100%') + .height(40) .onClick(() => { - this.isShow = true; + this.navPathStack.pushPathByName('AVTranscoderPage', null); }) - Button($r('app.string.HDR_video_transcode')) + + Button($r('app.string.transcoder_base_on_avcodec')) + .width('100%') + .height(40) + .onClick(() => { + this.navPathStack.pushPathByName('AVCodecPage', 0); + }) + + Button($r('app.string.transcoder_base_on_videoprocess')) + .width('100%') + .height(40) + .onClick(() => { + this.navPathStack.pushPathByName('AVCodecPage', 1); + }) + + Button($r('app.string.transform_colorspace')) .width('100%') - .height('40vp') + .height(40) .onClick(() => { - this.navPathStack.pushPathByName('Transcode', null); + this.navPathStack.pushPathByName('AVCodecPage', 2); }) } .height('100%') @@ -103,6 +86,7 @@ struct Index { .margin({ bottom: '40vp' }) .justifyContent(FlexAlign.End) } + .title($r('app.string.EntryAbility_label')) .hideToolBar(true) } } \ No newline at end of file diff --git a/entry/src/main/ets/pages/Recording.ets b/entry/src/main/ets/pages/Recording.ets index 0a927bdc6636f339ade5afbe4314a31cbe17eb78..55fb205b5fb93768d402e39d1ed560b2945f9ac2 100644 --- a/entry/src/main/ets/pages/Recording.ets +++ b/entry/src/main/ets/pages/Recording.ets @@ -13,59 +13,56 @@ * limitations under the License. */ -import { BusinessError } from '@kit.BasicServicesKit'; import { CameraDataModel } from '../model/CameraDateModel'; -import { PermissionsFromUser } from '../common/utils/PermissionsUtils'; +import { checkPermissions, PermissionsFromUser } from '../common/utils/PermissionsUtils'; import { AVPlayerController } from '../controller/AVPlayerController'; import { SaveDialog } from '../view/SaveDialog'; -import { hilog } from '@kit.PerformanceAnalysisKit'; - -const TAG = '[Recording]'; +import { Logger } from '../common/utils/Logger'; +import { setScreenState } from '../common/utils/WindowUtils'; @Builder export function RecordingBuilder() { Recording(); } +const TAG = '[Recording]'; + @Component -struct Recording { +export struct Recording { @Consume('navPathStack') PathStack: NavPathStack; @State mode: number = 0; //0 avRecord 1 AVCodec @State viewState: number = 0; // Display video files @StorageLink('path') @Watch('pathChange') path: string = ''; @State avPlayerController: AVPlayerController = new AVPlayerController(); - private cameraData: CameraDataModel = new CameraDataModel(); private XComponentSurfaceId: string = ''; private XComponentController: XComponentController = new XComponentController(); aboutToAppear() { this.mode = this.PathStack.getParamByName('Recording').pop() as number; PermissionsFromUser(); + if (!checkPermissions()) { + try { + this.getUIContext().getPromptAction().showToast({ + message: $r('app.string.no_permission'), + duration: 2000, + bottom: 280 + }); + } catch (error) { + Logger.error(TAG, `showToast fail, error code is =${error.code}, message is = ${error.message}`); + } + } + setScreenState(true, this.getUIContext().getHostContext()); } aboutToDisappear(): void { this.avPlayerController.videoRelease(); + setScreenState(false, this.getUIContext().getHostContext()); + AppStorage.set('path', ''); } build() { NavDestination() { Column() { - Row() { - Text($r('app.string.HDR_video_recording')) - .fontWeight(700) - .fontSize(18) - .margin({ - left: 16, - bottom: 5 - }) - } - .width('100%') - .justifyContent(FlexAlign.SpaceBetween) - .alignRules({ - 'top': { 'anchor': '__container__', 'align': VerticalAlign.Top }, - 'left': { 'anchor': '__container__', 'align': HorizontalAlign.Start } - }) - Stack({ alignContent: Alignment.Center }) { Text($r('app.string.video_record_before_are')) .fontSize(14) @@ -88,47 +85,42 @@ struct Recording { } } .onClick(() => { - this.avPlayerController.isPlaying === true ? this.avPlayerController.videoPause() : + this.avPlayerController.isPlaying ? this.avPlayerController.videoPause() : this.avPlayerController.videoPlay() }) .width('100%') .layoutWeight(1) - Row() { + Column() { Button($r('app.string.record_video')) - .layoutWeight(1) .backgroundColor($r('app.color.button_bg_color')) .fontColor($r('app.color.button_text_color')) .fontWeight(500) - .margin({ right: 6 }) + .width('100%') + .margin({ + bottom: 12 + }) .onClick(async () => { - AppStorage.set('cameraData', this.cameraData) - this.getUIContext() - .getRouter() - .pushUrl({ url: 'pages/CameraPage', params: { mode: this.mode } }) - .catch((error: BusinessError) => { - hilog.error(0x0000, TAG, - `pushUrl fail, error code is =${error.code}, message is = ${error.message}`); - }) + this.PathStack.pushPathByName('CameraPage', this.mode); }) - SaveDialog() + if (this.path !== '') { + SaveDialog() + } } - .justifyContent(FlexAlign.Center) .margin({ left: 16, top: 12, - right: 16, - bottom: 12 + right: 16 }) } .height('100%') + .justifyContent(FlexAlign.Center) .margin({ top: 10 }) - } + .title(this.mode ? $r('app.string.record_base_on_avcodec') : $r('app.string.record_base_on_avrecorder')) .onReady((context: NavDestinationContext) => { this.PathStack = context.pathStack; }) - .hideTitleBar(true) } pathChange() { diff --git a/entry/src/main/ets/view/MultiStatusButton.ets b/entry/src/main/ets/view/MultiStatusButton.ets index ec5409ae755e305e28bf3a0de073d15ce1f54663..ad72ef8add9a2eb21d27cd9e27c267856ff63366 100644 --- a/entry/src/main/ets/view/MultiStatusButton.ets +++ b/entry/src/main/ets/view/MultiStatusButton.ets @@ -12,12 +12,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -enum StatusEnum { - UNAVAILABLE = 0, // unavailable - AVAILABLE = 1, // available - SELECTED = 2 // selected -} +import { StatusEnum } from '../common/CommonEnum'; @Component export struct MultiStatusButton { @@ -33,8 +28,8 @@ export struct MultiStatusButton { .fontColor($r('sys.color.dialog_outer_border_color')) .backgroundColor($r('sys.color.container_modal_button_hover_baseboard')) .borderRadius(16) - .padding({left: 16}) - .margin({top: 8}) + .padding({ left: 16 }) + .margin({ top: 8 }) } else if (this.status === StatusEnum.AVAILABLE) { Text(this.text) .width('100%') @@ -42,9 +37,9 @@ export struct MultiStatusButton { .fontColor($r('sys.color.black')) .backgroundColor($r('sys.color.container_modal_button_hover_baseboard')) .borderRadius(16) - .padding({left: 16}) - .margin({top: 8}) - .onTouch(()=>{ + .padding({ left: 16 }) + .margin({ top: 8 }) + .onTouch(() => { this.status = StatusEnum.SELECTED; }) } else if (this.status === StatusEnum.SELECTED) { @@ -54,13 +49,13 @@ export struct MultiStatusButton { .fontColor('#0A59F7') .backgroundColor('#0C0A59F7') .borderRadius(16) - .padding({left: 16}) - .margin({top: 8}) + .padding({ left: 16 }) + .margin({ top: 8 }) .borderWidth(1) .borderColor('#0A59F7') } } - .margin({top: 8}) + .margin({ top: 8 }) .width('100%') .height(56) } diff --git a/entry/src/main/ets/view/SaveDialog.ets b/entry/src/main/ets/view/SaveDialog.ets index 79f3e4ed4d5f1d28b44ac8fee134f356299e51dc..dbc4790c13b0430c7229dfdb904356990d8f8120 100644 --- a/entry/src/main/ets/view/SaveDialog.ets +++ b/entry/src/main/ets/view/SaveDialog.ets @@ -18,6 +18,7 @@ import { saveFile } from '../common/utils/VideoOperationUtils'; @CustomDialog struct SaveCustomDialog { controller: CustomDialogController; + build() { Column() { Text($r('app.string.save_button_title')) @@ -32,7 +33,6 @@ struct SaveCustomDialog { .fontSize(16) .textAlign(TextAlign.Center) - Row() { Button($r('app.string.save_button_cancel')) .onClick(() => { @@ -75,11 +75,13 @@ export struct SaveDialog { build() { Button($r('app.string.save_video')) - .layoutWeight(1) .backgroundColor($r('app.color.button_bg_color')) .fontColor($r('app.color.button_text_color')) .fontWeight(500) - .margin({ right: 6 }) + .width('100%') + .margin({ + bottom: 12 + }) .size({ width: '100%', height: '40vp' diff --git a/entry/src/main/resources/base/element/string.json b/entry/src/main/resources/base/element/string.json index b52c8efb0607f2cf845e57df7bead57cecac3a09..3089f097e75210826a1cf2ab7fc17afe14cd3006 100644 --- a/entry/src/main/resources/base/element/string.json +++ b/entry/src/main/resources/base/element/string.json @@ -119,6 +119,54 @@ { "name": "video_processing_not_support", "value": "The video does not support the current operation" + }, + { + "name": "play_video_base_on_avplayer", + "value": "Play HDR Base On Avplayer" + }, + { + "name": "play_video_base_on_avcodec", + "value": "Play HDR Base On AVCodec" + }, + { + "name": "record_base_on_avrecorder", + "value": "Record Base On AVRecorder" + }, + { + "name": "record_base_on_avcodec", + "value": "Record Base On AVCodec" + }, + { + "name": "Video_reminder_message", + "value": "Please do not abort it, transcoding!!!" + }, + { + "name": "transcoder_base_on_avtranscoder", + "value": "Transcode Base On AVTranscoder" + }, + { + "name": "transcoder_base_on_avcodec", + "value": "Transcode Base On AVCodec" + }, + { + "name": "transcoder_base_on_videoprocess", + "value": "Transcode Base On VideoProcessing" + }, + { + "name": "transform_colorspace", + "value": "VideoProcessing Color Conversion" + }, + { + "name": "transcoder_error", + "value": "Transcode Error" + }, + { + "name": "no_permission", + "value": "No Permission" + }, + { + "name": "video_is_not_hdr", + "value": "Video is not HDR" } ] } \ No newline at end of file diff --git a/entry/src/main/resources/base/profile/main_pages.json b/entry/src/main/resources/base/profile/main_pages.json index 53a9564675d22d96833419154b088260c35c301f..1898d94f58d6128ab712be2c68acc7c98e9ab9ce 100644 --- a/entry/src/main/resources/base/profile/main_pages.json +++ b/entry/src/main/resources/base/profile/main_pages.json @@ -1,6 +1,5 @@ { "src": [ - "pages/Index", - "pages/CameraPage" + "pages/Index" ] } diff --git a/entry/src/main/resources/base/profile/route_map.json b/entry/src/main/resources/base/profile/route_map.json index be81c6b068a1341bc7216bc0b6ff7772c70d2d6d..3324d20dc9cb1e6b23e3a22cfa144bb4b8d0a5b9 100644 --- a/entry/src/main/resources/base/profile/route_map.json +++ b/entry/src/main/resources/base/profile/route_map.json @@ -1,20 +1,52 @@ { "routerMap": [ { - "name": "Transcode", - "pageSourceFile": "src/main/ets/pages/Transcode.ets", - "buildFunction": "TranscodeBuilder", + "name": "Recording", + "pageSourceFile": "src/main/ets/pages/Recording.ets", + "buildFunction": "RecordingBuilder", "data": { - "description" : "this is Transcode" + "description" : "this is video Recording" } }, { - "name": "Recording", - "pageSourceFile": "src/main/ets/pages/Recording.ets", - "buildFunction": "RecordingBuilder", + "name": "AVPlayerPage", + "pageSourceFile": "src/main/ets/pages/AVPlayerPage.ets", + "buildFunction": "AVPlayerPageBuilder", "data": { "description" : "this is video Recording" } + }, + { + "name": "CameraPage", + "pageSourceFile": "src/main/ets/pages/CameraPage.ets", + "buildFunction": "CameraPageBuilder", + "data": { + "description" : "this is video Recording" + } + }, + { + "name": "AVCodecPlayPage", + "pageSourceFile": "src/main/ets/pages/AVCodecPlayPage.ets", + "buildFunction": "AVCodecPlayPageBuilder", + "data": { + "description" : "this is video Recording" + } + }, + { + "name": "AVTranscoderPage", + "pageSourceFile": "src/main/ets/pages/AVTranscoderPage.ets", + "buildFunction": "AVTranscoderPageBuilder", + "data": { + "description" : "this is AVTranscoder Page" + } + }, + { + "name": "AVCodecPage", + "pageSourceFile": "src/main/ets/pages/AVCodecPage.ets", + "buildFunction": "AVCodecPageBuilder", + "data": { + "description" : "this is AVCodec Page" + } } ] } \ No newline at end of file diff --git a/entry/src/main/resources/en_US/element/string.json b/entry/src/main/resources/en_US/element/string.json index 233ff82eb3083f6c62c1d1173f372d865f67608b..0a09622c34b0cd0984650ea6d123ff9d894d9e10 100644 --- a/entry/src/main/resources/en_US/element/string.json +++ b/entry/src/main/resources/en_US/element/string.json @@ -107,6 +107,54 @@ { "name": "video_processing_not_support", "value": "The video does not support the current operation" + }, + { + "name": "play_video_base_on_avplayer", + "value": "Play HDR Base On Avplayer" + }, + { + "name": "play_video_base_on_avcodec", + "value": "Play HDR Base On AVCodec" + }, + { + "name": "record_base_on_avrecorder", + "value": "Record Base On AVRecorder" + }, + { + "name": "record_base_on_avcodec", + "value": "Record Base On AVCodec" + }, + { + "name": "Video_reminder_message", + "value": "Please do not abort it, transcoding!!!" + }, + { + "name": "transcoder_base_on_avtranscoder", + "value": "Transcode Base On AVTranscoder" + }, + { + "name": "transcoder_base_on_avcodec", + "value": "Transcode Base On AVCodec" + }, + { + "name": "transcoder_base_on_videoprocess", + "value": "Transcode Base On VideoProcessing" + }, + { + "name": "transform_colorspace", + "value": "VideoProcessing Color Conversion" + }, + { + "name": "transcoder_error", + "value": "Transcode Error" + }, + { + "name": "no_permission", + "value": "No Permission" + }, + { + "name": "video_is_not_hdr", + "value": "Video is not HDR" } ] } \ No newline at end of file diff --git a/entry/src/main/resources/zh_CN/element/string.json b/entry/src/main/resources/zh_CN/element/string.json index d55b2a92819c76f0366d33070d21d88058fb0eed..7ea8088a4bfc5324ea4c08039beed5f9033bdf0c 100644 --- a/entry/src/main/resources/zh_CN/element/string.json +++ b/entry/src/main/resources/zh_CN/element/string.json @@ -99,6 +99,54 @@ { "name": "Optimize_video", "value": "优化视频" + }, + { + "name": "play_video_base_on_avplayer", + "value": "基于AVPlayer播放HDR" + }, + { + "name": "play_video_base_on_avcodec", + "value": "基于AVCodec播放HDR" + }, + { + "name": "record_base_on_avrecorder", + "value": "基于AVRecorder录制HDR" + }, + { + "name": "record_base_on_avcodec", + "value": "基于AVCodec录制HDR" + }, + { + "name": "Video_reminder_message", + "value": "转码中,请勿中止操作!!!" + }, + { + "name": "transcoder_base_on_avtranscoder", + "value": "基于AVTranscoder转码" + }, + { + "name": "transcoder_base_on_avcodec", + "value": "基于AVCodec转码" + }, + { + "name": "transcoder_base_on_videoprocess", + "value": "基于VideoProcessing转码" + }, + { + "name": "transform_colorspace", + "value": "基于VideoProcessing转换色彩空间" + }, + { + "name": "transcoder_error", + "value": "转码错误" + }, + { + "name": "no_permission", + "value": "用户未授权" + }, + { + "name": "video_is_not_hdr", + "value": "视频非HDR格式" } ] } \ No newline at end of file diff --git a/screenshots/devices/MainPage.png b/screenshots/devices/MainPage.png new file mode 100644 index 0000000000000000000000000000000000000000..d661497985f8348edcbe3d79851e0ba732ee67cf Binary files /dev/null and b/screenshots/devices/MainPage.png differ diff --git a/screenshots/devices/MainPage_en.png b/screenshots/devices/MainPage_en.png new file mode 100644 index 0000000000000000000000000000000000000000..ab0885e922084b035864460eebf371f81ca9d892 Binary files /dev/null and b/screenshots/devices/MainPage_en.png differ diff --git a/screenshots/devices/PlayerPage.png b/screenshots/devices/PlayerPage.png new file mode 100644 index 0000000000000000000000000000000000000000..a4545c50bf98d4e6fb2c07ee27f6ed575dfb8a51 Binary files /dev/null and b/screenshots/devices/PlayerPage.png differ diff --git a/screenshots/devices/PlayerPage_en.png b/screenshots/devices/PlayerPage_en.png new file mode 100644 index 0000000000000000000000000000000000000000..b07b74cb108c8571bd70821274e7cbc7e068d0f5 Binary files /dev/null and b/screenshots/devices/PlayerPage_en.png differ diff --git a/screenshots/devices/RecordPage.png b/screenshots/devices/RecordPage.png new file mode 100644 index 0000000000000000000000000000000000000000..4d0381b8b2e1101c5a7c1b8c83eb7535d5844e1d Binary files /dev/null and b/screenshots/devices/RecordPage.png differ diff --git a/screenshots/devices/RecordPage_en.png b/screenshots/devices/RecordPage_en.png new file mode 100644 index 0000000000000000000000000000000000000000..672fb95702b9c90cea79d3b99e7cecf11c75ffa4 Binary files /dev/null and b/screenshots/devices/RecordPage_en.png differ diff --git a/screenshots/devices/TranscoderPage.png b/screenshots/devices/TranscoderPage.png new file mode 100644 index 0000000000000000000000000000000000000000..284f13538d6486910e06654db61b80bde4072612 Binary files /dev/null and b/screenshots/devices/TranscoderPage.png differ diff --git a/screenshots/devices/TranscoderPage_en.png b/screenshots/devices/TranscoderPage_en.png new file mode 100644 index 0000000000000000000000000000000000000000..614d88f83e30c8766aed3883a3be94f5ccd40359 Binary files /dev/null and b/screenshots/devices/TranscoderPage_en.png differ diff --git a/screenshots/devices/dialog.png b/screenshots/devices/dialog.png deleted file mode 100644 index 2efa94d567ec1812d5f80540ce599dea06d39fa9..0000000000000000000000000000000000000000 Binary files a/screenshots/devices/dialog.png and /dev/null differ diff --git a/screenshots/devices/dialog_en.png b/screenshots/devices/dialog_en.png deleted file mode 100644 index 009af6982a57ef11751ade5550bfe008d00549da..0000000000000000000000000000000000000000 Binary files a/screenshots/devices/dialog_en.png and /dev/null differ diff --git a/screenshots/devices/index.png b/screenshots/devices/index.png deleted file mode 100644 index ca046da2894d2d0a61729a62226815d0b6a8d811..0000000000000000000000000000000000000000 Binary files a/screenshots/devices/index.png and /dev/null differ diff --git a/screenshots/devices/index_en.png b/screenshots/devices/index_en.png deleted file mode 100644 index 80a13e6624b09c7ea6262c18565f98987ecdec25..0000000000000000000000000000000000000000 Binary files a/screenshots/devices/index_en.png and /dev/null differ diff --git a/screenshots/devices/record.png b/screenshots/devices/record.png deleted file mode 100644 index 022a8d6dc5fc382b5c7b3815c703f8ef3e98ce69..0000000000000000000000000000000000000000 Binary files a/screenshots/devices/record.png and /dev/null differ diff --git a/screenshots/devices/transcode.png b/screenshots/devices/transcode.png deleted file mode 100644 index ccced88c695f8b8d3751aaf68af1a939afcd383b..0000000000000000000000000000000000000000 Binary files a/screenshots/devices/transcode.png and /dev/null differ diff --git a/screenshots/devices/transcode_en.png b/screenshots/devices/transcode_en.png deleted file mode 100644 index cfdca324428fb0dd066f0985b685645f08011723..0000000000000000000000000000000000000000 Binary files a/screenshots/devices/transcode_en.png and /dev/null differ