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 "soundpool_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_SOUNDPOOL, "SoundPoolCallBackNapi"};
24 }
25
26 namespace OHOS {
27 namespace Media {
SoundPoolCallBackNapi(napi_env env)28 SoundPoolCallBackNapi::SoundPoolCallBackNapi(napi_env env)
29 {
30 env_ = env;
31 MEDIA_LOGI("0x%{public}06" PRIXPTR "Instances create", FAKE_POINTER(this));
32 }
33
OnLoadCompleted(int32_t soundId)34 void SoundPoolCallBackNapi::OnLoadCompleted(int32_t soundId)
35 {
36 MEDIA_LOGI("OnLoadCompleted recived soundId:%{public}d", soundId);
37 SendLoadCompletedCallback(soundId);
38 }
39
OnPlayFinished()40 void SoundPoolCallBackNapi::OnPlayFinished()
41 {
42 MEDIA_LOGI("OnPlayFinished recived");
43 SendPlayCompletedCallback();
44 }
45
OnError(int32_t errorCode)46 void SoundPoolCallBackNapi::OnError(int32_t errorCode)
47 {
48 MEDIA_LOGI("OnError recived:error:%{public}d", errorCode);
49 if (errorCode == MSERR_INVALID_OPERATION) {
50 SendErrorCallback(MSERR_EXT_API9_OPERATE_NOT_PERMIT,
51 "The soundpool timed out. Please confirm that the input stream is normal.");
52 } else if (errorCode == MSERR_NO_MEMORY) {
53 SendErrorCallback(MSERR_EXT_API9_NO_MEMORY, "soundpool memery error.");
54 } else if (errorCode == MSERR_SERVICE_DIED) {
55 SendErrorCallback(MSERR_EXT_API9_SERVICE_DIED, "releated server died");
56 } else {
57 SendErrorCallback(MSERR_EXT_API9_IO, "IO error happened.");
58 }
59 }
60
SaveCallbackReference(const std::string & name,std::weak_ptr<AutoRef> ref)61 void SoundPoolCallBackNapi::SaveCallbackReference(const std::string &name, std::weak_ptr<AutoRef> ref)
62 {
63 std::lock_guard<std::mutex> lock(mutex_);
64 refMap_[name] = ref;
65 MEDIA_LOGI("Set callback type: %{public}s", name.c_str());
66 }
67
CancelCallbackReference(const std::string & name)68 void SoundPoolCallBackNapi::CancelCallbackReference(const std::string &name)
69 {
70 std::lock_guard<std::mutex> lock(mutex_);
71 auto iter = refMap_.find(name);
72 if (iter != refMap_.end()) {
73 refMap_.erase(iter);
74 }
75 MEDIA_LOGI("Cancel callback type: %{public}s", name.c_str());
76 }
77
ClearCallbackReference()78 void SoundPoolCallBackNapi::ClearCallbackReference()
79 {
80 std::lock_guard<std::mutex> lock(mutex_);
81 refMap_.clear();
82 MEDIA_LOGI("ClearCallback!");
83 }
84
SendErrorCallback(int32_t errCode,const std::string & msg)85 void SoundPoolCallBackNapi::SendErrorCallback(int32_t errCode, const std::string &msg)
86 {
87 std::lock_guard<std::mutex> lock(mutex_);
88 if (refMap_.find(SoundPoolEvent::EVENT_ERROR) == refMap_.end()) {
89 MEDIA_LOGW("can not find error callback!");
90 return;
91 }
92
93 SoundPoolJsCallBack *cb = new(std::nothrow) SoundPoolJsCallBack();
94 CHECK_AND_RETURN_LOG(cb != nullptr, "cb is nullptr");
95 cb->autoRef = refMap_.at(SoundPoolEvent::EVENT_ERROR);
96 cb->callbackName = SoundPoolEvent::EVENT_ERROR;
97 cb->errorCode = errCode;
98 cb->errorMsg = msg;
99 return OnJsErrorCallBack(cb);
100 }
101
SendLoadCompletedCallback(int32_t soundId)102 void SoundPoolCallBackNapi::SendLoadCompletedCallback(int32_t soundId)
103 {
104 std::lock_guard<std::mutex> lock(mutex_);
105 if (refMap_.find(SoundPoolEvent::EVENT_LOAD_COMPLETED) == refMap_.end()) {
106 MEDIA_LOGW("can not find loadcompleted callback!");
107 return;
108 }
109
110 SoundPoolJsCallBack *cb = new(std::nothrow) SoundPoolJsCallBack();
111 CHECK_AND_RETURN_LOG(cb != nullptr, "cb is nullptr");
112 cb->autoRef = refMap_.at(SoundPoolEvent::EVENT_LOAD_COMPLETED);
113 cb->callbackName = SoundPoolEvent::EVENT_LOAD_COMPLETED;
114 cb->loadSoundId = soundId;
115 return OnJsloadCompletedCallBack(cb);
116 }
117
SendPlayCompletedCallback()118 void SoundPoolCallBackNapi::SendPlayCompletedCallback()
119 {
120 std::lock_guard<std::mutex> lock(mutex_);
121 if (refMap_.find(SoundPoolEvent::EVENT_PLAY_FINISHED) == refMap_.end()) {
122 MEDIA_LOGW("can not find playfinished callback!");
123 return;
124 }
125
126 SoundPoolJsCallBack *cb = new(std::nothrow) SoundPoolJsCallBack();
127 CHECK_AND_RETURN_LOG(cb != nullptr, "cb is nullptr");
128 cb->autoRef = refMap_.at(SoundPoolEvent::EVENT_PLAY_FINISHED);
129 cb->callbackName = SoundPoolEvent::EVENT_PLAY_FINISHED;
130 return OnJsplayCompletedCallBack(cb);
131 }
132
OnJsErrorCallBack(SoundPoolJsCallBack * jsCb) const133 void SoundPoolCallBackNapi::OnJsErrorCallBack(SoundPoolJsCallBack *jsCb) const
134 {
135 ON_SCOPE_EXIT(0) {
136 delete jsCb;
137 };
138 uv_loop_s *loop = nullptr;
139 napi_get_uv_event_loop(env_, &loop);
140 CHECK_AND_RETURN_LOG(loop != nullptr, "Fail to get uv event loop");
141
142 uv_work_t *work = new(std::nothrow) uv_work_t;
143 CHECK_AND_RETURN_LOG(work != nullptr, "fail to new uv_work_t");
144 ON_SCOPE_EXIT(1) {
145 delete work;
146 };
147 work->data = reinterpret_cast<void *>(jsCb);
148 // async callback, jsWork and jsWork->data should be heap object.
149 int ret = uv_queue_work_with_qos(loop, work, [] (uv_work_t *work) {
150 MEDIA_LOGD("OnJsErrorCallBack uv_queue_work_with_qos");
151 }, [] (uv_work_t *work, int status) {
152 // Js Thread
153 CHECK_AND_RETURN_LOG(work != nullptr, "work is nullptr");
154 SoundPoolJsCallBack *event = reinterpret_cast<SoundPoolJsCallBack *>(work->data);
155 event->RunJsErrorCallBackTask(status, event);
156 delete event;
157 delete work;
158 }, uv_qos_user_initiated);
159 if (ret != 0) {
160 MEDIA_LOGI("fail to uv_queue_work_with_qos task");
161 }
162 CANCEL_SCOPE_EXIT_GUARD(0);
163 CANCEL_SCOPE_EXIT_GUARD(1);
164 }
165
RunJsErrorCallBackTask(int status,SoundPoolJsCallBack * event)166 void SoundPoolCallBackNapi::SoundPoolJsCallBack::RunJsErrorCallBackTask(int status, SoundPoolJsCallBack *event)
167 {
168 std::string request = event->callbackName;
169 do {
170 CHECK_AND_BREAK_LOG(status != UV_ECANCELED, "%{public}s canceled", request.c_str());
171 std::shared_ptr<AutoRef> ref = event->autoRef.lock();
172 CHECK_AND_BREAK_LOG(ref != nullptr, "%{public}s AutoRef is nullptr", request.c_str());
173
174 napi_handle_scope scope = nullptr;
175 napi_open_handle_scope(ref->env_, &scope);
176 CHECK_AND_BREAK_LOG(scope != nullptr, "%{public}s scope is nullptr", request.c_str());
177 ON_SCOPE_EXIT(0) {
178 napi_close_handle_scope(ref->env_, scope);
179 };
180
181 napi_value jsCallback = nullptr;
182 napi_status nstatus = napi_get_reference_value(ref->env_, ref->cb_, &jsCallback);
183 CHECK_AND_BREAK_LOG(nstatus == napi_ok && jsCallback != nullptr, "%{public}s get reference value fail",
184 request.c_str());
185
186 napi_value msgValStr = nullptr;
187 nstatus = napi_create_string_utf8(ref->env_, event->errorMsg.c_str(), NAPI_AUTO_LENGTH, &msgValStr);
188 CHECK_AND_BREAK_LOG(nstatus == napi_ok && msgValStr != nullptr, "create error message str fail");
189
190 napi_value args[1] = { nullptr };
191 nstatus = napi_create_error(ref->env_, nullptr, msgValStr, &args[0]);
192 CHECK_AND_BREAK_LOG(nstatus == napi_ok && args[0] != nullptr, "create error callback fail");
193
194 nstatus = CommonNapi::FillErrorArgs(ref->env_, event->errorCode, args[0]);
195 CHECK_AND_BREAK_LOG(nstatus == napi_ok, "create error callback fail");
196 // Call back function
197 napi_value result = nullptr;
198 nstatus = napi_call_function(ref->env_, nullptr, jsCallback, 1, args, &result);
199 CHECK_AND_BREAK_LOG(nstatus == napi_ok, "%{public}s fail to napi call function", request.c_str());
200 } while (0);
201 }
202
OnJsloadCompletedCallBack(SoundPoolJsCallBack * jsCb) const203 void SoundPoolCallBackNapi::OnJsloadCompletedCallBack(SoundPoolJsCallBack *jsCb) const
204 {
205 ON_SCOPE_EXIT(0) {
206 delete jsCb;
207 };
208 uv_loop_s *loop = nullptr;
209 napi_get_uv_event_loop(env_, &loop);
210 CHECK_AND_RETURN_LOG(loop != nullptr, "Fail to get uv event loop");
211
212 uv_work_t *work = new(std::nothrow) uv_work_t;
213 CHECK_AND_RETURN_LOG(work != nullptr, "fail to new uv_work_t");
214 ON_SCOPE_EXIT(1) {
215 delete work;
216 };
217 work->data = reinterpret_cast<void *>(jsCb);
218 // async callback, jsWork and jsWork->data should be heap object.
219 int ret = uv_queue_work_with_qos(loop, work, [] (uv_work_t *work) {
220 MEDIA_LOGD("OnJsloadCompletedCallBack uv_queue_work_with_qos");
221 }, [] (uv_work_t *work, int status) {
222 CHECK_AND_RETURN_LOG(work != nullptr, "work is nullptr");
223 if (work->data != nullptr) {
224 MEDIA_LOGD("work data not nullptr");
225 SoundPoolJsCallBack *event = reinterpret_cast<SoundPoolJsCallBack *>(work->data);
226 event->RunJsloadCompletedCallBackTask(status, event);
227 delete event;
228 }
229 delete work;
230 }, uv_qos_user_initiated);
231 if (ret != 0) {
232 MEDIA_LOGI("fail to uv_queue_work_with_qos task");
233 }
234 CANCEL_SCOPE_EXIT_GUARD(0);
235 CANCEL_SCOPE_EXIT_GUARD(1);
236 }
237
RunJsloadCompletedCallBackTask(int status,SoundPoolJsCallBack * event)238 void SoundPoolCallBackNapi::SoundPoolJsCallBack::RunJsloadCompletedCallBackTask(int status,
239 SoundPoolJsCallBack *event)
240 {
241 std::string request = event->callbackName;
242 do {
243 CHECK_AND_BREAK_LOG(status != UV_ECANCELED, "%{public}s canceled", request.c_str());
244 std::shared_ptr<AutoRef> ref = event->autoRef.lock();
245 CHECK_AND_BREAK_LOG(ref != nullptr, "%{public}s AutoRef is nullptr", request.c_str());
246 napi_handle_scope scope = nullptr;
247 napi_open_handle_scope(ref->env_, &scope);
248 CHECK_AND_BREAK_LOG(scope != nullptr, "%{public}s scope is nullptr", request.c_str());
249 ON_SCOPE_EXIT(0) {
250 napi_close_handle_scope(ref->env_, scope);
251 };
252 napi_value jsCallback = nullptr;
253 napi_status nstatus = napi_get_reference_value(ref->env_, ref->cb_, &jsCallback);
254 CHECK_AND_BREAK_LOG(nstatus == napi_ok && jsCallback != nullptr, "%{public}s get reference value fail",
255 request.c_str());
256 napi_value args[1] = { nullptr };
257 nstatus = napi_create_int32(ref->env_, event->loadSoundId, &args[0]);
258 CHECK_AND_BREAK_LOG(nstatus == napi_ok && args[0] != nullptr,
259 "%{public}s fail to create callback", request.c_str());
260 const size_t argCount = 1;
261 napi_value result = nullptr;
262 nstatus = napi_call_function(ref->env_, nullptr, jsCallback, argCount, args, &result);
263 CHECK_AND_BREAK_LOG(nstatus == napi_ok, "%{public}s fail to napi call function", request.c_str());
264 } while (0);
265 }
266
OnJsplayCompletedCallBack(SoundPoolJsCallBack * jsCb) const267 void SoundPoolCallBackNapi::OnJsplayCompletedCallBack(SoundPoolJsCallBack *jsCb) const
268 {
269 ON_SCOPE_EXIT(0) {
270 delete jsCb;
271 };
272 uv_loop_s *loop = nullptr;
273 napi_get_uv_event_loop(env_, &loop);
274 CHECK_AND_RETURN_LOG(loop != nullptr, "Fail to get uv event loop");
275
276 uv_work_t *work = new(std::nothrow) uv_work_t;
277 CHECK_AND_RETURN_LOG(work != nullptr, "fail to new uv_work_t");
278 ON_SCOPE_EXIT(1) {
279 delete work;
280 };
281 work->data = reinterpret_cast<void *>(jsCb);
282 // async callback, jsWork and jsWork->data should be heap object.
283 int ret = uv_queue_work_with_qos(loop, work, [] (uv_work_t *work) {
284 MEDIA_LOGD("OnJsplayCompletedCallBack uv_queue_work_with_qos");
285 }, [] (uv_work_t *work, int status) {
286 // Js Thread
287 CHECK_AND_RETURN_LOG(work != nullptr, "work is nullptr");
288 if (work->data != nullptr) {
289 MEDIA_LOGI("work data not nullptr");
290 SoundPoolJsCallBack *event = reinterpret_cast<SoundPoolJsCallBack *>(work->data);
291 event->RunJsplayCompletedCallBackTask(status, event);
292 delete event;
293 }
294 delete work;
295 }, uv_qos_user_initiated);
296 if (ret != 0) {
297 MEDIA_LOGI("fail to uv_queue_work_with_qos task");
298 }
299
300 CANCEL_SCOPE_EXIT_GUARD(0);
301 CANCEL_SCOPE_EXIT_GUARD(1);
302 }
303
RunJsplayCompletedCallBackTask(int status,SoundPoolJsCallBack * event)304 void SoundPoolCallBackNapi::SoundPoolJsCallBack::RunJsplayCompletedCallBackTask(int status,
305 SoundPoolJsCallBack *event)
306 {
307 std::string request = event->callbackName;
308 do {
309 CHECK_AND_BREAK_LOG(status != UV_ECANCELED, "%{public}s canceled", request.c_str());
310 std::shared_ptr<AutoRef> ref = event->autoRef.lock();
311 CHECK_AND_BREAK_LOG(ref != nullptr, "%{public}s AutoRef is nullptr", request.c_str());
312
313 napi_handle_scope scope = nullptr;
314 napi_open_handle_scope(ref->env_, &scope);
315 CHECK_AND_BREAK_LOG(scope != nullptr, "%{public}s scope is nullptr", request.c_str());
316 ON_SCOPE_EXIT(0) {
317 napi_close_handle_scope(ref->env_, scope);
318 };
319 napi_value jsCallback = nullptr;
320 napi_status nstatus = napi_get_reference_value(ref->env_, ref->cb_, &jsCallback);
321 CHECK_AND_BREAK_LOG(nstatus == napi_ok && jsCallback != nullptr, "%{public}s get reference value fail",
322 request.c_str());
323
324 napi_value result = nullptr;
325 nstatus = napi_call_function(ref->env_, nullptr, jsCallback, 0, nullptr, &result);
326 CHECK_AND_BREAK_LOG(nstatus == napi_ok, "%{public}s fail to napi call function", request.c_str());
327 } while (0);
328 }
329
330 } // namespace Media
331 } // namespace OHOS