• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-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_callback_napi.h"
17 
18 #include <uv.h>
19 
20 #include "audio_errors.h"
21 #include "audio_log.h"
22 
23 namespace OHOS {
24 namespace AudioStandard {
AudioRendererCallbackNapi(napi_env env)25 AudioRendererCallbackNapi::AudioRendererCallbackNapi(napi_env env)
26     : env_(env)
27 {
28     AUDIO_DEBUG_LOG("instance create");
29 }
30 
~AudioRendererCallbackNapi()31 AudioRendererCallbackNapi::~AudioRendererCallbackNapi()
32 {
33     AUDIO_DEBUG_LOG("instance destroy");
34 }
35 
SaveCallbackReference(const std::string & callbackName,napi_value args)36 void AudioRendererCallbackNapi::SaveCallbackReference(const std::string &callbackName, napi_value args)
37 {
38     std::lock_guard<std::mutex> lock(mutex_);
39     napi_ref callback = nullptr;
40     const int32_t refCount = 1;
41     napi_status status = napi_create_reference(env_, args, refCount, &callback);
42     CHECK_AND_RETURN_LOG(status == napi_ok && callback != nullptr,
43                          "creating reference for callback fail");
44 
45     std::shared_ptr<AutoRef> cb = std::make_shared<AutoRef>(env_, callback);
46     if (callbackName == INTERRUPT_CALLBACK_NAME || callbackName == AUDIO_INTERRUPT_CALLBACK_NAME) {
47         interruptCallback_ = cb;
48     } else if (callbackName == STATE_CHANGE_CALLBACK_NAME) {
49         stateChangeCallback_ = cb;
50     } else {
51         AUDIO_ERR_LOG("Unknown callback type: %{public}s", callbackName.c_str());
52     }
53 }
54 
RemoveCallbackReference(const std::string & callbackName)55 void AudioRendererCallbackNapi::RemoveCallbackReference(const std::string &callbackName)
56 {
57     std::lock_guard<std::mutex> lock(mutex_);
58 
59     if (callbackName == AUDIO_INTERRUPT_CALLBACK_NAME) {
60         interruptCallback_ = nullptr;
61     } else if (callbackName == STATE_CHANGE_CALLBACK_NAME) {
62         stateChangeCallback_ = nullptr;
63     } else {
64         AUDIO_ERR_LOG("Unknown callback type: %{public}s", callbackName.c_str());
65     }
66 }
67 
SetValueInt32(const napi_env & env,const std::string & fieldStr,const int intValue,napi_value & result)68 static void SetValueInt32(const napi_env& env, const std::string& fieldStr, const int intValue, napi_value& result)
69 {
70     napi_value value = nullptr;
71     napi_create_int32(env, intValue, &value);
72     napi_set_named_property(env, result, fieldStr.c_str(), value);
73 }
74 
NativeInterruptEventToJsObj(const napi_env & env,napi_value & jsObj,const InterruptEvent & interruptEvent)75 static void NativeInterruptEventToJsObj(const napi_env& env, napi_value& jsObj,
76     const InterruptEvent& interruptEvent)
77 {
78         napi_create_object(env, &jsObj);
79         SetValueInt32(env, "eventType", static_cast<int32_t>(interruptEvent.eventType), jsObj);
80         SetValueInt32(env, "forceType", static_cast<int32_t>(interruptEvent.forceType), jsObj);
81         SetValueInt32(env, "hintType", static_cast<int32_t>(interruptEvent.hintType), jsObj);
82 }
83 
OnInterrupt(const InterruptEvent & interruptEvent)84 void AudioRendererCallbackNapi::OnInterrupt(const InterruptEvent &interruptEvent)
85 {
86     std::lock_guard<std::mutex> lock(mutex_);
87     AUDIO_DEBUG_LOG("OnInterrupt is called");
88     AUDIO_DEBUG_LOG("hintType: %{public}d", interruptEvent.hintType);
89     CHECK_AND_RETURN_LOG(interruptCallback_ != nullptr, "Cannot find the reference of interrupt callback");
90 
91     std::unique_ptr<AudioRendererJsCallback> cb = std::make_unique<AudioRendererJsCallback>();
92     CHECK_AND_RETURN_LOG(cb != nullptr, "No memory");
93     cb->callback = interruptCallback_;
94     cb->callbackName = INTERRUPT_CALLBACK_NAME;
95     cb->interruptEvent = interruptEvent;
96     return OnJsCallbackInterrupt(cb);
97 }
98 
OnJsCallbackInterrupt(std::unique_ptr<AudioRendererJsCallback> & jsCb)99 void AudioRendererCallbackNapi::OnJsCallbackInterrupt(std::unique_ptr<AudioRendererJsCallback> &jsCb)
100 {
101     uv_loop_s *loop = nullptr;
102     napi_get_uv_event_loop(env_, &loop);
103     if (loop == nullptr) {
104         return;
105     }
106 
107     uv_work_t *work = new(std::nothrow) uv_work_t;
108     if (work == nullptr) {
109         AUDIO_ERR_LOG("OnJsCallBackInterrupt: No memory");
110         return;
111     }
112     if (jsCb.get() == nullptr) {
113         AUDIO_ERR_LOG("OnJsCallBackInterrupt: jsCb.get() is null");
114         delete work;
115         return;
116     }
117     work->data = reinterpret_cast<void *>(jsCb.get());
118 
119     int ret = uv_queue_work(loop, work, [] (uv_work_t *work) {}, [] (uv_work_t *work, int status) {
120         // Js Thread
121         AudioRendererJsCallback *event = reinterpret_cast<AudioRendererJsCallback *>(work->data);
122         std::string request = event->callbackName;
123         napi_env env = event->callback->env_;
124         napi_ref callback = event->callback->cb_;
125         AUDIO_DEBUG_LOG("JsCallBack %{public}s, uv_queue_work start", request.c_str());
126         do {
127             CHECK_AND_BREAK_LOG(status != UV_ECANCELED, "%{public}s cancelled", request.c_str());
128 
129             napi_value jsCallback = nullptr;
130             napi_status nstatus = napi_get_reference_value(env, callback, &jsCallback);
131             CHECK_AND_BREAK_LOG(nstatus == napi_ok && jsCallback != nullptr, "%{public}s get reference value fail",
132                 request.c_str());
133 
134             // Call back function
135             napi_value args[1] = { nullptr };
136             NativeInterruptEventToJsObj(env, args[0], event->interruptEvent);
137             CHECK_AND_BREAK_LOG(nstatus == napi_ok && args[0] != nullptr,
138                 "%{public}s fail to create Interrupt callback", request.c_str());
139 
140             const size_t argCount = 1;
141             napi_value result = nullptr;
142             nstatus = napi_call_function(env, nullptr, jsCallback, argCount, args, &result);
143             CHECK_AND_BREAK_LOG(nstatus == napi_ok, "%{public}s fail to call Interrupt callback", request.c_str());
144         } while (0);
145         delete event;
146         delete work;
147     });
148     if (ret != 0) {
149         AUDIO_ERR_LOG("Failed to execute libuv work queue");
150         delete work;
151     } else {
152         jsCb.release();
153     }
154 }
155 
OnStateChange(const RendererState state,const StateChangeCmdType cmdType)156 void AudioRendererCallbackNapi::OnStateChange(const RendererState state,
157     const StateChangeCmdType __attribute__((unused)) cmdType)
158 {
159     std::lock_guard<std::mutex> lock(mutex_);
160     AUDIO_DEBUG_LOG("OnStateChange is called");
161     AUDIO_DEBUG_LOG("state: %{public}d", state);
162     CHECK_AND_RETURN_LOG(stateChangeCallback_ != nullptr, "Cannot find the reference of stateChange callback");
163 
164     std::unique_ptr<AudioRendererJsCallback> cb = std::make_unique<AudioRendererJsCallback>();
165     CHECK_AND_RETURN_LOG(cb != nullptr, "No memory");
166     cb->callback = stateChangeCallback_;
167     cb->callbackName = STATE_CHANGE_CALLBACK_NAME;
168     cb->state = state;
169     return OnJsCallbackStateChange(cb);
170 }
171 
OnJsCallbackStateChange(std::unique_ptr<AudioRendererJsCallback> & jsCb)172 void AudioRendererCallbackNapi::OnJsCallbackStateChange(std::unique_ptr<AudioRendererJsCallback> &jsCb)
173 {
174     uv_loop_s *loop = nullptr;
175     napi_get_uv_event_loop(env_, &loop);
176     if (loop == nullptr) {
177         return;
178     }
179 
180     uv_work_t *work = new(std::nothrow) uv_work_t;
181     if (work == nullptr) {
182         AUDIO_ERR_LOG("OnJsCallbackStateChange: No memory");
183         return;
184     }
185     if (jsCb.get() == nullptr) {
186         AUDIO_ERR_LOG("OnJsCallbackStateChange: jsCb.get() is null");
187         delete work;
188         return;
189     }
190     work->data = reinterpret_cast<void *>(jsCb.get());
191 
192     int ret = uv_queue_work(loop, work, [] (uv_work_t *work) {}, [] (uv_work_t *work, int status) {
193         // Js Thread
194         AudioRendererJsCallback *event = reinterpret_cast<AudioRendererJsCallback *>(work->data);
195         std::string request = event->callbackName;
196         napi_env env = event->callback->env_;
197         napi_ref callback = event->callback->cb_;
198         AUDIO_DEBUG_LOG("JsCallBack %{public}s, uv_queue_work start", request.c_str());
199         do {
200             CHECK_AND_BREAK_LOG(status != UV_ECANCELED, "%{public}s cancelled", request.c_str());
201 
202             napi_value jsCallback = nullptr;
203             napi_status nstatus = napi_get_reference_value(env, callback, &jsCallback);
204             CHECK_AND_BREAK_LOG(nstatus == napi_ok && jsCallback != nullptr, "%{public}s get reference value fail",
205                 request.c_str());
206 
207             // Call back function
208             napi_value args[1] = { nullptr };
209             nstatus = napi_create_int32(env, event->state, &args[0]);
210             CHECK_AND_BREAK_LOG(nstatus == napi_ok && args[0] != nullptr,
211                 "%{public}s fail to create Interrupt callback", request.c_str());
212 
213             const size_t argCount = 1;
214             napi_value result = nullptr;
215             nstatus = napi_call_function(env, nullptr, jsCallback, argCount, args, &result);
216             CHECK_AND_BREAK_LOG(nstatus == napi_ok, "%{public}s fail to call Interrupt callback", request.c_str());
217         } while (0);
218         delete event;
219         delete work;
220     });
221     if (ret != 0) {
222         AUDIO_ERR_LOG("Failed to execute libuv work queue");
223         delete work;
224     } else {
225         jsCb.release();
226     }
227 }
228 }  // namespace AudioStandard
229 }  // namespace OHOS
230