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