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