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 std::string request = event->callbackName;
156 do {
157 CHECK_AND_BREAK_LOG(status != UV_ECANCELED, "%{public}s canceled", request.c_str());
158 std::shared_ptr<AutoRef> ref = event->autoRef.lock();
159 CHECK_AND_BREAK_LOG(ref != nullptr, "%{public}s AutoRef is nullptr", request.c_str());
160
161 napi_handle_scope scope = nullptr;
162 napi_open_handle_scope(ref->env_, &scope);
163 CHECK_AND_BREAK_LOG(scope != nullptr, "%{public}s scope is nullptr", request.c_str());
164 ON_SCOPE_EXIT(0) {
165 napi_close_handle_scope(ref->env_, scope);
166 };
167
168 napi_value jsCallback = nullptr;
169 napi_status nstatus = napi_get_reference_value(ref->env_, ref->cb_, &jsCallback);
170 CHECK_AND_BREAK_LOG(nstatus == napi_ok && jsCallback != nullptr, "%{public}s get reference value fail",
171 request.c_str());
172
173 napi_value msgValStr = nullptr;
174 nstatus = napi_create_string_utf8(ref->env_, event->errorMsg.c_str(), NAPI_AUTO_LENGTH, &msgValStr);
175 CHECK_AND_BREAK_LOG(nstatus == napi_ok && msgValStr != nullptr, "create error message str fail");
176
177 napi_value args[1] = { nullptr };
178 nstatus = napi_create_error(ref->env_, nullptr, msgValStr, &args[0]);
179 CHECK_AND_BREAK_LOG(nstatus == napi_ok && args[0] != nullptr, "create error callback fail");
180
181 nstatus = CommonNapi::FillErrorArgs(ref->env_, event->errorCode, args[0]);
182 CHECK_AND_BREAK_LOG(nstatus == napi_ok, "create error callback fail");
183 // Call back function
184 napi_value result = nullptr;
185 nstatus = napi_call_function(ref->env_, nullptr, jsCallback, 1, args, &result);
186 CHECK_AND_BREAK_LOG(nstatus == napi_ok, "%{public}s fail to napi call function", request.c_str());
187 } while (0);
188 delete event;
189 delete work;
190 }, uv_qos_user_initiated);
191 CHECK_AND_RETURN_LOG(ret == 0, "fail to uv_queue_work_with_qos task");
192 CANCEL_SCOPE_EXIT_GUARD(0);
193 CANCEL_SCOPE_EXIT_GUARD(1);
194 }
195
OnJsloadCompletedCallBack(SoundPoolJsCallBack * jsCb) const196 void SoundPoolCallBackNapi::OnJsloadCompletedCallBack(SoundPoolJsCallBack *jsCb) const
197 {
198 ON_SCOPE_EXIT(0) {
199 delete jsCb;
200 };
201 uv_loop_s *loop = nullptr;
202 napi_get_uv_event_loop(env_, &loop);
203 CHECK_AND_RETURN_LOG(loop != nullptr, "Fail to get uv event loop");
204
205 uv_work_t *work = new(std::nothrow) uv_work_t;
206 CHECK_AND_RETURN_LOG(work != nullptr, "fail to new uv_work_t");
207 ON_SCOPE_EXIT(1) {
208 delete work;
209 };
210 work->data = reinterpret_cast<void *>(jsCb);
211 // async callback, jsWork and jsWork->data should be heap object.
212 int ret = uv_queue_work_with_qos(loop, work, [] (uv_work_t *work) {
213 MEDIA_LOGD("OnJsloadCompletedCallBack uv_queue_work_with_qos");
214 }, [] (uv_work_t *work, int status) {
215 CHECK_AND_RETURN_LOG(work != nullptr, "work is nullptr");
216 if (work->data != nullptr) {
217 MEDIA_LOGD("work data not nullptr");
218 SoundPoolJsCallBack *event = reinterpret_cast<SoundPoolJsCallBack *>(work->data);
219 std::string request = event->callbackName;
220 do {
221 CHECK_AND_BREAK_LOG(status != UV_ECANCELED, "%{public}s canceled", request.c_str());
222 std::shared_ptr<AutoRef> ref = event->autoRef.lock();
223 CHECK_AND_BREAK_LOG(ref != nullptr, "%{public}s AutoRef is nullptr", request.c_str());
224 napi_handle_scope scope = nullptr;
225 napi_open_handle_scope(ref->env_, &scope);
226 CHECK_AND_BREAK_LOG(scope != nullptr, "%{public}s scope is nullptr", request.c_str());
227 ON_SCOPE_EXIT(0) {
228 napi_close_handle_scope(ref->env_, scope);
229 };
230 napi_value jsCallback = nullptr;
231 napi_status nstatus = napi_get_reference_value(ref->env_, ref->cb_, &jsCallback);
232 CHECK_AND_BREAK_LOG(nstatus == napi_ok && jsCallback != nullptr, "%{public}s get reference value fail",
233 request.c_str());
234 napi_value args[1] = { nullptr };
235 nstatus = napi_create_int32(ref->env_, event->loadSoundId, &args[0]);
236 CHECK_AND_BREAK_LOG(nstatus == napi_ok && args[0] != nullptr,
237 "%{public}s fail to create callback", request.c_str());
238 const size_t argCount = 1;
239 napi_value result = nullptr;
240 nstatus = napi_call_function(ref->env_, nullptr, jsCallback, argCount, args, &result);
241 CHECK_AND_BREAK_LOG(nstatus == napi_ok, "%{public}s fail to napi call function", request.c_str());
242 } while (0);
243 delete event;
244 }
245 delete work;
246 }, uv_qos_user_initiated);
247 CHECK_AND_RETURN_LOG(ret == 0, "fail to uv_queue_work_with_qos task");
248 CANCEL_SCOPE_EXIT_GUARD(0);
249 CANCEL_SCOPE_EXIT_GUARD(1);
250 }
251
OnJsplayCompletedCallBack(SoundPoolJsCallBack * jsCb) const252 void SoundPoolCallBackNapi::OnJsplayCompletedCallBack(SoundPoolJsCallBack *jsCb) const
253 {
254 ON_SCOPE_EXIT(0) {
255 delete jsCb;
256 };
257 uv_loop_s *loop = nullptr;
258 napi_get_uv_event_loop(env_, &loop);
259 CHECK_AND_RETURN_LOG(loop != nullptr, "Fail to get uv event loop");
260
261 uv_work_t *work = new(std::nothrow) uv_work_t;
262 CHECK_AND_RETURN_LOG(work != nullptr, "fail to new uv_work_t");
263 ON_SCOPE_EXIT(1) {
264 delete work;
265 };
266 work->data = reinterpret_cast<void *>(jsCb);
267 // async callback, jsWork and jsWork->data should be heap object.
268 int ret = uv_queue_work_with_qos(loop, work, [] (uv_work_t *work) {
269 MEDIA_LOGD("OnJsplayCompletedCallBack uv_queue_work_with_qos");
270 }, [] (uv_work_t *work, int status) {
271 // Js Thread
272 CHECK_AND_RETURN_LOG(work != nullptr, "work is nullptr");
273 if (work->data != nullptr) {
274 MEDIA_LOGI("work data not nullptr");
275 SoundPoolJsCallBack *event = reinterpret_cast<SoundPoolJsCallBack *>(work->data);
276 std::string request = event->callbackName;
277 do {
278 CHECK_AND_BREAK_LOG(status != UV_ECANCELED, "%{public}s canceled", request.c_str());
279 std::shared_ptr<AutoRef> ref = event->autoRef.lock();
280 CHECK_AND_BREAK_LOG(ref != nullptr, "%{public}s AutoRef is nullptr", request.c_str());
281
282 napi_handle_scope scope = nullptr;
283 napi_open_handle_scope(ref->env_, &scope);
284 CHECK_AND_BREAK_LOG(scope != nullptr, "%{public}s scope is nullptr", request.c_str());
285 ON_SCOPE_EXIT(0) {
286 napi_close_handle_scope(ref->env_, scope);
287 };
288 napi_value jsCallback = nullptr;
289 napi_status nstatus = napi_get_reference_value(ref->env_, ref->cb_, &jsCallback);
290 CHECK_AND_BREAK_LOG(nstatus == napi_ok && jsCallback != nullptr, "%{public}s get reference value fail",
291 request.c_str());
292
293 napi_value result = nullptr;
294 nstatus = napi_call_function(ref->env_, nullptr, jsCallback, 0, nullptr, &result);
295 CHECK_AND_BREAK_LOG(nstatus == napi_ok, "%{public}s fail to napi call function", request.c_str());
296 } while (0);
297 delete event;
298 }
299 delete work;
300 }, uv_qos_user_initiated);
301 CHECK_AND_RETURN_LOG(ret == 0, "fail to uv_queue_work_with_qos task");
302
303 CANCEL_SCOPE_EXIT_GUARD(0);
304 CANCEL_SCOPE_EXIT_GUARD(1);
305 }
306 } // namespace Media
307 } // namespace OHOS