* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "PluginManager.h"
#include "SampleInfo.h"
#include "multimedia/video_processing_engine/video_processing.h"
#include "multimedia/video_processing_engine/video_processing_types.h"
#include "AVCodecSampleLog.h"
#include <ace/xcomponent/native_interface_xcomponent.h>
#include <cerrno>
#include <cstdint>
#include <hilog/log.h>
#include <mutex>
#include <native_buffer/buffer_common.h>
#include <string>
#include <sys/mman.h>
#include <unistd.h>
#include <sys/poll.h>
#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) {
if (iter->second != nullptr) {
delete iter->second;
iter->second = nullptr;
}
}
nativeXComponentMap_.clear();
for(auto iter = pluginRenderMap_.begin(); iter != pluginRenderMap_.end(); ++iter) {
if (iter->second != nullptr) {
delete iter->second;
iter->second = nullptr;
}
}
pluginRenderMap_.clear();
}
void PluginManager::SetNativeXComponent(std::string &id, OH_NativeXComponent *nativeXComponent) {
if (nativeXComponent == nullptr) {
return;
}
if (nativeXComponentMap_.find(id) == nativeXComponentMap_.end()) {
nativeXComponentMap_[id] = nativeXComponent;
return;
}
if (nativeXComponentMap_[id] != nativeXComponent) {
OH_NativeXComponent *tmp = nativeXComponentMap_[id];
tmp = nullptr;
nativeXComponentMap_[id] = nativeXComponent;
}
}
PluginRender* PluginManager::GetRender(std::string &id) {
if (pluginRenderMap_.find(id) == pluginRenderMap_.end()) {
PluginRender *instance = PluginRender::GetInstance(id);
pluginRenderMap_[id] = instance;
return instance;
}
return pluginRenderMap_[id];
}
void PluginManager::Export(napi_env env, napi_value exports) {
if ((env == nullptr) || (exports == nullptr)) {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, LOG_TAG, "Export: env or exports is null");
return;
}
napi_value exportInstance = nullptr;
if (napi_get_named_property(env, exports, OH_NATIVE_XCOMPONENT_OBJ, &exportInstance) != napi_ok) {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, LOG_TAG, "Export: napi_get_named_property fail");
return;
}
OH_NativeXComponent *nativeXComponent = nullptr;
if (napi_unwrap(env, exportInstance, reinterpret_cast<void **>(&nativeXComponent)) != napi_ok) {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, LOG_TAG, "Export: napi_unwrap fail");
return;
}
char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {'\0'};
uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
if (OH_NativeXComponent_GetXComponentId(nativeXComponent, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, LOG_TAG, "Export unable to get XComponent id");
return;
}
std::string id(idStr);
auto context = PluginManager::GetInstance();
if ((context != nullptr) && (nativeXComponent != nullptr)) {
context->SetNativeXComponent(id, nativeXComponent);
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, LOG_TAG, "SetNativeXComponent: %{public}s", id.c_str());
auto render = context->GetRender(id);
if (render != nullptr) {
render->RegisterCallback(nativeXComponent);
render->Export(env, exports);
}
}
}
void PluginManager::InitProcessing(SampleInfo &sampleInfo) {
VideoProcessing_ErrorCode ret = VIDEO_PROCESSING_ERROR_INVALID_VALUE;
int32_t processType = sampleInfo.processType;
if (processType == 1) {
int8_t level = sampleInfo.zoomLevel;
OH_AVFormat *format = OH_AVFormat_Create();
if (level == 0) {
OH_AVFormat_SetIntValue(format, VIDEO_DETAIL_ENHANCER_PARAMETER_KEY_QUALITY_LEVEL, VIDEO_DETAIL_ENHANCER_QUALITY_LEVEL_NONE);
} else if (level == 1) {
OH_AVFormat_SetIntValue(format, VIDEO_DETAIL_ENHANCER_PARAMETER_KEY_QUALITY_LEVEL, VIDEO_DETAIL_ENHANCER_QUALITY_LEVEL_LOW);
} else if (level == 2) {
OH_AVFormat_SetIntValue(format, VIDEO_DETAIL_ENHANCER_PARAMETER_KEY_QUALITY_LEVEL, VIDEO_DETAIL_ENHANCER_QUALITY_LEVEL_MEDIUM);
} else if (level == 3) {
OH_AVFormat_SetIntValue(format, VIDEO_DETAIL_ENHANCER_PARAMETER_KEY_QUALITY_LEVEL, VIDEO_DETAIL_ENHANCER_QUALITY_LEVEL_HIGH);
}
ret = OH_VideoProcessing_SetParameter(processor, format);
CHECK_AND_RETURN_LOG(ret == VIDEO_PROCESSING_SUCCESS, "OH_VideoProcessing_SetParameter failed");
} else {
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");
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");
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, LOG_TAG, "InitProcessing succeed");
}
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 processType = sampleInfo.processType;
if (processType == 1) {
ret = OH_VideoProcessing_Create(&processor, VIDEO_PROCESSING_TYPE_DETAIL_ENHANCER);
} else if (processType == 2) {
ret = OH_VideoProcessing_Create(&processor, VIDEO_PROCESSING_TYPE_METADATA_GENERATION);
} else if (processType == 3) {
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");
}
void PluginManager::StartProcessing() {
PluginManager::GetInstance()->sampleInfo_->vpErrorCode = 0;
VideoProcessing_ErrorCode ret = OH_VideoProcessing_Start(processor);
if (ret != VIDEO_PROCESSING_SUCCESS) {
ResetVP(5, 0);
}
CHECK_AND_RETURN_LOG(ret == VIDEO_PROCESSING_SUCCESS, "OH_VideoProcessing_Start failed, ret: %{public}d", ret);
isVPRunning = true;
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, LOG_TAG, "StartProcessing succeed");
}
void PluginManager::SetSampleInfo(SampleInfo *sampleInfo) {
sampleInfo_ = sampleInfo;
}
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();
}
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");
}
}
void PluginManager::Release() {
if (isVPRunning) {
StopProcessing();
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");
}
}
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<std::thread>(&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);
}
static uint32_t value = 0x00;
value++;
uint32_t *pixel = static_cast<uint32_t *>(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;
}
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, LOG_TAG, "StartProcessing succeed");
return 0;
}
}