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 "video_recorder_napi.h"
17 #include "recorder_callback_napi.h"
18 #include "media_log.h"
19 #include "media_errors.h"
20 #include "common_napi.h"
21 #include "directory_ex.h"
22 #include "string_ex.h"
23 #include "surface_utils.h"
24 #include "recorder_napi_utils.h"
25
26 namespace {
27 constexpr OHOS::HiviewDFX::HiLogLabel LABEL = {LOG_CORE, LOG_DOMAIN, "VideoRecorderNapi"};
28 }
29
30 namespace OHOS {
31 namespace Media {
32 thread_local napi_ref VideoRecorderNapi::constructor_ = nullptr;
33 const std::string CLASS_NAME = "VideoRecorder";
34
VideoRecorderNapi()35 VideoRecorderNapi::VideoRecorderNapi()
36 {
37 MEDIA_LOGD("0x%{public}06" PRIXPTR "Instances create", FAKE_POINTER(this));
38 }
39
~VideoRecorderNapi()40 VideoRecorderNapi::~VideoRecorderNapi()
41 {
42 CancelCallback();
43 recorder_ = nullptr;
44 callbackNapi_ = nullptr;
45 MEDIA_LOGD("0x%{public}06" PRIXPTR "Instances destroy", FAKE_POINTER(this));
46 }
47
Init(napi_env env,napi_value exports)48 napi_value VideoRecorderNapi::Init(napi_env env, napi_value exports)
49 {
50 napi_property_descriptor properties[] = {
51 DECLARE_NAPI_FUNCTION("prepare", Prepare),
52 DECLARE_NAPI_FUNCTION("getInputSurface", GetInputSurface),
53 DECLARE_NAPI_FUNCTION("start", Start),
54 DECLARE_NAPI_FUNCTION("pause", Pause),
55 DECLARE_NAPI_FUNCTION("resume", Resume),
56 DECLARE_NAPI_FUNCTION("stop", Stop),
57 DECLARE_NAPI_FUNCTION("reset", Reset),
58 DECLARE_NAPI_FUNCTION("release", Release),
59 DECLARE_NAPI_FUNCTION("on", On),
60
61 DECLARE_NAPI_GETTER("state", GetState),
62 };
63
64 napi_property_descriptor staticProperty[] = {
65 DECLARE_NAPI_STATIC_FUNCTION("createVideoRecorder", CreateVideoRecorder),
66 };
67
68 napi_value constructor = nullptr;
69 napi_status status = napi_define_class(env, CLASS_NAME.c_str(), NAPI_AUTO_LENGTH, Constructor, nullptr,
70 sizeof(properties) / sizeof(properties[0]), properties, &constructor);
71 CHECK_AND_RETURN_RET_LOG(status == napi_ok, nullptr, "Failed to define VideoRecorder class");
72
73 status = napi_create_reference(env, constructor, 1, &constructor_);
74 CHECK_AND_RETURN_RET_LOG(status == napi_ok, nullptr, "Failed to create reference of constructor");
75
76 status = napi_set_named_property(env, exports, CLASS_NAME.c_str(), constructor);
77 CHECK_AND_RETURN_RET_LOG(status == napi_ok, nullptr, "Failed to set constructor");
78
79 status = napi_define_properties(env, exports, sizeof(staticProperty) / sizeof(staticProperty[0]), staticProperty);
80 CHECK_AND_RETURN_RET_LOG(status == napi_ok, nullptr, "Failed to define static function");
81
82 MEDIA_LOGD("Init success");
83
84 return exports;
85 }
86
Constructor(napi_env env,napi_callback_info info)87 napi_value VideoRecorderNapi::Constructor(napi_env env, napi_callback_info info)
88 {
89 napi_value result = nullptr;
90 napi_get_undefined(env, &result);
91
92 napi_value jsThis = nullptr;
93 size_t argCount = 0;
94 napi_status status = napi_get_cb_info(env, info, &argCount, nullptr, &jsThis, nullptr);
95 if (status != napi_ok) {
96 MEDIA_LOGE ("Failed to retrieve details about the callback");
97 return result;
98 }
99
100 VideoRecorderNapi *recorderNapi = new(std::nothrow) VideoRecorderNapi();
101 CHECK_AND_RETURN_RET_LOG(recorderNapi != nullptr, result, "No memory!");
102
103 recorderNapi->env_ = env;
104 recorderNapi->recorder_ = RecorderFactory::CreateRecorder();
105 CHECK_AND_RETURN_RET_LOG(recorderNapi->recorder_ != nullptr, result, "failed to CreateRecorder");
106
107 if (recorderNapi->callbackNapi_ == nullptr && recorderNapi->recorder_ != nullptr) {
108 recorderNapi->callbackNapi_ = std::make_shared<RecorderCallbackNapi>(env, true);
109 (void)recorderNapi->recorder_->SetRecorderCallback(recorderNapi->callbackNapi_);
110 }
111
112 status = napi_wrap(env, jsThis, reinterpret_cast<void *>(recorderNapi),
113 VideoRecorderNapi::Destructor, nullptr, nullptr);
114 if (status != napi_ok) {
115 delete recorderNapi;
116 MEDIA_LOGE("Failed to warp native instance!");
117 return result;
118 }
119
120 MEDIA_LOGD("Constructor success");
121 return jsThis;
122 }
123
Destructor(napi_env env,void * nativeObject,void * finalize)124 void VideoRecorderNapi::Destructor(napi_env env, void *nativeObject, void *finalize)
125 {
126 (void)env;
127 (void)finalize;
128 if (nativeObject != nullptr) {
129 VideoRecorderNapi *napi = reinterpret_cast<VideoRecorderNapi *>(nativeObject);
130 if (napi->surface_ != nullptr) {
131 auto id = napi->surface_->GetUniqueId();
132 if (napi->IsSurfaceIdVaild(id)) {
133 (void)SurfaceUtils::GetInstance()->Remove(id);
134 }
135 }
136 delete napi;
137 }
138 MEDIA_LOGD("Destructor success");
139 }
140
CreateVideoRecorder(napi_env env,napi_callback_info info)141 napi_value VideoRecorderNapi::CreateVideoRecorder(napi_env env, napi_callback_info info)
142 {
143 MEDIA_LOGD("CreateVideoRecorder In");
144
145 napi_value result = nullptr;
146 napi_get_undefined(env, &result);
147
148 // get args
149 napi_value jsThis = nullptr;
150 napi_value args[1] = { nullptr };
151 size_t argCount = 1;
152 napi_status status = napi_get_cb_info(env, info, &argCount, args, &jsThis, nullptr);
153 CHECK_AND_RETURN_RET_LOG(status == napi_ok, result, "failed to napi_get_cb_info");
154
155 std::unique_ptr<VideoRecorderAsyncContext> asyncCtx = std::make_unique<VideoRecorderAsyncContext>(env);
156
157 asyncCtx->callbackRef = CommonNapi::CreateReference(env, args[0]);
158 asyncCtx->deferred = CommonNapi::CreatePromise(env, asyncCtx->callbackRef, result);
159 asyncCtx->JsResult = std::make_unique<MediaJsResultInstance>(constructor_);
160 asyncCtx->ctorFlag = true;
161
162 napi_value resource = nullptr;
163 napi_create_string_utf8(env, "createVideoRecorder", NAPI_AUTO_LENGTH, &resource);
164 NAPI_CALL(env, napi_create_async_work(env, nullptr, resource, [](napi_env env, void* data) {},
165 MediaAsyncContext::CompleteCallback, static_cast<void *>(asyncCtx.get()), &asyncCtx->work));
166 NAPI_CALL(env, napi_queue_async_work(env, asyncCtx->work));
167 asyncCtx.release();
168
169 return result;
170 }
171
SignError(VideoRecorderAsyncContext * asyncCtx,int32_t code,const std::string & param1,const std::string & param2)172 static void SignError(VideoRecorderAsyncContext *asyncCtx, int32_t code,
173 const std::string ¶m1, const std::string ¶m2)
174 {
175 std::string message = MSExtErrorAPI9ToString(static_cast<MediaServiceExtErrCodeAPI9>(code), param1, param2);
176 asyncCtx->SignError(code, message);
177 }
178
Prepare(napi_env env,napi_callback_info info)179 napi_value VideoRecorderNapi::Prepare(napi_env env, napi_callback_info info)
180 {
181 MEDIA_LOGD("Prepare In");
182
183 napi_value result = nullptr;
184 napi_get_undefined(env, &result);
185 napi_value jsThis = nullptr;
186 napi_value args[2] = { nullptr };
187
188 auto asyncCtx = std::make_unique<VideoRecorderAsyncContext>(env);
189
190 // get args
191 size_t argCount = 2;
192 napi_status status = napi_get_cb_info(env, info, &argCount, args, &jsThis, nullptr);
193 if (status != napi_ok || jsThis == nullptr) {
194 SignError(asyncCtx.get(), MSERR_EXT_API9_INVALID_PARAMETER, "prepare", "");
195 }
196
197 // get recordernapi
198 (void)napi_unwrap(env, jsThis, reinterpret_cast<void **>(&asyncCtx->napi));
199
200 // get param
201 napi_valuetype valueType = napi_undefined;
202 if (args[0] == nullptr || napi_typeof(env, args[0], &valueType) != napi_ok || valueType != napi_object) {
203 SignError(asyncCtx.get(), MSERR_EXT_API9_INVALID_PARAMETER, "prepare", "");
204 }
205
206 std::string urlPath = CommonNapi::GetPropertyString(env, args[0], "url");
207
208 VideoRecorderProperties videoProperties;
209
210 asyncCtx->napi->GetConfig(env, args[0], asyncCtx, videoProperties);
211
212 if (asyncCtx->napi->GetVideoRecorderProperties(env, args[0], videoProperties) != MSERR_OK) {
213 SignError(asyncCtx.get(), MSERR_EXT_API9_INVALID_PARAMETER, "prepare", "");
214 }
215
216 // set param
217 if (asyncCtx->napi->SetVideoRecorderProperties(asyncCtx, videoProperties) != MSERR_OK) {
218 SignError(asyncCtx.get(), MSERR_EXT_API9_INVALID_PARAMETER, "prepare", "");
219 }
220 if (asyncCtx->napi->SetUrl(urlPath) != MSERR_OK) {
221 SignError(asyncCtx.get(), MSERR_EXT_API9_INVALID_PARAMETER, "urlPath", "");
222 }
223
224 asyncCtx->callbackRef = CommonNapi::CreateReference(env, args[1]);
225 asyncCtx->deferred = CommonNapi::CreatePromise(env, asyncCtx->callbackRef, result);
226
227 asyncCtx->napi->currentStates_ = VideoRecorderState::STATE_PREPARED;
228 // async work
229 napi_value resource = nullptr;
230 napi_create_string_utf8(env, "Prepare", NAPI_AUTO_LENGTH, &resource);
231 NAPI_CALL(env, napi_create_async_work(env, nullptr, resource, [](napi_env env, void* data) {
232 auto threadCtx = reinterpret_cast<VideoRecorderAsyncContext *>(data);
233 CHECK_AND_RETURN_LOG(threadCtx != nullptr, "threadCtx is nullptr!");
234 if (threadCtx->napi == nullptr || threadCtx->napi->recorder_ == nullptr) {
235 SignError(threadCtx, MSERR_EXT_API9_OPERATE_NOT_PERMIT, "prepare", "");
236 return;
237 }
238
239 if (threadCtx->napi->recorder_->Prepare() != MSERR_OK) {
240 SignError(threadCtx, MSERR_EXT_API9_OPERATE_NOT_PERMIT, "prepare", "");
241 }
242 }, MediaAsyncContext::CompleteCallback, static_cast<void *>(asyncCtx.get()), &asyncCtx->work));
243 NAPI_CALL(env, napi_queue_async_work(env, asyncCtx->work));
244 asyncCtx.release();
245 return result;
246 }
247
GetInputSurface(napi_env env,napi_callback_info info)248 napi_value VideoRecorderNapi::GetInputSurface(napi_env env, napi_callback_info info)
249 {
250 MEDIA_LOGD("GetInputSurface In");
251
252 napi_value result = nullptr;
253 napi_get_undefined(env, &result);
254
255 auto asyncCtx = std::make_unique<VideoRecorderAsyncContext>(env);
256 // get args
257 napi_value jsThis = nullptr;
258 napi_value args[1] = {nullptr};
259 size_t argCount = 1;
260 napi_status status = napi_get_cb_info(env, info, &argCount, args, &jsThis, nullptr);
261 if (status != napi_ok || jsThis == nullptr) {
262 SignError(asyncCtx.get(), MSERR_EXT_API9_INVALID_PARAMETER, "GetInputSurface", "");
263 }
264
265 // get recordernapi
266 (void)napi_unwrap(env, jsThis, reinterpret_cast<void **>(&asyncCtx->napi));
267
268 asyncCtx->callbackRef = CommonNapi::CreateReference(env, args[0]);
269 asyncCtx->deferred = CommonNapi::CreatePromise(env, asyncCtx->callbackRef, result);
270
271 napi_value resource = nullptr;
272 napi_create_string_utf8(env, "GetInputSurface", NAPI_AUTO_LENGTH, &resource);
273 NAPI_CALL(env, napi_create_async_work(env, nullptr, resource, [](napi_env env, void* data) {
274 auto threadCtx = reinterpret_cast<VideoRecorderAsyncContext *>(data);
275 CHECK_AND_RETURN_LOG(threadCtx != nullptr, "threadCtx is nullptr!");
276 if (threadCtx->napi == nullptr || threadCtx->napi->recorder_ == nullptr) {
277 SignError(threadCtx, MSERR_EXT_API9_OPERATE_NOT_PERMIT, "GetInputSurface", "");
278 return;
279 }
280
281 threadCtx->napi->surface_ = threadCtx->napi->recorder_->GetSurface(threadCtx->napi->videoSourceID); // source id
282 if (threadCtx->napi->surface_ != nullptr) {
283 SurfaceError error = SurfaceUtils::GetInstance()->Add(threadCtx->napi->surface_->GetUniqueId(),
284 threadCtx->napi->surface_);
285 if (error != SURFACE_ERROR_OK) {
286 SignError(threadCtx, MSERR_EXT_API9_OPERATE_NOT_PERMIT, "GetInputSurface", "");
287 }
288 auto surfaceId = std::to_string(threadCtx->napi->surface_->GetUniqueId());
289 threadCtx->JsResult = std::make_unique<MediaJsResultString>(surfaceId);
290 } else {
291 SignError(threadCtx, MSERR_EXT_API9_OPERATE_NOT_PERMIT, "GetInputSurface", "");
292 }
293 }, MediaAsyncContext::CompleteCallback, static_cast<void *>(asyncCtx.get()), &asyncCtx->work));
294 NAPI_CALL(env, napi_queue_async_work(env, asyncCtx->work));
295 asyncCtx.release();
296 return result;
297 }
298
Start(napi_env env,napi_callback_info info)299 napi_value VideoRecorderNapi::Start(napi_env env, napi_callback_info info)
300 {
301 MEDIA_LOGD("Start In");
302
303 napi_value result = nullptr;
304 napi_get_undefined(env, &result);
305
306 auto asyncCtx = std::make_unique<VideoRecorderAsyncContext>(env);
307
308 napi_value jsThis = nullptr;
309 napi_value args[1] = { nullptr };
310 size_t argCount = 1;
311 napi_status status = napi_get_cb_info(env, info, &argCount, args, &jsThis, nullptr);
312 if (status != napi_ok || jsThis == nullptr) {
313 SignError(asyncCtx.get(), MSERR_EXT_API9_INVALID_PARAMETER, "Start", "");
314 }
315
316 // get recordernapi
317 (void)napi_unwrap(env, jsThis, reinterpret_cast<void **>(&asyncCtx->napi));
318
319 asyncCtx->callbackRef = CommonNapi::CreateReference(env, args[0]);
320 asyncCtx->deferred = CommonNapi::CreatePromise(env, asyncCtx->callbackRef, result);
321
322 // async work
323 napi_value resource = nullptr;
324 napi_create_string_utf8(env, "Start", NAPI_AUTO_LENGTH, &resource);
325 NAPI_CALL(env, napi_create_async_work(env, nullptr, resource, [](napi_env env, void* data) {
326 auto threadCtx = reinterpret_cast<VideoRecorderAsyncContext *>(data);
327 CHECK_AND_RETURN_LOG(threadCtx != nullptr, "threadCtx is nullptr!");
328 if (threadCtx->napi == nullptr || threadCtx->napi->recorder_ == nullptr) {
329 SignError(threadCtx, MSERR_EXT_API9_OPERATE_NOT_PERMIT, "Start", "");
330 return;
331 }
332 if (threadCtx->napi->recorder_->Start() != MSERR_OK) {
333 SignError(threadCtx, MSERR_EXT_API9_OPERATE_NOT_PERMIT, "Start", "");
334 }
335 threadCtx->napi->currentStates_ = VideoRecorderState::STATE_PLAYING;
336 }, MediaAsyncContext::CompleteCallback, static_cast<void *>(asyncCtx.get()), &asyncCtx->work));
337 NAPI_CALL(env, napi_queue_async_work(env, asyncCtx->work));
338 asyncCtx.release();
339 return result;
340 }
341
Pause(napi_env env,napi_callback_info info)342 napi_value VideoRecorderNapi::Pause(napi_env env, napi_callback_info info)
343 {
344 MEDIA_LOGD("Pause In");
345
346 napi_value result = nullptr;
347 napi_get_undefined(env, &result);
348
349 auto asyncCtx = std::make_unique<VideoRecorderAsyncContext>(env);
350
351 napi_value jsThis = nullptr;
352 napi_value args[1] = { nullptr };
353 size_t argCount = 1;
354 napi_status status = napi_get_cb_info(env, info, &argCount, args, &jsThis, nullptr);
355 if (status != napi_ok || jsThis == nullptr) {
356 SignError(asyncCtx.get(), MSERR_EXT_API9_INVALID_PARAMETER, "Pause", "");
357 }
358
359 // get recordernapi
360 (void)napi_unwrap(env, jsThis, reinterpret_cast<void **>(&asyncCtx->napi));
361
362 asyncCtx->callbackRef = CommonNapi::CreateReference(env, args[0]);
363 asyncCtx->deferred = CommonNapi::CreatePromise(env, asyncCtx->callbackRef, result);
364
365 // async work
366 napi_value resource = nullptr;
367 napi_create_string_utf8(env, "Pause", NAPI_AUTO_LENGTH, &resource);
368 NAPI_CALL(env, napi_create_async_work(env, nullptr, resource, [](napi_env env, void* data) {
369 auto threadCtx = reinterpret_cast<VideoRecorderAsyncContext *>(data);
370 CHECK_AND_RETURN_LOG(threadCtx != nullptr, "threadCtx is nullptr!");
371 if (threadCtx->napi == nullptr || threadCtx->napi->recorder_ == nullptr) {
372 SignError(threadCtx, MSERR_EXT_API9_OPERATE_NOT_PERMIT, "Pause", "");
373 return;
374 }
375 if (threadCtx->napi->recorder_->Pause() != MSERR_OK) {
376 SignError(threadCtx, MSERR_EXT_API9_OPERATE_NOT_PERMIT, "Pause", "");
377 }
378 threadCtx->napi->currentStates_ = VideoRecorderState::STATE_PAUSED;
379 }, MediaAsyncContext::CompleteCallback, static_cast<void *>(asyncCtx.get()), &asyncCtx->work));
380 NAPI_CALL(env, napi_queue_async_work(env, asyncCtx->work));
381 asyncCtx.release();
382 return result;
383 }
384
Resume(napi_env env,napi_callback_info info)385 napi_value VideoRecorderNapi::Resume(napi_env env, napi_callback_info info)
386 {
387 MEDIA_LOGD("Resume In");
388
389 napi_value result = nullptr;
390 napi_get_undefined(env, &result);
391
392 auto asyncCtx = std::make_unique<VideoRecorderAsyncContext>(env);
393
394 napi_value jsThis = nullptr;
395 napi_value args[1] = { nullptr };
396 size_t argCount = 1;
397 napi_status status = napi_get_cb_info(env, info, &argCount, args, &jsThis, nullptr);
398 if (status != napi_ok || jsThis == nullptr) {
399 SignError(asyncCtx.get(), MSERR_EXT_API9_INVALID_PARAMETER, "Resume", "");
400 }
401
402 // get recordernapi
403 (void)napi_unwrap(env, jsThis, reinterpret_cast<void **>(&asyncCtx->napi));
404
405 asyncCtx->callbackRef = CommonNapi::CreateReference(env, args[0]);
406 asyncCtx->deferred = CommonNapi::CreatePromise(env, asyncCtx->callbackRef, result);
407
408 // async work
409 napi_value resource = nullptr;
410 napi_create_string_utf8(env, "Resume", NAPI_AUTO_LENGTH, &resource);
411 NAPI_CALL(env, napi_create_async_work(env, nullptr, resource, [](napi_env env, void* data) {
412 auto threadCtx = reinterpret_cast<VideoRecorderAsyncContext *>(data);
413 CHECK_AND_RETURN_LOG(threadCtx != nullptr, "threadCtx is nullptr!");
414 if (threadCtx->napi == nullptr || threadCtx->napi->recorder_ == nullptr) {
415 SignError(threadCtx, MSERR_EXT_API9_OPERATE_NOT_PERMIT, "Resume", "");
416 return;
417 }
418 if (threadCtx->napi->recorder_->Resume() != MSERR_OK) {
419 SignError(threadCtx, MSERR_EXT_API9_OPERATE_NOT_PERMIT, "Resume", "");
420 }
421 threadCtx->napi->currentStates_ = VideoRecorderState::STATE_PLAYING;
422 }, MediaAsyncContext::CompleteCallback, static_cast<void *>(asyncCtx.get()), &asyncCtx->work));
423 NAPI_CALL(env, napi_queue_async_work(env, asyncCtx->work));
424 asyncCtx.release();
425 return result;
426 }
427
Stop(napi_env env,napi_callback_info info)428 napi_value VideoRecorderNapi::Stop(napi_env env, napi_callback_info info)
429 {
430 MEDIA_LOGD("Stop In");
431
432 napi_value result = nullptr;
433 napi_get_undefined(env, &result);
434
435 auto asyncCtx = std::make_unique<VideoRecorderAsyncContext>(env);
436
437 napi_value jsThis = nullptr;
438 napi_value args[1] = { nullptr };
439 size_t argCount = 1;
440 napi_status status = napi_get_cb_info(env, info, &argCount, args, &jsThis, nullptr);
441 if (status != napi_ok || jsThis == nullptr) {
442 SignError(asyncCtx.get(), MSERR_EXT_API9_INVALID_PARAMETER, "Stop", "");
443 }
444
445 // get recordernapi
446 (void)napi_unwrap(env, jsThis, reinterpret_cast<void **>(&asyncCtx->napi));
447
448 asyncCtx->callbackRef = CommonNapi::CreateReference(env, args[0]);
449 asyncCtx->deferred = CommonNapi::CreatePromise(env, asyncCtx->callbackRef, result);
450
451 // async work
452 napi_value resource = nullptr;
453 napi_create_string_utf8(env, "Stop", NAPI_AUTO_LENGTH, &resource);
454 NAPI_CALL(env, napi_create_async_work(env, nullptr, resource, [](napi_env env, void* data) {
455 auto threadCtx = reinterpret_cast<VideoRecorderAsyncContext *>(data);
456 CHECK_AND_RETURN_LOG(threadCtx != nullptr, "threadCtx is nullptr!");
457 if (threadCtx->napi == nullptr || threadCtx->napi->recorder_ == nullptr) {
458 SignError(threadCtx, MSERR_EXT_API9_OPERATE_NOT_PERMIT, "Stop", "");
459 return;
460 }
461 if (threadCtx->napi->recorder_->Stop(false) != MSERR_OK) {
462 SignError(threadCtx, MSERR_EXT_API9_OPERATE_NOT_PERMIT, "Stop", "");
463 }
464 threadCtx->napi->currentStates_ = VideoRecorderState::STATE_STOPPED;
465 }, MediaAsyncContext::CompleteCallback, static_cast<void *>(asyncCtx.get()), &asyncCtx->work));
466 NAPI_CALL(env, napi_queue_async_work(env, asyncCtx->work));
467 asyncCtx.release();
468 return result;
469 }
470
Reset(napi_env env,napi_callback_info info)471 napi_value VideoRecorderNapi::Reset(napi_env env, napi_callback_info info)
472 {
473 MEDIA_LOGD("Reset In");
474
475 napi_value result = nullptr;
476 napi_get_undefined(env, &result);
477
478 auto asyncCtx = std::make_unique<VideoRecorderAsyncContext>(env);
479
480 napi_value jsThis = nullptr;
481 napi_value args[1] = { nullptr };
482 size_t argCount = 1;
483 napi_status status = napi_get_cb_info(env, info, &argCount, args, &jsThis, nullptr);
484 if (status != napi_ok || jsThis == nullptr) {
485 SignError(asyncCtx.get(), MSERR_EXT_API9_INVALID_PARAMETER, "Reset", "");
486 }
487
488 // get recordernapi
489 (void)napi_unwrap(env, jsThis, reinterpret_cast<void **>(&asyncCtx->napi));
490
491 asyncCtx->callbackRef = CommonNapi::CreateReference(env, args[0]);
492 asyncCtx->deferred = CommonNapi::CreatePromise(env, asyncCtx->callbackRef, result);
493
494 // async work
495 napi_value resource = nullptr;
496 napi_create_string_utf8(env, "Reset", NAPI_AUTO_LENGTH, &resource);
497 NAPI_CALL(env, napi_create_async_work(env, nullptr, resource, [](napi_env env, void* data) {
498 auto threadCtx = reinterpret_cast<VideoRecorderAsyncContext *>(data);
499 CHECK_AND_RETURN_LOG(threadCtx != nullptr, "threadCtx is nullptr!");
500 if (threadCtx->napi == nullptr || threadCtx->napi->recorder_ == nullptr) {
501 SignError(threadCtx, MSERR_EXT_API9_OPERATE_NOT_PERMIT, "Reset", "");
502 return;
503 }
504 if (threadCtx->napi->surface_ != nullptr) {
505 auto id = threadCtx->napi->surface_->GetUniqueId();
506 if (threadCtx->napi->IsSurfaceIdVaild(id)) {
507 (void)SurfaceUtils::GetInstance()->Remove(id);
508 }
509 threadCtx->napi->surface_ = nullptr;
510 }
511 if (threadCtx->napi->recorder_->Reset() != MSERR_OK) {
512 SignError(threadCtx, MSERR_EXT_API9_OPERATE_NOT_PERMIT, "Reset", "");
513 }
514 threadCtx->napi->currentStates_ = VideoRecorderState::STATE_IDLE;
515 }, MediaAsyncContext::CompleteCallback, static_cast<void *>(asyncCtx.get()), &asyncCtx->work));
516 NAPI_CALL(env, napi_queue_async_work(env, asyncCtx->work));
517 asyncCtx.release();
518 return result;
519 }
520
Release(napi_env env,napi_callback_info info)521 napi_value VideoRecorderNapi::Release(napi_env env, napi_callback_info info)
522 {
523 MEDIA_LOGD("Release In");
524
525 napi_value result = nullptr;
526 napi_get_undefined(env, &result);
527
528 auto asyncCtx = std::make_unique<VideoRecorderAsyncContext>(env);
529
530 napi_value jsThis = nullptr;
531 napi_value args[1] = { nullptr };
532 size_t argCount = 1;
533 napi_status status = napi_get_cb_info(env, info, &argCount, args, &jsThis, nullptr);
534 if (status != napi_ok || jsThis == nullptr) {
535 SignError(asyncCtx.get(), MSERR_EXT_API9_INVALID_PARAMETER, "Release", "");
536 }
537
538 // get recordernapi
539 (void)napi_unwrap(env, jsThis, reinterpret_cast<void **>(&asyncCtx->napi));
540
541 asyncCtx->callbackRef = CommonNapi::CreateReference(env, args[0]);
542 asyncCtx->deferred = CommonNapi::CreatePromise(env, asyncCtx->callbackRef, result);
543
544 // async work
545 napi_value resource = nullptr;
546 napi_create_string_utf8(env, "Release", NAPI_AUTO_LENGTH, &resource);
547 NAPI_CALL(env, napi_create_async_work(env, nullptr, resource, [](napi_env env, void *data) {
548 auto threadCtx = reinterpret_cast<VideoRecorderAsyncContext *>(data);
549 CHECK_AND_RETURN_LOG(threadCtx != nullptr, "threadCtx is nullptr!");
550 if (threadCtx->napi == nullptr || threadCtx->napi->recorder_ == nullptr) {
551 SignError(threadCtx, MSERR_EXT_API9_OPERATE_NOT_PERMIT, "Release", "");
552 return;
553 }
554 if (threadCtx->napi->surface_ != nullptr) {
555 auto id = threadCtx->napi->surface_->GetUniqueId();
556 if (threadCtx->napi->IsSurfaceIdVaild(id)) {
557 (void)SurfaceUtils::GetInstance()->Remove(id);
558 }
559 threadCtx->napi->surface_ = nullptr;
560 }
561 if (threadCtx->napi->recorder_->Release() != MSERR_OK) {
562 SignError(threadCtx, MSERR_EXT_API9_OPERATE_NOT_PERMIT, "Release", "");
563 }
564 threadCtx->napi->currentStates_ = VideoRecorderState::STATE_IDLE;
565 threadCtx->napi->CancelCallback();
566 }, MediaAsyncContext::CompleteCallback, static_cast<void *>(asyncCtx.get()), &asyncCtx->work));
567 NAPI_CALL(env, napi_queue_async_work(env, asyncCtx->work));
568 asyncCtx.release();
569 return result;
570 }
571
On(napi_env env,napi_callback_info info)572 napi_value VideoRecorderNapi::On(napi_env env, napi_callback_info info)
573 {
574 napi_value result = nullptr;
575 napi_get_undefined(env, &result);
576
577 static constexpr size_t minArgCount = 2;
578 size_t argCount = minArgCount;
579 napi_value args[minArgCount] = { nullptr, nullptr };
580 napi_value jsThis = nullptr;
581 napi_status status = napi_get_cb_info(env, info, &argCount, args, &jsThis, nullptr);
582 if (status != napi_ok || jsThis == nullptr || args[0] == nullptr || args[1] == nullptr) {
583 MEDIA_LOGE("Failed to retrieve details about the callback");
584 return result;
585 }
586
587 VideoRecorderNapi *recorderNapi = nullptr;
588 status = napi_unwrap(env, jsThis, reinterpret_cast<void **>(&recorderNapi));
589 CHECK_AND_RETURN_RET_LOG(status == napi_ok && recorderNapi != nullptr, result, "Failed to retrieve instance");
590
591 napi_valuetype valueType0 = napi_undefined;
592 napi_valuetype valueType1 = napi_undefined;
593 if (napi_typeof(env, args[0], &valueType0) != napi_ok || valueType0 != napi_string ||
594 napi_typeof(env, args[1], &valueType1) != napi_ok || valueType1 != napi_function) {
595 recorderNapi->ErrorCallback(MSERR_EXT_INVALID_VAL);
596 return result;
597 }
598
599 std::string callbackName = CommonNapi::GetStringArgument(env, args[0]);
600 MEDIA_LOGD("callbackName: %{public}s", callbackName.c_str());
601
602 napi_ref ref = nullptr;
603 status = napi_create_reference(env, args[1], 1, &ref);
604 CHECK_AND_RETURN_RET_LOG(status == napi_ok && ref != nullptr, result, "failed to create reference!");
605
606 std::shared_ptr<AutoRef> autoRef = std::make_shared<AutoRef>(env, ref);
607 recorderNapi->SetCallbackReference(callbackName, autoRef);
608 return result;
609 }
610
611
GetConfig(napi_env env,napi_value args,std::unique_ptr<VideoRecorderAsyncContext> & ctx,VideoRecorderProperties & properties)612 void VideoRecorderNapi::GetConfig(napi_env env, napi_value args,
613 std::unique_ptr<VideoRecorderAsyncContext> &ctx, VideoRecorderProperties &properties)
614 {
615 int32_t audioSource = AUDIO_SOURCE_INVALID;
616 int32_t videoSource = VIDEO_SOURCE_BUTT;
617
618 bool ret = CommonNapi::GetPropertyInt32(env, args, "audioSourceType", audioSource);
619 if (ret) {
620 // audio + video
621 properties.audioSourceType = static_cast<AudioSourceType>(audioSource);
622 } else {
623 // pure video
624 ctx->napi->isPureVideo = true;
625 MEDIA_LOGI("No audioSource Type input!");
626 }
627
628 (void)CommonNapi::GetPropertyInt32(env, args, "videoSourceType", videoSource);
629 properties.videoSourceType = static_cast<VideoSourceType>(videoSource);
630
631 (void)CommonNapi::GetPropertyInt32(env, args, "rotation", properties.orientationHint);
632
633 napi_value geoLocation = nullptr;
634 napi_get_named_property(env, args, "location", &geoLocation);
635 double tempLatitude = 0;
636 double tempLongitude = 0;
637 (void)CommonNapi::GetPropertyDouble(env, geoLocation, "latitude", tempLatitude);
638 (void)CommonNapi::GetPropertyDouble(env, geoLocation, "longitude", tempLongitude);
639 properties.location.latitude = static_cast<float>(tempLatitude);
640 properties.location.longitude = static_cast<float>(tempLongitude);
641 }
642
GetVideoRecorderProperties(napi_env env,napi_value args,VideoRecorderProperties & properties)643 int32_t VideoRecorderNapi::GetVideoRecorderProperties(napi_env env, napi_value args,
644 VideoRecorderProperties &properties)
645 {
646 napi_value item = nullptr;
647 napi_get_named_property(env, args, "profile", &item);
648
649 (void)CommonNapi::GetPropertyInt32(env, item, "audioBitrate", properties.profile.audioBitrate);
650 (void)CommonNapi::GetPropertyInt32(env, item, "audioChannels", properties.profile.audioChannels);
651 std::string audioCodec = CommonNapi::GetPropertyString(env, item, "audioCodec");
652 (void)MapMimeToAudioCodecFormat(audioCodec, properties.profile.audioCodecFormat);
653 (void)CommonNapi::GetPropertyInt32(env, item, "audioSampleRate", properties.profile.auidoSampleRate);
654 (void)CommonNapi::GetPropertyInt32(env, item, "durationTime", properties.profile.duration);
655 std::string outputFile = CommonNapi::GetPropertyString(env, item, "fileFormat");
656 (void)MapExtensionNameToOutputFormat(outputFile, properties.profile.outputFormat);
657 (void)CommonNapi::GetPropertyInt32(env, item, "videoBitrate", properties.profile.videoBitrate);
658 std::string videoCodec = CommonNapi::GetPropertyString(env, item, "videoCodec");
659 (void)MapMimeToVideoCodecFormat(videoCodec, properties.profile.videoCodecFormat);
660 (void)CommonNapi::GetPropertyInt32(env, item, "videoFrameWidth", properties.profile.videoFrameWidth);
661 (void)CommonNapi::GetPropertyInt32(env, item, "videoFrameHeight", properties.profile.videoFrameHeight);
662 (void)CommonNapi::GetPropertyInt32(env, item, "videoFrameRate", properties.profile.videoFrameRate);
663
664 return MSERR_OK;
665 }
666
SetVideoRecorderProperties(std::unique_ptr<VideoRecorderAsyncContext> & ctx,const VideoRecorderProperties & properties)667 int32_t VideoRecorderNapi::SetVideoRecorderProperties(std::unique_ptr<VideoRecorderAsyncContext> &ctx,
668 const VideoRecorderProperties &properties)
669 {
670 int32_t ret;
671 CHECK_AND_RETURN_RET(recorder_ != nullptr, MSERR_INVALID_OPERATION);
672 if (ctx->napi->isPureVideo != true) {
673 // audio + video
674 ret = recorder_->SetAudioSource(properties.audioSourceType, ctx->napi->audioSourceID);
675 CHECK_AND_RETURN_RET_LOG(ret == MSERR_OK, MSERR_INVALID_OPERATION, "Fail to set AudioSource");
676
677 ret = recorder_->SetVideoSource(properties.videoSourceType, ctx->napi->videoSourceID);
678 CHECK_AND_RETURN_RET_LOG(ret == MSERR_OK, MSERR_INVALID_OPERATION, "Fail to set VideoSource");
679
680 ret = recorder_->SetOutputFormat(properties.profile.outputFormat);
681 CHECK_AND_RETURN_RET_LOG(ret == MSERR_OK, MSERR_INVALID_OPERATION, "Fail to set OutputFormat");
682
683 ret = recorder_->SetAudioEncoder(ctx->napi->audioSourceID, properties.profile.audioCodecFormat);
684 CHECK_AND_RETURN_RET_LOG(ret == MSERR_OK, MSERR_INVALID_OPERATION, "Fail to set audioCodec");
685
686 ret = recorder_->SetAudioSampleRate(ctx->napi->audioSourceID, properties.profile.auidoSampleRate);
687 CHECK_AND_RETURN_RET_LOG(ret == MSERR_OK, MSERR_INVALID_OPERATION, "Fail to set auidoSampleRate");
688
689 ret = recorder_->SetAudioChannels(ctx->napi->audioSourceID, properties.profile.audioChannels);
690 CHECK_AND_RETURN_RET_LOG(ret == MSERR_OK, MSERR_INVALID_OPERATION, "Fail to set audioChannels");
691
692 ret = recorder_->SetAudioEncodingBitRate(ctx->napi->audioSourceID, properties.profile.audioBitrate);
693 CHECK_AND_RETURN_RET_LOG(ret == MSERR_OK, MSERR_INVALID_OPERATION, "Fail to set audioBitrate");
694 } else {
695 ret = recorder_->SetVideoSource(properties.videoSourceType, ctx->napi->videoSourceID);
696 CHECK_AND_RETURN_RET_LOG(ret == MSERR_OK, MSERR_INVALID_OPERATION, "Fail to set VideoSource");
697
698 ret = recorder_->SetOutputFormat(properties.profile.outputFormat);
699 CHECK_AND_RETURN_RET_LOG(ret == MSERR_OK, MSERR_INVALID_OPERATION, "Fail to set OutputFormat");
700 }
701 ret = recorder_->SetVideoEncoder(ctx->napi->videoSourceID, properties.profile.videoCodecFormat);
702 CHECK_AND_RETURN_RET_LOG(ret == MSERR_OK, MSERR_INVALID_OPERATION, "Fail to set videoCodec");
703
704 ret = recorder_->SetVideoSize(ctx->napi->videoSourceID, properties.profile.videoFrameWidth,
705 properties.profile.videoFrameHeight);
706 CHECK_AND_RETURN_RET_LOG(ret == MSERR_OK, MSERR_INVALID_OPERATION, "Fail to set videoSize");
707
708 ret = recorder_->SetVideoFrameRate(ctx->napi->videoSourceID, properties.profile.videoFrameRate);
709 CHECK_AND_RETURN_RET_LOG(ret == MSERR_OK, MSERR_INVALID_OPERATION, "Fail to set videoFrameRate");
710
711 ret = recorder_->SetVideoEncodingBitRate(ctx->napi->videoSourceID, properties.profile.videoBitrate);
712 CHECK_AND_RETURN_RET_LOG(ret == MSERR_OK, MSERR_INVALID_OPERATION, "Fail to set videoBitrate");
713
714 recorder_->SetLocation(properties.location.latitude, properties.location.longitude);
715 recorder_->SetOrientationHint(properties.orientationHint);
716
717 return MSERR_OK;
718 }
719
SetUrl(const std::string & urlPath)720 int32_t VideoRecorderNapi::SetUrl(const std::string &urlPath)
721 {
722 CHECK_AND_RETURN_RET_LOG(recorder_ != nullptr, MSERR_INVALID_OPERATION, "No memory");
723 const std::string fdHead = "fd://";
724
725 if (urlPath.find(fdHead) != std::string::npos) {
726 int32_t fd = -1;
727 std::string inputFd = urlPath.substr(fdHead.size());
728 CHECK_AND_RETURN_RET(StrToInt(inputFd, fd) == true, MSERR_INVALID_VAL);
729 CHECK_AND_RETURN_RET(fd >= 0, MSERR_INVALID_OPERATION);
730 CHECK_AND_RETURN_RET(recorder_->SetOutputFile(fd) == MSERR_OK, MSERR_INVALID_OPERATION);
731 } else {
732 MEDIA_LOGE("invalid input uri, not a fd!");
733 return MSERR_INVALID_OPERATION;
734 }
735
736 return MSERR_OK;
737 }
738
IsSurfaceIdVaild(uint64_t surfaceID)739 bool VideoRecorderNapi::IsSurfaceIdVaild(uint64_t surfaceID)
740 {
741 auto surface = SurfaceUtils::GetInstance()->GetSurface(surfaceID);
742 if (surface == nullptr) {
743 return false;
744 }
745 return true;
746 }
747
GetState(napi_env env,napi_callback_info info)748 napi_value VideoRecorderNapi::GetState(napi_env env, napi_callback_info info)
749 {
750 napi_value jsThis = nullptr;
751 napi_value result = nullptr;
752 napi_get_undefined(env, &result);
753
754 size_t argCount = 0;
755 napi_status status = napi_get_cb_info(env, info, &argCount, nullptr, &jsThis, nullptr);
756 CHECK_AND_RETURN_RET_LOG(status == napi_ok, result, "Failed to retrieve details about the callback");
757
758 VideoRecorderNapi *recorderNapi = nullptr;
759 status = napi_unwrap(env, jsThis, reinterpret_cast<void **>(&recorderNapi));
760 CHECK_AND_RETURN_RET_LOG(status == napi_ok, result, "Failed to retrieve instance");
761
762 std::string curState = VideoRecorderState::STATE_ERROR;
763 if (recorderNapi->callbackNapi_ != nullptr) {
764 curState = recorderNapi->currentStates_;
765 MEDIA_LOGD("GetState success, State: %{public}s", curState.c_str());
766 }
767
768 napi_value jsResult = nullptr;
769 status = napi_create_string_utf8(env, curState.c_str(), NAPI_AUTO_LENGTH, &jsResult);
770 CHECK_AND_RETURN_RET_LOG(status == napi_ok, result, "napi_create_string_utf8 error");
771 return jsResult;
772 }
773
774 // Synchronous interface can use this to report error
ErrorCallback(MediaServiceExtErrCode errCode)775 void VideoRecorderNapi::ErrorCallback(MediaServiceExtErrCode errCode)
776 {
777 if (callbackNapi_ != nullptr) {
778 auto napiCb = std::static_pointer_cast<RecorderCallbackNapi>(callbackNapi_);
779 napiCb->SendErrorCallback(errCode);
780 }
781 }
782
SetCallbackReference(const std::string & callbackName,std::shared_ptr<AutoRef> ref)783 void VideoRecorderNapi::SetCallbackReference(const std::string &callbackName, std::shared_ptr<AutoRef> ref)
784 {
785 refMap_[callbackName] = ref;
786 if (callbackNapi_ != nullptr) {
787 auto napiCb = std::static_pointer_cast<RecorderCallbackNapi>(callbackNapi_);
788 napiCb->SaveCallbackReference(callbackName, ref);
789 }
790 }
791
CancelCallback()792 void VideoRecorderNapi::CancelCallback()
793 {
794 if (callbackNapi_ != nullptr) {
795 auto napiCb = std::static_pointer_cast<RecorderCallbackNapi>(callbackNapi_);
796 napiCb->ClearCallbackReference();
797 }
798 }
799 } // namespace Media
800 } // namespace OHOS
801