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