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, "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
OnJsStateCallBack(RecordJsCallback * jsCb) const104 void RecorderCallbackNapi::OnJsStateCallBack(RecordJsCallback *jsCb) const
105 {
106 uv_loop_s *loop = nullptr;
107 napi_get_uv_event_loop(env_, &loop);
108 if (loop == nullptr) {
109 MEDIA_LOGE("fail to get uv event loop");
110 delete jsCb;
111 return;
112 }
113
114 uv_work_t *work = new(std::nothrow) uv_work_t;
115 if (work == nullptr) {
116 MEDIA_LOGE("fail to new uv_work_t");
117 delete jsCb;
118 return;
119 }
120
121 work->data = reinterpret_cast<void *>(jsCb);
122 int ret = uv_queue_work(loop, work, [] (uv_work_t *work) {}, [] (uv_work_t *work, int status) {
123 // Js Thread
124 CHECK_AND_RETURN_LOG(work != nullptr, "work is nullptr");
125 RecordJsCallback *event = reinterpret_cast<RecordJsCallback *>(work->data);
126 std::string request = event->callbackName;
127 MEDIA_LOGD("OnJsStateCallBack %{public}s, uv_queue_work start", request.c_str());
128 do {
129 CHECK_AND_BREAK_LOG(status != UV_ECANCELED, "%{public}s canceled", request.c_str());
130 std::shared_ptr<AutoRef> ref = event->autoRef.lock();
131 CHECK_AND_BREAK_LOG(ref != nullptr, "%{public}s AutoRef is nullptr", request.c_str());
132
133 napi_handle_scope scope = nullptr;
134 napi_open_handle_scope(ref->env_, &scope);
135 CHECK_AND_BREAK_LOG(scope != nullptr, "%{public}s scope is nullptr", request.c_str());
136 ON_SCOPE_EXIT(0) { napi_close_handle_scope(ref->env_, scope); };
137
138 napi_value jsCallback = nullptr;
139 napi_status nstatus = napi_get_reference_value(ref->env_, ref->cb_, &jsCallback);
140 CHECK_AND_BREAK_LOG(nstatus == napi_ok && jsCallback != nullptr, "%{public}s get reference value fail",
141 request.c_str());
142
143 napi_value result = nullptr;
144 nstatus = napi_call_function(ref->env_, nullptr, jsCallback, 0, nullptr, &result);
145 CHECK_AND_BREAK_LOG(nstatus == napi_ok, "%{public}s fail to napi call function", request.c_str());
146 } while (0);
147 delete event;
148 delete work;
149 });
150 if (ret != 0) {
151 MEDIA_LOGE("fail to uv_queue_work task");
152 delete jsCb;
153 delete work;
154 }
155 }
156
OnJsErrorCallBack(RecordJsCallback * jsCb) const157 void RecorderCallbackNapi::OnJsErrorCallBack(RecordJsCallback *jsCb) const
158 {
159 uv_loop_s *loop = nullptr;
160 napi_get_uv_event_loop(env_, &loop);
161
162 uv_work_t *work = new(std::nothrow) uv_work_t;
163 if (work == nullptr) {
164 MEDIA_LOGE("No memory");
165 delete jsCb;
166 return;
167 }
168 work->data = reinterpret_cast<void *>(jsCb);
169
170 // async callback, jsWork and jsWork->data should be heap object.
171 int ret = uv_queue_work(loop, work, [] (uv_work_t *work) {}, [] (uv_work_t *work, int status) {
172 // Js Thread
173 CHECK_AND_RETURN_LOG(work != nullptr, "work is nullptr");
174 RecordJsCallback *event = reinterpret_cast<RecordJsCallback *>(work->data);
175 std::string request = event->callbackName;
176 MEDIA_LOGD("JsCallBack %{public}s, uv_queue_work start", request.c_str());
177 do {
178 CHECK_AND_BREAK_LOG(status != UV_ECANCELED, "%{public}s canceled", request.c_str());
179 std::shared_ptr<AutoRef> ref = event->autoRef.lock();
180 CHECK_AND_BREAK_LOG(ref != nullptr, "%{public}s AutoRef is nullptr", request.c_str());
181
182 napi_handle_scope scope = nullptr;
183 napi_open_handle_scope(ref->env_, &scope);
184 CHECK_AND_BREAK_LOG(scope != nullptr, "%{public}s scope is nullptr", request.c_str());
185 ON_SCOPE_EXIT(0) { napi_close_handle_scope(ref->env_, scope); };
186
187 napi_value jsCallback = nullptr;
188 napi_status nstatus = napi_get_reference_value(ref->env_, ref->cb_, &jsCallback);
189 CHECK_AND_BREAK_LOG(nstatus == napi_ok && jsCallback != nullptr, "%{public}s get reference value fail",
190 request.c_str());
191
192 napi_value msgValStr = nullptr;
193 nstatus = napi_create_string_utf8(ref->env_, event->errorMsg.c_str(), NAPI_AUTO_LENGTH, &msgValStr);
194 CHECK_AND_BREAK_LOG(nstatus == napi_ok && msgValStr != nullptr, "%{public}s fail to get error code value",
195 request.c_str());
196
197 napi_value args[1] = { nullptr };
198 nstatus = napi_create_error(ref->env_, nullptr, msgValStr, &args[0]);
199 CHECK_AND_BREAK_LOG(nstatus == napi_ok && args[0] != nullptr, "%{public}s fail to create error callback",
200 request.c_str());
201
202 nstatus = CommonNapi::FillErrorArgs(ref->env_, event->errorCode, args[0]);
203 CHECK_AND_RETURN_LOG(nstatus == napi_ok, "create error callback fail");
204
205 // Call back function
206 napi_value result = nullptr;
207 nstatus = napi_call_function(ref->env_, nullptr, jsCallback, 1, args, &result);
208 CHECK_AND_BREAK_LOG(nstatus == napi_ok, "%{public}s fail to napi call function", request.c_str());
209 } while (0);
210 delete event;
211 delete work;
212 });
213 if (ret != 0) {
214 MEDIA_LOGE("Failed to execute libuv work queue");
215 delete jsCb;
216 delete work;
217 }
218 }
219 } // namespace Media
220 } // namespace OHOS
221