1 /*
2 * Copyright (c) 2021-2022 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 "input/camera_input_napi.h"
17 #include <uv.h>
18 #include "input/camera_info_napi.h"
19
20 namespace OHOS {
21 namespace CameraStandard {
22 using namespace std;
23 using OHOS::HiviewDFX::HiLog;
24 using OHOS::HiviewDFX::HiLogLabel;
25
26 namespace {
27 constexpr HiLogLabel LABEL = {LOG_CORE, LOG_DOMAIN, "CameraNapi"};
28 }
29
30 thread_local napi_ref CameraInputNapi::sConstructor_ = nullptr;
31 thread_local sptr<CameraInput> CameraInputNapi::sCameraInput_ = nullptr;
32 thread_local uint32_t CameraInputNapi::cameraInputTaskId = CAMERA_INPUT_TASKID;
33
OnErrorCallbackAsync(const int32_t errorType,const int32_t errorMsg) const34 void ErrorCallbackListener::OnErrorCallbackAsync(const int32_t errorType, const int32_t errorMsg) const
35 {
36 MEDIA_DEBUG_LOG("OnErrorCallbackAsync is called");
37 uv_loop_s* loop = nullptr;
38 napi_get_uv_event_loop(env_, &loop);
39 if (!loop) {
40 MEDIA_ERR_LOG("failed to get event loop");
41 return;
42 }
43 uv_work_t* work = new(std::nothrow) uv_work_t;
44 if (!work) {
45 MEDIA_ERR_LOG("failed to allocate work");
46 return;
47 }
48 std::unique_ptr<ErrorCallbackInfo> callbackInfo = std::make_unique<ErrorCallbackInfo>(errorType, errorMsg, this);
49 work->data = callbackInfo.get();
50 int ret = uv_queue_work(loop, work, [] (uv_work_t* work) {}, [] (uv_work_t* work, int status) {
51 ErrorCallbackInfo* callbackInfo = reinterpret_cast<ErrorCallbackInfo *>(work->data);
52 if (callbackInfo) {
53 callbackInfo->listener_->OnErrorCallback(callbackInfo->errorType_, callbackInfo->errorMsg_);
54 delete callbackInfo;
55 }
56 delete work;
57 });
58 if (ret) {
59 MEDIA_ERR_LOG("failed to execute work");
60 delete work;
61 } else {
62 callbackInfo.release();
63 }
64 }
65
OnErrorCallback(const int32_t errorType,const int32_t errorMsg) const66 void ErrorCallbackListener::OnErrorCallback(const int32_t errorType, const int32_t errorMsg) const
67 {
68 MEDIA_DEBUG_LOG("OnErrorCallback is called");
69 napi_value result;
70 napi_value callback = nullptr;
71 napi_value retVal;
72 napi_value propValue;
73 napi_create_int32(env_, errorType, &propValue);
74 napi_create_object(env_, &result);
75 napi_set_named_property(env_, result, "code", propValue);
76 napi_get_reference_value(env_, callbackRef_, &callback);
77 napi_call_function(env_, nullptr, callback, ARGS_ONE, &result, &retVal);
78 }
79
OnError(const int32_t errorType,const int32_t errorMsg) const80 void ErrorCallbackListener::OnError(const int32_t errorType, const int32_t errorMsg) const
81 {
82 MEDIA_DEBUG_LOG("OnError is called!, errorType: %{public}d", errorType);
83 OnErrorCallbackAsync(errorType, errorMsg);
84 }
85
CameraInputNapi()86 CameraInputNapi::CameraInputNapi() : env_(nullptr), wrapper_(nullptr)
87 {
88 }
89
~CameraInputNapi()90 CameraInputNapi::~CameraInputNapi()
91 {
92 MEDIA_INFO_LOG("~CameraInputNapi is called");
93 if (wrapper_ != nullptr) {
94 napi_delete_reference(env_, wrapper_);
95 }
96 if (cameraInput_) {
97 MEDIA_INFO_LOG("~CameraInputNapi cameraInput_ is not null");
98 cameraInput_ = nullptr;
99 }
100 }
101
CameraInputNapiDestructor(napi_env env,void * nativeObject,void * finalize_hint)102 void CameraInputNapi::CameraInputNapiDestructor(napi_env env, void* nativeObject, void* finalize_hint)
103 {
104 MEDIA_INFO_LOG("CameraInputNapiDestructor is called");
105 CameraInputNapi* cameraObj = reinterpret_cast<CameraInputNapi*>(nativeObject);
106 if (cameraObj != nullptr) {
107 delete cameraObj;
108 }
109 }
110
Init(napi_env env,napi_value exports)111 napi_value CameraInputNapi::Init(napi_env env, napi_value exports)
112 {
113 MEDIA_DEBUG_LOG("Init is called");
114 napi_status status;
115 napi_value ctorObj;
116 int32_t refCount = 1;
117
118 // todo: Open and Close in native have not implemented
119 napi_property_descriptor camera_input_props[] = {
120 DECLARE_NAPI_FUNCTION("open", Open),
121 DECLARE_NAPI_FUNCTION("close", Close),
122 DECLARE_NAPI_FUNCTION("release", Release),
123 DECLARE_NAPI_FUNCTION("on", On)
124 };
125
126 status = napi_define_class(env, CAMERA_INPUT_NAPI_CLASS_NAME, NAPI_AUTO_LENGTH,
127 CameraInputNapiConstructor, nullptr,
128 sizeof(camera_input_props) / sizeof(camera_input_props[PARAM0]),
129 camera_input_props, &ctorObj);
130 if (status == napi_ok) {
131 status = napi_create_reference(env, ctorObj, refCount, &sConstructor_);
132 if (status == napi_ok) {
133 status = napi_set_named_property(env, exports, CAMERA_INPUT_NAPI_CLASS_NAME, ctorObj);
134 if (status == napi_ok) {
135 return exports;
136 }
137 }
138 }
139 MEDIA_ERR_LOG("Init call Failed!");
140 return nullptr;
141 }
142
143 // Constructor callback
CameraInputNapiConstructor(napi_env env,napi_callback_info info)144 napi_value CameraInputNapi::CameraInputNapiConstructor(napi_env env, napi_callback_info info)
145 {
146 MEDIA_INFO_LOG("CameraInputNapiConstructor is called");
147 napi_status status;
148 napi_value result = nullptr;
149 napi_value thisVar = nullptr;
150
151 napi_get_undefined(env, &result);
152 CAMERA_NAPI_GET_JS_OBJ_WITH_ZERO_ARGS(env, info, status, thisVar);
153
154 if (status == napi_ok && thisVar != nullptr) {
155 std::unique_ptr<CameraInputNapi> obj = std::make_unique<CameraInputNapi>();
156 obj->env_ = env;
157 obj->cameraInput_ = sCameraInput_;
158 status = napi_wrap(env, thisVar, reinterpret_cast<void*>(obj.get()),
159 CameraInputNapi::CameraInputNapiDestructor, nullptr, nullptr);
160 if (status == napi_ok) {
161 obj.release();
162 return thisVar;
163 } else {
164 MEDIA_ERR_LOG("Failure wrapping js to native napi");
165 }
166 }
167 MEDIA_ERR_LOG("CameraInputNapiConstructor call Failed!");
168 return result;
169 }
170
CreateCameraInput(napi_env env,sptr<CameraInput> cameraInput)171 napi_value CameraInputNapi::CreateCameraInput(napi_env env, sptr<CameraInput> cameraInput)
172 {
173 MEDIA_INFO_LOG("CreateCameraInput is called");
174 CAMERA_SYNC_TRACE;
175 napi_status status;
176 napi_value result = nullptr;
177 napi_value constructor;
178 if (cameraInput == nullptr) {
179 return result;
180 }
181 status = napi_get_reference_value(env, sConstructor_, &constructor);
182 if (status == napi_ok) {
183 sCameraInput_ = cameraInput;
184 status = napi_new_instance(env, constructor, 0, nullptr, &result);
185 if (status == napi_ok && result != nullptr) {
186 return result;
187 } else {
188 MEDIA_ERR_LOG("Failed to create Camera input instance");
189 }
190 }
191 napi_get_undefined(env, &result);
192 MEDIA_ERR_LOG("CreateCameraInput call Failed!");
193 return result;
194 }
195
GetCameraInput()196 sptr<CameraInput> CameraInputNapi::GetCameraInput()
197 {
198 return cameraInput_;
199 }
200
CommonCompleteCallback(napi_env env,napi_status status,void * data)201 void CommonCompleteCallback(napi_env env, napi_status status, void* data)
202 {
203 MEDIA_DEBUG_LOG("CommonCompleteCallback is called");
204 auto context = static_cast<CameraInputAsyncContext*>(data);
205
206 if (context == nullptr) {
207 MEDIA_ERR_LOG("Async context is null");
208 return;
209 }
210
211 std::unique_ptr<JSAsyncContextOutput> jsContext = std::make_unique<JSAsyncContextOutput>();
212
213 MEDIA_INFO_LOG("%{public}s, modeForAsync = %{public}d, status = %{public}d",
214 context->funcName.c_str(), context->modeForAsync, context->status);
215 switch (context->modeForAsync) {
216 case OPEN_ASYNC_CALLBACK:
217 if (context->objectInfo && context->objectInfo->GetCameraInput()) {
218 context->errorCode = context->objectInfo->GetCameraInput()->Open();
219 context->status = context->errorCode == 0;
220 jsContext->status = context->status;
221 }
222 break;
223 case CLOSE_ASYNC_CALLBACK:
224 if (context->objectInfo && context->objectInfo->GetCameraInput()) {
225 context->errorCode = context->objectInfo->GetCameraInput()->Close();
226 context->status = context->errorCode == 0;
227 jsContext->status = context->status;
228 }
229 break;
230 case RELEASE_ASYNC_CALLBACK:
231 if (context->objectInfo && context->objectInfo->GetCameraInput()) {
232 context->objectInfo->GetCameraInput()->Release();
233 jsContext->status = context->status;
234 }
235 break;
236 default:
237 break;
238 }
239
240 if (!context->status) {
241 CameraNapiUtils::CreateNapiErrorObject(env, context->errorCode, context->errorMsg.c_str(), jsContext);
242 } else {
243 napi_get_undefined(env, &jsContext->data);
244 }
245
246 if (!context->funcName.empty() && context->taskId > 0) {
247 // Finish async trace
248 CAMERA_FINISH_ASYNC_TRACE(context->funcName, context->taskId);
249 jsContext->funcName = context->funcName.c_str();
250 }
251
252 if (context->work != nullptr) {
253 CameraNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef, context->work, *jsContext);
254 }
255 delete context;
256 }
257
Open(napi_env env,napi_callback_info info)258 napi_value CameraInputNapi::Open(napi_env env, napi_callback_info info)
259 {
260 MEDIA_INFO_LOG("Open is called");
261 napi_status status;
262 napi_value result = nullptr;
263 const int32_t refCount = 1;
264 napi_value resource = nullptr;
265 size_t argc = ARGS_ONE;
266 napi_value argv[ARGS_ONE] = {0};
267 napi_value thisVar = nullptr;
268
269 CAMERA_NAPI_GET_JS_ARGS(env, info, argc, argv, thisVar);
270 NAPI_ASSERT(env, argc <= ARGS_ONE, "requires 1 parameter maximum");
271 napi_get_boolean(env, true, &result);
272 CAMERA_NAPI_CHECK_NULL_PTR_RETURN_UNDEFINED(env, result, result, "Failed to obtain arguments");
273 std::unique_ptr<CameraInputAsyncContext> asyncContext = std::make_unique<CameraInputAsyncContext>();
274 status = napi_unwrap(env, thisVar, reinterpret_cast<void**>(&asyncContext->objectInfo));
275 if (status == napi_ok && asyncContext->objectInfo != nullptr) {
276 if (argc == ARGS_ONE) {
277 CAMERA_NAPI_GET_JS_ASYNC_CB_REF(env, argv[PARAM0], refCount, asyncContext->callbackRef);
278 }
279
280 CAMERA_NAPI_CREATE_PROMISE(env, asyncContext->callbackRef, asyncContext->deferred, result);
281 CAMERA_NAPI_CREATE_RESOURCE_NAME(env, resource, "Open");
282
283 status = napi_create_async_work(
284 env, nullptr, resource, [](napi_env env, void* data) {
285 auto context = static_cast<CameraInputAsyncContext*>(data);
286 context->status = false;
287 // Start async trace
288 context->funcName = "CameraInputNapi::Open";
289 context->taskId = CameraNapiUtils::IncreamentAndGet(cameraInputTaskId);
290 CAMERA_START_ASYNC_TRACE(context->funcName, context->taskId);
291 if (context->objectInfo != nullptr) {
292 context->status = true;
293 context->modeForAsync = OPEN_ASYNC_CALLBACK;
294 }
295 },
296 CommonCompleteCallback, static_cast<void*>(asyncContext.get()), &asyncContext->work);
297 if (status != napi_ok) {
298 MEDIA_ERR_LOG("Failed to create napi_create_async_work for CameraInputNapi::Open");
299 if (asyncContext->callbackRef != nullptr) {
300 napi_delete_reference(env, asyncContext->callbackRef);
301 }
302 napi_get_undefined(env, &result);
303 } else {
304 napi_queue_async_work(env, asyncContext->work);
305 asyncContext.release();
306 }
307 } else {
308 MEDIA_ERR_LOG("Open call Failed!");
309 }
310 return result;
311 }
312
Close(napi_env env,napi_callback_info info)313 napi_value CameraInputNapi::Close(napi_env env, napi_callback_info info)
314 {
315 MEDIA_INFO_LOG("Close is called");
316 napi_status status;
317 napi_value result = nullptr;
318 const int32_t refCount = 1;
319 napi_value resource = nullptr;
320 size_t argc = ARGS_ONE;
321 napi_value argv[ARGS_ONE] = {0};
322 napi_value thisVar = nullptr;
323
324 CAMERA_NAPI_GET_JS_ARGS(env, info, argc, argv, thisVar);
325 NAPI_ASSERT(env, argc <= ARGS_ONE, "requires 1 parameter maximum");
326 napi_get_boolean(env, true, &result);
327 CAMERA_NAPI_CHECK_NULL_PTR_RETURN_UNDEFINED(env, result, result, "Failed to obtain arguments");
328 std::unique_ptr<CameraInputAsyncContext> asyncContext = std::make_unique<CameraInputAsyncContext>();
329 status = napi_unwrap(env, thisVar, reinterpret_cast<void**>(&asyncContext->objectInfo));
330 if (status == napi_ok && asyncContext->objectInfo != nullptr) {
331 if (argc == ARGS_ONE) {
332 CAMERA_NAPI_GET_JS_ASYNC_CB_REF(env, argv[PARAM0], refCount, asyncContext->callbackRef);
333 }
334
335 CAMERA_NAPI_CREATE_PROMISE(env, asyncContext->callbackRef, asyncContext->deferred, result);
336 CAMERA_NAPI_CREATE_RESOURCE_NAME(env, resource, "Close");
337
338 status = napi_create_async_work(
339 env, nullptr, resource, [](napi_env env, void* data) {
340 auto context = static_cast<CameraInputAsyncContext*>(data);
341 context->status = false;
342 // Start async trace
343 context->funcName = "CameraInputNapi::Close";
344 context->taskId = CameraNapiUtils::IncreamentAndGet(cameraInputTaskId);
345 CAMERA_START_ASYNC_TRACE(context->funcName, context->taskId);
346 if (context->objectInfo != nullptr) {
347 context->status = true;
348 context->modeForAsync = CLOSE_ASYNC_CALLBACK;
349 }
350 },
351 CommonCompleteCallback, static_cast<void*>(asyncContext.get()), &asyncContext->work);
352 if (status != napi_ok) {
353 MEDIA_ERR_LOG("Failed to create napi_create_async_work for CameraInputNapi::Close");
354 if (asyncContext->callbackRef != nullptr) {
355 napi_delete_reference(env, asyncContext->callbackRef);
356 }
357 napi_get_undefined(env, &result);
358 } else {
359 napi_queue_async_work(env, asyncContext->work);
360 asyncContext.release();
361 }
362 } else {
363 MEDIA_ERR_LOG("Close call Failed!");
364 }
365 return result;
366 }
367
Release(napi_env env,napi_callback_info info)368 napi_value CameraInputNapi::Release(napi_env env, napi_callback_info info)
369 {
370 MEDIA_INFO_LOG("Release is called");
371 napi_status status;
372 napi_value result = nullptr;
373 const int32_t refCount = 1;
374 napi_value resource = nullptr;
375 size_t argc = ARGS_ONE;
376 napi_value argv[ARGS_ONE] = {0};
377 napi_value thisVar = nullptr;
378
379 CAMERA_NAPI_GET_JS_ARGS(env, info, argc, argv, thisVar);
380 NAPI_ASSERT(env, argc <= ARGS_ONE, "requires 1 parameter maximum");
381 napi_get_boolean(env, true, &result);
382 CAMERA_NAPI_CHECK_NULL_PTR_RETURN_UNDEFINED(env, result, result, "Failed to obtain arguments");
383 std::unique_ptr<CameraInputAsyncContext> asyncContext = std::make_unique<CameraInputAsyncContext>();
384 status = napi_unwrap(env, thisVar, reinterpret_cast<void**>(&asyncContext->objectInfo));
385 if (status == napi_ok && asyncContext->objectInfo != nullptr) {
386 if (argc == ARGS_ONE) {
387 CAMERA_NAPI_GET_JS_ASYNC_CB_REF(env, argv[PARAM0], refCount, asyncContext->callbackRef);
388 }
389
390 CAMERA_NAPI_CREATE_PROMISE(env, asyncContext->callbackRef, asyncContext->deferred, result);
391 CAMERA_NAPI_CREATE_RESOURCE_NAME(env, resource, "Release");
392
393 status = napi_create_async_work(
394 env, nullptr, resource, [](napi_env env, void* data) {
395 auto context = static_cast<CameraInputAsyncContext*>(data);
396 context->status = false;
397 // Start async trace
398 context->funcName = "CameraInputNapi::Release";
399 context->taskId = CameraNapiUtils::IncreamentAndGet(cameraInputTaskId);
400 CAMERA_START_ASYNC_TRACE(context->funcName, context->taskId);
401 if (context->objectInfo != nullptr) {
402 context->status = true;
403 context->modeForAsync = RELEASE_ASYNC_CALLBACK;
404 }
405 },
406 CommonCompleteCallback, static_cast<void*>(asyncContext.get()), &asyncContext->work);
407 if (status != napi_ok) {
408 MEDIA_ERR_LOG("Failed to create napi_create_async_work for CameraInputNapi::Release");
409 if (asyncContext->callbackRef != nullptr) {
410 napi_delete_reference(env, asyncContext->callbackRef);
411 }
412 napi_get_undefined(env, &result);
413 } else {
414 napi_queue_async_work(env, asyncContext->work);
415 asyncContext.release();
416 }
417 } else {
418 MEDIA_ERR_LOG("Release call Failed!");
419 }
420 return result;
421 }
422
RegisterCallback(napi_env env,const string & eventType,napi_ref callbackRef)423 void CameraInputNapi::RegisterCallback(napi_env env, const string &eventType, napi_ref callbackRef)
424 {
425 MEDIA_DEBUG_LOG("RegisterCallback is called");
426 if (eventType.empty()) {
427 MEDIA_ERR_LOG("Failed to Register Callback callback name is empty!");
428 return;
429 }
430
431 if (eventType.compare("error") == 0) {
432 // Set callback for error
433 shared_ptr<ErrorCallbackListener> errorCallback = make_shared<ErrorCallbackListener>(env, callbackRef);
434 cameraInput_->SetErrorCallback(errorCallback);
435 } else {
436 MEDIA_ERR_LOG("Incorrect callback event type provided for camera input!");
437 }
438 }
439
On(napi_env env,napi_callback_info info)440 napi_value CameraInputNapi::On(napi_env env, napi_callback_info info)
441 {
442 MEDIA_INFO_LOG("On is called");
443 CAMERA_SYNC_TRACE;
444 napi_value undefinedResult = nullptr;
445 size_t argCount = ARGS_THREE;
446 napi_value argv[ARGS_THREE] = {nullptr};
447 napi_value thisVar = nullptr;
448 size_t res = 0;
449 char buffer[SIZE];
450 const int32_t refCount = 1;
451 CameraInputNapi* obj = nullptr;
452 CameraDeviceNapi* cameraDeviceNapi = nullptr;
453 napi_status status;
454
455 napi_get_undefined(env, &undefinedResult);
456
457 CAMERA_NAPI_GET_JS_ARGS(env, info, argCount, argv, thisVar);
458
459 NAPI_ASSERT(env, argCount == ARGS_THREE, "requires 3 parameters");
460
461 if (thisVar == nullptr || argv[PARAM0] == nullptr || argv[PARAM1] == nullptr || argv[PARAM2] == nullptr) {
462 MEDIA_ERR_LOG("Failed to retrieve details about the callback");
463 return undefinedResult;
464 }
465
466 status = napi_unwrap(env, thisVar, reinterpret_cast<void**>(&obj));
467 if (status == napi_ok && obj != nullptr) {
468 napi_valuetype valueType = napi_undefined;
469 if (napi_typeof(env, argv[PARAM0], &valueType) != napi_ok || valueType != napi_string
470 || napi_typeof(env, argv[PARAM1], &valueType) != napi_ok || valueType != napi_object
471 || napi_typeof(env, argv[PARAM2], &valueType) != napi_ok || valueType != napi_function) {
472 return undefinedResult;
473 }
474
475 napi_get_value_string_utf8(env, argv[PARAM0], buffer, SIZE, &res);
476 std::string eventType = std::string(buffer);
477
478 status = napi_unwrap(env, argv[PARAM1], reinterpret_cast<void **>(&cameraDeviceNapi));
479 if (status != napi_ok || cameraDeviceNapi == nullptr) {
480 MEDIA_ERR_LOG("Could not able to read cameraDevice argument!");
481 return undefinedResult;
482 }
483
484 napi_ref callbackRef;
485 napi_create_reference(env, argv[PARAM2], refCount, &callbackRef);
486
487 obj->RegisterCallback(env, eventType, callbackRef);
488 } else {
489 MEDIA_ERR_LOG("On call Failed!");
490 }
491 return undefinedResult;
492 }
493 } // namespace CameraStandard
494 } // namespace OHOS
495