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 &param1, const std::string &param2, 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