1 /*
2 * Copyright (c) 2022 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_state_callback_napi.h"
17
18 #include <uv.h>
19
20 #include "audio_errors.h"
21 #include "audio_log.h"
22
23 using namespace std;
24
25 namespace OHOS {
26 namespace AudioStandard {
AudioRendererStateCallbackNapi(napi_env env)27 AudioRendererStateCallbackNapi::AudioRendererStateCallbackNapi(napi_env env)
28 : env_(env)
29 {
30 AUDIO_DEBUG_LOG("AudioRendererStateCallbackNapi: instance create");
31 }
32
~AudioRendererStateCallbackNapi()33 AudioRendererStateCallbackNapi::~AudioRendererStateCallbackNapi()
34 {
35 AUDIO_DEBUG_LOG("AudioRendererStateCallbackNapi: instance destroy");
36 }
37
SaveCallbackReference(napi_value args)38 void AudioRendererStateCallbackNapi::SaveCallbackReference(napi_value args)
39 {
40 std::lock_guard<std::mutex> lock(mutex_);
41 napi_ref callback = nullptr;
42 const int32_t refCount = 1;
43 napi_status status = napi_create_reference(env_, args, refCount, &callback);
44 CHECK_AND_RETURN_LOG(status == napi_ok && callback != nullptr,
45 "AudioRendererStateCallbackNapi: creating reference for callback fail");
46
47 std::shared_ptr<AutoRef> cb = std::make_shared<AutoRef>(env_, callback);
48 CHECK_AND_RETURN_LOG(cb != nullptr, "AudioRendererStateCallbackNapi: creating callback failed");
49
50 rendererStateCallback_ = cb;
51 }
52
OnRendererStateChange(const std::vector<std::unique_ptr<AudioRendererChangeInfo>> & audioRendererChangeInfos)53 void AudioRendererStateCallbackNapi::OnRendererStateChange(
54 const std::vector<std::unique_ptr<AudioRendererChangeInfo>> &audioRendererChangeInfos)
55 {
56 AUDIO_INFO_LOG("AudioRendererStateCallbackNapi: OnRendererStateChange entered");
57
58 std::lock_guard<std::mutex> lock(mutex_);
59
60 std::unique_ptr<AudioRendererStateJsCallback> cb = std::make_unique<AudioRendererStateJsCallback>();
61 CHECK_AND_RETURN_LOG(cb != nullptr, "No memory!!");
62
63 std::vector<std::unique_ptr<AudioRendererChangeInfo>> rendererChangeInfos;
64 for (const auto &changeInfo : audioRendererChangeInfos) {
65 rendererChangeInfos.push_back(std::make_unique<AudioRendererChangeInfo>(*changeInfo));
66 }
67
68 cb->callback = rendererStateCallback_;
69 cb->changeInfos = move(rendererChangeInfos);
70
71 return OnJsCallbackRendererState(cb);
72 }
73
SetValueInt32(const napi_env & env,const std::string & fieldStr,const int intValue,napi_value & obj)74 static void SetValueInt32(const napi_env& env, const std::string& fieldStr, const int intValue, napi_value& obj)
75 {
76 napi_value value = nullptr;
77 napi_create_int32(env, intValue, &value);
78 napi_set_named_property(env, obj, fieldStr.c_str(), value);
79 }
80
SetValueString(const napi_env & env,const std::string & fieldStr,const std::string stringValue,napi_value & result)81 static void SetValueString(const napi_env &env, const std::string &fieldStr, const std::string stringValue,
82 napi_value &result)
83 {
84 napi_value value = nullptr;
85 napi_create_string_utf8(env, stringValue.c_str(), NAPI_AUTO_LENGTH, &value);
86 napi_set_named_property(env, result, fieldStr.c_str(), value);
87 }
88
SetDeviceDescriptors(const napi_env & env,napi_value & jsChangeInfoObj,const DeviceInfo & deviceInfo)89 static void SetDeviceDescriptors(const napi_env& env, napi_value &jsChangeInfoObj, const DeviceInfo &deviceInfo)
90 {
91 napi_value jsDeviceDescriptorsObj = nullptr;
92 napi_value valueParam = nullptr;
93 napi_create_array_with_length(env, 1, &jsDeviceDescriptorsObj);
94
95 (void)napi_create_object(env, &valueParam);
96 SetValueInt32(env, "deviceRole", static_cast<int32_t>(deviceInfo.deviceRole), valueParam);
97 SetValueInt32(env, "deviceType", static_cast<int32_t>(deviceInfo.deviceType), valueParam);
98 SetValueInt32(env, "id", static_cast<int32_t>(deviceInfo.deviceId), valueParam);
99 SetValueString(env, "name", deviceInfo.deviceName, valueParam);
100 SetValueString(env, "address", deviceInfo.macAddress, valueParam);
101
102 napi_value value = nullptr;
103 napi_value sampleRates;
104 napi_create_array_with_length(env, 1, &sampleRates);
105 napi_create_int32(env, deviceInfo.audioStreamInfo.samplingRate, &value);
106 napi_set_element(env, sampleRates, 0, value);
107 napi_set_named_property(env, valueParam, "sampleRates", sampleRates);
108
109 napi_value channelCounts;
110 napi_create_array_with_length(env, 1, &channelCounts);
111 napi_create_int32(env, deviceInfo.audioStreamInfo.channels, &value);
112 napi_set_element(env, channelCounts, 0, value);
113 napi_set_named_property(env, valueParam, "channelCounts", channelCounts);
114
115 napi_value channelMasks;
116 napi_create_array_with_length(env, 1, &channelMasks);
117 napi_create_int32(env, deviceInfo.channelMasks, &value);
118 napi_set_element(env, channelMasks, 0, value);
119 napi_set_named_property(env, valueParam, "channelMasks", channelMasks);
120
121 napi_set_element(env, jsDeviceDescriptorsObj, 0, valueParam);
122 napi_set_named_property(env, jsChangeInfoObj, "deviceDescriptors", jsDeviceDescriptorsObj);
123 }
124
NativeRendererChangeInfoToJsObj(const napi_env & env,napi_value & jsArrayChangeInfoObj,const vector<unique_ptr<AudioRendererChangeInfo>> & changeInfos)125 static void NativeRendererChangeInfoToJsObj(const napi_env &env, napi_value &jsArrayChangeInfoObj,
126 const vector<unique_ptr<AudioRendererChangeInfo>> &changeInfos)
127 {
128 napi_value jsChangeInfoObj = nullptr;
129 napi_value jsRenInfoObj = nullptr;
130
131 size_t size = changeInfos.size();
132 int32_t position = 0;
133 napi_create_array_with_length(env, size, &jsArrayChangeInfoObj);
134 for (const unique_ptr<AudioRendererChangeInfo> &changeInfo: changeInfos) {
135 napi_create_object(env, &jsChangeInfoObj);
136 SetValueInt32(env, "streamId", static_cast<int32_t>(changeInfo->sessionId), jsChangeInfoObj);
137 SetValueInt32(env, "rendererState", static_cast<int32_t>(changeInfo->rendererState), jsChangeInfoObj);
138 SetValueInt32(env, "clientUid", static_cast<int32_t>(changeInfo->clientUID), jsChangeInfoObj);
139 napi_create_object(env, &jsRenInfoObj);
140 SetValueInt32(env, "content", static_cast<int32_t>(changeInfo->rendererInfo.contentType), jsRenInfoObj);
141 SetValueInt32(env, "usage", static_cast<int32_t>(changeInfo->rendererInfo.streamUsage), jsRenInfoObj);
142 SetValueInt32(env, "rendererFlags", changeInfo->rendererInfo.rendererFlags, jsRenInfoObj);
143 napi_set_named_property(env, jsChangeInfoObj, "rendererInfo", jsRenInfoObj);
144 SetDeviceDescriptors(env, jsChangeInfoObj, changeInfo->outputDeviceInfo);
145
146 napi_set_element(env, jsArrayChangeInfoObj, position, jsChangeInfoObj);
147 position++;
148 }
149 }
150
OnJsCallbackRendererState(std::unique_ptr<AudioRendererStateJsCallback> & jsCb)151 void AudioRendererStateCallbackNapi::OnJsCallbackRendererState(std::unique_ptr<AudioRendererStateJsCallback> &jsCb)
152 {
153 uv_loop_s *loop = nullptr;
154 napi_get_uv_event_loop(env_, &loop);
155 if (loop == nullptr) {
156 return;
157 }
158
159 uv_work_t *work = new(std::nothrow) uv_work_t;
160 if (work == nullptr) {
161 AUDIO_ERR_LOG("AudioRendererStateCallbackNapi: OnJsCallbackRendererState: No memory");
162 return;
163 }
164
165 work->data = reinterpret_cast<void *>(jsCb.get());
166
167 int ret = uv_queue_work(loop, work, [] (uv_work_t *work) {}, [] (uv_work_t *work, int status) {
168 // Js Thread
169 AudioRendererStateJsCallback *event = reinterpret_cast<AudioRendererStateJsCallback *>(work->data);
170 if (event == nullptr || event->callback == nullptr) {
171 AUDIO_ERR_LOG("AudioRendererStateCallbackNapi: OnJsCallbackRendererState: No memory");
172 return;
173 }
174 napi_env env = event->callback->env_;
175 napi_ref callback = event->callback->cb_;
176
177 do {
178 napi_value jsCallback = nullptr;
179 napi_status nstatus = napi_get_reference_value(env, callback, &jsCallback);
180 CHECK_AND_BREAK_LOG(nstatus == napi_ok && jsCallback != nullptr, "callback get reference value fail");
181
182 // Call back function
183 napi_value args[1] = { nullptr };
184 NativeRendererChangeInfoToJsObj(env, args[0], event->changeInfos);
185
186 CHECK_AND_BREAK_LOG(nstatus == napi_ok && args[0] != nullptr,
187 " fail to convert to jsobj");
188
189 const size_t argCount = 1;
190 napi_value result = nullptr;
191 nstatus = napi_call_function(env, nullptr, jsCallback, argCount, args, &result);
192 CHECK_AND_BREAK_LOG(nstatus == napi_ok, "Fail to call renderstate callback");
193 } while (0);
194 delete event;
195 delete work;
196 });
197 if (ret != 0) {
198 AUDIO_ERR_LOG("Failed to execute libuv work queue");
199 delete work;
200 } else {
201 jsCb.release();
202 }
203 }
204 } // namespace AudioStandard
205 } // namespace OHOS
206