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