• 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 {
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