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 "recorder_callback_napi.h"
17 #include <uv.h>
18 #include "media_errors.h"
19 #include "media_log.h"
20 #include "scope_guard.h"
21 
22 namespace {
23     constexpr OHOS::HiviewDFX::HiLogLabel LABEL = {LOG_CORE, LOG_DOMAIN_RECORDER, "RecorderCallbackNapi"};
24 }
25 
26 namespace OHOS {
27 namespace Media {
RecorderCallbackNapi(napi_env env,bool isVideo)28 RecorderCallbackNapi::RecorderCallbackNapi(napi_env env, bool isVideo)
29     : env_(env), isVideo_(isVideo)
30 {
31     MEDIA_LOGD("0x%{public}06" PRIXPTR " Instances create", FAKE_POINTER(this));
32 }
33 
~RecorderCallbackNapi()34 RecorderCallbackNapi::~RecorderCallbackNapi()
35 {
36     MEDIA_LOGD("0x%{public}06" PRIXPTR " Instances destroy", FAKE_POINTER(this));
37 }
38 
SaveCallbackReference(const std::string & name,std::weak_ptr<AutoRef> ref)39 void RecorderCallbackNapi::SaveCallbackReference(const std::string &name, std::weak_ptr<AutoRef> ref)
40 {
41     std::lock_guard<std::mutex> lock(mutex_);
42     refMap_[name] = ref;
43 }
44 
ClearCallbackReference()45 void RecorderCallbackNapi::ClearCallbackReference()
46 {
47     std::lock_guard<std::mutex> lock(mutex_);
48     refMap_.clear();
49 }
50 
SendErrorCallback(int32_t errCode)51 void RecorderCallbackNapi::SendErrorCallback(int32_t errCode)
52 {
53     std::lock_guard<std::mutex> lock(mutex_);
54     if (refMap_.find(ERROR_CALLBACK_NAME) == refMap_.end()) {
55         MEDIA_LOGW("can not find error callback!");
56         return;
57     }
58 
59     RecordJsCallback *cb = new(std::nothrow) RecordJsCallback();
60     CHECK_AND_RETURN_LOG(cb != nullptr, "cb is nullptr");
61     cb->autoRef = refMap_.at(ERROR_CALLBACK_NAME);
62     cb->callbackName = ERROR_CALLBACK_NAME;
63     if (isVideo_) {
64         cb->errorMsg = MSExtErrorAPI9ToString(static_cast<MediaServiceExtErrCodeAPI9>(errCode), "", "");
65     } else {
66         cb->errorMsg = MSExtErrorToString(static_cast<MediaServiceExtErrCode>(errCode));
67     }
68     cb->errorCode = errCode;
69     return OnJsErrorCallBack(cb);
70 }
71 
SendStateCallback(const std::string & callbackName)72 void RecorderCallbackNapi::SendStateCallback(const std::string &callbackName)
73 {
74     std::lock_guard<std::mutex> lock(mutex_);
75     if (refMap_.find(callbackName) == refMap_.end()) {
76         MEDIA_LOGW("can not find %{public}s callback!", callbackName.c_str());
77         return;
78     }
79 
80     RecordJsCallback *cb = new(std::nothrow) RecordJsCallback();
81     CHECK_AND_RETURN_LOG(cb != nullptr, "cb is nullptr");
82     cb->autoRef = refMap_.at(callbackName);
83     cb->callbackName = callbackName;
84     return OnJsStateCallBack(cb);
85 }
86 
OnError(RecorderErrorType errorType,int32_t errCode)87 void RecorderCallbackNapi::OnError(RecorderErrorType errorType, int32_t errCode)
88 {
89     MEDIA_LOGD("OnError is called, name: %{public}d, error message: %{public}d", errorType, errCode);
90     if (isVideo_) {
91         MediaServiceExtErrCode err = MSErrorToExtError(static_cast<MediaServiceErrCode>(errCode));
92         return SendErrorCallback(err);
93     } else {
94         MediaServiceExtErrCodeAPI9 err = MSErrorToExtErrorAPI9(static_cast<MediaServiceErrCode>(errCode));
95         return SendErrorCallback(err);
96     }
97 }
98 
OnInfo(int32_t type,int32_t extra)99 void RecorderCallbackNapi::OnInfo(int32_t type, int32_t extra)
100 {
101     MEDIA_LOGD("OnInfo() is called, type: %{public}d, extra: %{public}d", type, extra);
102 }
103 
OnAudioCaptureChange(const AudioRecorderChangeInfo & audioRecorderChangeInfo)104 void RecorderCallbackNapi::OnAudioCaptureChange(const AudioRecorderChangeInfo &audioRecorderChangeInfo)
105 {
106     (void)audioRecorderChangeInfo;
107 }
108 
OnJsStateCallBack(RecordJsCallback * jsCb) const109 void RecorderCallbackNapi::OnJsStateCallBack(RecordJsCallback *jsCb) const
110 {
111     uv_loop_s *loop = nullptr;
112     napi_get_uv_event_loop(env_, &loop);
113     if (loop == nullptr) {
114         MEDIA_LOGE("fail to get uv event loop");
115         delete jsCb;
116         return;
117     }
118 
119     uv_work_t *work = new(std::nothrow) uv_work_t;
120     if (work == nullptr) {
121         MEDIA_LOGE("fail to new uv_work_t");
122         delete jsCb;
123         return;
124     }
125 
126     work->data = reinterpret_cast<void *>(jsCb);
127     int ret = uv_queue_work(loop, work, [] (uv_work_t *work) {}, [] (uv_work_t *work, int status) {
128         // Js Thread
129         CHECK_AND_RETURN_LOG(work != nullptr, "work is nullptr");
130         RecordJsCallback *event = reinterpret_cast<RecordJsCallback *>(work->data);
131         std::string request = event->callbackName;
132         MEDIA_LOGD("OnJsStateCallBack %{public}s, uv_queue_work start", request.c_str());
133         do {
134             CHECK_AND_BREAK_LOG(status != UV_ECANCELED, "%{public}s canceled", request.c_str());
135             std::shared_ptr<AutoRef> ref = event->autoRef.lock();
136             CHECK_AND_BREAK_LOG(ref != nullptr, "%{public}s AutoRef is nullptr", request.c_str());
137 
138             napi_handle_scope scope = nullptr;
139             napi_open_handle_scope(ref->env_, &scope);
140             CHECK_AND_BREAK_LOG(scope != nullptr, "%{public}s scope is nullptr", request.c_str());
141             ON_SCOPE_EXIT(0) {
142                 napi_close_handle_scope(ref->env_, scope);
143             };
144 
145             napi_value jsCallback = nullptr;
146             napi_status nstatus = napi_get_reference_value(ref->env_, ref->cb_, &jsCallback);
147             CHECK_AND_BREAK_LOG(nstatus == napi_ok && jsCallback != nullptr, "%{public}s get reference value fail",
148                 request.c_str());
149 
150             napi_value result = nullptr;
151             nstatus = napi_call_function(ref->env_, nullptr, jsCallback, 0, nullptr, &result);
152             CHECK_AND_BREAK_LOG(nstatus == napi_ok, "%{public}s fail to napi call function", request.c_str());
153         } while (0);
154         delete event;
155         delete work;
156     });
157     if (ret != 0) {
158         MEDIA_LOGE("fail to uv_queue_work task");
159         delete jsCb;
160         delete work;
161     }
162 }
163 
OnJsErrorCallBack(RecordJsCallback * jsCb) const164 void RecorderCallbackNapi::OnJsErrorCallBack(RecordJsCallback *jsCb) const
165 {
166     uv_loop_s *loop = nullptr;
167     napi_get_uv_event_loop(env_, &loop);
168 
169     uv_work_t *work = new(std::nothrow) uv_work_t;
170     if (work == nullptr) {
171         MEDIA_LOGE("No memory");
172         delete jsCb;
173         return;
174     }
175     work->data = reinterpret_cast<void *>(jsCb);
176 
177     // async callback, jsWork and jsWork->data should be heap object.
178     int ret = uv_queue_work(loop, work, [] (uv_work_t *work) {}, [] (uv_work_t *work, int status) {
179         // Js Thread
180         CHECK_AND_RETURN_LOG(work != nullptr, "work is nullptr");
181         RecordJsCallback *event = reinterpret_cast<RecordJsCallback *>(work->data);
182         std::string request = event->callbackName;
183         MEDIA_LOGD("JsCallBack %{public}s, uv_queue_work start", request.c_str());
184         do {
185             CHECK_AND_BREAK_LOG(status != UV_ECANCELED, "%{public}s canceled", request.c_str());
186             std::shared_ptr<AutoRef> ref = event->autoRef.lock();
187             CHECK_AND_BREAK_LOG(ref != nullptr, "%{public}s AutoRef is nullptr", request.c_str());
188 
189             napi_handle_scope scope = nullptr;
190             napi_open_handle_scope(ref->env_, &scope);
191             CHECK_AND_BREAK_LOG(scope != nullptr, "%{public}s scope is nullptr", request.c_str());
192             ON_SCOPE_EXIT(0) { napi_close_handle_scope(ref->env_, scope); };
193 
194             napi_value jsCallback = nullptr;
195             napi_status nstatus = napi_get_reference_value(ref->env_, ref->cb_, &jsCallback);
196             CHECK_AND_BREAK_LOG(nstatus == napi_ok && jsCallback != nullptr, "%{public}s get reference value fail",
197                 request.c_str());
198 
199             napi_value msgValStr = nullptr;
200             nstatus = napi_create_string_utf8(ref->env_, event->errorMsg.c_str(), NAPI_AUTO_LENGTH, &msgValStr);
201             CHECK_AND_BREAK_LOG(nstatus == napi_ok && msgValStr != nullptr, "%{public}s fail to get error code value",
202                 request.c_str());
203 
204             napi_value args[1] = { nullptr };
205             nstatus = napi_create_error(ref->env_, nullptr, msgValStr, &args[0]);
206             CHECK_AND_BREAK_LOG(nstatus == napi_ok && args[0] != nullptr, "%{public}s fail to create error callback",
207                 request.c_str());
208 
209             nstatus = CommonNapi::FillErrorArgs(ref->env_, event->errorCode, args[0]);
210             CHECK_AND_BREAK_LOG(nstatus == napi_ok, "create error callback fail");
211 
212             // Call back function
213             napi_value result = nullptr;
214             nstatus = napi_call_function(ref->env_, nullptr, jsCallback, 1, args, &result);
215             CHECK_AND_BREAK_LOG(nstatus == napi_ok, "%{public}s fail to napi call function", request.c_str());
216         } while (0);
217         delete event;
218         delete work;
219     });
220     if (ret != 0) {
221         MEDIA_LOGE("Failed to execute libuv work queue");
222         delete jsCb;
223         delete work;
224     }
225 }
226 } // namespace Media
227 } // namespace OHOS
228