1 /*
2 * Copyright (c) 2024 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 "detail_enhancer_image_fwk.h"
17
18 #include <dlfcn.h>
19
20 #include "detail_enhancer_common.h"
21 #include "extension_manager.h"
22 #include "native_buffer.h"
23 #include "surface_buffer.h"
24 #include "video_processing_client.h"
25 #include "securec.h"
26 #include "vpe_log.h"
27 #include "vpe_sa_constants.h"
28 #include "vpe_trace.h"
29
30 namespace {
31 enum RectLevelItem {
32 RECT_LEVEL_INVALID = -1,
33 RECT_MIN_WIDTH,
34 RECT_MAX_WIDTH,
35 RECT_MIN_HEIGHT,
36 RECT_MAX_HEIGHT,
37 RECT_LEVEL_NUM,
38 };
39
40 constexpr float EPSILON = 1e-6; // extremely small value
41 const int MAX_URL_LENGTH = 100;
42 const int SUPPORTED_MIN_WIDTH = 32;
43 const int SUPPORTED_MIN_HEIGHT = 32;
44 const int SUPPORTED_MAX_WIDTH = 20000; // 20000 max support width
45 const int SUPPORTED_MAX_HEIGHT = 20000; // 20000 max support height
46 const int TIMEOUT_THRESHOLD = 10; // 10 millisecond
47 const std::unordered_set<int32_t> SUPPORTED_FORMATS = {
48 OHOS::GRAPHIC_PIXEL_FMT_BGRA_8888, // BGRA
49 OHOS::GRAPHIC_PIXEL_FMT_RGBA_8888, // RGBA
50 OHOS::GRAPHIC_PIXEL_FMT_YCBCR_420_SP, // NV12
51 OHOS::GRAPHIC_PIXEL_FMT_YCRCB_420_SP, // NV21
52 OHOS::GRAPHIC_PIXEL_FMT_YCBCR_420_P, // YU12
53 OHOS::GRAPHIC_PIXEL_FMT_YCRCB_420_P, // YV12
54 OHOS::GRAPHIC_PIXEL_FMT_RGBA_1010102, // RGBA_1010102
55 };
56 const std::vector<std::array<int, RECT_LEVEL_NUM>> SUPER_LEVEL_TARGET_RECT = {
57 {1, 1104, 1, 848},
58 {1, 1104, 1, 1488},
59 {1, 1488, 1, 1104},
60 {1, 1872, 1, 1360},
61 };
62
IsValidSurfaceBuffer(const OHOS::sptr<OHOS::SurfaceBuffer> & buffer)63 inline bool IsValidSurfaceBuffer(const OHOS::sptr<OHOS::SurfaceBuffer>& buffer)
64 {
65 CHECK_AND_RETURN_RET_LOG(buffer != nullptr, false, "buffer is nullptr!!");
66 return SUPPORTED_FORMATS.find(buffer->GetFormat()) != SUPPORTED_FORMATS.end() &&
67 buffer->GetWidth() > SUPPORTED_MIN_WIDTH && buffer->GetHeight() > SUPPORTED_MIN_HEIGHT &&
68 buffer->GetWidth() <= SUPPORTED_MAX_WIDTH && buffer->GetHeight() <= SUPPORTED_MAX_HEIGHT;
69 }
70
71 std::atomic<int32_t> g_instanceId = -1;
72 std::timed_mutex g_externLock{};
73 }
74
75 namespace OHOS {
76 namespace Media {
77 namespace VideoProcessingEngine {
DetailEnhancerImageFwk(int type)78 DetailEnhancerImageFwk::DetailEnhancerImageFwk(int type)
79 {
80 type_ = (type >= IMAGE && type <= VIDEO) ? type : IMAGE;
81 Extension::ExtensionManager::GetInstance().IncreaseInstance();
82 }
83
~DetailEnhancerImageFwk()84 DetailEnhancerImageFwk::~DetailEnhancerImageFwk()
85 {
86 Clear();
87 Extension::ExtensionManager::GetInstance().DecreaseInstance();
88 }
89
Create(int type)90 std::shared_ptr<DetailEnhancerImage> DetailEnhancerImage::Create(int type)
91 {
92 CHECK_AND_RETURN_RET_LOG(type <= VIDEO && type >= IMAGE, nullptr, "type is invalid!!");
93 std::shared_ptr<DetailEnhancerImage> impl = std::make_shared<DetailEnhancerImageFwk>(type);
94 CHECK_AND_RETURN_RET_LOG(impl != nullptr, nullptr, "failed to init DetailEnhancerImage");
95 return impl;
96 }
97
GetAlgorithm(int level)98 std::shared_ptr<DetailEnhancerBase> DetailEnhancerImageFwk::GetAlgorithm(int level)
99 {
100 if (level < DETAIL_ENH_LEVEL_NONE || level > DETAIL_ENH_LEVEL_VIDEO) {
101 VPE_LOGE("Invalid level:%{public}d", level);
102 return nullptr;
103 }
104 std::lock_guard<std::mutex> lock(lock_);
105 auto createdImpl = algorithms_.find(level);
106 if (createdImpl != algorithms_.end()) [[likely]] {
107 return createdImpl->second;
108 }
109 auto algo = CreateAlgorithm(level);
110 if (algo.get() == nullptr) {
111 return nullptr;
112 }
113 VPE_LOGD("level:%d enable:%d", level, enableProtection_);
114 auto ret = algo->EnableProtection(enableProtection_);
115 CHECK_AND_RETURN_RET_LOG(ret == VPE_ALGO_ERR_OK, nullptr,
116 "Failed to EnableProtection(%{public}d) for level:%{pubcli}d ret:%{pubcli}d", enableProtection_, level, ret);
117 algorithms_[level] = algo;
118 return algorithms_[level];
119 }
120
CreateAlgorithm(int level)121 std::shared_ptr<DetailEnhancerBase> DetailEnhancerImageFwk::CreateAlgorithm(int level)
122 {
123 auto& manager = Extension::ExtensionManager::GetInstance();
124 VPE_SYNC_TRACE;
125 std::shared_ptr<DetailEnhancerBase> algoImpl = manager.CreateDetailEnhancer(level);
126 VPE_LOGE("level:%{public}d", level);
127 if (algoImpl == nullptr) {
128 VPE_LOGE("Extension create failed, get a empty impl, level: %{public}d", level);
129 return nullptr;
130 }
131 if (algoImpl->Init() != VPE_ALGO_ERR_OK) {
132 VPE_LOGE("Init failed, extension level: %{public}d", level);
133 return nullptr;
134 }
135 return algoImpl;
136 }
137
SetParameter(const DetailEnhancerParameters & parameter)138 VPEAlgoErrCode DetailEnhancerImageFwk::SetParameter(const DetailEnhancerParameters& parameter)
139 {
140 CHECK_AND_RETURN_RET_LOG(parameter.level >= DETAIL_ENH_LEVEL_NONE && parameter.level <= DETAIL_ENH_LEVEL_HIGH &&
141 parameter.uri.length() < MAX_URL_LENGTH, VPE_ALGO_ERR_INVALID_VAL, "Invalid parameter");
142 std::lock_guard<std::mutex> lock(lock_);
143 parameter_ = parameter;
144 parameterUpdated = true;
145 hasParameter_ = true;
146 VPE_LOGI("DetailEnhancerImageFwk SetParameter Succeed");
147 return VPE_ALGO_ERR_OK;
148 }
149
GetParameter(DetailEnhancerParameters & parameter) const150 VPEAlgoErrCode DetailEnhancerImageFwk::GetParameter(DetailEnhancerParameters& parameter) const
151 {
152 std::lock_guard<std::mutex> lock(lock_);
153 parameter = parameter_;
154 VPE_LOGI("DetailEnhancerImageFwk SetParameter Succeed");
155 return VPE_ALGO_ERR_OK;
156 }
157
IsValidProcessedObject(const sptr<SurfaceBuffer> & input,const sptr<SurfaceBuffer> & output)158 bool DetailEnhancerImageFwk::IsValidProcessedObject(const sptr<SurfaceBuffer>& input,
159 const sptr<SurfaceBuffer>& output)
160 {
161 CHECK_AND_RETURN_RET_LOG((input != nullptr) && (output != nullptr),
162 false, "Input or output is nullptr");
163 CHECK_AND_RETURN_RET_LOG(input->GetFormat() == output->GetFormat(), false,
164 "The input format and output format need to be consistent");
165 CHECK_AND_RETURN_RET_LOG(IsValidSurfaceBuffer(input) && IsValidSurfaceBuffer(output), false, "Invalid buffer");
166 return true;
167 }
168
EvaluateTargetLevel(const sptr<SurfaceBuffer> & input,const sptr<SurfaceBuffer> & output,float widthRatio,float heightRatio) const169 int DetailEnhancerImageFwk::EvaluateTargetLevel(const sptr<SurfaceBuffer>& input,
170 const sptr<SurfaceBuffer>& output, float widthRatio, float heightRatio) const
171 {
172 CHECK_AND_RETURN_RET_LOG((input != nullptr) && (output != nullptr), false, "Input or output is nullptr");
173 if (parameter_.level == DETAIL_ENH_LEVEL_HIGH) {
174 int inputW = input->GetWidth();
175 int inputH = input->GetHeight();
176 if (widthRatio < 1.0 && heightRatio < 1.0 && // 1.0 means zoom out
177 // 0.5 means rounding, 2 means two pixels
178 std::abs(static_cast<int>(widthRatio * inputW + 0.5) - static_cast<int>(heightRatio * inputW + 0.5)) <= 2 &&
179 // 0.5 means rounding, 2 means two pixels
180 std::abs(static_cast<int>(widthRatio * inputH + 0.5) - static_cast<int>(heightRatio * inputH + 0.5)) <= 2) {
181 VPE_LOGI("Prioritize using extream vision algo when scaling down scenes");
182 return DETAIL_ENH_LEVEL_HIGH;
183 }
184 return DETAIL_ENH_LEVEL_HIGH_AISR;
185 }
186 return parameter_.level;
187 }
188
ProcessVideo(const sptr<SurfaceBuffer> & input,const sptr<SurfaceBuffer> & output)189 VPEAlgoErrCode DetailEnhancerImageFwk::ProcessVideo(const sptr<SurfaceBuffer>& input,
190 const sptr<SurfaceBuffer>& output)
191 {
192 auto algoImpl = GetAlgorithm(DETAIL_ENH_LEVEL_VIDEO);
193 if (algoImpl == nullptr) {
194 VPE_LOGE("Get Algorithm impl for video failed!");
195 return VPE_ALGO_ERR_UNKNOWN;
196 }
197 if (parameterUpdated.load() && (algoImpl->SetParameter(parameter_) != VPE_ALGO_ERR_OK)) {
198 VPE_LOGE("set parameter failed!");
199 return VPE_ALGO_ERR_UNKNOWN;
200 } else {
201 parameterUpdated = false;
202 }
203 UpdateLastAlgorithm(algoImpl);
204 if (ProcessAlgorithm(algoImpl, input, output) != VPE_ALGO_ERR_OK) {
205 VPE_LOGE("process video failed");
206 return VPE_ALGO_ERR_UNKNOWN;
207 }
208 return VPE_ALGO_ERR_OK;
209 }
210
Process(const sptr<SurfaceBuffer> & input,const sptr<SurfaceBuffer> & output)211 VPEAlgoErrCode DetailEnhancerImageFwk::Process(const sptr<SurfaceBuffer>& input, const sptr<SurfaceBuffer>& output)
212 {
213 auto err = DoProcess(input, output);
214 if (err != VPE_ALGO_ERR_OK) {
215 std::lock_guard<std::mutex> lock(restoreLock_);
216 if (!needRestore_) {
217 return err;
218 }
219 VPE_LOGD("Clear status to restore algorithms.");
220 Clear();
221 needRestore_ = false;
222 VPE_LOGD("Try to process again.");
223 err = DoProcess(input, output);
224 VPE_LOGD("process return %{public}d", err);
225 }
226 return err;
227 }
228
DoProcess(const sptr<SurfaceBuffer> & input,const sptr<SurfaceBuffer> & output)229 VPEAlgoErrCode DetailEnhancerImageFwk::DoProcess(const sptr<SurfaceBuffer>& input, const sptr<SurfaceBuffer>& output)
230 {
231 CHECK_AND_RETURN_RET_LOG(IsValidProcessedObject(input, output), VPE_ALGO_ERR_INVALID_VAL, "Invalid input!");
232 VPE_SYNC_TRACE;
233 if (type_ == VIDEO) {
234 return ProcessVideo(input, output);
235 }
236 float widthRatio = static_cast<float>(output->GetWidth()) / static_cast<float>(input->GetWidth());
237 float heightRatio = static_cast<float>(output->GetHeight()) / static_cast<float>(input->GetHeight());
238 int targetLevel = EvaluateTargetLevel(input, output, widthRatio, heightRatio);
239 if (targetLevel < DETAIL_ENH_LEVEL_HIGH_AISR &&
240 std::fabs(widthRatio - 1.0f) < EPSILON && std::fabs(heightRatio - 1.0f) < EPSILON) {
241 VPE_LOGI("The current scaling ratio is 1.0, and the algorithm is not AISR, so copy it directly.");
242 return (memcpy_s(output->GetVirAddr(), output->GetSize(), input->GetVirAddr(), input->GetSize()) == EOK) ?
243 VPE_ALGO_ERR_OK : VPE_ALGO_ERR_UNKNOWN;
244 }
245 bool processSuccessfully = false;
246 for (int level = targetLevel; level >= DETAIL_ENH_LEVEL_NONE; level--) {
247 auto algoImpl = GetAlgorithm(level);
248 if (algoImpl == nullptr) {
249 VPE_LOGE("Get Algorithm impl for %{public}d failed!", level);
250 continue;
251 }
252 parameter_.level = static_cast<DetailEnhancerLevel>((level == DETAIL_ENH_LEVEL_HIGH_AISR) ?
253 DETAIL_ENH_LEVEL_HIGH : level); // map level
254 if (algoImpl->SetParameter(parameter_) != VPE_ALGO_ERR_OK) {
255 VPE_LOGE("set parameter failed!");
256 return VPE_ALGO_ERR_UNKNOWN;
257 }
258 UpdateLastAlgorithm(algoImpl);
259 if (ProcessAlgorithm(algoImpl, input, output) == VPE_ALGO_ERR_OK) {
260 processSuccessfully = true;
261 break;
262 } else if (level == DETAIL_ENH_LEVEL_HIGH_AISR) {
263 VPE_LOGD("AISR processed failed, try to process by EVE");
264 } else if (level > DETAIL_ENH_LEVEL_NONE) {
265 VPE_LOGW("Failed to process with level %{public}d", level);
266 } else {
267 VPE_LOGE("Failed to process with detail enhancer");
268 return VPE_ALGO_ERR_UNKNOWN;
269 }
270 }
271 return processSuccessfully ? VPE_ALGO_ERR_OK : VPE_ALGO_ERR_INVALID_VAL;
272 }
273
EnableProtection(bool enable)274 VPEAlgoErrCode DetailEnhancerImageFwk::EnableProtection(bool enable)
275 {
276 std::lock_guard<std::mutex> lock(lock_);
277 VPEAlgoErrCode ret = VPE_ALGO_ERR_OK;
278 for (auto& [level, algo] : algorithms_) {
279 if (algo == nullptr) {
280 VPE_LOGW("Algorithm for level:%{pubcli}d is null!", level);
281 continue;
282 }
283 VPE_LOGD("level:%d enable:%d", level, enable);
284 ret = algo->EnableProtection(enable);
285 if (ret != VPE_ALGO_ERR_OK) {
286 VPE_LOGW("Failed to EnableProtection(%{public}d) for level:%{pubcli}d ret:%{pubcli}d", enable, level, ret);
287 }
288 }
289 enableProtection_ = enable;
290 return ret;
291 }
292
ResetProtectionStatus()293 VPEAlgoErrCode DetailEnhancerImageFwk::ResetProtectionStatus()
294 {
295 std::lock_guard<std::mutex> lock(lock_);
296 if (lastAlgorithm_ == nullptr) {
297 return VPE_ALGO_ERR_OK;
298 }
299 return lastAlgorithm_->ResetProtectionStatus();
300 }
301
UpdateLastAlgorithm(const std::shared_ptr<DetailEnhancerBase> & algorithm)302 void DetailEnhancerImageFwk::UpdateLastAlgorithm(const std::shared_ptr<DetailEnhancerBase>& algorithm)
303 {
304 std::lock_guard<std::mutex> lock(lock_);
305 lastAlgorithm_ = algorithm;
306 }
307
Clear()308 void DetailEnhancerImageFwk::Clear()
309 {
310 std::lock_guard<std::mutex> lock(lock_);
311 lastAlgorithm_ = nullptr;
312 algorithms_.clear();
313 if (hasParameter_) {
314 parameterUpdated = true;
315 }
316 }
317
ProcessAlgorithm(const std::shared_ptr<DetailEnhancerBase> & algo,const sptr<SurfaceBuffer> & input,const sptr<SurfaceBuffer> & output)318 VPEAlgoErrCode DetailEnhancerImageFwk::ProcessAlgorithm(const std::shared_ptr<DetailEnhancerBase>& algo,
319 const sptr<SurfaceBuffer>& input, const sptr<SurfaceBuffer>& output)
320 {
321 CHECK_AND_RETURN_RET_LOG(algo != nullptr, VPE_ALGO_ERR_INVALID_VAL, "Invalid input: algorithm is null!");
322 auto err = algo->Process(input, output);
323 if (static_cast<VPEAlgoErrExCode>(err) == VPE_ALGO_ERR_INVALID_CLIENT_ID) {
324 VPE_LOGD("needRestore_ = true");
325 std::lock_guard<std::mutex> lock(restoreLock_);
326 needRestore_ = true;
327 }
328 return err;
329 }
330
DetailEnhancerCreate(int32_t * instance)331 int32_t DetailEnhancerCreate(int32_t* instance)
332 {
333 CHECK_AND_RETURN_RET_LOG(g_externLock.try_lock_for(std::chrono::milliseconds(TIMEOUT_THRESHOLD)),
334 VPE_ALGO_ERR_INVALID_VAL, "get lock timeout");
335 if (instance == nullptr) {
336 VPE_LOGE("invalid instance");
337 g_externLock.unlock();
338 return VPE_ALGO_ERR_INVALID_VAL;
339 }
340 if (g_instanceId != -1) {
341 // if there is an instance, return it
342 *instance = g_instanceId;
343 g_externLock.unlock();
344 return VPE_ALGO_ERR_OK;
345 }
346 auto detailEnh = DetailEnhancerImage::Create();
347 if (detailEnh == nullptr) {
348 VPE_LOGE("cannot create instance");
349 g_externLock.unlock();
350 return VPE_ALGO_ERR_INVALID_VAL;
351 }
352 Extension::ExtensionManager::InstanceVariableType instanceVar { detailEnh };
353 int32_t newId = Extension::ExtensionManager::GetInstance().NewInstanceId(instanceVar);
354 if (newId == -1) {
355 VPE_LOGE("cannot create more instance");
356 g_externLock.unlock();
357 return VPE_ALGO_ERR_NO_MEMORY;
358 }
359 *instance = newId;
360 g_instanceId = newId;
361 g_externLock.unlock();
362 return VPE_ALGO_ERR_OK;
363 }
364
CreateSurfaceBufFromNativeWindow(OHNativeWindowBuffer * image)365 sptr<SurfaceBuffer> CreateSurfaceBufFromNativeWindow(OHNativeWindowBuffer* image)
366 {
367 OH_NativeBuffer* imageNativeBuffer = nullptr;
368 CHECK_AND_RETURN_RET_LOG(OH_NativeBuffer_FromNativeWindowBuffer(image, &imageNativeBuffer) == GSERROR_OK,
369 nullptr, "invalid input or output image");
370 sptr<SurfaceBuffer> imageSurfaceBuffer(SurfaceBuffer::NativeBufferToSurfaceBuffer(imageNativeBuffer));
371 return imageSurfaceBuffer;
372 }
373
DetailEnhancerProcessImage(int32_t instance,OHNativeWindowBuffer * inputImage,OHNativeWindowBuffer * outputImage,int32_t level)374 int32_t DetailEnhancerProcessImage(int32_t instance, OHNativeWindowBuffer* inputImage,
375 OHNativeWindowBuffer* outputImage, int32_t level)
376 {
377 CHECK_AND_RETURN_RET_LOG(g_externLock.try_lock_for(std::chrono::milliseconds(TIMEOUT_THRESHOLD)),
378 VPE_ALGO_ERR_INVALID_VAL, "get lock timeout");
379 if (inputImage == nullptr || outputImage == nullptr) {
380 VPE_LOGE("invalid parameters");
381 g_externLock.unlock();
382 return VPE_ALGO_ERR_INVALID_VAL;
383 }
384 auto someInstance = Extension::ExtensionManager::GetInstance().GetInstance(instance);
385 if (someInstance == std::nullopt) {
386 VPE_LOGE("invalid instance");
387 g_externLock.unlock();
388 return VPE_ALGO_ERR_INVALID_VAL;
389 }
390 VPEAlgoErrCode ret = VPE_ALGO_ERR_INVALID_VAL;
391 auto visitFunc = [inputImage, outputImage, &ret, &level](auto&& var) {
392 using VarType = std::decay_t<decltype(var)>;
393 if constexpr (std::is_same_v<VarType, std::shared_ptr<DetailEnhancerImage>>) {
394 sptr<SurfaceBuffer> inputImageSurfaceBuffer = CreateSurfaceBufFromNativeWindow(inputImage);
395 sptr<SurfaceBuffer> outputImageSurfaceBuffer = CreateSurfaceBufFromNativeWindow(outputImage);
396 DetailEnhancerParameters param {
397 .uri = "",
398 .level = static_cast<DetailEnhancerLevel>(level),
399 };
400 var->SetParameter(param);
401 ret = var->Process(inputImageSurfaceBuffer, outputImageSurfaceBuffer);
402 } else {
403 VPE_LOGE("instance may be miss used");
404 }
405 };
406 std::visit(visitFunc, *someInstance);
407 g_externLock.unlock();
408 return ret;
409 }
410
DetailEnhancerDestroy(int32_t * instance)411 int32_t DetailEnhancerDestroy(int32_t* instance)
412 {
413 CHECK_AND_RETURN_RET_LOG(instance != nullptr, VPE_ALGO_ERR_INVALID_VAL, "instance is null");
414 int ret = Extension::ExtensionManager::GetInstance().RemoveInstanceReference(*instance);
415 if (ret == VPE_ALGO_ERR_OK) {
416 g_instanceId = -1;
417 }
418 return ret;
419 }
420 } // namespace VideoProcessingEngine
421 } // namespace Media
422 } // namespace OHOS
423