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