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 {
28 static const std::unordered_map<int32_t, int32_t> errorCodeMap = {
29 {MSERR_INVALID_OPERATION, MSERR_EXT_API9_OPERATE_NOT_PERMIT},
30 {MSERR_NO_MEMORY, MSERR_EXT_API9_NO_MEMORY},
31 {MSERR_SERVICE_DIED, MSERR_EXT_API9_SERVICE_DIED},
32 {MSERR_UNSUPPORT_FILE, MSERR_EXT_API9_IO},
33 {MSERR_INVALID_VAL, MSERR_EXT_API9_IO}
34 };
35
36 static const std::unordered_map<int32_t, std::string> errorMessageMap = {
37 {MSERR_EXT_API9_OPERATE_NOT_PERMIT, "The soundpool timed out. Please confirm that the input stream is normal."},
38 {MSERR_EXT_API9_NO_MEMORY, "soundpool memery error."},
39 {MSERR_EXT_API9_SERVICE_DIED, "releated server died"},
40 {MSERR_EXT_API9_IO, "IO error happened."}
41 };
42
SoundPoolCallBackNapi(napi_env env)43 SoundPoolCallBackNapi::SoundPoolCallBackNapi(napi_env env)
44 {
45 env_ = env;
46 MEDIA_LOGI("0x%{public}06" PRIXPTR "Instances create", FAKE_POINTER(this));
47 }
48
OnLoadCompleted(int32_t soundId)49 void SoundPoolCallBackNapi::OnLoadCompleted(int32_t soundId)
50 {
51 MEDIA_LOGI("OnLoadCompleted recived soundId:%{public}d", soundId);
52 SendLoadCompletedCallback(soundId);
53 }
54
OnPlayFinished(int32_t streamID)55 void SoundPoolCallBackNapi::OnPlayFinished(int32_t streamID)
56 {
57 MEDIA_LOGI("OnPlayFinished recived");
58 SendPlayCompletedCallback(streamID);
59 }
60
OnError(int32_t errorCode)61 void SoundPoolCallBackNapi::OnError(int32_t errorCode)
62 {
63 MEDIA_LOGI("OnError recived:error:%{public}d", errorCode);
64 int32_t externalsErrorCode = GetExternalErrorCode(errorCode);
65 std::string errorMsg = GetErrorMsg(externalsErrorCode);
66 SendErrorCallback(externalsErrorCode, errorMsg);
67 }
68
OnErrorOccurred(Format & errorInfo)69 void SoundPoolCallBackNapi::OnErrorOccurred(Format &errorInfo)
70 {
71 MEDIA_LOGI("OnErrorOccurred recived");
72 int32_t errorCode;
73 errorInfo.GetIntValue(SoundPoolKeys::ERROR_CODE, errorCode);
74 int32_t externalsErrorCode = GetExternalErrorCode(errorCode);
75 std::string errorMsg = GetErrorMsg(externalsErrorCode);
76 errorInfo.PutIntValue(SoundPoolKeys::ERROR_CODE, externalsErrorCode);
77 errorInfo.PutStringValue(SoundPoolKeys::ERROR_MESSAGE, errorMsg);
78 SendErrorOccurredCallback(errorInfo);
79 }
80
GetExternalErrorCode(int32_t internalCode)81 int32_t SoundPoolCallBackNapi::GetExternalErrorCode(int32_t internalCode)
82 {
83 auto it = errorCodeMap.find(internalCode);
84 if (it != errorCodeMap.end()) {
85 return it->second;
86 }
87 return MSERR_EXT_API9_IO;
88 }
89
GetErrorMsg(int32_t errorCode)90 std::string SoundPoolCallBackNapi::GetErrorMsg(int32_t errorCode)
91 {
92 auto it = errorMessageMap.find(errorCode);
93 if (it != errorMessageMap.end()) {
94 return it->second;
95 }
96 return "unknown error";
97 }
98
SaveCallbackReference(const std::string & name,std::weak_ptr<AutoRef> ref)99 void SoundPoolCallBackNapi::SaveCallbackReference(const std::string &name, std::weak_ptr<AutoRef> ref)
100 {
101 std::lock_guard<std::mutex> lock(mutex_);
102 refMap_[name] = ref;
103 MEDIA_LOGI("Set callback type: %{public}s", name.c_str());
104 }
105
CancelCallbackReference(const std::string & name)106 void SoundPoolCallBackNapi::CancelCallbackReference(const std::string &name)
107 {
108 std::lock_guard<std::mutex> lock(mutex_);
109 auto iter = refMap_.find(name);
110 if (iter != refMap_.end()) {
111 refMap_.erase(iter);
112 }
113 MEDIA_LOGI("Cancel callback type: %{public}s", name.c_str());
114 }
115
ClearCallbackReference()116 void SoundPoolCallBackNapi::ClearCallbackReference()
117 {
118 std::lock_guard<std::mutex> lock(mutex_);
119 refMap_.clear();
120 MEDIA_LOGI("ClearCallback!");
121 }
122
SendErrorCallback(int32_t errCode,const std::string & msg)123 void SoundPoolCallBackNapi::SendErrorCallback(int32_t errCode, const std::string &msg)
124 {
125 std::lock_guard<std::mutex> lock(mutex_);
126 if (refMap_.find(SoundPoolEvent::EVENT_ERROR) == refMap_.end()) {
127 MEDIA_LOGW("can not find error callback!");
128 return;
129 }
130
131 SoundPoolJsCallBack *cb = new(std::nothrow) SoundPoolJsCallBack();
132 CHECK_AND_RETURN_LOG(cb != nullptr, "cb is nullptr");
133 cb->autoRef = refMap_.at(SoundPoolEvent::EVENT_ERROR);
134 cb->callbackName = SoundPoolEvent::EVENT_ERROR;
135 cb->errorCode = errCode;
136 cb->errorMsg = msg;
137 return OnJsErrorCallBack(cb);
138 }
139
SendErrorOccurredCallback(const Format & errorInfo)140 void SoundPoolCallBackNapi::SendErrorOccurredCallback(const Format &errorInfo)
141 {
142 std::lock_guard<std::mutex> lock(mutex_);
143 if (refMap_.find(SoundPoolEvent::EVENT_ERROR_OCCURRED) == refMap_.end()) {
144 MEDIA_LOGW("can not find errorOccurred callback!");
145 return;
146 }
147 SoundPoolJsCallBack *cb = new(std::nothrow) SoundPoolJsCallBack();
148 CHECK_AND_RETURN_LOG(cb != nullptr, "cb is nullptr");
149 int32_t errorCode;
150 std::string msg;
151 errorInfo.GetIntValue(SoundPoolKeys::ERROR_CODE, errorCode);
152 errorInfo.GetStringValue(SoundPoolKeys::ERROR_MESSAGE, msg);
153 if (errorInfo.ContainKey(SoundPoolKeys::STREAM_ID)) {
154 int32_t streamId;
155 errorInfo.GetIntValue(SoundPoolKeys::STREAM_ID, streamId);
156 cb->playFinishedStreamID = streamId;
157 }
158 if (errorInfo.ContainKey(SoundPoolKeys::ERROR_TYPE_FLAG)) {
159 int32_t errorType;
160 errorInfo.GetIntValue(SoundPoolKeys::ERROR_TYPE_FLAG, errorType);
161 cb->errorType = static_cast<ERROR_TYPE>(errorType);
162 }
163 if (errorInfo.ContainKey(SoundPoolKeys::SOUND_ID)) {
164 int32_t soundId;
165 errorInfo.GetIntValue(SoundPoolKeys::SOUND_ID, soundId);
166 cb->loadSoundId = soundId;
167 }
168 cb->autoRef = refMap_.at(SoundPoolEvent::EVENT_ERROR_OCCURRED);
169 cb->callbackName = SoundPoolEvent::EVENT_ERROR_OCCURRED;
170 cb->errorCode = errorCode;
171 cb->errorMsg = msg;
172 return OnJsErrorOccurredCallBack(cb);
173 }
174
SendLoadCompletedCallback(int32_t soundId)175 void SoundPoolCallBackNapi::SendLoadCompletedCallback(int32_t soundId)
176 {
177 std::lock_guard<std::mutex> lock(mutex_);
178 if (refMap_.find(SoundPoolEvent::EVENT_LOAD_COMPLETED) == refMap_.end()) {
179 MEDIA_LOGW("can not find loadcompleted callback!");
180 return;
181 }
182
183 SoundPoolJsCallBack *cb = new(std::nothrow) SoundPoolJsCallBack();
184 CHECK_AND_RETURN_LOG(cb != nullptr, "cb is nullptr");
185 cb->autoRef = refMap_.at(SoundPoolEvent::EVENT_LOAD_COMPLETED);
186 cb->callbackName = SoundPoolEvent::EVENT_LOAD_COMPLETED;
187 cb->loadSoundId = soundId;
188 return OnJsloadCompletedCallBack(cb);
189 }
190
SendPlayCompletedCallback(int32_t streamID)191 void SoundPoolCallBackNapi::SendPlayCompletedCallback(int32_t streamID)
192 {
193 std::lock_guard<std::mutex> lock(mutex_);
194 if (refMap_.find(SoundPoolEvent::EVENT_PLAY_FINISHED) == refMap_.end() &&
195 refMap_.find(SoundPoolEvent::EVENT_PLAY_FINISHED_WITH_STREAM_ID) == refMap_.end()) {
196 MEDIA_LOGW("can not find playfinished callback!");
197 return;
198 }
199
200 SoundPoolJsCallBack *cb = new(std::nothrow) SoundPoolJsCallBack();
201 CHECK_AND_RETURN_LOG(cb != nullptr, "cb is nullptr");
202 std::weak_ptr<AutoRef> autoRefFinished;
203 std::weak_ptr<AutoRef> autoRefFinishedStreamID;
204 auto it = refMap_.find(SoundPoolEvent::EVENT_PLAY_FINISHED);
205 if (it != refMap_.end()) {
206 autoRefFinished = it->second;
207 }
208 auto itStreamId = refMap_.find(SoundPoolEvent::EVENT_PLAY_FINISHED_WITH_STREAM_ID);
209 if (itStreamId != refMap_.end()) {
210 autoRefFinishedStreamID = itStreamId->second;
211 }
212
213 if (std::shared_ptr<AutoRef> ref = autoRefFinishedStreamID.lock()) {
214 cb->autoRef = autoRefFinishedStreamID;
215 cb->callbackName = SoundPoolEvent::EVENT_PLAY_FINISHED_WITH_STREAM_ID;
216 cb->playFinishedStreamID = streamID;
217 } else {
218 cb->autoRef = autoRefFinished;
219 cb->callbackName = SoundPoolEvent::EVENT_PLAY_FINISHED;
220 }
221 return OnJsplayCompletedCallBack(cb);
222 }
223
OnJsErrorCallBack(SoundPoolJsCallBack * jsCb) const224 void SoundPoolCallBackNapi::OnJsErrorCallBack(SoundPoolJsCallBack *jsCb) const
225 {
226 auto task = [event = jsCb]() {
227 event->RunJsErrorCallBackTask(event);
228 delete event;
229 };
230
231 auto ret = napi_send_event(env_, task, napi_eprio_immediate);
232 if (ret != napi_status::napi_ok) {
233 MEDIA_LOGE("Failed to SendEvent CallBack, ret = %{public}d", ret);
234 delete jsCb;
235 }
236 }
237
OnJsErrorOccurredCallBack(SoundPoolJsCallBack * jsCb) const238 void SoundPoolCallBackNapi::OnJsErrorOccurredCallBack(SoundPoolJsCallBack *jsCb) const
239 {
240 auto task = [event = jsCb]() {
241 event->RunJsErrorOccurredCallBackTask(event);
242 delete event;
243 };
244
245 auto ret = napi_send_event(env_, task, napi_eprio_immediate);
246 if (ret != napi_status::napi_ok) {
247 MEDIA_LOGE("Failed to SendEvent CallBack, ret = %{public}d", ret);
248 delete jsCb;
249 }
250 }
251
RunJsErrorCallBackTask(SoundPoolJsCallBack * event)252 void SoundPoolCallBackNapi::SoundPoolJsCallBack::RunJsErrorCallBackTask(SoundPoolJsCallBack *event)
253 {
254 std::string request = event->callbackName;
255 do {
256 std::shared_ptr<AutoRef> ref = event->autoRef.lock();
257 CHECK_AND_BREAK_LOG(ref != nullptr, "%{public}s AutoRef is nullptr", request.c_str());
258
259 napi_handle_scope scope = nullptr;
260 napi_open_handle_scope(ref->env_, &scope);
261 CHECK_AND_BREAK_LOG(scope != nullptr, "%{public}s scope is nullptr", request.c_str());
262 ON_SCOPE_EXIT(0) {
263 napi_close_handle_scope(ref->env_, scope);
264 };
265
266 napi_value jsCallback = nullptr;
267 napi_status nstatus = napi_get_reference_value(ref->env_, ref->cb_, &jsCallback);
268 CHECK_AND_BREAK_LOG(nstatus == napi_ok && jsCallback != nullptr, "%{public}s get reference value fail",
269 request.c_str());
270
271 napi_value msgValStr = nullptr;
272 nstatus = napi_create_string_utf8(ref->env_, event->errorMsg.c_str(), NAPI_AUTO_LENGTH, &msgValStr);
273 CHECK_AND_BREAK_LOG(nstatus == napi_ok && msgValStr != nullptr, "create error message str fail");
274
275 napi_value args[1] = { nullptr };
276 nstatus = napi_create_error(ref->env_, nullptr, msgValStr, &args[0]);
277 CHECK_AND_BREAK_LOG(nstatus == napi_ok && args[0] != nullptr, "create error callback fail");
278
279 nstatus = CommonNapi::FillErrorArgs(ref->env_, event->errorCode, args[0]);
280 CHECK_AND_BREAK_LOG(nstatus == napi_ok, "create error callback fail");
281 // Call back function
282 napi_value result = nullptr;
283 nstatus = napi_call_function(ref->env_, nullptr, jsCallback, 1, args, &result);
284 CHECK_AND_BREAK_LOG(nstatus == napi_ok, "%{public}s fail to napi call function", request.c_str());
285 } while (0);
286 }
287
RunJsErrorOccurredCallBackTask(SoundPoolJsCallBack * event)288 void SoundPoolCallBackNapi::SoundPoolJsCallBack::RunJsErrorOccurredCallBackTask(SoundPoolJsCallBack *event)
289 {
290 std::string request = event->callbackName;
291 MEDIA_LOGI("errorOccurredCallback event: errorMsg %{public}s, errorCode %{public}d, soundId %{public}d,"
292 "streamId %{public}d", event->errorMsg.c_str(), event->errorCode, event->loadSoundId,
293 event->playFinishedStreamID);
294 std::shared_ptr<AutoRef> ref = event->autoRef.lock();
295 CHECK_AND_RETURN_LOG(ref != nullptr, "%{public}s AutoRef is nullptr", request.c_str());
296 napi_handle_scope scope = nullptr;
297 napi_open_handle_scope(ref->env_, &scope);
298 CHECK_AND_RETURN_LOG(scope != nullptr, "%{public}s scope is nullptr", request.c_str());
299 ON_SCOPE_EXIT(0) {
300 napi_close_handle_scope(ref->env_, scope);
301 };
302 napi_value jsCallback = nullptr;
303 napi_status nstatus = napi_get_reference_value(ref->env_, ref->cb_, &jsCallback);
304 CHECK_AND_RETURN_LOG(nstatus == napi_ok && jsCallback != nullptr, "%{public}s get reference value fail",
305 request.c_str());
306 constexpr size_t argCount = 1;
307 napi_value args[argCount] = {};
308 napi_create_object(ref->env_, &args[0]);
309
310 napi_value errCode = nullptr;
311 int status = CommonNapi::CreateError(ref->env_, event->errorCode, event->errorMsg, errCode);
312 CHECK_AND_RETURN_LOG(status == napi_ok && errCode != nullptr,
313 " fail to convert to errorCode");
314 napi_set_named_property(ref->env_, args[0], "errorCode", errCode);
315 bool res = CommonNapi::SetPropertyInt32(ref->env_, args[0], "errorType", event->errorType);
316 CHECK_AND_RETURN_LOG(res, " fail to convert to errorType");
317 res = CommonNapi::SetPropertyInt32(ref->env_, args[0], "soundId", event->loadSoundId);
318 CHECK_AND_RETURN_LOG(res, " fail to convert to soundId");
319 if (event->playFinishedStreamID > 0) {
320 res = CommonNapi::SetPropertyInt32(ref->env_, args[0], "streamId", event->playFinishedStreamID);
321 CHECK_AND_RETURN_LOG(res, " fail to convert to streamId");
322 }
323
324 napi_value result = nullptr;
325 nstatus = napi_call_function(ref->env_, nullptr, jsCallback, argCount, args, &result);
326 CHECK_AND_RETURN_LOG(nstatus == napi_ok, "%{public}s fail to napi call function", request.c_str());
327 }
328
OnJsloadCompletedCallBack(SoundPoolJsCallBack * jsCb) const329 void SoundPoolCallBackNapi::OnJsloadCompletedCallBack(SoundPoolJsCallBack *jsCb) const
330 {
331 auto task = [event = jsCb]() {
332 event->RunJsloadCompletedCallBackTask(event);
333 delete event;
334 };
335
336 auto ret = napi_send_event(env_, task, napi_eprio_immediate);
337 if (ret != napi_status::napi_ok) {
338 MEDIA_LOGE("Failed to SendEvent CallBack, ret = %{public}d", ret);
339 delete jsCb;
340 }
341 }
342
RunJsloadCompletedCallBackTask(SoundPoolJsCallBack * event)343 void SoundPoolCallBackNapi::SoundPoolJsCallBack::RunJsloadCompletedCallBackTask(SoundPoolJsCallBack *event)
344 {
345 std::string request = event->callbackName;
346 do {
347 std::shared_ptr<AutoRef> ref = event->autoRef.lock();
348 CHECK_AND_BREAK_LOG(ref != nullptr, "%{public}s AutoRef is nullptr", request.c_str());
349 napi_handle_scope scope = nullptr;
350 napi_open_handle_scope(ref->env_, &scope);
351 CHECK_AND_BREAK_LOG(scope != nullptr, "%{public}s scope is nullptr", request.c_str());
352 ON_SCOPE_EXIT(0) {
353 napi_close_handle_scope(ref->env_, scope);
354 };
355 napi_value jsCallback = nullptr;
356 napi_status nstatus = napi_get_reference_value(ref->env_, ref->cb_, &jsCallback);
357 CHECK_AND_BREAK_LOG(nstatus == napi_ok && jsCallback != nullptr, "%{public}s get reference value fail",
358 request.c_str());
359 napi_value args[1] = { nullptr };
360 nstatus = napi_create_int32(ref->env_, event->loadSoundId, &args[0]);
361 CHECK_AND_BREAK_LOG(nstatus == napi_ok && args[0] != nullptr,
362 "%{public}s fail to create callback", request.c_str());
363 const size_t argCount = 1;
364 napi_value result = nullptr;
365 nstatus = napi_call_function(ref->env_, nullptr, jsCallback, argCount, args, &result);
366 CHECK_AND_BREAK_LOG(nstatus == napi_ok, "%{public}s fail to napi call function", request.c_str());
367 } while (0);
368 }
369
OnJsplayCompletedCallBack(SoundPoolJsCallBack * jsCb) const370 void SoundPoolCallBackNapi::OnJsplayCompletedCallBack(SoundPoolJsCallBack *jsCb) const
371 {
372 auto task = [event = jsCb]() {
373 event->RunJsplayCompletedCallBackTask(event);
374 delete event;
375 };
376
377 auto ret = napi_send_event(env_, task, napi_eprio_immediate);
378 if (ret != napi_status::napi_ok) {
379 MEDIA_LOGE("Failed to SendEvent CallBack, ret = %{public}d", ret);
380 delete jsCb;
381 }
382 }
383
RunJsplayCompletedCallBackTask(SoundPoolJsCallBack * event)384 void SoundPoolCallBackNapi::SoundPoolJsCallBack::RunJsplayCompletedCallBackTask(SoundPoolJsCallBack *event)
385 {
386 std::string request = event->callbackName;
387 do {
388 std::shared_ptr<AutoRef> ref = event->autoRef.lock();
389 CHECK_AND_BREAK_LOG(ref != nullptr, "%{public}s AutoRef is nullptr", request.c_str());
390
391 napi_handle_scope scope = nullptr;
392 napi_open_handle_scope(ref->env_, &scope);
393 CHECK_AND_BREAK_LOG(scope != nullptr, "%{public}s scope is nullptr", request.c_str());
394 ON_SCOPE_EXIT(0) {
395 napi_close_handle_scope(ref->env_, scope);
396 };
397 napi_value jsCallback = nullptr;
398 napi_status nstatus = napi_get_reference_value(ref->env_, ref->cb_, &jsCallback);
399 CHECK_AND_BREAK_LOG(nstatus == napi_ok && jsCallback != nullptr, "%{public}s get reference value fail",
400 request.c_str());
401
402 if (request == SoundPoolEvent::EVENT_PLAY_FINISHED_WITH_STREAM_ID) {
403 napi_value args[1] = { nullptr };
404 nstatus = napi_create_int32(ref->env_, event->playFinishedStreamID, &args[0]);
405 CHECK_AND_BREAK_LOG(nstatus == napi_ok && args[0] != nullptr,
406 "%{public}s fail to create callback", request.c_str());
407 const size_t argCount = 1;
408 napi_value result = nullptr;
409 nstatus = napi_call_function(ref->env_, nullptr, jsCallback, argCount, args, &result);
410 CHECK_AND_BREAK_LOG(nstatus == napi_ok, "%{public}s fail to napi call function", request.c_str());
411 } else {
412 napi_value result = nullptr;
413 nstatus = napi_call_function(ref->env_, nullptr, jsCallback, 0, nullptr, &result);
414 CHECK_AND_BREAK_LOG(nstatus == napi_ok, "%{public}s fail to napi call function", request.c_str());
415 }
416 } while (0);
417 }
418
419 } // namespace Media
420 } // namespace OHOS