• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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