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 "output/video_output_napi.h"
17 #include <uv.h>
18 #include "hilog/log.h"
19
20 namespace OHOS {
21 namespace CameraStandard {
22 using OHOS::HiviewDFX::HiLog;
23 using OHOS::HiviewDFX::HiLogLabel;
24
25 namespace {
26 constexpr HiLogLabel LABEL = {LOG_CORE, LOG_DOMAIN, "VideoOutputNapi"};
27 }
28
29 thread_local napi_ref VideoOutputNapi::sConstructor_ = nullptr;
30 thread_local sptr<VideoOutput> VideoOutputNapi::sVideoOutput_ = nullptr;
31 thread_local uint32_t VideoOutputNapi::videoOutputTaskId = CAMERA_VIDEO_OUTPUT_TASKID;
32
VideoCallbackListener(napi_env env)33 VideoCallbackListener::VideoCallbackListener(napi_env env) : env_(env) {}
34
UpdateJSCallbackAsync(std::string propName,const int32_t value) const35 void VideoCallbackListener::UpdateJSCallbackAsync(std::string propName, const int32_t value) const
36 {
37 uv_loop_s* loop = nullptr;
38 napi_get_uv_event_loop(env_, &loop);
39 if (!loop) {
40 MEDIA_ERR_LOG("VideoCallbackListener:UpdateJSCallbackAsync() 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("VideoCallbackListener:UpdateJSCallbackAsync() failed to allocate work");
46 return;
47 }
48 std::unique_ptr<VideoOutputCallbackInfo> callbackInfo =
49 std::make_unique<VideoOutputCallbackInfo>(propName, value, this);
50 work->data = callbackInfo.get();
51 int ret = uv_queue_work(loop, work, [] (uv_work_t* work) {}, [] (uv_work_t* work, int status) {
52 VideoOutputCallbackInfo* callbackInfo = reinterpret_cast<VideoOutputCallbackInfo *>(work->data);
53 if (callbackInfo) {
54 callbackInfo->listener_->UpdateJSCallback(callbackInfo->eventName_, callbackInfo->value_);
55 delete callbackInfo;
56 }
57 delete work;
58 });
59 if (ret) {
60 MEDIA_ERR_LOG("VideoCallbackListener:UpdateJSCallbackAsync() failed to execute work");
61 delete work;
62 } else {
63 callbackInfo.release();
64 }
65 }
66
OnFrameStarted() const67 void VideoCallbackListener::OnFrameStarted() const
68 {
69 CAMERA_SYNC_TRACE;
70 MEDIA_INFO_LOG("VideoCallbackListener::OnFrameStarted");
71 UpdateJSCallbackAsync("OnFrameStarted", -1);
72 }
73
OnFrameEnded(const int32_t frameCount) const74 void VideoCallbackListener::OnFrameEnded(const int32_t frameCount) const
75 {
76 CAMERA_SYNC_TRACE;
77 MEDIA_INFO_LOG("VideoCallbackListener::OnFrameEnded frameCount: %{public}d", frameCount);
78 UpdateJSCallbackAsync("OnFrameEnded", frameCount);
79 }
80
OnError(const int32_t errorCode) const81 void VideoCallbackListener::OnError(const int32_t errorCode) const
82 {
83 MEDIA_INFO_LOG("VideoCallbackListener::OnError errorCode: %{public}d", errorCode);
84 UpdateJSCallbackAsync("OnError", errorCode);
85 }
86
SetCallbackRef(const std::string & eventType,const napi_ref & callbackRef)87 void VideoCallbackListener::SetCallbackRef(const std::string &eventType, const napi_ref &callbackRef)
88 {
89 if (eventType.compare("frameStart") == 0) {
90 frameStartCallbackRef_ = callbackRef;
91 } else if (eventType.compare("frameEnd") == 0) {
92 frameEndCallbackRef_ = callbackRef;
93 } else if (eventType.compare("error") == 0) {
94 errorCallbackRef_ = callbackRef;
95 } else {
96 MEDIA_ERR_LOG("Incorrect video callback event type received from JS");
97 }
98 }
99
UpdateJSCallback(std::string propName,const int32_t value) const100 void VideoCallbackListener::UpdateJSCallback(std::string propName, const int32_t value) const
101 {
102 napi_value result[ARGS_ONE];
103 napi_value callback = nullptr;
104 napi_value retVal;
105 napi_value propValue;
106 int32_t jsErrorCodeUnknown = -1;
107
108 if (propName.compare("OnFrameStarted") == 0) {
109 CAMERA_NAPI_CHECK_NULL_PTR_RETURN_VOID(frameStartCallbackRef_,
110 "OnFrameStart callback is not registered by JS");
111 napi_get_undefined(env_, &result[PARAM0]);
112 napi_get_reference_value(env_, frameStartCallbackRef_, &callback);
113 } else if (propName.compare("OnFrameEnded") == 0) {
114 CAMERA_NAPI_CHECK_NULL_PTR_RETURN_VOID(frameEndCallbackRef_,
115 "OnFrameEnd callback is not registered by JS");
116 napi_get_undefined(env_, &result[PARAM0]);
117 napi_get_reference_value(env_, frameEndCallbackRef_, &callback);
118 } else {
119 CAMERA_NAPI_CHECK_NULL_PTR_RETURN_VOID(errorCallbackRef_,
120 "OnError callback is not registered by JS");
121 napi_create_object(env_, &result[PARAM0]);
122 napi_create_int32(env_, jsErrorCodeUnknown, &propValue);
123 napi_set_named_property(env_, result[PARAM0], "code", propValue);
124 napi_get_reference_value(env_, errorCallbackRef_, &callback); // should errorcode be valued as -1
125 if (errorCallbackRef_ != nullptr) {
126 napi_delete_reference(env_, errorCallbackRef_);
127 }
128 }
129
130 napi_call_function(env_, nullptr, callback, ARGS_ONE, result, &retVal);
131 }
132
VideoOutputNapi()133 VideoOutputNapi::VideoOutputNapi() : env_(nullptr), wrapper_(nullptr)
134 {
135 }
136
~VideoOutputNapi()137 VideoOutputNapi::~VideoOutputNapi()
138 {
139 if (wrapper_ != nullptr) {
140 napi_delete_reference(env_, wrapper_);
141 }
142 if (videoOutput_) {
143 videoOutput_ = nullptr;
144 }
145 if (videoCallback_) {
146 videoCallback_ = nullptr;
147 }
148 }
149
VideoOutputNapiDestructor(napi_env env,void * nativeObject,void * finalize_hint)150 void VideoOutputNapi::VideoOutputNapiDestructor(napi_env env, void* nativeObject, void* finalize_hint)
151 {
152 VideoOutputNapi* videoOutput = reinterpret_cast<VideoOutputNapi*>(nativeObject);
153 if (videoOutput != nullptr) {
154 videoOutput->~VideoOutputNapi();
155 }
156 }
157
Init(napi_env env,napi_value exports)158 napi_value VideoOutputNapi::Init(napi_env env, napi_value exports)
159 {
160 napi_status status;
161 napi_value ctorObj;
162 int32_t refCount = 1;
163
164 napi_property_descriptor video_output_props[] = {
165 DECLARE_NAPI_FUNCTION("start", Start),
166 DECLARE_NAPI_FUNCTION("stop", Stop),
167 DECLARE_NAPI_FUNCTION("getFrameRateRange", GetFrameRateRange),
168 DECLARE_NAPI_FUNCTION("setFrameRateRange", SetFrameRateRange),
169 DECLARE_NAPI_FUNCTION("release", Release),
170 DECLARE_NAPI_FUNCTION("on", On)
171 };
172
173 status = napi_define_class(env, CAMERA_VIDEO_OUTPUT_NAPI_CLASS_NAME, NAPI_AUTO_LENGTH,
174 VideoOutputNapiConstructor, nullptr,
175 sizeof(video_output_props) / sizeof(video_output_props[PARAM0]),
176 video_output_props, &ctorObj);
177 if (status == napi_ok) {
178 status = napi_create_reference(env, ctorObj, refCount, &sConstructor_);
179 if (status == napi_ok) {
180 status = napi_set_named_property(env, exports, CAMERA_VIDEO_OUTPUT_NAPI_CLASS_NAME, ctorObj);
181 if (status == napi_ok) {
182 return exports;
183 }
184 }
185 }
186
187 return nullptr;
188 }
189
190 // Constructor callback
VideoOutputNapiConstructor(napi_env env,napi_callback_info info)191 napi_value VideoOutputNapi::VideoOutputNapiConstructor(napi_env env, napi_callback_info info)
192 {
193 napi_status status;
194 napi_value result = nullptr;
195 napi_value thisVar = nullptr;
196
197 napi_get_undefined(env, &result);
198 CAMERA_NAPI_GET_JS_OBJ_WITH_ZERO_ARGS(env, info, status, thisVar);
199
200 if (status == napi_ok && thisVar != nullptr) {
201 std::unique_ptr<VideoOutputNapi> obj = std::make_unique<VideoOutputNapi>();
202 if (obj != nullptr) {
203 obj->env_ = env;
204 obj->videoOutput_ = sVideoOutput_;
205
206 std::shared_ptr<VideoCallbackListener> callback =
207 std::make_shared<VideoCallbackListener>(VideoCallbackListener(env));
208 ((sptr<VideoOutput> &)(obj->videoOutput_))->SetCallback(callback);
209 obj->videoCallback_ = callback;
210
211 status = napi_wrap(env, thisVar, reinterpret_cast<void*>(obj.get()),
212 VideoOutputNapi::VideoOutputNapiDestructor, nullptr, &(obj->wrapper_));
213 if (status == napi_ok) {
214 obj.release();
215 return thisVar;
216 } else {
217 MEDIA_ERR_LOG("Failure wrapping js to native napi");
218 }
219 }
220 }
221
222 return result;
223 }
224
ConvertJSArgsToNative(napi_env env,size_t argc,const napi_value argv[],VideoOutputAsyncContext & asyncContext)225 static napi_value ConvertJSArgsToNative(napi_env env, size_t argc, const napi_value argv[],
226 VideoOutputAsyncContext &asyncContext)
227 {
228 std::string str = "";
229 std::vector<std::string> strArr;
230 std::string order = "";
231 const int32_t refCount = 1;
232 napi_value result;
233 auto context = &asyncContext;
234
235 NAPI_ASSERT(env, argv != nullptr, "Argument list is empty");
236
237 for (size_t i = PARAM0; i < argc; i++) {
238 napi_valuetype valueType = napi_undefined;
239 napi_typeof(env, argv[i], &valueType);
240
241 if (i == PARAM0 && valueType == napi_number) {
242 napi_get_value_int32(env, argv[i], &context->minFrameRate);
243 } else if (i == PARAM1 && valueType == napi_number) {
244 napi_get_value_int32(env, argv[i], &context->maxFrameRate);
245 } else if (i == PARAM2 && valueType == napi_function) {
246 napi_create_reference(env, argv[i], refCount, &context->callbackRef);
247 } else {
248 NAPI_ASSERT(env, false, "type mismatch");
249 }
250 }
251 // Return true napi_value if params are successfully obtained
252 napi_get_boolean(env, true, &result);
253 return result;
254 }
255
CommonCompleteCallback(napi_env env,napi_status status,void * data)256 static void CommonCompleteCallback(napi_env env, napi_status status, void* data)
257 {
258 auto context = static_cast<VideoOutputAsyncContext*>(data);
259
260 if (context == nullptr) {
261 MEDIA_ERR_LOG("Async context is null");
262 return;
263 }
264
265 std::unique_ptr<JSAsyncContextOutput> jsContext = std::make_unique<JSAsyncContextOutput>();
266
267 if (!context->status) {
268 CameraNapiUtils::CreateNapiErrorObject(env, context->errorCode, context->errorMsg.c_str(), jsContext);
269 } else {
270 jsContext->status = true;
271 napi_get_undefined(env, &jsContext->error);
272 if (context->bRetBool) {
273 napi_get_boolean(env, context->status, &jsContext->data);
274 } else {
275 napi_get_undefined(env, &jsContext->data);
276 }
277 }
278
279 if (!context->funcName.empty() && context->taskId > 0) {
280 // Finish async trace
281 CAMERA_FINISH_ASYNC_TRACE(context->funcName, context->taskId);
282 jsContext->funcName = context->funcName;
283 }
284
285 if (context->work != nullptr) {
286 CameraNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
287 context->work, *jsContext);
288 }
289 delete context;
290 }
291
GetVideoOutput()292 sptr<VideoOutput> VideoOutputNapi::GetVideoOutput()
293 {
294 return videoOutput_;
295 }
296
IsVideoOutput(napi_env env,napi_value obj)297 bool VideoOutputNapi::IsVideoOutput(napi_env env, napi_value obj)
298 {
299 bool result = false;
300 napi_status status;
301 napi_value constructor = nullptr;
302
303 status = napi_get_reference_value(env, sConstructor_, &constructor);
304 if (status == napi_ok) {
305 status = napi_instanceof(env, obj, constructor, &result);
306 if (status != napi_ok) {
307 result = false;
308 }
309 }
310
311 return result;
312 }
313
CreateVideoOutput(napi_env env,VideoProfile & profile,std::string surfaceId)314 napi_value VideoOutputNapi::CreateVideoOutput(napi_env env, VideoProfile &profile, std::string surfaceId)
315 {
316 CAMERA_SYNC_TRACE;
317 napi_status status;
318 napi_value result = nullptr;
319 napi_value constructor;
320
321 status = napi_get_reference_value(env, sConstructor_, &constructor);
322 if (status == napi_ok) {
323 uint64_t iSurfaceId;
324 std::istringstream iss(surfaceId);
325 iss >> iSurfaceId;
326 sptr<Surface> surface = SurfaceUtils::GetInstance()->GetSurface(iSurfaceId);
327 if (surface == nullptr) {
328 MEDIA_ERR_LOG("failed to get surface from SurfaceUtils");
329 return result;
330 }
331 surface->SetUserData(CameraManager::surfaceFormat, std::to_string(profile.GetCameraFormat()));
332 int retCode = CameraManager::GetInstance()->CreateVideoOutput(profile, surface, &sVideoOutput_);
333 if (!CameraNapiUtils::CheckError(env, retCode)) {
334 return nullptr;
335 }
336 if (sVideoOutput_ == nullptr) {
337 MEDIA_ERR_LOG("failed to create VideoOutput");
338 return result;
339 }
340 status = napi_new_instance(env, constructor, 0, nullptr, &result);
341 sVideoOutput_ = nullptr;
342 if (status == napi_ok && result != nullptr) {
343 return result;
344 } else {
345 MEDIA_ERR_LOG("Failed to create video output instance");
346 }
347 }
348
349 napi_get_undefined(env, &result);
350 return result;
351 }
352
Start(napi_env env,napi_callback_info info)353 napi_value VideoOutputNapi::Start(napi_env env, napi_callback_info info)
354 {
355 napi_status status;
356 napi_value result = nullptr;
357 const int32_t refCount = 1;
358 napi_value resource = nullptr;
359 size_t argc = ARGS_ONE;
360 napi_value argv[ARGS_ONE] = {0};
361 napi_value thisVar = nullptr;
362
363 CAMERA_NAPI_GET_JS_ARGS(env, info, argc, argv, thisVar);
364 NAPI_ASSERT(env, argc <= ARGS_ONE, "requires 1 parameter maximum");
365
366 napi_get_undefined(env, &result);
367 std::unique_ptr<VideoOutputAsyncContext> asyncContext = std::make_unique<VideoOutputAsyncContext>();
368 status = napi_unwrap(env, thisVar, reinterpret_cast<void**>(&asyncContext->objectInfo));
369 if (status == napi_ok && asyncContext->objectInfo != nullptr) {
370 if (argc == ARGS_ONE) {
371 CAMERA_NAPI_GET_JS_ASYNC_CB_REF(env, argv[PARAM0], refCount, asyncContext->callbackRef);
372 }
373 CAMERA_NAPI_CREATE_PROMISE(env, asyncContext->callbackRef, asyncContext->deferred, result);
374 CAMERA_NAPI_CREATE_RESOURCE_NAME(env, resource, "Start");
375 status = napi_create_async_work(env, nullptr, resource,
376 [](napi_env env, void* data) {
377 auto context = static_cast<VideoOutputAsyncContext*>(data);
378 context->status = false;
379 // Start async trace
380 context->funcName = "VideoOutputNapi::Start";
381 context->taskId = CameraNapiUtils::IncreamentAndGet(videoOutputTaskId);
382 CAMERA_START_ASYNC_TRACE(context->funcName, context->taskId);
383 if (context->objectInfo != nullptr) {
384 context->bRetBool = false;
385 context->errorCode = ((sptr<VideoOutput> &)(context->objectInfo->videoOutput_))->Start();
386 context->status = context->errorCode == 0;
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 VideoOutputNapi::Start");
392 napi_get_undefined(env, &result);
393 } else {
394 napi_queue_async_work(env, asyncContext->work);
395 asyncContext.release();
396 }
397 }
398
399 return result;
400 }
401
Stop(napi_env env,napi_callback_info info)402 napi_value VideoOutputNapi::Stop(napi_env env, napi_callback_info info)
403 {
404 napi_status status;
405 napi_value result = nullptr;
406 const int32_t refCount = 1;
407 napi_value resource = nullptr;
408 size_t argc = ARGS_ONE;
409 napi_value argv[ARGS_ONE] = {0};
410 napi_value thisVar = nullptr;
411
412 CAMERA_NAPI_GET_JS_ARGS(env, info, argc, argv, thisVar);
413 NAPI_ASSERT(env, argc <= 1, "requires 1 parameter maximum");
414
415 napi_get_undefined(env, &result);
416 std::unique_ptr<VideoOutputAsyncContext> asyncContext = std::make_unique<VideoOutputAsyncContext>();
417 status = napi_unwrap(env, thisVar, reinterpret_cast<void**>(&asyncContext->objectInfo));
418 if (status == napi_ok && asyncContext->objectInfo != nullptr) {
419 if (argc == ARGS_ONE) {
420 CAMERA_NAPI_GET_JS_ASYNC_CB_REF(env, argv[PARAM0], refCount, asyncContext->callbackRef);
421 }
422 CAMERA_NAPI_CREATE_PROMISE(env, asyncContext->callbackRef, asyncContext->deferred, result);
423 CAMERA_NAPI_CREATE_RESOURCE_NAME(env, resource, "Stop");
424 status = napi_create_async_work(env, nullptr, resource,
425 [](napi_env env, void* data) {
426 auto context = static_cast<VideoOutputAsyncContext*>(data);
427 context->status = false;
428 // Start async trace
429 context->funcName = "VideoOutputNapi::Stop";
430 context->taskId = CameraNapiUtils::IncreamentAndGet(videoOutputTaskId);
431 CAMERA_START_ASYNC_TRACE(context->funcName, context->taskId);
432 if (context->objectInfo != nullptr) {
433 context->bRetBool = false;
434 context->errorCode = ((sptr<VideoOutput> &)(context->objectInfo->videoOutput_))->Stop();
435 context->status = context->errorCode == 0;
436 }
437 },
438 CommonCompleteCallback, static_cast<void*>(asyncContext.get()), &asyncContext->work);
439 if (status != napi_ok) {
440 MEDIA_ERR_LOG("Failed to create napi_create_async_work for VideoOutputNapi::Stop");
441 napi_get_undefined(env, &result);
442 } else {
443 napi_queue_async_work(env, asyncContext->work);
444 asyncContext.release();
445 }
446 }
447
448 return result;
449 }
450
GetFrameRateRangeAsyncCallbackComplete(napi_env env,napi_status status,void * data)451 void GetFrameRateRangeAsyncCallbackComplete(napi_env env, napi_status status, void* data)
452 {
453 auto context = static_cast<VideoOutputAsyncContext*>(data);
454 napi_value frameRateRange = nullptr;
455
456 CAMERA_NAPI_CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
457
458 std::unique_ptr<JSAsyncContextOutput> jsContext = std::make_unique<JSAsyncContextOutput>();
459 jsContext->status = true;
460 napi_get_undefined(env, &jsContext->error);
461 if ((!context->vecFrameRateRangeList.empty()) && (napi_create_array(env, &frameRateRange) == napi_ok)) {
462 int32_t j = 0;
463 for (size_t i = 0; i < context->vecFrameRateRangeList.size(); i++) {
464 int32_t frameRate = context->vecFrameRateRangeList[i];
465 napi_value value;
466 if (napi_create_int32(env, frameRate, &value) == napi_ok) {
467 napi_set_element(env, frameRateRange, j, value);
468 j++;
469 }
470 }
471 jsContext->data = frameRateRange;
472 } else {
473 MEDIA_ERR_LOG("vecFrameRateRangeList is empty or failed to create array!");
474 CameraNapiUtils::CreateNapiErrorObject(env, context->errorCode,
475 "vecFrameRateRangeList is empty or failed to create array!", jsContext);
476 }
477
478 if (!context->funcName.empty() && context->taskId > 0) {
479 // Finish async trace
480 CAMERA_FINISH_ASYNC_TRACE(context->funcName, context->taskId);
481 jsContext->funcName = context->funcName;
482 }
483
484 if (context->work != nullptr) {
485 CameraNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
486 context->work, *jsContext);
487 }
488 delete context;
489 }
490
GetFrameRateRange(napi_env env,napi_callback_info info)491 napi_value VideoOutputNapi::GetFrameRateRange(napi_env env, napi_callback_info info)
492 {
493 CAMERA_SYNC_TRACE;
494 napi_status status;
495 napi_value result = nullptr;
496 const int32_t refCount = 1;
497 napi_value resource = nullptr;
498 size_t argc = ARGS_ONE;
499 napi_value argv[ARGS_ONE] = {0};
500 napi_value thisVar = nullptr;
501
502 CAMERA_NAPI_GET_JS_ARGS(env, info, argc, argv, thisVar);
503 NAPI_ASSERT(env, (argc <= ARGS_ONE), "requires 1 parameter maximum");
504
505 napi_get_undefined(env, &result);
506 auto asyncContext = std::make_unique<VideoOutputAsyncContext>();
507 status = napi_unwrap(env, thisVar, reinterpret_cast<void**>(&asyncContext->objectInfo));
508 if (status == napi_ok && asyncContext->objectInfo != nullptr) {
509 if (argc == ARGS_ONE) {
510 CAMERA_NAPI_GET_JS_ASYNC_CB_REF(env, argv[PARAM0], refCount, asyncContext->callbackRef);
511 }
512 CAMERA_NAPI_CREATE_PROMISE(env, asyncContext->callbackRef, asyncContext->deferred, result);
513 CAMERA_NAPI_CREATE_RESOURCE_NAME(env, resource, "GetFrameRateRange");
514 status = napi_create_async_work(
515 env, nullptr, resource, [](napi_env env, void* data) {
516 auto context = static_cast<VideoOutputAsyncContext*>(data);
517 context->status = false;
518 if (context->objectInfo != nullptr) {
519 if (!context->vecFrameRateRangeList.empty()) {
520 context->status = true;
521 } else {
522 context->status = false;
523 MEDIA_ERR_LOG("GetFrameRateRange vecFrameRateRangeList is empty!");
524 }
525 }
526 },
527 GetFrameRateRangeAsyncCallbackComplete, static_cast<void*>(asyncContext.get()), &asyncContext->work);
528 if (status != napi_ok) {
529 MEDIA_ERR_LOG("Failed to create napi_create_async_work for GetFrameRateRange");
530 napi_get_undefined(env, &result);
531 } else {
532 napi_queue_async_work(env, asyncContext->work);
533 asyncContext.release();
534 }
535 }
536
537 return result;
538 }
539
isFrameRateRangeAvailable(napi_env env,void * data)540 bool isFrameRateRangeAvailable(napi_env env, void* data)
541 {
542 bool invalidFrameRate = true;
543 const int32_t FRAME_RATE_RANGE_STEP = 2;
544 auto context = static_cast<VideoOutputAsyncContext*>(data);
545 if (context == nullptr) {
546 MEDIA_ERR_LOG("Async context is null");
547 return invalidFrameRate;
548 }
549
550 if (!context->vecFrameRateRangeList.empty()) {
551 for (size_t i = 0; i < (context->vecFrameRateRangeList.size() - 1); i += FRAME_RATE_RANGE_STEP) {
552 int32_t minVal = context->vecFrameRateRangeList[i];
553 int32_t maxVal = context->vecFrameRateRangeList[i + 1];
554 if ((context->minFrameRate == minVal) && (context->maxFrameRate == maxVal)) {
555 invalidFrameRate = false;
556 break;
557 }
558 }
559 } else {
560 MEDIA_ERR_LOG("isFrameRateRangeAvailable: vecFrameRateRangeList is empty!");
561 }
562 return invalidFrameRate;
563 }
564
SetFrameRateRange(napi_env env,napi_callback_info info)565 napi_value VideoOutputNapi::SetFrameRateRange(napi_env env, napi_callback_info info)
566 {
567 CAMERA_SYNC_TRACE;
568 napi_status status;
569 napi_value result = nullptr;
570 napi_value resource = nullptr;
571 size_t argc = ARGS_THREE;
572 napi_value argv[ARGS_THREE] = {0};
573 napi_value thisVar = nullptr;
574
575 CAMERA_NAPI_GET_JS_ARGS(env, info, argc, argv, thisVar);
576 NAPI_ASSERT(env, (argc == ARGS_TWO || argc == ARGS_THREE), "requires 3 parameters maximum");
577
578 napi_get_undefined(env, &result);
579 auto asyncContext = std::make_unique<VideoOutputAsyncContext>();
580 status = napi_unwrap(env, thisVar, reinterpret_cast<void**>(&asyncContext->objectInfo));
581 if (status == napi_ok && asyncContext->objectInfo != nullptr) {
582 result = ConvertJSArgsToNative(env, argc, argv, *asyncContext);
583 CAMERA_NAPI_CHECK_NULL_PTR_RETURN_UNDEFINED(env, result, result, "Failed to obtain arguments");
584 CAMERA_NAPI_CREATE_PROMISE(env, asyncContext->callbackRef, asyncContext->deferred, result);
585 CAMERA_NAPI_CREATE_RESOURCE_NAME(env, resource, "SetFrameRateRange");
586 status = napi_create_async_work(
587 env, nullptr, resource,
588 [](napi_env env, void* data) {
589 auto context = static_cast<VideoOutputAsyncContext*>(data);
590 context->status = false;
591 // Start async trace
592 context->funcName = "VideoOutputNapi::SetFrameRateRange";
593 context->taskId = CameraNapiUtils::IncreamentAndGet(videoOutputTaskId);
594 CAMERA_START_ASYNC_TRACE(context->funcName, context->taskId);
595 if (context->objectInfo != nullptr) {
596 context->bRetBool = false;
597 bool isValidRange = isFrameRateRangeAvailable(env, data);
598 if (!isValidRange) {
599 context->status = true;
600 } else {
601 MEDIA_ERR_LOG("Failed to get range values for SetFrameRateRange");
602 context->errorMsg = "Failed to get range values for SetFrameRateRange";
603 }
604 }
605 },
606 CommonCompleteCallback, static_cast<void*>(asyncContext.get()), &asyncContext->work);
607 if (status != napi_ok) {
608 MEDIA_ERR_LOG("Failed to create napi_create_async_work for SetFrameRateRange");
609 napi_get_undefined(env, &result);
610 } else {
611 napi_queue_async_work(env, asyncContext->work);
612 asyncContext.release();
613 }
614 }
615
616 return result;
617 }
618
Release(napi_env env,napi_callback_info info)619 napi_value VideoOutputNapi::Release(napi_env env, napi_callback_info info)
620 {
621 napi_status status;
622 napi_value result = nullptr;
623 const int32_t refCount = 1;
624 napi_value resource = nullptr;
625 size_t argc = ARGS_ONE;
626 napi_value argv[ARGS_ONE] = {0};
627 napi_value thisVar = nullptr;
628
629 CAMERA_NAPI_GET_JS_ARGS(env, info, argc, argv, thisVar);
630 NAPI_ASSERT(env, argc <= 1, "requires 1 parameter maximum");
631
632 napi_get_undefined(env, &result);
633 std::unique_ptr<VideoOutputAsyncContext> asyncContext = std::make_unique<VideoOutputAsyncContext>();
634 status = napi_unwrap(env, thisVar, reinterpret_cast<void**>(&asyncContext->objectInfo));
635 if (status == napi_ok && asyncContext->objectInfo != nullptr) {
636 if (argc == ARGS_ONE) {
637 CAMERA_NAPI_GET_JS_ASYNC_CB_REF(env, argv[PARAM0], refCount, asyncContext->callbackRef);
638 }
639
640 CAMERA_NAPI_CREATE_PROMISE(env, asyncContext->callbackRef, asyncContext->deferred, result);
641 CAMERA_NAPI_CREATE_RESOURCE_NAME(env, resource, "Release");
642
643 status = napi_create_async_work(
644 env, nullptr, resource, [](napi_env env, void* data) {
645 auto context = static_cast<VideoOutputAsyncContext*>(data);
646 context->status = false;
647 // Start async trace
648 context->funcName = "VideoOutputNapi::Release";
649 context->taskId = CameraNapiUtils::IncreamentAndGet(videoOutputTaskId);
650 CAMERA_START_ASYNC_TRACE(context->funcName, context->taskId);
651 if (context->objectInfo != nullptr) {
652 context->bRetBool = false;
653 context->status = true;
654 ((sptr<VideoOutput> &)(context->objectInfo->videoOutput_))->Release();
655 }
656 },
657 CommonCompleteCallback, static_cast<void*>(asyncContext.get()), &asyncContext->work);
658 if (status != napi_ok) {
659 MEDIA_ERR_LOG("Failed to create napi_create_async_work for VideoOutputNapi::Release");
660 napi_get_undefined(env, &result);
661 } else {
662 napi_queue_async_work(env, asyncContext->work);
663 asyncContext.release();
664 }
665 }
666
667 return result;
668 }
669
On(napi_env env,napi_callback_info info)670 napi_value VideoOutputNapi::On(napi_env env, napi_callback_info info)
671 {
672 CAMERA_SYNC_TRACE;
673 napi_value undefinedResult = nullptr;
674 size_t argCount = ARGS_TWO;
675 napi_value argv[ARGS_TWO] = {nullptr};
676 napi_value thisVar = nullptr;
677 size_t res = 0;
678 char buffer[SIZE];
679 const int32_t refCount = 1;
680 VideoOutputNapi* obj = nullptr;
681 napi_status status;
682
683 napi_get_undefined(env, &undefinedResult);
684
685 CAMERA_NAPI_GET_JS_ARGS(env, info, argCount, argv, thisVar);
686 NAPI_ASSERT(env, argCount == ARGS_TWO, "requires 2 parameters");
687
688 if (thisVar == nullptr || argv[PARAM0] == nullptr || argv[PARAM1] == nullptr) {
689 MEDIA_ERR_LOG("Failed to retrieve details about the callback");
690 return undefinedResult;
691 }
692
693 status = napi_unwrap(env, thisVar, reinterpret_cast<void**>(&obj));
694 if (status == napi_ok && obj != nullptr) {
695 napi_valuetype valueType = napi_undefined;
696 if (napi_typeof(env, argv[PARAM0], &valueType) != napi_ok || valueType != napi_string
697 || napi_typeof(env, argv[PARAM1], &valueType) != napi_ok || valueType != napi_function) {
698 return undefinedResult;
699 }
700
701 napi_get_value_string_utf8(env, argv[PARAM0], buffer, SIZE, &res);
702 std::string eventType = std::string(buffer);
703
704 napi_ref callbackRef;
705 napi_create_reference(env, argv[PARAM1], refCount, &callbackRef);
706
707 if (!eventType.empty()) {
708 obj->videoCallback_->SetCallbackRef(eventType, callbackRef);
709 } else {
710 MEDIA_ERR_LOG("Failed to Register Callback: event type is empty!");
711 }
712 }
713
714 return undefinedResult;
715 }
716 } // namespace CameraStandard
717 } // namespace OHOS
718