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(int32_t streamID)40 void SoundPoolCallBackNapi::OnPlayFinished(int32_t streamID)
41 {
42 MEDIA_LOGI("OnPlayFinished recived");
43 SendPlayCompletedCallback(streamID);
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(int32_t streamID)118 void SoundPoolCallBackNapi::SendPlayCompletedCallback(int32_t streamID)
119 {
120 std::lock_guard<std::mutex> lock(mutex_);
121 if (refMap_.find(SoundPoolEvent::EVENT_PLAY_FINISHED) == refMap_.end() &&
122 refMap_.find(SoundPoolEvent::EVENT_PLAY_FINISHED_WITH_STREAM_ID) == refMap_.end()) {
123 MEDIA_LOGW("can not find playfinished callback!");
124 return;
125 }
126
127 SoundPoolJsCallBack *cb = new(std::nothrow) SoundPoolJsCallBack();
128 CHECK_AND_RETURN_LOG(cb != nullptr, "cb is nullptr");
129 std::weak_ptr<AutoRef> autoRefFinished;
130 std::weak_ptr<AutoRef> autoRefFinishedStreamID;
131 auto it = refMap_.find(SoundPoolEvent::EVENT_PLAY_FINISHED);
132 if (it != refMap_.end()) {
133 autoRefFinished = it->second;
134 }
135 auto itStreamId = refMap_.find(SoundPoolEvent::EVENT_PLAY_FINISHED_WITH_STREAM_ID);
136 if (itStreamId != refMap_.end()) {
137 autoRefFinishedStreamID = itStreamId->second;
138 }
139
140 if (std::shared_ptr<AutoRef> ref = autoRefFinishedStreamID.lock()) {
141 cb->autoRef = autoRefFinishedStreamID;
142 cb->callbackName = SoundPoolEvent::EVENT_PLAY_FINISHED_WITH_STREAM_ID;
143 cb->playFinishedStreamID = streamID;
144 } else {
145 cb->autoRef = autoRefFinished;
146 cb->callbackName = SoundPoolEvent::EVENT_PLAY_FINISHED;
147 }
148 return OnJsplayCompletedCallBack(cb);
149 }
150
OnJsErrorCallBack(SoundPoolJsCallBack * jsCb) const151 void SoundPoolCallBackNapi::OnJsErrorCallBack(SoundPoolJsCallBack *jsCb) const
152 {
153 ON_SCOPE_EXIT(0) {
154 delete jsCb;
155 };
156 uv_loop_s *loop = nullptr;
157 napi_get_uv_event_loop(env_, &loop);
158 CHECK_AND_RETURN_LOG(loop != nullptr, "Fail to get uv event loop");
159
160 uv_work_t *work = new(std::nothrow) uv_work_t;
161 CHECK_AND_RETURN_LOG(work != nullptr, "fail to new uv_work_t");
162 ON_SCOPE_EXIT(1) {
163 delete work;
164 };
165 work->data = reinterpret_cast<void *>(jsCb);
166 // async callback, jsWork and jsWork->data should be heap object.
167 int ret = uv_queue_work_with_qos(loop, work, [] (uv_work_t *work) {
168 MEDIA_LOGD("OnJsErrorCallBack uv_queue_work_with_qos");
169 }, [] (uv_work_t *work, int status) {
170 // Js Thread
171 CHECK_AND_RETURN_LOG(work != nullptr, "work is nullptr");
172 SoundPoolJsCallBack *event = reinterpret_cast<SoundPoolJsCallBack *>(work->data);
173 event->RunJsErrorCallBackTask(status, event);
174 delete event;
175 delete work;
176 }, uv_qos_user_initiated);
177 if (ret != 0) {
178 MEDIA_LOGI("fail to uv_queue_work_with_qos task");
179 }
180 CANCEL_SCOPE_EXIT_GUARD(0);
181 CANCEL_SCOPE_EXIT_GUARD(1);
182 }
183
RunJsErrorCallBackTask(int status,SoundPoolJsCallBack * event)184 void SoundPoolCallBackNapi::SoundPoolJsCallBack::RunJsErrorCallBackTask(int status, SoundPoolJsCallBack *event)
185 {
186 std::string request = event->callbackName;
187 do {
188 CHECK_AND_BREAK_LOG(status != UV_ECANCELED, "%{public}s canceled", request.c_str());
189 std::shared_ptr<AutoRef> ref = event->autoRef.lock();
190 CHECK_AND_BREAK_LOG(ref != nullptr, "%{public}s AutoRef is nullptr", request.c_str());
191
192 napi_handle_scope scope = nullptr;
193 napi_open_handle_scope(ref->env_, &scope);
194 CHECK_AND_BREAK_LOG(scope != nullptr, "%{public}s scope is nullptr", request.c_str());
195 ON_SCOPE_EXIT(0) {
196 napi_close_handle_scope(ref->env_, scope);
197 };
198
199 napi_value jsCallback = nullptr;
200 napi_status nstatus = napi_get_reference_value(ref->env_, ref->cb_, &jsCallback);
201 CHECK_AND_BREAK_LOG(nstatus == napi_ok && jsCallback != nullptr, "%{public}s get reference value fail",
202 request.c_str());
203
204 napi_value msgValStr = nullptr;
205 nstatus = napi_create_string_utf8(ref->env_, event->errorMsg.c_str(), NAPI_AUTO_LENGTH, &msgValStr);
206 CHECK_AND_BREAK_LOG(nstatus == napi_ok && msgValStr != nullptr, "create error message str fail");
207
208 napi_value args[1] = { nullptr };
209 nstatus = napi_create_error(ref->env_, nullptr, msgValStr, &args[0]);
210 CHECK_AND_BREAK_LOG(nstatus == napi_ok && args[0] != nullptr, "create error callback fail");
211
212 nstatus = CommonNapi::FillErrorArgs(ref->env_, event->errorCode, args[0]);
213 CHECK_AND_BREAK_LOG(nstatus == napi_ok, "create error callback fail");
214 // Call back function
215 napi_value result = nullptr;
216 nstatus = napi_call_function(ref->env_, nullptr, jsCallback, 1, args, &result);
217 CHECK_AND_BREAK_LOG(nstatus == napi_ok, "%{public}s fail to napi call function", request.c_str());
218 } while (0);
219 }
220
OnJsloadCompletedCallBack(SoundPoolJsCallBack * jsCb) const221 void SoundPoolCallBackNapi::OnJsloadCompletedCallBack(SoundPoolJsCallBack *jsCb) const
222 {
223 ON_SCOPE_EXIT(0) {
224 delete jsCb;
225 };
226 uv_loop_s *loop = nullptr;
227 napi_get_uv_event_loop(env_, &loop);
228 CHECK_AND_RETURN_LOG(loop != nullptr, "Fail to get uv event loop");
229
230 uv_work_t *work = new(std::nothrow) uv_work_t;
231 CHECK_AND_RETURN_LOG(work != nullptr, "fail to new uv_work_t");
232 ON_SCOPE_EXIT(1) {
233 delete work;
234 };
235 work->data = reinterpret_cast<void *>(jsCb);
236 // async callback, jsWork and jsWork->data should be heap object.
237 int ret = uv_queue_work_with_qos(loop, work, [] (uv_work_t *work) {
238 MEDIA_LOGD("OnJsloadCompletedCallBack uv_queue_work_with_qos");
239 }, [] (uv_work_t *work, int status) {
240 CHECK_AND_RETURN_LOG(work != nullptr, "work is nullptr");
241 if (work->data != nullptr) {
242 MEDIA_LOGD("work data not nullptr");
243 SoundPoolJsCallBack *event = reinterpret_cast<SoundPoolJsCallBack *>(work->data);
244 event->RunJsloadCompletedCallBackTask(status, event);
245 delete event;
246 }
247 delete work;
248 }, uv_qos_user_initiated);
249 if (ret != 0) {
250 MEDIA_LOGI("fail to uv_queue_work_with_qos task");
251 }
252 CANCEL_SCOPE_EXIT_GUARD(0);
253 CANCEL_SCOPE_EXIT_GUARD(1);
254 }
255
RunJsloadCompletedCallBackTask(int status,SoundPoolJsCallBack * event)256 void SoundPoolCallBackNapi::SoundPoolJsCallBack::RunJsloadCompletedCallBackTask(int status,
257 SoundPoolJsCallBack *event)
258 {
259 std::string request = event->callbackName;
260 do {
261 CHECK_AND_BREAK_LOG(status != UV_ECANCELED, "%{public}s canceled", request.c_str());
262 std::shared_ptr<AutoRef> ref = event->autoRef.lock();
263 CHECK_AND_BREAK_LOG(ref != nullptr, "%{public}s AutoRef is nullptr", request.c_str());
264 napi_handle_scope scope = nullptr;
265 napi_open_handle_scope(ref->env_, &scope);
266 CHECK_AND_BREAK_LOG(scope != nullptr, "%{public}s scope is nullptr", request.c_str());
267 ON_SCOPE_EXIT(0) {
268 napi_close_handle_scope(ref->env_, scope);
269 };
270 napi_value jsCallback = nullptr;
271 napi_status nstatus = napi_get_reference_value(ref->env_, ref->cb_, &jsCallback);
272 CHECK_AND_BREAK_LOG(nstatus == napi_ok && jsCallback != nullptr, "%{public}s get reference value fail",
273 request.c_str());
274 napi_value args[1] = { nullptr };
275 nstatus = napi_create_int32(ref->env_, event->loadSoundId, &args[0]);
276 CHECK_AND_BREAK_LOG(nstatus == napi_ok && args[0] != nullptr,
277 "%{public}s fail to create callback", request.c_str());
278 const size_t argCount = 1;
279 napi_value result = nullptr;
280 nstatus = napi_call_function(ref->env_, nullptr, jsCallback, argCount, args, &result);
281 CHECK_AND_BREAK_LOG(nstatus == napi_ok, "%{public}s fail to napi call function", request.c_str());
282 } while (0);
283 }
284
OnJsplayCompletedCallBack(SoundPoolJsCallBack * jsCb) const285 void SoundPoolCallBackNapi::OnJsplayCompletedCallBack(SoundPoolJsCallBack *jsCb) const
286 {
287 ON_SCOPE_EXIT(0) {
288 delete jsCb;
289 };
290 uv_loop_s *loop = nullptr;
291 napi_get_uv_event_loop(env_, &loop);
292 CHECK_AND_RETURN_LOG(loop != nullptr, "Fail to get uv event loop");
293
294 uv_work_t *work = new(std::nothrow) uv_work_t;
295 CHECK_AND_RETURN_LOG(work != nullptr, "fail to new uv_work_t");
296 ON_SCOPE_EXIT(1) {
297 delete work;
298 };
299 work->data = reinterpret_cast<void *>(jsCb);
300 // async callback, jsWork and jsWork->data should be heap object.
301 int ret = uv_queue_work_with_qos(loop, work, [] (uv_work_t *work) {
302 MEDIA_LOGD("OnJsplayCompletedCallBack uv_queue_work_with_qos");
303 }, [] (uv_work_t *work, int status) {
304 // Js Thread
305 CHECK_AND_RETURN_LOG(work != nullptr, "work is nullptr");
306 if (work->data != nullptr) {
307 MEDIA_LOGI("work data not nullptr");
308 SoundPoolJsCallBack *event = reinterpret_cast<SoundPoolJsCallBack *>(work->data);
309 event->RunJsplayCompletedCallBackTask(status, event);
310 delete event;
311 }
312 delete work;
313 }, uv_qos_user_initiated);
314 if (ret != 0) {
315 MEDIA_LOGI("fail to uv_queue_work_with_qos task");
316 }
317
318 CANCEL_SCOPE_EXIT_GUARD(0);
319 CANCEL_SCOPE_EXIT_GUARD(1);
320 }
321
RunJsplayCompletedCallBackTask(int status,SoundPoolJsCallBack * event)322 void SoundPoolCallBackNapi::SoundPoolJsCallBack::RunJsplayCompletedCallBackTask(int status,
323 SoundPoolJsCallBack *event)
324 {
325 std::string request = event->callbackName;
326 do {
327 CHECK_AND_BREAK_LOG(status != UV_ECANCELED, "%{public}s canceled", request.c_str());
328 std::shared_ptr<AutoRef> ref = event->autoRef.lock();
329 CHECK_AND_BREAK_LOG(ref != nullptr, "%{public}s AutoRef is nullptr", request.c_str());
330
331 napi_handle_scope scope = nullptr;
332 napi_open_handle_scope(ref->env_, &scope);
333 CHECK_AND_BREAK_LOG(scope != nullptr, "%{public}s scope is nullptr", request.c_str());
334 ON_SCOPE_EXIT(0) {
335 napi_close_handle_scope(ref->env_, scope);
336 };
337 napi_value jsCallback = nullptr;
338 napi_status nstatus = napi_get_reference_value(ref->env_, ref->cb_, &jsCallback);
339 CHECK_AND_BREAK_LOG(nstatus == napi_ok && jsCallback != nullptr, "%{public}s get reference value fail",
340 request.c_str());
341
342 if (request == SoundPoolEvent::EVENT_PLAY_FINISHED_WITH_STREAM_ID) {
343 napi_value args[1] = { nullptr };
344 nstatus = napi_create_int32(ref->env_, event->playFinishedStreamID, &args[0]);
345 CHECK_AND_BREAK_LOG(nstatus == napi_ok && args[0] != nullptr,
346 "%{public}s fail to create callback", request.c_str());
347 const size_t argCount = 1;
348 napi_value result = nullptr;
349 nstatus = napi_call_function(ref->env_, nullptr, jsCallback, argCount, args, &result);
350 CHECK_AND_BREAK_LOG(nstatus == napi_ok, "%{public}s fail to napi call function", request.c_str());
351 } else {
352 napi_value result = nullptr;
353 nstatus = napi_call_function(ref->env_, nullptr, jsCallback, 0, nullptr, &result);
354 CHECK_AND_BREAK_LOG(nstatus == napi_ok, "%{public}s fail to napi call function", request.c_str());
355 }
356 } while (0);
357 }
358
359 } // namespace Media
360 } // namespace OHOS