• 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 "js_native_api.h"
20 #include "napi_audio_renderer_callback.h"
21 #include "napi_param_utils.h"
22 #include "napi_audio_error.h"
23 
24 namespace OHOS {
25 namespace AudioStandard {
26 
NapiAudioRendererCallback(napi_env env)27 NapiAudioRendererCallback::NapiAudioRendererCallback(napi_env env)
28     : env_(env)
29 {
30     AUDIO_DEBUG_LOG("instance create");
31 }
32 
~NapiAudioRendererCallback()33 NapiAudioRendererCallback::~NapiAudioRendererCallback()
34 {
35     if (regArStateChgTsfn_) {
36         napi_release_threadsafe_function(arStateChgTsfn_, napi_tsfn_abort);
37     } else if (regArInterruptTsfn_) {
38         napi_release_threadsafe_function(arInterruptTsfn_, napi_tsfn_abort);
39     }
40     AUDIO_DEBUG_LOG("instance destroy");
41 }
42 
OnInterrupt(const InterruptEvent & interruptEvent)43 void NapiAudioRendererCallback::OnInterrupt(const InterruptEvent &interruptEvent)
44 {
45     std::lock_guard<std::mutex> lock(mutex_);
46     AUDIO_DEBUG_LOG("OnInterrupt is called,hintType: %{public}d", interruptEvent.hintType);
47     CHECK_AND_RETURN_LOG(interruptCallback_ != nullptr, "Cannot find the reference of interrupt callback");
48 
49     std::unique_ptr<AudioRendererJsCallback> cb = std::make_unique<AudioRendererJsCallback>();
50     CHECK_AND_RETURN_LOG(cb != nullptr, "No memory");
51     cb->callback = interruptCallback_;
52     cb->callbackName = INTERRUPT_CALLBACK_NAME;
53     cb->interruptEvent = interruptEvent;
54     return OnJsCallbackInterrupt(cb);
55 }
56 
OnStateChange(const RendererState state,const StateChangeCmdType cmdType)57 void NapiAudioRendererCallback::OnStateChange(const RendererState state,
58     const StateChangeCmdType __attribute__((unused)) cmdType)
59 {
60     std::lock_guard<std::mutex> lock(mutex_);
61     AUDIO_DEBUG_LOG("OnStateChange is called, state: %{public}d", state);
62     CHECK_AND_RETURN_LOG(stateChangeCallback_ != nullptr, "Cannot find the reference of stateChange callback");
63 
64     std::unique_ptr<AudioRendererJsCallback> cb = std::make_unique<AudioRendererJsCallback>();
65     CHECK_AND_RETURN_LOG(cb != nullptr, "No memory");
66     cb->callback = stateChangeCallback_;
67     cb->callbackName = STATE_CHANGE_CALLBACK_NAME;
68     cb->state = state;
69     return OnJsCallbackStateChange(cb);
70 }
71 
SaveCallbackReference(const std::string & callbackName,napi_value args)72 void NapiAudioRendererCallback::SaveCallbackReference(const std::string &callbackName, napi_value args)
73 {
74     std::lock_guard<std::mutex> lock(mutex_);
75     // create function that will operate while save callback reference success.
76     std::function<void(std::shared_ptr<AutoRef> generatedCallback)> successed =
77         [this, callbackName](std::shared_ptr<AutoRef> generatedCallback) {
78         if (callbackName == INTERRUPT_CALLBACK_NAME || callbackName == AUDIO_INTERRUPT_CALLBACK_NAME) {
79             interruptCallback_ = generatedCallback;
80             return;
81         }
82         if (callbackName == STATE_CHANGE_CALLBACK_NAME) {
83             stateChangeCallback_ = generatedCallback;
84             return;
85         }
86     };
87     NapiAudioRendererCallbackInner::SaveCallbackReferenceInner(callbackName, args, successed);
88 }
89 
RemoveCallbackReference(const std::string & callbackName,napi_env env,napi_value callback,napi_value args)90 void NapiAudioRendererCallback::RemoveCallbackReference(const std::string &callbackName, napi_env env,
91     napi_value callback, napi_value args)
92 {
93     std::lock_guard<std::mutex> lock(mutex_);
94     // create function that will operate while save callback reference success.
95     std::function<void()> successed = [this, callbackName]() {
96         if (callbackName == INTERRUPT_CALLBACK_NAME || callbackName == AUDIO_INTERRUPT_CALLBACK_NAME) {
97             interruptCallback_ = nullptr;
98             return;
99         }
100         if (callbackName == STATE_CHANGE_CALLBACK_NAME) {
101             stateChangeCallback_ = nullptr;
102             return;
103         }
104     };
105     RemoveCallbackReferenceInner(callbackName, env, callback, successed);
106 }
107 
GetCallback(const std::string & callbackName)108 std::shared_ptr<AutoRef> NapiAudioRendererCallback::GetCallback(const std::string &callbackName)
109 {
110     std::shared_ptr<AutoRef> cb = nullptr;
111     if (callbackName == INTERRUPT_CALLBACK_NAME || callbackName == AUDIO_INTERRUPT_CALLBACK_NAME) {
112         return interruptCallback_;
113     }
114     if (callbackName == STATE_CHANGE_CALLBACK_NAME) {
115         return stateChangeCallback_;
116     }
117     AUDIO_ERR_LOG("NapiAudioRendererCallback->GetCallback Unknown callback type: %{public}s", callbackName.c_str());
118     return cb;
119 }
120 
CreateArInterrupt(napi_env env)121 void NapiAudioRendererCallback::CreateArInterrupt(napi_env env)
122 {
123     regArInterruptTsfn_ = true;
124     std::string callbackName = "Interrupt";
125     napi_value cbName;
126     napi_create_string_utf8(env, callbackName.c_str(), callbackName.length(), &cbName);
127     napi_create_threadsafe_function(env, nullptr, nullptr, cbName, 0, 1, nullptr,
128         InterruptTsfnFinalize, nullptr, SafeJsCallbackInterruptWork, &arInterruptTsfn_);
129 }
130 
CreateArStateChange(napi_env env)131 void NapiAudioRendererCallback::CreateArStateChange(napi_env env)
132 {
133     regArStateChgTsfn_ = true;
134     napi_value cbName;
135     std::string callbackName = "stateChange";
136     napi_create_string_utf8(env_, callbackName.c_str(), callbackName.length(), &cbName);
137     napi_create_threadsafe_function(env, nullptr, nullptr, cbName, 0, 1, nullptr,
138         StateChangeTsfnFinalize, nullptr, SafeJsCallbackStateChangeWork, &arStateChgTsfn_);
139 }
140 
GetArInterruptTsfnFlag()141 bool NapiAudioRendererCallback::GetArInterruptTsfnFlag()
142 {
143     return regArInterruptTsfn_;
144 }
145 
GetArStateChangeTsfnFlag()146 bool NapiAudioRendererCallback::GetArStateChangeTsfnFlag()
147 {
148     return regArStateChgTsfn_;
149 }
150 
GetEnv()151 napi_env &NapiAudioRendererCallback::GetEnv()
152 {
153     return env_;
154 }
155 
CheckIfTargetCallbackName(const std::string & callbackName)156 bool NapiAudioRendererCallback::CheckIfTargetCallbackName(const std::string &callbackName)
157 {
158     if (callbackName == INTERRUPT_CALLBACK_NAME || callbackName == AUDIO_INTERRUPT_CALLBACK_NAME ||
159         callbackName == STATE_CHANGE_CALLBACK_NAME) {
160         return true;
161     }
162     return false;
163 }
164 
SafeJsCallbackInterruptWork(napi_env env,napi_value js_cb,void * context,void * data)165 void NapiAudioRendererCallback::SafeJsCallbackInterruptWork(napi_env env, napi_value js_cb, void *context, void *data)
166 {
167     AudioRendererJsCallback *event = reinterpret_cast<AudioRendererJsCallback *>(data);
168     CHECK_AND_RETURN_LOG((event != nullptr) && (event->callback != nullptr),
169         "OnJsCallbackInterrupt: no memory");
170     std::shared_ptr<AudioRendererJsCallback> safeContext(
171         static_cast<AudioRendererJsCallback*>(data),
172         [](AudioRendererJsCallback *ptr) {
173             delete ptr;
174     });
175     std::string request = event->callbackName;
176     napi_ref callback = event->callback->cb_;
177     napi_handle_scope scope = nullptr;
178     napi_open_handle_scope(env, &scope);
179     CHECK_AND_RETURN_LOG(scope != nullptr, "scope is nullptr");
180     AUDIO_INFO_LOG("SafeJsCallbackInterruptWork: safe js callback working.");
181 
182     do {
183         napi_value jsCallback = nullptr;
184         napi_status nstatus = napi_get_reference_value(env, callback, &jsCallback);
185         CHECK_AND_BREAK_LOG(nstatus == napi_ok && jsCallback != nullptr, "%{public}s get reference value fail",
186             request.c_str());
187         // Call back function
188         napi_value args[ARGS_ONE] = { nullptr };
189         NapiParamUtils::SetInterruptEvent(env, event->interruptEvent, args[0]);
190         CHECK_AND_BREAK_LOG(nstatus == napi_ok && args[PARAM0] != nullptr,
191             "%{public}s fail to create Interrupt callback", request.c_str());
192 
193         const size_t argCount = ARGS_ONE;
194         napi_value result = nullptr;
195         nstatus = napi_call_function(env, nullptr, jsCallback, argCount, args, &result);
196         CHECK_AND_BREAK_LOG(nstatus == napi_ok, "%{public}s fail to call Interrupt callback", request.c_str());
197     } while (0);
198     napi_close_handle_scope(env, scope);
199 }
200 
InterruptTsfnFinalize(napi_env env,void * data,void * hint)201 void NapiAudioRendererCallback::InterruptTsfnFinalize(napi_env env, void *data, void *hint)
202 {
203     AUDIO_INFO_LOG("StateChangeTsfnFinalize: safe thread resource release.");
204 }
205 
OnJsCallbackInterrupt(std::unique_ptr<AudioRendererJsCallback> & jsCb)206 void NapiAudioRendererCallback::OnJsCallbackInterrupt(std::unique_ptr<AudioRendererJsCallback> &jsCb)
207 {
208     if (jsCb.get() == nullptr) {
209         AUDIO_ERR_LOG("OnJsCallbackInterrupt: jsCb.get() is null");
210         return;
211     }
212 
213     AudioRendererJsCallback *event = jsCb.release();
214     CHECK_AND_RETURN_LOG((event != nullptr) && (event->callback != nullptr), "event is nullptr.");
215 
216     napi_acquire_threadsafe_function(arInterruptTsfn_);
217     napi_call_threadsafe_function(arInterruptTsfn_, event, napi_tsfn_blocking);
218 }
219 
SafeJsCallbackStateChangeWork(napi_env env,napi_value js_cb,void * context,void * data)220 void NapiAudioRendererCallback::SafeJsCallbackStateChangeWork(napi_env env, napi_value js_cb, void *context, void *data)
221 {
222     AudioRendererJsCallback *event = reinterpret_cast<AudioRendererJsCallback *>(data);
223     CHECK_AND_RETURN_LOG((event != nullptr) && (event->callback != nullptr),
224         "OnJsCallbackStateChange: no memory");
225     std::shared_ptr<AudioRendererJsCallback> safeContext(
226         static_cast<AudioRendererJsCallback*>(data),
227         [](AudioRendererJsCallback *ptr) {
228             delete ptr;
229     });
230     std::string request = event->callbackName;
231     napi_ref callback = event->callback->cb_;
232     napi_handle_scope scope = nullptr;
233     napi_open_handle_scope(env, &scope);
234     CHECK_AND_RETURN_LOG(scope != nullptr, "scope is nullptr");
235     AUDIO_INFO_LOG("SafeJsCallbackStateChangeWork: safe js callback working.");
236 
237     do {
238         napi_value jsCallback = nullptr;
239         napi_status nstatus = napi_get_reference_value(env, callback, &jsCallback);
240         CHECK_AND_BREAK_LOG(nstatus == napi_ok && jsCallback != nullptr, "%{public}s get reference value fail",
241             request.c_str());
242 
243         // Call back function
244         napi_value args[1] = { nullptr };
245         nstatus = NapiParamUtils::SetValueInt32(env, event->state, args[0]);
246         CHECK_AND_BREAK_LOG(nstatus == napi_ok && args[PARAM0] != nullptr,
247             "%{public}s fail to create Interrupt callback", request.c_str());
248 
249         const size_t argCount = 1;
250         napi_value result = nullptr;
251         nstatus = napi_call_function(env, nullptr, jsCallback, argCount, args, &result);
252         CHECK_AND_BREAK_LOG(nstatus == napi_ok, "%{public}s fail to call Interrupt callback", request.c_str());
253     } while (0);
254     napi_close_handle_scope(env, scope);
255 }
256 
StateChangeTsfnFinalize(napi_env env,void * data,void * hint)257 void NapiAudioRendererCallback::StateChangeTsfnFinalize(napi_env env, void *data, void *hint)
258 {
259     AUDIO_INFO_LOG("StateChangeTsfnFinalize: safe thread resource release.");
260 }
261 
OnJsCallbackStateChange(std::unique_ptr<AudioRendererJsCallback> & jsCb)262 void NapiAudioRendererCallback::OnJsCallbackStateChange(std::unique_ptr<AudioRendererJsCallback> &jsCb)
263 {
264     if (jsCb.get() == nullptr) {
265         AUDIO_ERR_LOG("OnJsCallbackStateChange: jsCb.get() is null");
266         return;
267     }
268 
269     AudioRendererJsCallback *event = jsCb.release();
270     CHECK_AND_RETURN_LOG((event != nullptr) && (event->callback != nullptr), "event is nullptr.");
271 
272     napi_acquire_threadsafe_function(arStateChgTsfn_);
273     napi_call_threadsafe_function(arStateChgTsfn_, event, napi_tsfn_blocking);
274 }
275 }  // namespace AudioStandard
276 }  // namespace OHOS