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