1 /*
2 * Copyright (C) 2021 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 "image_napi.h"
17 #include "media_errors.h"
18 #include "hilog/log.h"
19 #include "image_napi_utils.h"
20
21 using OHOS::HiviewDFX::HiLog;
22 using std::string;
23 using std::shared_ptr;
24 using std::unique_ptr;
25 using std::vector;
26 using std::make_shared;
27 using std::make_unique;
28
29 namespace {
30 constexpr OHOS::HiviewDFX::HiLogLabel LABEL = {LOG_CORE, LOG_DOMAIN, "ImageNapi"};
31 }
32
33 namespace OHOS {
34 namespace Media {
35 static const std::string CLASS_NAME = "ImageNapi";
36 std::shared_ptr<ImageReceiver> ImageNapi::staticImageReceiverInstance_ = nullptr;
37 sptr<SurfaceBuffer> ImageNapi::staticInstance_ = nullptr;
38 thread_local napi_ref ImageNapi::sConstructor_ = nullptr;
39
40 const int ARGS0 = 0;
41 const int ARGS1 = 1;
42 const int ARGS2 = 2;
43 const int PARAM0 = 0;
44 const int PARAM1 = 1;
45 const int PARAM2 = 2;
46 const int NUM0 = 0;
47
ImageNapi()48 ImageNapi::ImageNapi()
49 :env_(nullptr), wrapper_(nullptr)
50 {}
51
~ImageNapi()52 ImageNapi::~ImageNapi()
53 {
54 NativeRelease();
55 if (wrapper_ != nullptr) {
56 napi_delete_reference(env_, wrapper_);
57 }
58 }
59
CommonCallbackRoutine(napi_env env,ImageAsyncContext * & context,const napi_value & valueParam)60 static void CommonCallbackRoutine(napi_env env, ImageAsyncContext* &context,
61 const napi_value &valueParam)
62 {
63 IMAGE_FUNCTION_IN();
64 napi_value result[2] = {0};
65 napi_value retVal;
66 napi_value callback = nullptr;
67
68 napi_get_undefined(env, &result[0]);
69 napi_get_undefined(env, &result[1]);
70
71 if (context->status == SUCCESS) {
72 result[1] = valueParam;
73 }
74
75 if (context->deferred) {
76 if (context->status == SUCCESS) {
77 napi_resolve_deferred(env, context->deferred, result[1]);
78 } else {
79 napi_reject_deferred(env, context->deferred, result[0]);
80 }
81 } else {
82 napi_create_uint32(env, context->status, &result[0]);
83 napi_get_reference_value(env, context->callbackRef, &callback);
84 napi_call_function(env, nullptr, callback, PARAM2, result, &retVal);
85 napi_delete_reference(env, context->callbackRef);
86 }
87
88 napi_delete_async_work(env, context->work);
89
90 delete context;
91 context = nullptr;
92 IMAGE_FUNCTION_OUT();
93 }
94
NativeRelease()95 void ImageNapi::NativeRelease()
96 {
97 if (imageReceiver_ != nullptr) {
98 imageReceiver_->ReleaseBuffer(sSurfaceBuffer_);
99 imageReceiver_ = nullptr;
100 }
101 if (sSurfaceBuffer_ != nullptr) {
102 sSurfaceBuffer_ = nullptr;
103 }
104 }
105
Init(napi_env env,napi_value exports)106 napi_value ImageNapi::Init(napi_env env, napi_value exports)
107 {
108 IMAGE_FUNCTION_IN();
109 napi_property_descriptor props[] = {
110 DECLARE_NAPI_GETTER("clipRect", JSGetClipRect),
111 DECLARE_NAPI_GETTER("size", JsGetSize),
112 DECLARE_NAPI_GETTER("format", JsGetFormat),
113 DECLARE_NAPI_FUNCTION("getComponent", JsGetComponent),
114 DECLARE_NAPI_FUNCTION("release", JsRelease),
115 };
116 napi_value constructor = nullptr;
117
118 IMG_NAPI_CHECK_RET_D(IMG_IS_OK(
119 napi_define_class(env, CLASS_NAME.c_str(), NAPI_AUTO_LENGTH, Constructor,
120 nullptr, IMG_ARRAY_SIZE(props), props, &constructor)),
121 nullptr,
122 IMAGE_ERR("define class fail")
123 );
124
125 IMG_NAPI_CHECK_RET_D(IMG_IS_OK(
126 napi_create_reference(env, constructor, 1, &sConstructor_)),
127 nullptr,
128 IMAGE_ERR("create reference fail")
129 );
130
131 IMG_NAPI_CHECK_RET_D(IMG_IS_OK(
132 napi_set_named_property(env, exports, CLASS_NAME.c_str(), constructor)),
133 nullptr,
134 IMAGE_ERR("set named property fail")
135 );
136
137 IMAGE_DEBUG("Init success");
138
139 IMAGE_FUNCTION_OUT();
140 return exports;
141 }
142
Constructor(napi_env env,napi_callback_info info)143 napi_value ImageNapi::Constructor(napi_env env, napi_callback_info info)
144 {
145 napi_value undefineVar = nullptr;
146 napi_get_undefined(env, &undefineVar);
147
148 napi_status status;
149 napi_value thisVar = nullptr;
150
151 IMAGE_FUNCTION_IN();
152 status = napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr);
153 if (status == napi_ok && thisVar != nullptr) {
154 std::unique_ptr<ImageNapi> reference = std::make_unique<ImageNapi>();
155 if (reference != nullptr) {
156 reference->env_ = env;
157 reference->sSurfaceBuffer_ = staticInstance_;
158 reference->imageReceiver_ = staticImageReceiverInstance_;
159 staticImageReceiverInstance_ = nullptr;
160 status = napi_wrap(env, thisVar, reinterpret_cast<void *>(reference.get()),
161 ImageNapi::Destructor, nullptr, &(reference->wrapper_));
162 if (status == napi_ok) {
163 IMAGE_FUNCTION_OUT();
164 reference.release();
165 return thisVar;
166 } else {
167 IMAGE_ERR("Failure wrapping js to native napi");
168 }
169 }
170 }
171
172 return undefineVar;
173 }
174
Destructor(napi_env env,void * nativeObject,void * finalize)175 void ImageNapi::Destructor(napi_env env, void *nativeObject, void *finalize)
176 {
177 ImageNapi *pImageNapi = reinterpret_cast<ImageNapi*>(nativeObject);
178
179 if (IMG_NOT_NULL(pImageNapi)) {
180 pImageNapi->~ImageNapi();
181 }
182 }
183
Create(napi_env env,sptr<SurfaceBuffer> surfaceBuffer,std::shared_ptr<ImageReceiver> imageReceiver)184 napi_value ImageNapi::Create(napi_env env, sptr<SurfaceBuffer> surfaceBuffer,
185 std::shared_ptr<ImageReceiver> imageReceiver)
186 {
187 napi_status status;
188 napi_value constructor = nullptr, result = nullptr;
189
190 IMAGE_FUNCTION_IN();
191 if (surfaceBuffer == nullptr) {
192 IMAGE_ERR("surfaceBuffer is nullptr");
193 return result;
194 }
195
196 napi_get_undefined(env, &result);
197
198 status = napi_get_reference_value(env, sConstructor_, &constructor);
199 if (IMG_IS_OK(status)) {
200 staticInstance_ = surfaceBuffer;
201 staticImageReceiverInstance_ = imageReceiver;
202 status = napi_new_instance(env, constructor, 0, nullptr, &result);
203 if (status == napi_ok) {
204 IMAGE_FUNCTION_OUT();
205 return result;
206 } else {
207 IMAGE_ERR("New instance could not be obtained");
208 }
209 }
210
211 IMAGE_ERR("Failed to get reference of constructor");
212 return result;
213 }
214
UnwarpContext(napi_env env,napi_callback_info info)215 unique_ptr<ImageAsyncContext> ImageNapi::UnwarpContext(napi_env env, napi_callback_info info)
216 {
217 napi_status status;
218 napi_value thisVar = nullptr;
219 size_t argc = ARGS0;
220
221 IMAGE_FUNCTION_IN();
222
223 status = napi_get_cb_info(env, info, &argc, nullptr, &thisVar, nullptr);
224 if (status != napi_ok) {
225 IMAGE_ERR("fail to napi_get_cb_info %{public}d", status);
226 return nullptr;
227 }
228
229 unique_ptr<ImageAsyncContext> context = make_unique<ImageAsyncContext>();
230 status = napi_unwrap(env, thisVar, reinterpret_cast<void**>(&context->constructor_));
231 if (status != napi_ok || context->constructor_ == nullptr) {
232 IMAGE_ERR("fail to unwrap constructor_ %{public}d", status);
233 return nullptr;
234 }
235 return context;
236 }
237
BuildIntProperty(napi_env env,const std::string name,int32_t val,napi_value result)238 static void BuildIntProperty(napi_env env, const std::string name,
239 int32_t val, napi_value result)
240 {
241 napi_value nVal;
242 napi_create_int32(env, val, &nVal);
243 napi_set_named_property(env, result, name.c_str(), nVal);
244 }
245
BuildJsSize(napi_env env,int32_t width,int32_t height)246 static napi_value BuildJsSize(napi_env env, int32_t width, int32_t height)
247 {
248 napi_value result = nullptr;
249
250 napi_create_object(env, &result);
251
252 BuildIntProperty(env, "width", width, result);
253 BuildIntProperty(env, "height", height, result);
254 return result;
255 }
256
BuildJsRegion(napi_env env,int32_t width,int32_t height,int32_t x,int32_t y)257 static napi_value BuildJsRegion(napi_env env, int32_t width,
258 int32_t height, int32_t x, int32_t y)
259 {
260 napi_value result = nullptr;
261
262 napi_create_object(env, &result);
263
264 napi_set_named_property(env, result, "size", BuildJsSize(env, width, height));
265
266 BuildIntProperty(env, "x", x, result);
267 BuildIntProperty(env, "y", y, result);
268 return result;
269 }
270
JSGetClipRect(napi_env env,napi_callback_info info)271 napi_value ImageNapi::JSGetClipRect(napi_env env, napi_callback_info info)
272 {
273 napi_value result = nullptr;
274 unique_ptr<ImageAsyncContext> context;
275
276 IMAGE_FUNCTION_IN();
277 napi_get_undefined(env, &result);
278 context = UnwarpContext(env, info);
279 if (context == nullptr) {
280 return result;
281 }
282
283 if (context->constructor_ == nullptr) {
284 IMAGE_ERR("Image context is nullptr");
285 return result;
286 }
287 auto surfaceBuffer = context->constructor_->sSurfaceBuffer_;
288
289 if (surfaceBuffer == nullptr) {
290 IMAGE_ERR("Image surface buffer is nullptr");
291 return result;
292 }
293
294 return BuildJsRegion(env, surfaceBuffer->GetWidth(), surfaceBuffer->GetHeight(), NUM0, NUM0);
295 }
296
JsGetSize(napi_env env,napi_callback_info info)297 napi_value ImageNapi::JsGetSize(napi_env env, napi_callback_info info)
298 {
299 napi_value result = nullptr;
300 unique_ptr<ImageAsyncContext> context;
301
302 IMAGE_FUNCTION_IN();
303 napi_get_undefined(env, &result);
304 context = UnwarpContext(env, info);
305 if (context == nullptr) {
306 return result;
307 }
308
309 if (context->constructor_ == nullptr) {
310 IMAGE_ERR("Image context is nullptr");
311 return result;
312 }
313 auto surfaceBuffer = context->constructor_->sSurfaceBuffer_;
314
315 if (surfaceBuffer == nullptr) {
316 IMAGE_ERR("Image surface buffer is nullptr");
317 return result;
318 }
319
320 return BuildJsSize(env, surfaceBuffer->GetWidth(), surfaceBuffer->GetHeight());
321 }
322
JsGetFormat(napi_env env,napi_callback_info info)323 napi_value ImageNapi::JsGetFormat(napi_env env, napi_callback_info info)
324 {
325 napi_value result = nullptr;
326 unique_ptr<ImageAsyncContext> context;
327
328 IMAGE_FUNCTION_IN();
329 napi_get_undefined(env, &result);
330 context = UnwarpContext(env, info);
331 if (context == nullptr) {
332 return result;
333 }
334
335 if (context->constructor_ == nullptr) {
336 IMAGE_ERR("Image context is nullptr");
337 return result;
338 }
339 auto surfaceBuffer = context->constructor_->sSurfaceBuffer_;
340
341 if (surfaceBuffer == nullptr) {
342 IMAGE_ERR("Image surface buffer is nullptr");
343 return result;
344 }
345
346 napi_create_int32(env, surfaceBuffer->GetFormat(), &result);
347 return result;
348 }
349
JSReleaseCallBack(napi_env env,napi_status status,ImageAsyncContext * context)350 static void JSReleaseCallBack(napi_env env, napi_status status,
351 ImageAsyncContext* context)
352 {
353 IMAGE_FUNCTION_IN();
354 napi_value result = nullptr;
355 napi_get_undefined(env, &result);
356
357 context->constructor_->NativeRelease();
358 context->status = SUCCESS;
359
360 IMAGE_FUNCTION_OUT();
361 CommonCallbackRoutine(env, context, result);
362 }
363
JsRelease(napi_env env,napi_callback_info info)364 napi_value ImageNapi::JsRelease(napi_env env, napi_callback_info info)
365 {
366 IMAGE_FUNCTION_IN();
367 napi_status status;
368 napi_value result = nullptr, thisVar = nullptr;
369 size_t argc = ARGS1;
370 napi_value argv[ARGS1] = {0};
371 int32_t refCount = 1;
372
373 napi_get_undefined(env, &result);
374
375 status = napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr);
376 if (status != napi_ok) {
377 IMAGE_ERR("fail to napi_get_cb_info %{public}d", status);
378 return result;
379 }
380
381 unique_ptr<ImageAsyncContext> context = UnwarpContext(env, info);
382 if (context == nullptr) {
383 IMAGE_ERR("fail to unwrap constructor_ %{public}d", status);
384 return result;
385 }
386
387 if (argc == ARGS1) {
388 auto argType = ImageNapiUtils::getType(env, argv[PARAM0]);
389 if (argType == napi_function) {
390 napi_create_reference(env, argv[PARAM0], refCount, &context->callbackRef);
391 } else {
392 IMAGE_ERR("Unsupport arg 0 type: %{public}d", argType);
393 return result;
394 }
395 }
396
397 if (context->callbackRef == nullptr) {
398 napi_create_promise(env, &(context->deferred), &result);
399 } else {
400 napi_get_undefined(env, &result);
401 }
402
403 napi_value resource = nullptr;
404 napi_create_string_utf8(env, "JsRelease", NAPI_AUTO_LENGTH, &resource);
405 status = napi_create_async_work(
406 env, nullptr, resource, [](napi_env env, void* data) {},
407 reinterpret_cast<napi_async_complete_callback>(JSReleaseCallBack),
408 static_cast<void *>(context.get()), &(context->work));
409 if (status != napi_ok) {
410 IMAGE_ERR("fail to create async work %{public}d", status);
411 return result;
412 }
413
414 status = napi_queue_async_work(env, context->work);
415 if (status != napi_ok) {
416 IMAGE_ERR("fail to queue async work %{public}d", status);
417 return result;
418 }
419
420 context.release();
421
422 IMAGE_FUNCTION_OUT();
423 return result;
424 }
425
JsGetComponentCallBack(napi_env env,napi_status status,ImageAsyncContext * context)426 void ImageNapi::JsGetComponentCallBack(napi_env env, napi_status status,
427 ImageAsyncContext* context)
428 {
429 IMAGE_FUNCTION_IN();
430 napi_value result = nullptr;
431
432 napi_create_object(env, &result);
433
434 uint32_t bufferSize = context->constructor_->sSurfaceBuffer_->GetSize();
435 void *buffer = context->constructor_->sSurfaceBuffer_->GetVirAddr();
436
437 napi_value array;
438 if (!ImageNapiUtils::CreateArrayBuffer(env, buffer, bufferSize, &array)) {
439 context->status = ERROR;
440 HiLog::Error(LABEL, "napi_create_arraybuffer failed!");
441 napi_get_undefined(env, &result);
442 } else {
443 context->status = SUCCESS;
444 napi_set_named_property(env, result, "byteBuffer", array);
445 }
446
447 BuildIntProperty(env, "componentType", context->componentType, result);
448 BuildIntProperty(env, "rowStride", 0, result);
449 BuildIntProperty(env, "pixelStride", 0, result);
450
451 IMAGE_FUNCTION_OUT();
452 CommonCallbackRoutine(env, context, result);
453 }
454
JsGetComponentArgs(napi_env env,size_t argc,napi_value * argv,int32_t * componentType,napi_ref * callbackRef)455 static bool JsGetComponentArgs(napi_env env, size_t argc, napi_value* argv,
456 int32_t* componentType, napi_ref* callbackRef)
457 {
458 int32_t refCount = 1;
459 if (argc == ARGS1 || argc == ARGS2) {
460 auto argType = ImageNapiUtils::getType(env, argv[PARAM0]);
461 if (argType == napi_number) {
462 napi_get_value_int32(env, argv[PARAM0], componentType);
463 } else {
464 IMAGE_ERR("Unsupport arg 0 type: %{public}d", argType);
465 return false;
466 }
467 }
468
469 if (argc == ARGS2) {
470 auto argType = ImageNapiUtils::getType(env, argv[PARAM1]);
471 if (argType == napi_function) {
472 napi_create_reference(env, argv[PARAM1], refCount, callbackRef);
473 } else {
474 IMAGE_ERR("Unsupport arg 1 type: %{public}d", argType);
475 return false;
476 }
477 }
478 return true;
479 }
480
JsGetComponent(napi_env env,napi_callback_info info)481 napi_value ImageNapi::JsGetComponent(napi_env env, napi_callback_info info)
482 {
483 IMAGE_FUNCTION_IN();
484 napi_status status;
485 napi_value result = nullptr, thisVar = nullptr;
486 size_t argc = ARGS2;
487 napi_value argv[ARGS2] = {0};
488
489 napi_get_undefined(env, &result);
490
491 status = napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr);
492 if (status != napi_ok) {
493 IMAGE_ERR("fail to napi_get_cb_info %{public}d", status);
494 return result;
495 }
496
497 unique_ptr<ImageAsyncContext> context = UnwarpContext(env, info);
498 if (context == nullptr) {
499 IMAGE_ERR("fail to unwrap constructor_ %{public}d", status);
500 return result;
501 }
502
503 if (!JsGetComponentArgs(env, argc, argv,
504 &(context->componentType),
505 &(context->callbackRef))) {
506 return result;
507 }
508
509 if (context->callbackRef == nullptr) {
510 napi_create_promise(env, &(context->deferred), &result);
511 } else {
512 napi_get_undefined(env, &result);
513 }
514
515 napi_value resource = nullptr;
516 napi_create_string_utf8(env, "JsGetComponent", NAPI_AUTO_LENGTH, &resource);
517 status = napi_create_async_work(
518 env, nullptr, resource, [](napi_env env, void* data) {},
519 reinterpret_cast<napi_async_complete_callback>(JsGetComponentCallBack),
520 static_cast<void *>(context.get()), &(context->work));
521 if (status != napi_ok) {
522 IMAGE_ERR("fail to create async work %{public}d", status);
523 return result;
524 }
525
526 status = napi_queue_async_work(env, context->work);
527 if (status != napi_ok) {
528 IMAGE_ERR("fail to queue async work %{public}d", status);
529 return result;
530 }
531 context.release();
532
533 IMAGE_FUNCTION_OUT();
534 return result;
535 }
536 } // namespace Media
537 } // namespace OHOS
538