/* * 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 "video_processing_server.h" #include #include #include #include #include #include "algorithm_errors.h" #include "surface_buffer.h" #include "vpe_model_path.h" #include "vpe_sa_constants.h" using namespace OHOS::Media::VideoProcessingEngine; using namespace OHOS; using namespace std::placeholders; using VpeAlgo = IVideoProcessingAlgorithm; namespace { const int VPE_INFO_FILE_MAX_LENGTH = 20485760; const std::string UNLOAD_HANLDER = "unload_vpe_sa_handler"; const std::string UNLOAD_TASK_ID = "unload_vpe_sa"; constexpr int32_t DELAY_TIME = 180000; REGISTER_SYSTEM_ABILITY_BY_ID(VideoProcessingServer, VIDEO_PROCESSING_SERVER_SA_ID, false); } VideoProcessingServer::VideoProcessingServer(int32_t saId, bool runOnCreate) : SystemAbility(saId, runOnCreate) { VPE_LOGD("VideoProcessingServer construct!"); } VideoProcessingServer::~VideoProcessingServer() { VPE_LOGD("VideoProcessingServer destruction!"); } ErrCode VideoProcessingServer::LoadInfo(int32_t key, SurfaceBufferInfo& bufferInfo) { if (key < 0 || key >= VPE_MODEL_KEY_NUM) { VPE_LOGE("Input key %{public}d is invalid!", key); UnloadVideoProcessingSA(); return ERR_INVALID_DATA; } std::string path = VPE_MODEL_PATHS[key]; VPE_LOGD("LoadInfoForVpe %{public}s", path.c_str()); bufferInfo.surfacebuffer = SurfaceBuffer::Create(); if (bufferInfo.surfacebuffer == nullptr) { VPE_LOGE("Create surface buffer failed"); UnloadVideoProcessingSA(); return ERR_NULL_OBJECT; } std::unique_ptr fileStream = std::make_unique(path, std::ios::binary); if (!fileStream->is_open()) { VPE_LOGE("file is not open %{public}s", path.c_str()); UnloadVideoProcessingSA(); return ERR_NULL_OBJECT; } fileStream->seekg(0, std::ios::end); int fileLength = fileStream->tellg(); fileStream->seekg(0, std::ios::beg); if (fileLength < 0 || fileLength > VPE_INFO_FILE_MAX_LENGTH) { VPE_LOGE("fileLength %{public}d is too short or too long!", fileLength); UnloadVideoProcessingSA(); return ERR_INVALID_DATA; } BufferRequestConfig inputCfg; inputCfg.width = fileLength; inputCfg.height = 1; VPE_LOGD("FileLength: %{public}d", fileLength); inputCfg.strideAlignment = fileLength; inputCfg.usage = BUFFER_USAGE_CPU_READ | BUFFER_USAGE_CPU_WRITE | BUFFER_USAGE_MEM_DMA; inputCfg.format = GRAPHIC_PIXEL_FMT_YCBCR_420_SP; inputCfg.timeout = 0; GSError err = bufferInfo.surfacebuffer->Alloc(inputCfg); if (err != GSERROR_OK) { VPE_LOGE("Alloc surface buffer failed"); UnloadVideoProcessingSA(); return ERR_INVALID_DATA; } fileStream->read(reinterpret_cast(bufferInfo.surfacebuffer->GetVirAddr()), fileLength); fileStream->close(); UnloadVideoProcessingSA(); return ERR_NONE; } // For optimized SA ErrCode VideoProcessingServer::Create(const std::string& feature, const std::string& clientName, int32_t& clientID) { VPE_LOGD(">>> feature:%{public}s client:%{public}s", feature.c_str(), clientName.c_str()); uint32_t id; std::lock_guard lock(lock_); auto ret = CreateLocked(feature, clientName, id); if (ret == VPE_ALGO_ERR_OK) { clientID = static_cast(id); VPE_LOGD("<<< feature:%{public}s client:%{public}s isWorking_:%{public}d {new ID:%{public}u}", feature.c_str(), clientName.c_str(), isWorking_.load(), id); } DelayUnloadTaskLocked(); return ret; } ErrCode VideoProcessingServer::Destroy(int32_t clientID) { std::lock_guard lock(lock_); auto ret = DestroyLocked(static_cast(clientID)); DelayUnloadTaskLocked(); return ret; } ErrCode VideoProcessingServer::SetParameter(int32_t clientID, int32_t tag, const std::vector& parameter) { return Execute(clientID, std::bind(&VpeAlgo::SetParameter, _1, _2, tag, parameter), VPE_LOG_INFO); } ErrCode VideoProcessingServer::GetParameter(int32_t clientID, int32_t tag, std::vector& parameter) { return Execute(clientID, std::bind(&VpeAlgo::GetParameter, _1, _2, tag, parameter), VPE_LOG_INFO); } ErrCode VideoProcessingServer::UpdateMetadata(int32_t clientID, SurfaceBufferInfo& image) { CHECK_AND_RETURN_RET_LOG(image.surfacebuffer != nullptr, VPE_ALGO_ERR_INVALID_PARAM, "Invalid input: image is null!"); return Execute(clientID, std::bind(&VpeAlgo::UpdateMetadata, _1, _2, image), VPE_LOG_INFO); } ErrCode VideoProcessingServer::Process(int32_t clientID, const SurfaceBufferInfo& input, SurfaceBufferInfo& output) { CHECK_AND_RETURN_RET_LOG(input.surfacebuffer != nullptr && output.surfacebuffer != nullptr, VPE_ALGO_ERR_INVALID_PARAM, "Invalid input: input or output is null!"); return Execute(clientID, std::bind(&VpeAlgo::Process, _1, _2, input, output), VPE_LOG_INFO); } ErrCode VideoProcessingServer::ComposeImage(int32_t clientID, const SurfaceBufferInfo& inputSdrImage, const SurfaceBufferInfo& inputGainmap, SurfaceBufferInfo& outputHdrImage, bool legacy) { CHECK_AND_RETURN_RET_LOG(inputSdrImage.surfacebuffer != nullptr && inputGainmap.surfacebuffer != nullptr && outputHdrImage.surfacebuffer != nullptr, VPE_ALGO_ERR_INVALID_PARAM, "Invalid input: input or output is null!"); return Execute(clientID, std::bind(&VpeAlgo::ComposeImage, _1, _2, inputSdrImage, inputGainmap, outputHdrImage, legacy), VPE_LOG_INFO); } ErrCode VideoProcessingServer::DecomposeImage(int32_t clientID, const SurfaceBufferInfo& inputImage, SurfaceBufferInfo& outputSdrImage, SurfaceBufferInfo& outputGainmap) { CHECK_AND_RETURN_RET_LOG(inputImage.surfacebuffer != nullptr && outputSdrImage.surfacebuffer != nullptr && outputGainmap.surfacebuffer != nullptr, VPE_ALGO_ERR_INVALID_PARAM, "Invalid input: input or output is null!"); return Execute(clientID, std::bind(&VpeAlgo::DecomposeImage, _1, _2, inputImage, outputSdrImage, outputGainmap), VPE_LOG_INFO); } void VideoProcessingServer::UnloadVideoProcessingSA() { if (CreateUnloadHandler()) { VPE_LOGI("CreateUnloadHandler success!"); DelayUnloadTask(); } else { return; } VPE_LOGD("Start/Update Delay Time Unload VPE SA!"); return; } void VideoProcessingServer::OnStart(const SystemAbilityOnDemandReason& startReason) { VPE_LOGD("Start VPE SA because %{public}s.", startReason.GetName().c_str()); if (CreateUnloadHandler()) { VPE_LOGI("CreateUnloadHandler success!"); DelayUnloadTask(); } CHECK_AND_RETURN_LOG(Publish(this), "Failed to publish SA!"); } void VideoProcessingServer::OnStop(const SystemAbilityOnDemandReason& stopReason) { VPE_LOGD("Stop VPE SA because %{public}s.", stopReason.GetName().c_str()); DestroyUnloadHandler(); ClearAlgorithms(); } ErrCode VideoProcessingServer::CreateLocked(const std::string& feature, const std::string& clientName, uint32_t& id) { AlgoPtr algo = nullptr; bool isNew = false; auto it = algorithms_.find(feature); if (it == algorithms_.end() || it->second == nullptr) { algo = factory_.Create(feature); CHECK_AND_RETURN_RET_LOG(algo != nullptr, VPE_ALGO_ERR_NO_MEMORY, "Failed to create '%{public}s' for '%{public}s'!", feature.c_str(), clientName.c_str()); CHECK_AND_RETURN_RET_LOG(algo->Initialize() == VPE_ALGO_ERR_OK, ERR_INVALID_STATE, "Failed to initialize '%{public}s' for '%{public}s'!", feature.c_str(), clientName.c_str()); isNew = true; } else { algo = it->second; } CHECK_AND_RETURN_RET_LOG(algo->Add(clientName, id) == VPE_ALGO_ERR_OK, ERR_INVALID_DATA, "Failed to add client to '%{public}s' for '%{public}s'!", feature.c_str(), clientName.c_str()); clients_[id] = feature; if (isNew) { algorithms_[feature] = algo; } isWorking_ = true; return VPE_ALGO_ERR_OK; } ErrCode VideoProcessingServer::DestroyLocked(uint32_t id) { auto it = clients_.find(id); if (it == clients_.end()) [[unlikely]] { VPE_LOGE("Invalid input: no client for ID=%{public}d", id); return VPE_ALGO_ERR_INVALID_CLIENT_ID; } std::string feature = it->second; clients_.erase(it); isWorking_ = !clients_.empty(); VPE_LOGD("isWorking_:%{public}d", isWorking_.load()); auto itAlgo = algorithms_.find(feature); if (itAlgo == algorithms_.end()) [[unlikely]] { VPE_LOGE("Invalid input: no '%{public}s' for ID=%{public}d", feature.c_str(), id); return VPE_ALGO_ERR_INVALID_VAL; } if (itAlgo->second == nullptr) [[unlikely]] { VPE_LOGE("Invalid input: null '%{public}s' for ID=%{public}d", feature.c_str(), id); algorithms_.erase(itAlgo); return VPE_ALGO_ERR_INVALID_VAL; } auto algo = itAlgo->second; auto ret = algo->Del(id); CHECK_AND_LOG(ret == VPE_ALGO_ERR_OK, "Failed to del(ID=%{public}d) of '%{public}s'", id, feature.c_str()); if (!algo->HasClient()) { ret = algo->Deinitialize(); CHECK_AND_LOG(ret == VPE_ALGO_ERR_OK, "Failed to deinitialize of '%{public}s' for ID=%{public}d", feature.c_str(), id); algorithms_.erase(itAlgo); } return ret; } bool VideoProcessingServer::CreateUnloadHandler() { std::lock_guard lock(lock_); return CreateUnloadHandlerLocked(); } bool VideoProcessingServer::CreateUnloadHandlerLocked() { if (unloadHandler_ != nullptr) { return true; } unloadHandler_ = std::make_shared(AppExecFwk::EventRunner::Create(UNLOAD_HANLDER)); return unloadHandler_ != nullptr; } void VideoProcessingServer::DestroyUnloadHandler() { std::lock_guard lock(lock_); if (unloadHandler_ != nullptr) { unloadHandler_->RemoveAllEvents(); unloadHandler_->RemoveTask(UNLOAD_TASK_ID); unloadHandler_ = nullptr; } } void VideoProcessingServer::DelayUnloadTask() { std::lock_guard lock(lock_); DelayUnloadTaskLocked(); } void VideoProcessingServer::DelayUnloadTaskLocked() { VPE_LOGD("delay unload task begin, isWorking_:%{public}d", isWorking_.load()); CHECK_AND_RETURN_LOG(CreateUnloadHandlerLocked(), "unloadHandler_ is NOT created!"); unloadHandler_->RemoveTask(UNLOAD_TASK_ID); VPE_LOGD("delay unload task post task(wait %{public}dms)", DELAY_TIME); auto task = [this]() { VPE_LOGD("do unload task, isWorking_:%{public}d", isWorking_.load()); auto samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager(); CHECK_AND_RETURN_LOG(samgr != nullptr, "Failed to GetSystemAbilityManager!"); CHECK_AND_RETURN_LOG(samgr->UnloadSystemAbility(VIDEO_PROCESSING_SERVER_SA_ID) == ERR_OK, "Failed to unload VPE SA!"); VPE_LOGI("kill VPE service success!"); }; unloadHandler_->PostTask(task, UNLOAD_TASK_ID, DELAY_TIME); } void VideoProcessingServer::ClearAlgorithms() { std::lock_guard lock(lock_); for (auto& [feature, algo] : algorithms_) { if (algo == nullptr) [[unlikely]] { VPE_LOGE("algorithm is null of '%{public}s'!", feature.c_str()); continue; } if (algo->Deinitialize() != VPE_ALGO_ERR_OK) [[unlikely]] { VPE_LOGE("Failed to deinitialize of '%{public}s'!", feature.c_str()); continue; } } algorithms_.clear(); clients_.clear(); isWorking_ = false; VPE_LOGD("isWorking_:%{public}d", isWorking_.load()); } ErrCode VideoProcessingServer::Execute(int clientID, std::function&& operation, const LogInfo& logInfo) { uint32_t id = static_cast(clientID); AlgoPtr algorithm; { std::lock_guard lock(lock_); auto it = clients_.find(id); if (it == clients_.end()) [[unlikely]] { VPE_ORG_LOGE(logInfo, "Invalid input: no client for ID=%{public}d!", id); DelayUnloadTaskLocked(); return VPE_ALGO_ERR_INVALID_CLIENT_ID; } auto itAlgo = algorithms_.find(it->second); if (itAlgo == algorithms_.end() || itAlgo->second == nullptr) [[unlikely]] { VPE_ORG_LOGE(logInfo, "Invalid input: no '%{public}s' for ID=%{public}d!", it->second.c_str(), id); DelayUnloadTaskLocked(); return VPE_ALGO_ERR_INVALID_VAL; } algorithm = itAlgo->second; } auto err = operation(algorithm, id); DelayUnloadTask(); return err; }