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