• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023 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 #ifndef LOG_TAG
16 #define LOG_TAG "NapiAudioRendererDeviceChangeCallback"
17 #endif
18 
19 #include "napi_audio_renderer_device_change_callback.h"
20 
21 namespace OHOS {
22 namespace AudioStandard {
NapiAudioRendererDeviceChangeCallback(napi_env env)23 NapiAudioRendererDeviceChangeCallback::NapiAudioRendererDeviceChangeCallback(napi_env env)
24     : env_(env)
25 {
26     AUDIO_INFO_LOG("instance create");
27 }
28 
~NapiAudioRendererDeviceChangeCallback()29 NapiAudioRendererDeviceChangeCallback::~NapiAudioRendererDeviceChangeCallback()
30 {
31     AUDIO_INFO_LOG("instance destroy");
32 }
33 
AddCallbackReference(napi_value args)34 void NapiAudioRendererDeviceChangeCallback::AddCallbackReference(napi_value args)
35 {
36     std::lock_guard<std::mutex> lock(mutex_);
37     napi_ref callback = nullptr;
38     const int32_t refCount = 1;
39     bool isEquals = false;
40     napi_value copyValue = nullptr;
41 
42     for (auto ref = callbacks_.begin(); ref != callbacks_.end(); ++ref) {
43         napi_get_reference_value(env_, *ref, &copyValue);
44         CHECK_AND_RETURN_LOG(napi_strict_equals(env_, copyValue, args, &isEquals) == napi_ok,
45             "get napi_strict_equals failed");
46         CHECK_AND_RETURN_LOG(!isEquals, "js Callback already exist");
47     }
48 
49     napi_status status = napi_create_reference(env_, args, refCount, &callback);
50     CHECK_AND_RETURN_LOG(status == napi_ok && callback != nullptr,
51         "AudioRendererDeviceChangeCallbackNapi: creating reference for callback fail");
52 
53     callbacks_.push_back(callback);
54     AUDIO_INFO_LOG("AddCallbackReference successful");
55 }
56 
RemoveCallbackReference(napi_env env,napi_value args)57 void NapiAudioRendererDeviceChangeCallback::RemoveCallbackReference(napi_env env, napi_value args)
58 {
59     std::lock_guard<std::mutex> lock(mutex_);
60     bool isEquals = false;
61     napi_value copyValue = nullptr;
62 
63     if (args == nullptr) {
64         for (auto ref = callbacks_.begin(); ref != callbacks_.end(); ++ref) {
65             napi_status ret = napi_delete_reference(env, *ref);
66             CHECK_AND_RETURN_LOG(napi_ok == ret, "delete callback reference failed");
67         }
68         callbacks_.clear();
69         AUDIO_INFO_LOG("Remove all JS Callback");
70         return;
71     }
72 
73     for (auto ref = callbacks_.begin(); ref != callbacks_.end(); ++ref) {
74         napi_get_reference_value(env, *ref, &copyValue);
75         CHECK_AND_RETURN_LOG(copyValue != nullptr, "copyValue is nullptr");
76         CHECK_AND_RETURN_LOG(napi_strict_equals(env, args, copyValue, &isEquals) == napi_ok,
77             "get napi_strict_equals failed");
78 
79         if (isEquals == true) {
80             AUDIO_INFO_LOG("found JS Callback, delete it!");
81             callbacks_.remove(*ref);
82             napi_status status = napi_delete_reference(env, *ref);
83             CHECK_AND_RETURN_LOG(status == napi_ok, "deleting reference for callback fail");
84             return;
85         }
86     }
87 
88     AUDIO_INFO_LOG("RemoveCallbackReference success");
89 }
90 
RemoveAllCallbacks()91 void NapiAudioRendererDeviceChangeCallback::RemoveAllCallbacks()
92 {
93     std::lock_guard<std::mutex> lock(mutex_);
94     for (auto ref = callbacks_.begin(); ref != callbacks_.end(); ++ref) {
95         napi_status ret = napi_delete_reference(env_, *ref);
96         CHECK_AND_RETURN_LOG(napi_ok == ret, "delete callback reference failed");
97     }
98     callbacks_.clear();
99     AUDIO_INFO_LOG("RemoveAllCallbacks successful");
100 }
101 
GetCallbackListSize() const102 int32_t NapiAudioRendererDeviceChangeCallback::GetCallbackListSize() const
103 {
104     return callbacks_.size();
105 }
106 
OnOutputDeviceChange(const DeviceInfo & deviceInfo,const AudioStreamDeviceChangeReason reason)107 void NapiAudioRendererDeviceChangeCallback::OnOutputDeviceChange(const DeviceInfo &deviceInfo,
108     const AudioStreamDeviceChangeReason reason)
109 {
110     std::lock_guard<std::mutex> lock(mutex_);
111     for (auto ref = callbacks_.begin(); ref != callbacks_.end(); ++ref) {
112         OnJsCallbackRendererDeviceInfo(*ref, deviceInfo);
113     }
114 }
115 
WorkCallbackCompleted(uv_work_t * work,int status)116 void NapiAudioRendererDeviceChangeCallback::WorkCallbackCompleted(uv_work_t *work, int status)
117 {
118     // Js Thread
119     std::shared_ptr<AudioRendererDeviceChangeJsCallback> context(
120         static_cast<AudioRendererDeviceChangeJsCallback*>(work->data),
121         [work](AudioRendererDeviceChangeJsCallback* ptr) {
122             delete ptr;
123             delete work;
124     });
125 
126     AudioRendererDeviceChangeJsCallback *event = reinterpret_cast<AudioRendererDeviceChangeJsCallback*>(work->data);
127     CHECK_AND_RETURN_LOG((event != nullptr) && (event->callback_ != nullptr),
128         "OnJsCallbackRendererDeviceInfo: No memory");
129 
130     napi_env env = event->env_;
131     napi_ref callback = event->callback_;
132     napi_handle_scope scope = nullptr;
133     napi_open_handle_scope(env, &scope);
134     CHECK_AND_RETURN_LOG(scope != nullptr, "scope is nullptr");
135     do {
136         napi_value jsCallback = nullptr;
137         napi_status nstatus = napi_get_reference_value(env, callback, &jsCallback);
138         CHECK_AND_BREAK_LOG(nstatus == napi_ok && jsCallback != nullptr, "callback get reference value fail");
139         // Call back function
140         napi_value args[ARGS_ONE] = { nullptr };
141         nstatus = NapiParamUtils::SetValueDeviceInfo(env, event->deviceInfo_, args[0]);
142         CHECK_AND_BREAK_LOG(nstatus == napi_ok && args[PARAM0] != nullptr,
143             " fail to convert to jsobj");
144 
145         const size_t argCount = 1;
146         napi_value result = nullptr;
147         nstatus = napi_call_function(env, nullptr, jsCallback, argCount, args, &result);
148         CHECK_AND_BREAK_LOG(nstatus == napi_ok, "Fail to call devicechange callback");
149     } while (0);
150     napi_close_handle_scope(env, scope);
151 }
152 
OnJsCallbackRendererDeviceInfo(napi_ref method,const DeviceInfo & deviceInfo)153 void NapiAudioRendererDeviceChangeCallback::OnJsCallbackRendererDeviceInfo(napi_ref method,
154     const DeviceInfo &deviceInfo)
155 {
156     uv_loop_s *loop = nullptr;
157     napi_get_uv_event_loop(env_, &loop);
158     CHECK_AND_RETURN_LOG(loop != nullptr, "OnJsCallbackRendererDeviceInfo loop_ is nullptr");
159     CHECK_AND_RETURN_LOG(method != nullptr, "OnJsCallbackRendererDeviceInfo method is nullptr");
160 
161     uv_work_t *work = new(std::nothrow) uv_work_t;
162     CHECK_AND_RETURN_LOG(work != nullptr, "OnJsCallbackRendererDeviceInfo: No memoryr");
163 
164     work->data = new AudioRendererDeviceChangeJsCallback {method, env_, deviceInfo};
165     if (work->data == nullptr) {
166         AUDIO_ERR_LOG("OnJsCallbackRendererDeviceInfo failed: No memory");
167         delete work;
168         return;
169     }
170 
171     AUDIO_ERR_LOG("OnJsCallbackRendererDeviceInfo");
172     int ret = uv_queue_work_with_qos(loop, work, [] (uv_work_t *work) {}, WorkCallbackCompleted, uv_qos_default);
173     if (ret != 0) {
174         AUDIO_ERR_LOG("Failed to execute libuv work queue");
175         if (work != nullptr) {
176             if (work->data != nullptr) {
177                 delete reinterpret_cast<AudioRendererDeviceChangeJsCallback*>(work->data);
178             }
179             delete work;
180         }
181     }
182 }
183 
NapiAudioRendererOutputDeviceChangeWithInfoCallback(napi_env env)184 NapiAudioRendererOutputDeviceChangeWithInfoCallback::NapiAudioRendererOutputDeviceChangeWithInfoCallback(napi_env env)
185     : env_(env)
186 {
187     AUDIO_INFO_LOG("instance create");
188 }
189 
~NapiAudioRendererOutputDeviceChangeWithInfoCallback()190 NapiAudioRendererOutputDeviceChangeWithInfoCallback::~NapiAudioRendererOutputDeviceChangeWithInfoCallback()
191 {
192     AUDIO_INFO_LOG("instance destroy");
193 }
194 
AddCallbackReference(napi_value args)195 void NapiAudioRendererOutputDeviceChangeWithInfoCallback::AddCallbackReference(napi_value args)
196 {
197     std::lock_guard<std::mutex> lock(mutex_);
198     napi_ref callback = nullptr;
199     const int32_t refCount = 1;
200     bool isEquals = false;
201     napi_value copyValue = nullptr;
202 
203     for (auto ref = callbacks_.begin(); ref != callbacks_.end(); ++ref) {
204         napi_get_reference_value(env_, *ref, &copyValue);
205         CHECK_AND_RETURN_LOG(napi_strict_equals(env_, copyValue, args, &isEquals) == napi_ok,
206             "get napi_strict_equals failed");
207         CHECK_AND_RETURN_LOG(!isEquals, "js Callback already exist");
208     }
209 
210     napi_status status = napi_create_reference(env_, args, refCount, &callback);
211     CHECK_AND_RETURN_LOG(status == napi_ok && callback != nullptr,
212         "creating reference for callback fail");
213 
214     callbacks_.push_back(callback);
215     AUDIO_INFO_LOG("successful");
216 }
217 
RemoveCallbackReference(napi_env env,napi_value args)218 void NapiAudioRendererOutputDeviceChangeWithInfoCallback::RemoveCallbackReference(napi_env env, napi_value args)
219 {
220     std::lock_guard<std::mutex> lock(mutex_);
221     bool isEquals = false;
222     napi_value copyValue = nullptr;
223 
224     if (args == nullptr) {
225         for (auto ref = callbacks_.begin(); ref != callbacks_.end(); ++ref) {
226             napi_status ret = napi_delete_reference(env, *ref);
227             CHECK_AND_RETURN_LOG(napi_ok == ret, "delete callback reference failed");
228         }
229         callbacks_.clear();
230         AUDIO_INFO_LOG("Remove all JS Callback");
231         return;
232     }
233 
234     for (auto ref = callbacks_.begin(); ref != callbacks_.end(); ++ref) {
235         napi_get_reference_value(env, *ref, &copyValue);
236         CHECK_AND_RETURN_LOG(copyValue != nullptr, "copyValue is nullptr");
237         CHECK_AND_RETURN_LOG(napi_strict_equals(env, args, copyValue, &isEquals) == napi_ok,
238             "get napi_strict_equals failed");
239 
240         if (isEquals == true) {
241             AUDIO_INFO_LOG("found JS Callback, delete it!");
242             callbacks_.remove(*ref);
243             napi_status status = napi_delete_reference(env, *ref);
244             CHECK_AND_RETURN_LOG(status == napi_ok, "deleting reference for callback fail");
245             return;
246         }
247     }
248 
249     AUDIO_INFO_LOG("RemoveCallbackReference success");
250 }
251 
RemoveAllCallbacks()252 void NapiAudioRendererOutputDeviceChangeWithInfoCallback::RemoveAllCallbacks()
253 {
254     std::lock_guard<std::mutex> lock(mutex_);
255     for (auto ref = callbacks_.begin(); ref != callbacks_.end(); ++ref) {
256         napi_status ret = napi_delete_reference(env_, *ref);
257         CHECK_AND_RETURN_LOG(napi_ok == ret, "delete callback reference failed");
258     }
259     callbacks_.clear();
260     AUDIO_INFO_LOG("RemoveAllCallbacks successful");
261 }
262 
GetCallbackListSize() const263 int32_t NapiAudioRendererOutputDeviceChangeWithInfoCallback::GetCallbackListSize() const
264 {
265     return callbacks_.size();
266 }
267 
OnOutputDeviceChange(const DeviceInfo & deviceInfo,const AudioStreamDeviceChangeReason reason)268 void NapiAudioRendererOutputDeviceChangeWithInfoCallback::OnOutputDeviceChange(const DeviceInfo &deviceInfo,
269     const AudioStreamDeviceChangeReason reason)
270 {
271     std::lock_guard<std::mutex> lock(mutex_);
272     for (auto ref = callbacks_.begin(); ref != callbacks_.end(); ++ref) {
273         OnJsCallbackOutputDeviceInfo(*ref, deviceInfo, reason);
274     }
275 }
276 
WorkCallbackCompleted(uv_work_t * work,int status)277 void NapiAudioRendererOutputDeviceChangeWithInfoCallback::WorkCallbackCompleted(uv_work_t *work, int status)
278 {
279     // Js Thread
280     std::shared_ptr<AudioRendererOutputDeviceChangeWithInfoJsCallback> context(
281         static_cast<AudioRendererOutputDeviceChangeWithInfoJsCallback*>(work->data),
282         [work](AudioRendererOutputDeviceChangeWithInfoJsCallback *ptr) {
283             delete ptr;
284             delete work;
285     });
286 
287     AudioRendererOutputDeviceChangeWithInfoJsCallback *event
288         = reinterpret_cast<AudioRendererOutputDeviceChangeWithInfoJsCallback*>(work->data);
289     CHECK_AND_RETURN_LOG((event != nullptr) && (event->callback_ != nullptr),
290         "OnJsCallbackRendererDeviceInfo: No memory");
291 
292     napi_env env = event->env_;
293     napi_ref callback = event->callback_;
294     napi_handle_scope scope = nullptr;
295     napi_open_handle_scope(env, &scope);
296     CHECK_AND_RETURN_LOG(scope != nullptr, "scope is nullptr");
297     do {
298         napi_value jsCallback = nullptr;
299         napi_status nstatus = napi_get_reference_value(env, callback, &jsCallback);
300         CHECK_AND_BREAK_LOG(nstatus == napi_ok && jsCallback != nullptr, "callback get reference value fail");
301         // Call back function
302         constexpr size_t argCount = ARGS_ONE;
303         napi_value args[argCount] = {};
304         napi_create_object(env, &args[PARAM0]);
305         napi_value deviceObj = nullptr;
306         nstatus = NapiParamUtils::SetValueDeviceInfo(env, event->deviceInfo_, deviceObj);
307         CHECK_AND_BREAK_LOG(nstatus == napi_ok && deviceObj != nullptr,
308             " fail to convert to jsobj");
309         napi_set_named_property(env, args[PARAM0], "devices", deviceObj);
310 
311         nstatus = NapiParamUtils::SetValueInt32(env, "changeReason", static_cast<const int32_t> (event->reason_),
312             args[PARAM0]);
313         CHECK_AND_BREAK_LOG(nstatus == napi_ok && deviceObj != nullptr,
314             " fail to convert to jsobj");
315 
316         napi_value result = nullptr;
317         nstatus = napi_call_function(env, nullptr, jsCallback, argCount, args, &result);
318         CHECK_AND_BREAK_LOG(nstatus == napi_ok, "Fail to call devicechange callback");
319     } while (0);
320     napi_close_handle_scope(env, scope);
321 }
322 
OnJsCallbackOutputDeviceInfo(napi_ref method,const DeviceInfo & deviceInfo,AudioStreamDeviceChangeReason reason)323 void NapiAudioRendererOutputDeviceChangeWithInfoCallback::OnJsCallbackOutputDeviceInfo(napi_ref method,
324     const DeviceInfo &deviceInfo, AudioStreamDeviceChangeReason reason)
325 {
326     uv_loop_s *loop = nullptr;
327     napi_get_uv_event_loop(env_, &loop);
328     CHECK_AND_RETURN_LOG(loop != nullptr, "OnJsCallbackOutputDeviceInfo loop_ is nullptr");
329     CHECK_AND_RETURN_LOG(method != nullptr, "OnJsCallbackOutputDeviceInfo method is nullptr");
330 
331     uv_work_t *work = new(std::nothrow) uv_work_t;
332     CHECK_AND_RETURN_LOG(work != nullptr, "OnJsCallbackOutputDeviceInfo: No memoryr");
333 
334     work->data = new AudioRendererOutputDeviceChangeWithInfoJsCallback {method, env_, deviceInfo, reason};
335     if (work->data == nullptr) {
336         AUDIO_ERR_LOG("OnJsCallbackOutputDeviceInfo failed: No memory");
337         delete work;
338         return;
339     }
340 
341     AUDIO_INFO_LOG("OnJsCallback");
342     int ret = uv_queue_work_with_qos(loop, work, [] (uv_work_t *work) {}, WorkCallbackCompleted, uv_qos_default);
343     if (ret != 0) {
344         AUDIO_ERR_LOG("Failed to execute libuv work queue");
345         if (work != nullptr) {
346             if (work->data != nullptr) {
347                 delete reinterpret_cast<AudioRendererOutputDeviceChangeWithInfoJsCallback*>(work->data);
348             }
349             delete work;
350         }
351     }
352 }
353 }  // namespace AudioStandard
354 }  // namespace OHOS