• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2025 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 "NapiAudioLoopbackCallback"
17 #endif
18 
19 #include "js_native_api.h"
20 #include "napi_audio_loopback_callback.h"
21 #include "napi_param_utils.h"
22 #include "napi_audio_error.h"
23 
24 namespace OHOS {
25 namespace AudioStandard {
26 
NapiAudioLoopbackCallback(napi_env env)27 NapiAudioLoopbackCallback::NapiAudioLoopbackCallback(napi_env env)
28     : env_(env)
29 {
30     AUDIO_DEBUG_LOG("instance create");
31 }
32 
~NapiAudioLoopbackCallback()33 NapiAudioLoopbackCallback::~NapiAudioLoopbackCallback()
34 {
35     if (regArStatusChgTsfn_) {
36         napi_release_threadsafe_function(arStatusChgTsfn_, napi_tsfn_abort);
37     }
38     AUDIO_DEBUG_LOG("instance destroy");
39 }
40 
OnStatusChange(const AudioLoopbackStatus status,const StateChangeCmdType cmdType)41 void NapiAudioLoopbackCallback::OnStatusChange(const AudioLoopbackStatus status,
42     const StateChangeCmdType __attribute__((unused)) cmdType)
43 {
44     std::lock_guard<std::mutex> lock(mutex_);
45     AUDIO_DEBUG_LOG("OnStatusChange is called, status: %{public}d", status);
46     CHECK_AND_RETURN_LOG(statusChangeCallback_ != nullptr, "Cannot find the reference of statusChange callback");
47 
48     std::unique_ptr<AudioLoopbackJsCallback> cb = std::make_unique<AudioLoopbackJsCallback>();
49     CHECK_AND_RETURN_LOG(cb != nullptr, "No memory");
50     cb->callback = statusChangeCallback_;
51     cb->callbackName = STATUS_CHANGE_CALLBACK_NAME;
52     cb->status = status;
53     return OnJsCallbackStatusChange(cb);
54 }
55 
SaveCallbackReference(const std::string & callbackName,napi_value args)56 void NapiAudioLoopbackCallback::SaveCallbackReference(const std::string &callbackName, napi_value args)
57 {
58     std::lock_guard<std::mutex> lock(mutex_);
59     // create function that will operate while save callback reference success.
60     std::function<void(std::shared_ptr<AutoRef> generatedCallback)> successed =
61         [this, callbackName](std::shared_ptr<AutoRef> generatedCallback) {
62         if (callbackName == STATUS_CHANGE_CALLBACK_NAME) {
63             statusChangeCallback_ = generatedCallback;
64             return;
65         }
66     };
67     SaveCallbackReferenceInner(callbackName, args, successed);
68 }
69 
RemoveCallbackReference(const std::string & callbackName,napi_env env,napi_value callback,napi_value args)70 void NapiAudioLoopbackCallback::RemoveCallbackReference(const std::string &callbackName, napi_env env,
71     napi_value callback, napi_value args)
72 {
73     std::lock_guard<std::mutex> lock(mutex_);
74     // create function that will operate while save callback reference success.
75     std::function<void()> successed = [this, callbackName]() {
76         if (callbackName == STATUS_CHANGE_CALLBACK_NAME) {
77             statusChangeCallback_ = nullptr;
78             return;
79         }
80     };
81     RemoveCallbackReferenceInner(callbackName, env, callback, successed);
82 }
83 
GetCallback(const std::string & callbackName)84 std::shared_ptr<AutoRef> NapiAudioLoopbackCallback::GetCallback(const std::string &callbackName)
85 {
86     std::shared_ptr<AutoRef> cb = nullptr;
87     if (callbackName == STATUS_CHANGE_CALLBACK_NAME) {
88         return statusChangeCallback_;
89     }
90     AUDIO_ERR_LOG("NapiAudioLoopbackCallback->GetCallback Unknown callback type: %{public}s", callbackName.c_str());
91     return cb;
92 }
93 
CreateArStatusChange(napi_env env)94 void NapiAudioLoopbackCallback::CreateArStatusChange(napi_env env)
95 {
96     regArStatusChgTsfn_ = true;
97     napi_value cbName;
98     std::string callbackName = "statusChange";
99     napi_create_string_utf8(env_, callbackName.c_str(), callbackName.length(), &cbName);
100     napi_create_threadsafe_function(env, nullptr, nullptr, cbName, 0, 1, nullptr,
101         StatusChangeTsfnFinalize, nullptr, SafeJsCallbackStatusChangeWork, &arStatusChgTsfn_);
102 }
103 
GetArStatusChangeTsfnFlag()104 bool NapiAudioLoopbackCallback::GetArStatusChangeTsfnFlag()
105 {
106     return regArStatusChgTsfn_;
107 }
108 
GetEnv()109 napi_env &NapiAudioLoopbackCallback::GetEnv()
110 {
111     return env_;
112 }
113 
CheckIfTargetCallbackName(const std::string & callbackName)114 bool NapiAudioLoopbackCallback::CheckIfTargetCallbackName(const std::string &callbackName)
115 {
116     if (callbackName == STATUS_CHANGE_CALLBACK_NAME) {
117         return true;
118     }
119     return false;
120 }
121 
SafeJsCallbackStatusChangeWork(napi_env env,napi_value js_cb,void * context,void * data)122 void NapiAudioLoopbackCallback::SafeJsCallbackStatusChangeWork(napi_env env, napi_value js_cb, void *context,
123     void *data)
124 {
125     AudioLoopbackJsCallback *event = reinterpret_cast<AudioLoopbackJsCallback *>(data);
126     CHECK_AND_RETURN_LOG((event != nullptr) && (event->callback != nullptr),
127         "OnJsCallbackStatusChange: no memory");
128     std::shared_ptr<AudioLoopbackJsCallback> safeContext(
129         static_cast<AudioLoopbackJsCallback*>(data),
130         [](AudioLoopbackJsCallback *ptr) {
131             delete ptr;
132     });
133     std::string request = event->callbackName;
134     napi_ref callback = event->callback->cb_;
135     napi_handle_scope scope = nullptr;
136     napi_open_handle_scope(env, &scope);
137     CHECK_AND_RETURN_LOG(scope != nullptr, "scope is nullptr");
138     AUDIO_INFO_LOG("SafeJsCallbackStatusChangeWork: safe js callback working.");
139 
140     do {
141         napi_value jsCallback = nullptr;
142         napi_status nstatus = napi_get_reference_value(env, callback, &jsCallback);
143         CHECK_AND_BREAK_LOG(nstatus == napi_ok && jsCallback != nullptr, "%{public}s get reference value fail",
144             request.c_str());
145 
146         // Call back function
147         napi_value args[1] = { nullptr };
148         nstatus = NapiParamUtils::SetValueInt32(env, event->status, args[0]);
149         CHECK_AND_BREAK_LOG(nstatus == napi_ok && args[PARAM0] != nullptr,
150             "%{public}s fail to create StatusChange callback", request.c_str());
151 
152         const size_t argCount = 1;
153         napi_value result = nullptr;
154         nstatus = napi_call_function(env, nullptr, jsCallback, argCount, args, &result);
155         CHECK_AND_BREAK_LOG(nstatus == napi_ok, "%{public}s fail to call StatusChange callback", request.c_str());
156     } while (0);
157     napi_close_handle_scope(env, scope);
158 }
159 
StatusChangeTsfnFinalize(napi_env env,void * data,void * hint)160 void NapiAudioLoopbackCallback::StatusChangeTsfnFinalize(napi_env env, void *data, void *hint)
161 {
162     AUDIO_INFO_LOG("StatusChangeTsfnFinalize: safe thread resource release.");
163 }
164 
OnJsCallbackStatusChange(std::unique_ptr<AudioLoopbackJsCallback> & jsCb)165 void NapiAudioLoopbackCallback::OnJsCallbackStatusChange(std::unique_ptr<AudioLoopbackJsCallback> &jsCb)
166 {
167     if (jsCb.get() == nullptr) {
168         AUDIO_ERR_LOG("OnJsCallbackStatusChange: jsCb.get() is null");
169         return;
170     }
171 
172     AudioLoopbackJsCallback *event = jsCb.release();
173     CHECK_AND_RETURN_LOG((event != nullptr) && (event->callback != nullptr), "event is nullptr.");
174 
175     napi_acquire_threadsafe_function(arStatusChgTsfn_);
176     napi_call_threadsafe_function(arStatusChgTsfn_, event, napi_tsfn_blocking);
177 }
178 }  // namespace AudioStandard
179 }  // namespace OHOS