• 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 
16 #include "audio_renderer_device_change_callback_napi.h"
17 #include <uv.h>
18 #include "audio_errors.h"
19 #include "audio_log.h"
20 
21 using namespace std;
22 
23 namespace OHOS {
24 namespace AudioStandard {
AudioRendererDeviceChangeCallbackNapi(napi_env env)25 AudioRendererDeviceChangeCallbackNapi::AudioRendererDeviceChangeCallbackNapi(napi_env env)
26     : env_(env)
27 {
28     AUDIO_INFO_LOG("instance create");
29 }
30 
~AudioRendererDeviceChangeCallbackNapi()31 AudioRendererDeviceChangeCallbackNapi::~AudioRendererDeviceChangeCallbackNapi()
32 {
33     AUDIO_INFO_LOG("instance destroy");
34 }
35 
AddCallbackReference(napi_value args)36 void AudioRendererDeviceChangeCallbackNapi::AddCallbackReference(napi_value args)
37 {
38     std::lock_guard<std::mutex> lock(mutex_);
39     napi_ref callback = nullptr;
40     const int32_t refCount = 1;
41     bool isEquals = false;
42     napi_value copyValue = nullptr;
43 
44     for (auto ref = callbacks_.begin(); ref != callbacks_.end(); ++ref) {
45         napi_get_reference_value(env_, *ref, &copyValue);
46         if (napi_ok != napi_strict_equals(env_, copyValue, args, &isEquals)) {
47             AUDIO_ERR_LOG("get napi_strict_equals failed");
48             return;
49         }
50         if (isEquals == true) {
51             AUDIO_INFO_LOG("js Callback already exist");
52             return;
53         }
54     }
55 
56     napi_status status = napi_create_reference(env_, args, refCount, &callback);
57     CHECK_AND_RETURN_LOG(status == napi_ok && callback != nullptr,
58         "AudioRendererDeviceChangeCallbackNapi: creating reference for callback fail");
59 
60     callbacks_.push_back(callback);
61     AUDIO_INFO_LOG("AddCallbackReference successful");
62 }
63 
RemoveCallbackReference(napi_env env,napi_value args)64 void AudioRendererDeviceChangeCallbackNapi::RemoveCallbackReference(napi_env env, napi_value args)
65 {
66     std::lock_guard<std::mutex> lock(mutex_);
67     bool isEquals = false;
68     napi_value copyValue = nullptr;
69 
70     if (args == nullptr) {
71         for (auto ref = callbacks_.begin(); ref != callbacks_.end(); ++ref) {
72             napi_status ret = napi_delete_reference(env, *ref);
73             CHECK_AND_RETURN_LOG(napi_ok == ret, "delete callback reference failed");
74         }
75         callbacks_.clear();
76         AUDIO_INFO_LOG("Remove all JS Callback");
77         return;
78     }
79 
80     for (auto ref = callbacks_.begin(); ref != callbacks_.end(); ++ref) {
81         napi_get_reference_value(env, *ref, &copyValue);
82         if (copyValue == nullptr) {
83             AUDIO_ERR_LOG("copyValue is nullptr");
84             return;
85         }
86         if (napi_ok != napi_strict_equals(env, args, copyValue, &isEquals)) {
87             AUDIO_ERR_LOG("get napi_strict_equals failed");
88             return;
89         }
90 
91         if (isEquals == true) {
92             AUDIO_INFO_LOG("found JS Callback, delete it!");
93             callbacks_.remove(*ref);
94             napi_status status = napi_delete_reference(env, *ref);
95             CHECK_AND_RETURN_LOG(status == napi_ok,
96                 "deleting reference for callback fail");
97             return;
98         }
99     }
100 
101     AUDIO_INFO_LOG("RemoveCallbackReference success");
102 }
103 
RemoveAllCallbacks()104 void AudioRendererDeviceChangeCallbackNapi::RemoveAllCallbacks()
105 {
106     std::lock_guard<std::mutex> lock(mutex_);
107     for (auto ref = callbacks_.begin(); ref != callbacks_.end(); ++ref) {
108         napi_status ret = napi_delete_reference(env_, *ref);
109         CHECK_AND_RETURN_LOG(napi_ok == ret, "delete callback reference failed");
110     }
111     callbacks_.clear();
112     AUDIO_INFO_LOG("RemoveAllCallbacks successful");
113 }
114 
OnStateChange(const DeviceInfo & deviceInfo)115 void AudioRendererDeviceChangeCallbackNapi::OnStateChange(const DeviceInfo &deviceInfo)
116 {
117     std::lock_guard<std::mutex> lock(mutex_);
118     for (auto ref = callbacks_.begin(); ref != callbacks_.end(); ++ref) {
119         OnJsCallbackRendererDeviceInfo(*ref, deviceInfo);
120     }
121 }
122 
SetValueInt32(const napi_env & env,const std::string & fieldStr,const int intValue,napi_value & obj)123 static void SetValueInt32(const napi_env& env, const std::string& fieldStr, const int intValue, napi_value& obj)
124 {
125     napi_value value = nullptr;
126     napi_create_int32(env, intValue, &value);
127     napi_set_named_property(env, obj, fieldStr.c_str(), value);
128 }
129 
SetValueString(const napi_env & env,const std::string & fieldStr,const std::string & stringValue,napi_value & result)130 static void SetValueString(const napi_env &env, const std::string &fieldStr, const std::string &stringValue,
131     napi_value &result)
132 {
133     napi_value value = nullptr;
134     napi_create_string_utf8(env, stringValue.c_str(), NAPI_AUTO_LENGTH, &value);
135     napi_set_named_property(env, result, fieldStr.c_str(), value);
136 }
137 
SetDeviceDescriptors(const napi_env & env,napi_value & jsChangeInfoObj,const DeviceInfo & deviceInfo)138 static void SetDeviceDescriptors(const napi_env& env, napi_value &jsChangeInfoObj, const DeviceInfo &deviceInfo)
139 {
140     SetValueInt32(env, "deviceRole", static_cast<int32_t>(deviceInfo.deviceRole), jsChangeInfoObj);
141     SetValueInt32(env, "deviceType", static_cast<int32_t>(deviceInfo.deviceType), jsChangeInfoObj);
142     SetValueInt32(env, "id", static_cast<int32_t>(deviceInfo.deviceId), jsChangeInfoObj);
143     SetValueString(env, "name", deviceInfo.deviceName, jsChangeInfoObj);
144     SetValueString(env, "address", deviceInfo.macAddress, jsChangeInfoObj);
145 
146     napi_value value = nullptr;
147     napi_value sampleRates;
148     napi_create_array_with_length(env, 1, &sampleRates);
149     napi_create_int32(env, deviceInfo.audioStreamInfo.samplingRate, &value);
150     napi_set_element(env, sampleRates, 0, value);
151     napi_set_named_property(env, jsChangeInfoObj, "sampleRates", sampleRates);
152 
153     napi_value channelCounts;
154     napi_create_array_with_length(env, 1, &channelCounts);
155     napi_create_int32(env, deviceInfo.audioStreamInfo.channels, &value);
156     napi_set_element(env, channelCounts, 0, value);
157     napi_set_named_property(env, jsChangeInfoObj, "channelCounts", channelCounts);
158 
159     napi_value channelMasks;
160     napi_create_array_with_length(env, 1, &channelMasks);
161     napi_create_int32(env, deviceInfo.channelMasks, &value);
162     napi_set_element(env, channelMasks, 0, value);
163     napi_set_named_property(env, jsChangeInfoObj, "channelMasks", channelMasks);
164 }
165 
NativeRendererChangeInfoToJsObj(const napi_env & env,napi_value & jsChangeDeviceInfosObj,const DeviceInfo & devInfo)166 static void NativeRendererChangeInfoToJsObj(const napi_env &env, napi_value &jsChangeDeviceInfosObj,
167     const DeviceInfo &devInfo)
168 {
169     napi_value valueParam = nullptr;
170     napi_create_array_with_length(env, 1, &jsChangeDeviceInfosObj);
171     napi_create_object(env, &valueParam);
172     SetDeviceDescriptors(env, valueParam, devInfo);
173     napi_set_element(env, jsChangeDeviceInfosObj, 0, valueParam);
174 }
175 
WorkCallbackCompleted(uv_work_t * work,int status)176 void AudioRendererDeviceChangeCallbackNapi::WorkCallbackCompleted(uv_work_t *work, int status)
177 {
178     // Js Thread
179     std::shared_ptr<AudioRendererDeviceChangeJsCallback> context(
180         static_cast<AudioRendererDeviceChangeJsCallback*>(work->data),
181         [work](AudioRendererDeviceChangeJsCallback* ptr) {
182             delete ptr;
183             delete work;
184     });
185 
186     AudioRendererDeviceChangeJsCallback *event = reinterpret_cast<AudioRendererDeviceChangeJsCallback*>(work->data);
187     if (event == nullptr || event->callback_ == nullptr) {
188         AUDIO_ERR_LOG("OnJsCallbackRendererDeviceInfo: No memory");
189         return;
190     }
191     napi_env env = event->env_;
192     napi_ref callback = event->callback_;
193 
194     do {
195         napi_value jsCallback = nullptr;
196         napi_status nstatus = napi_get_reference_value(env, callback, &jsCallback);
197         CHECK_AND_BREAK_LOG(nstatus == napi_ok && jsCallback != nullptr, "callback get reference value fail");
198         // Call back function
199         napi_value args[1] = { nullptr };
200         NativeRendererChangeInfoToJsObj(env, args[0], event->deviceInfo_);
201         CHECK_AND_BREAK_LOG(nstatus == napi_ok && args[0] != nullptr,
202             " fail to convert to jsobj");
203 
204         const size_t argCount = 1;
205         napi_value result = nullptr;
206         nstatus = napi_call_function(env, nullptr, jsCallback, argCount, args, &result);
207         CHECK_AND_BREAK_LOG(nstatus == napi_ok, "Fail to call devicechange callback");
208     } while (0);
209 }
210 
OnJsCallbackRendererDeviceInfo(napi_ref method,const DeviceInfo & deviceInfo)211 void AudioRendererDeviceChangeCallbackNapi::OnJsCallbackRendererDeviceInfo(napi_ref method,
212     const DeviceInfo &deviceInfo)
213 {
214     uv_loop_s *loop = nullptr;
215     napi_get_uv_event_loop(env_, &loop);
216     if (loop == nullptr) {
217         AUDIO_ERR_LOG("OnJsCallbackRendererDeviceInfo loop_ is nullptr");
218         return;
219     }
220 
221     if (method == nullptr) {
222         AUDIO_ERR_LOG("OnJsCallbackRendererDeviceInfo method is nullptr");
223         return;
224     }
225 
226     uv_work_t *work = new(std::nothrow) uv_work_t;
227     if (work == nullptr) {
228         AUDIO_ERR_LOG("OnJsCallbackRendererDeviceInfo: No memory");
229     }
230 
231     work->data = new AudioRendererDeviceChangeJsCallback {method, env_, deviceInfo};
232     if (work->data == nullptr) {
233         AUDIO_ERR_LOG("OnJsCallbackRendererDeviceInfo failed: No memory");
234     }
235     AUDIO_ERR_LOG("OnJsCallbackRendererDeviceInfo");
236     int ret = uv_queue_work(loop, work, [] (uv_work_t *work) {}, WorkCallbackCompleted);
237     if (ret != 0) {
238         AUDIO_ERR_LOG("Failed to execute libuv work queue");
239         delete work;
240     }
241 }
242 
GetCallbackListSize() const243 int32_t AudioRendererDeviceChangeCallbackNapi::GetCallbackListSize() const
244 {
245     return callbacks_.size();
246 }
247 }  // namespace AudioStandard
248 }  // namespace OHOS
249