1 /*
2 * Copyright (c) 2025 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "video_processing_server.h"
17
18 #include <fstream>
19 #include <iostream>
20 #include <sstream>
21
22 #include <iservice_registry.h>
23 #include <system_ability_definition.h>
24
25 #include "algorithm_errors.h"
26 #include "surface_buffer.h"
27 #include "vpe_model_path.h"
28 #include "vpe_sa_constants.h"
29
30 using namespace OHOS::Media::VideoProcessingEngine;
31 using namespace OHOS;
32 using namespace std::placeholders;
33
34 using VpeAlgo = IVideoProcessingAlgorithm;
35
36 namespace {
37 const int VPE_INFO_FILE_MAX_LENGTH = 20485760;
38 const std::string UNLOAD_HANLDER = "unload_vpe_sa_handler";
39 const std::string UNLOAD_TASK_ID = "unload_vpe_sa";
40 constexpr int32_t DELAY_TIME = 180000;
41 REGISTER_SYSTEM_ABILITY_BY_ID(VideoProcessingServer, VIDEO_PROCESSING_SERVER_SA_ID, false);
42 }
43
VideoProcessingServer(int32_t saId,bool runOnCreate)44 VideoProcessingServer::VideoProcessingServer(int32_t saId, bool runOnCreate) : SystemAbility(saId, runOnCreate)
45 {
46 VPE_LOGD("VideoProcessingServer construct!");
47 }
48
~VideoProcessingServer()49 VideoProcessingServer::~VideoProcessingServer()
50 {
51 VPE_LOGD("VideoProcessingServer destruction!");
52 }
53
LoadInfo(int32_t key,SurfaceBufferInfo & bufferInfo)54 ErrCode VideoProcessingServer::LoadInfo(int32_t key, SurfaceBufferInfo& bufferInfo)
55 {
56 if (key < 0 || key >= VPE_MODEL_KEY_NUM) {
57 VPE_LOGE("Input key %{public}d is invalid!", key);
58 UnloadVideoProcessingSA();
59 return ERR_INVALID_DATA;
60 }
61 std::string path = VPE_MODEL_PATHS[key];
62 VPE_LOGD("LoadInfoForVpe %{public}s", path.c_str());
63 bufferInfo.surfacebuffer = SurfaceBuffer::Create();
64 if (bufferInfo.surfacebuffer == nullptr) {
65 VPE_LOGE("Create surface buffer failed");
66 UnloadVideoProcessingSA();
67 return ERR_NULL_OBJECT;
68 }
69 std::unique_ptr<std::ifstream> fileStream = std::make_unique<std::ifstream>(path, std::ios::binary);
70 if (!fileStream->is_open()) {
71 VPE_LOGE("file is not open %{public}s", path.c_str());
72 UnloadVideoProcessingSA();
73 return ERR_NULL_OBJECT;
74 }
75 fileStream->seekg(0, std::ios::end);
76 int fileLength = fileStream->tellg();
77 fileStream->seekg(0, std::ios::beg);
78 if (fileLength < 0 || fileLength > VPE_INFO_FILE_MAX_LENGTH) {
79 VPE_LOGE("fileLength %{public}d is too short or too long!", fileLength);
80 UnloadVideoProcessingSA();
81 return ERR_INVALID_DATA;
82 }
83
84 BufferRequestConfig inputCfg;
85 inputCfg.width = fileLength;
86 inputCfg.height = 1;
87 VPE_LOGD("FileLength: %{public}d", fileLength);
88 inputCfg.strideAlignment = fileLength;
89 inputCfg.usage = BUFFER_USAGE_CPU_READ | BUFFER_USAGE_CPU_WRITE | BUFFER_USAGE_MEM_DMA;
90 inputCfg.format = GRAPHIC_PIXEL_FMT_YCBCR_420_SP;
91 inputCfg.timeout = 0;
92 GSError err = bufferInfo.surfacebuffer->Alloc(inputCfg);
93 if (err != GSERROR_OK) {
94 VPE_LOGE("Alloc surface buffer failed");
95 UnloadVideoProcessingSA();
96 return ERR_INVALID_DATA;
97 }
98 fileStream->read(reinterpret_cast<char*>(bufferInfo.surfacebuffer->GetVirAddr()), fileLength);
99 fileStream->close();
100 UnloadVideoProcessingSA();
101 return ERR_NONE;
102 }
103
104 // For optimized SA
Create(const std::string & feature,const std::string & clientName,int32_t & clientID)105 ErrCode VideoProcessingServer::Create(const std::string& feature, const std::string& clientName, int32_t& clientID)
106 {
107 VPE_LOGD(">>> feature:%{public}s client:%{public}s", feature.c_str(), clientName.c_str());
108 uint32_t id;
109 std::lock_guard<std::mutex> lock(lock_);
110 auto ret = CreateLocked(feature, clientName, id);
111 if (ret == VPE_ALGO_ERR_OK) {
112 clientID = static_cast<int>(id);
113 VPE_LOGD("<<< feature:%{public}s client:%{public}s isWorking_:%{public}d {new ID:%{public}u}",
114 feature.c_str(), clientName.c_str(), isWorking_.load(), id);
115 }
116 DelayUnloadTaskLocked();
117 return ret;
118 }
119
Destroy(int32_t clientID)120 ErrCode VideoProcessingServer::Destroy(int32_t clientID)
121 {
122 std::lock_guard<std::mutex> lock(lock_);
123 auto ret = DestroyLocked(static_cast<uint32_t>(clientID));
124 DelayUnloadTaskLocked();
125 return ret;
126 }
127
SetParameter(int32_t clientID,int32_t tag,const std::vector<uint8_t> & parameter)128 ErrCode VideoProcessingServer::SetParameter(int32_t clientID, int32_t tag, const std::vector<uint8_t>& parameter)
129 {
130 return Execute(clientID, std::bind(&VpeAlgo::SetParameter, _1, _2, tag, parameter), VPE_LOG_INFO);
131 }
132
GetParameter(int32_t clientID,int32_t tag,std::vector<uint8_t> & parameter)133 ErrCode VideoProcessingServer::GetParameter(int32_t clientID, int32_t tag, std::vector<uint8_t>& parameter)
134 {
135 return Execute(clientID, std::bind(&VpeAlgo::GetParameter, _1, _2, tag, parameter), VPE_LOG_INFO);
136 }
137
UpdateMetadata(int32_t clientID,SurfaceBufferInfo & image)138 ErrCode VideoProcessingServer::UpdateMetadata(int32_t clientID, SurfaceBufferInfo& image)
139 {
140 CHECK_AND_RETURN_RET_LOG(image.surfacebuffer != nullptr, VPE_ALGO_ERR_INVALID_PARAM,
141 "Invalid input: image is null!");
142 return Execute(clientID, std::bind(&VpeAlgo::UpdateMetadata, _1, _2, image), VPE_LOG_INFO);
143 }
144
Process(int32_t clientID,const SurfaceBufferInfo & input,SurfaceBufferInfo & output)145 ErrCode VideoProcessingServer::Process(int32_t clientID, const SurfaceBufferInfo& input, SurfaceBufferInfo& output)
146 {
147 CHECK_AND_RETURN_RET_LOG(input.surfacebuffer != nullptr && output.surfacebuffer != nullptr,
148 VPE_ALGO_ERR_INVALID_PARAM, "Invalid input: input or output is null!");
149 return Execute(clientID, std::bind(&VpeAlgo::Process, _1, _2, input, output), VPE_LOG_INFO);
150 }
151
ComposeImage(int32_t clientID,const SurfaceBufferInfo & inputSdrImage,const SurfaceBufferInfo & inputGainmap,SurfaceBufferInfo & outputHdrImage,bool legacy)152 ErrCode VideoProcessingServer::ComposeImage(int32_t clientID, const SurfaceBufferInfo& inputSdrImage,
153 const SurfaceBufferInfo& inputGainmap, SurfaceBufferInfo& outputHdrImage, bool legacy)
154 {
155 CHECK_AND_RETURN_RET_LOG(inputSdrImage.surfacebuffer != nullptr && inputGainmap.surfacebuffer != nullptr &&
156 outputHdrImage.surfacebuffer != nullptr, VPE_ALGO_ERR_INVALID_PARAM, "Invalid input: input or output is null!");
157 return Execute(clientID,
158 std::bind(&VpeAlgo::ComposeImage, _1, _2, inputSdrImage, inputGainmap, outputHdrImage, legacy), VPE_LOG_INFO);
159 }
160
DecomposeImage(int32_t clientID,const SurfaceBufferInfo & inputImage,SurfaceBufferInfo & outputSdrImage,SurfaceBufferInfo & outputGainmap)161 ErrCode VideoProcessingServer::DecomposeImage(int32_t clientID, const SurfaceBufferInfo& inputImage,
162 SurfaceBufferInfo& outputSdrImage, SurfaceBufferInfo& outputGainmap)
163 {
164 CHECK_AND_RETURN_RET_LOG(inputImage.surfacebuffer != nullptr && outputSdrImage.surfacebuffer != nullptr &&
165 outputGainmap.surfacebuffer != nullptr, VPE_ALGO_ERR_INVALID_PARAM, "Invalid input: input or output is null!");
166 return Execute(clientID,
167 std::bind(&VpeAlgo::DecomposeImage, _1, _2, inputImage, outputSdrImage, outputGainmap), VPE_LOG_INFO);
168 }
169
UnloadVideoProcessingSA()170 void VideoProcessingServer::UnloadVideoProcessingSA()
171 {
172 if (CreateUnloadHandler()) {
173 VPE_LOGI("CreateUnloadHandler success!");
174 DelayUnloadTask();
175 } else {
176 return;
177 }
178 VPE_LOGD("Start/Update Delay Time Unload VPE SA!");
179 return;
180 }
181
OnStart(const SystemAbilityOnDemandReason & startReason)182 void VideoProcessingServer::OnStart(const SystemAbilityOnDemandReason& startReason)
183 {
184 VPE_LOGD("Start VPE SA because %{public}s.", startReason.GetName().c_str());
185 if (CreateUnloadHandler()) {
186 VPE_LOGI("CreateUnloadHandler success!");
187 DelayUnloadTask();
188 }
189 CHECK_AND_RETURN_LOG(Publish(this), "Failed to publish SA!");
190 }
191
OnStop(const SystemAbilityOnDemandReason & stopReason)192 void VideoProcessingServer::OnStop(const SystemAbilityOnDemandReason& stopReason)
193 {
194 VPE_LOGD("Stop VPE SA because %{public}s.", stopReason.GetName().c_str());
195 DestroyUnloadHandler();
196 ClearAlgorithms();
197 }
198
CreateLocked(const std::string & feature,const std::string & clientName,uint32_t & id)199 ErrCode VideoProcessingServer::CreateLocked(const std::string& feature, const std::string& clientName, uint32_t& id)
200 {
201 AlgoPtr algo = nullptr;
202 bool isNew = false;
203 auto it = algorithms_.find(feature);
204 if (it == algorithms_.end() || it->second == nullptr) {
205 algo = factory_.Create(feature);
206 CHECK_AND_RETURN_RET_LOG(algo != nullptr, VPE_ALGO_ERR_NO_MEMORY,
207 "Failed to create '%{public}s' for '%{public}s'!", feature.c_str(), clientName.c_str());
208 CHECK_AND_RETURN_RET_LOG(algo->Initialize() == VPE_ALGO_ERR_OK, ERR_INVALID_STATE,
209 "Failed to initialize '%{public}s' for '%{public}s'!", feature.c_str(), clientName.c_str());
210 isNew = true;
211 } else {
212 algo = it->second;
213 }
214 CHECK_AND_RETURN_RET_LOG(algo->Add(clientName, id) == VPE_ALGO_ERR_OK, ERR_INVALID_DATA,
215 "Failed to add client to '%{public}s' for '%{public}s'!", feature.c_str(), clientName.c_str());
216 clients_[id] = feature;
217 if (isNew) {
218 algorithms_[feature] = algo;
219 }
220 isWorking_ = true;
221 return VPE_ALGO_ERR_OK;
222 }
223
DestroyLocked(uint32_t id)224 ErrCode VideoProcessingServer::DestroyLocked(uint32_t id)
225 {
226 auto it = clients_.find(id);
227 if (it == clients_.end()) [[unlikely]] {
228 VPE_LOGE("Invalid input: no client for ID=%{public}d", id);
229 return VPE_ALGO_ERR_INVALID_CLIENT_ID;
230 }
231 std::string feature = it->second;
232 clients_.erase(it);
233 isWorking_ = !clients_.empty();
234 VPE_LOGD("isWorking_:%{public}d", isWorking_.load());
235 auto itAlgo = algorithms_.find(feature);
236 if (itAlgo == algorithms_.end()) [[unlikely]] {
237 VPE_LOGE("Invalid input: no '%{public}s' for ID=%{public}d", feature.c_str(), id);
238 return VPE_ALGO_ERR_INVALID_VAL;
239 }
240 if (itAlgo->second == nullptr) [[unlikely]] {
241 VPE_LOGE("Invalid input: null '%{public}s' for ID=%{public}d", feature.c_str(), id);
242 algorithms_.erase(itAlgo);
243 return VPE_ALGO_ERR_INVALID_VAL;
244 }
245 auto algo = itAlgo->second;
246 auto ret = algo->Del(id);
247 CHECK_AND_LOG(ret == VPE_ALGO_ERR_OK, "Failed to del(ID=%{public}d) of '%{public}s'", id, feature.c_str());
248 if (!algo->HasClient()) {
249 ret = algo->Deinitialize();
250 CHECK_AND_LOG(ret == VPE_ALGO_ERR_OK, "Failed to deinitialize of '%{public}s' for ID=%{public}d",
251 feature.c_str(), id);
252 algorithms_.erase(itAlgo);
253 }
254 return ret;
255 }
256
CreateUnloadHandler()257 bool VideoProcessingServer::CreateUnloadHandler()
258 {
259 std::lock_guard<std::mutex> lock(lock_);
260 return CreateUnloadHandlerLocked();
261 }
262
CreateUnloadHandlerLocked()263 bool VideoProcessingServer::CreateUnloadHandlerLocked()
264 {
265 if (unloadHandler_ != nullptr) {
266 return true;
267 }
268 unloadHandler_ = std::make_shared<AppExecFwk::EventHandler>(AppExecFwk::EventRunner::Create(UNLOAD_HANLDER));
269 return unloadHandler_ != nullptr;
270 }
271
DestroyUnloadHandler()272 void VideoProcessingServer::DestroyUnloadHandler()
273 {
274 std::lock_guard<std::mutex> lock(lock_);
275 if (unloadHandler_ != nullptr) {
276 unloadHandler_->RemoveAllEvents();
277 unloadHandler_->RemoveTask(UNLOAD_TASK_ID);
278 unloadHandler_ = nullptr;
279 }
280 }
281
DelayUnloadTask()282 void VideoProcessingServer::DelayUnloadTask()
283 {
284 std::lock_guard<std::mutex> lock(lock_);
285 DelayUnloadTaskLocked();
286 }
287
DelayUnloadTaskLocked()288 void VideoProcessingServer::DelayUnloadTaskLocked()
289 {
290 VPE_LOGD("delay unload task begin, isWorking_:%{public}d", isWorking_.load());
291 CHECK_AND_RETURN_LOG(CreateUnloadHandlerLocked(), "unloadHandler_ is NOT created!");
292 unloadHandler_->RemoveTask(UNLOAD_TASK_ID);
293 VPE_LOGD("delay unload task post task(wait %{public}dms)", DELAY_TIME);
294 auto task = [this]() {
295 VPE_LOGD("do unload task, isWorking_:%{public}d", isWorking_.load());
296 auto samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
297 CHECK_AND_RETURN_LOG(samgr != nullptr, "Failed to GetSystemAbilityManager!");
298 CHECK_AND_RETURN_LOG(samgr->UnloadSystemAbility(VIDEO_PROCESSING_SERVER_SA_ID) == ERR_OK,
299 "Failed to unload VPE SA!");
300 VPE_LOGI("kill VPE service success!");
301 };
302 unloadHandler_->PostTask(task, UNLOAD_TASK_ID, DELAY_TIME);
303 }
304
ClearAlgorithms()305 void VideoProcessingServer::ClearAlgorithms()
306 {
307 std::lock_guard<std::mutex> lock(lock_);
308 for (auto& [feature, algo] : algorithms_) {
309 if (algo == nullptr) [[unlikely]] {
310 VPE_LOGE("algorithm is null of '%{public}s'!", feature.c_str());
311 continue;
312 }
313 if (algo->Deinitialize() != VPE_ALGO_ERR_OK) [[unlikely]] {
314 VPE_LOGE("Failed to deinitialize of '%{public}s'!", feature.c_str());
315 continue;
316 }
317 }
318 algorithms_.clear();
319 clients_.clear();
320 isWorking_ = false;
321 VPE_LOGD("isWorking_:%{public}d", isWorking_.load());
322 }
323
Execute(int clientID,std::function<int (AlgoPtr &,uint32_t)> && operation,const LogInfo & logInfo)324 ErrCode VideoProcessingServer::Execute(int clientID, std::function<int(AlgoPtr&, uint32_t)>&& operation,
325 const LogInfo& logInfo)
326 {
327 uint32_t id = static_cast<uint32_t>(clientID);
328 AlgoPtr algorithm;
329 {
330 std::lock_guard<std::mutex> lock(lock_);
331 auto it = clients_.find(id);
332 if (it == clients_.end()) [[unlikely]] {
333 VPE_ORG_LOGE(logInfo, "Invalid input: no client for ID=%{public}d!", id);
334 DelayUnloadTaskLocked();
335 return VPE_ALGO_ERR_INVALID_CLIENT_ID;
336 }
337 auto itAlgo = algorithms_.find(it->second);
338 if (itAlgo == algorithms_.end() || itAlgo->second == nullptr) [[unlikely]] {
339 VPE_ORG_LOGE(logInfo, "Invalid input: no '%{public}s' for ID=%{public}d!", it->second.c_str(), id);
340 DelayUnloadTaskLocked();
341 return VPE_ALGO_ERR_INVALID_VAL;
342 }
343 algorithm = itAlgo->second;
344 }
345 auto err = operation(algorithm, id);
346 DelayUnloadTask();
347 return err;
348 }
349