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