• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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