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 "NapiCapturerReadDataCallback"
17 #endif
18
19 #include "js_native_api.h"
20 #include "napi_audio_capturer_read_data_callback.h"
21 #include "audio_capturer_log.h"
22
23 namespace OHOS {
24 namespace AudioStandard {
25 static bool g_napiAudioCapturerIsNullptr = true;
26 static std::mutex g_asynccallbackMutex;
27 static const int32_t READ_CALLBACK_TIMEOUT_IN_MS = 1000; // 1s
28
NapiCapturerReadDataCallback(napi_env env,NapiAudioCapturer * napiCapturer)29 NapiCapturerReadDataCallback::NapiCapturerReadDataCallback(napi_env env, NapiAudioCapturer *napiCapturer)
30 : env_(env), napiCapturer_(napiCapturer)
31 {
32 AUDIO_DEBUG_LOG("instance create");
33 g_napiAudioCapturerIsNullptr = false;
34 }
35
~NapiCapturerReadDataCallback()36 NapiCapturerReadDataCallback::~NapiCapturerReadDataCallback()
37 {
38 if (napiCapturer_ != nullptr) {
39 napiCapturer_->readCallbackCv_.notify_all();
40 }
41 if (regAcReadDataTsfn_) {
42 napi_release_threadsafe_function(acReadDataTsfn_, napi_tsfn_abort);
43 }
44 AUDIO_DEBUG_LOG("instance destroy");
45 }
46
AddCallbackReference(const std::string & callbackName,napi_value args)47 void NapiCapturerReadDataCallback::AddCallbackReference(const std::string &callbackName, napi_value args)
48 {
49 std::lock_guard<std::mutex> lock(mutex_);
50 napi_ref callback = nullptr;
51 const int32_t refCount = 1;
52 napi_status status = napi_create_reference(env_, args, refCount, &callback);
53 CHECK_AND_RETURN_LOG(status == napi_ok && callback != nullptr, "creating reference for callback failed");
54
55 std::shared_ptr<AutoRef> cb = std::make_shared<AutoRef>(env_, callback);
56 if (callbackName == READ_DATA_CALLBACK_NAME) {
57 capturerReadDataCallback_ = cb;
58 isCallbackInited_ = true;
59 } else {
60 AUDIO_ERR_LOG("Unknown callback type: %{public}s", callbackName.c_str());
61 }
62 }
63
CreateReadDataTsfn(napi_env env)64 void NapiCapturerReadDataCallback::CreateReadDataTsfn(napi_env env)
65 {
66 regAcReadDataTsfn_ = true;
67 napi_value cbName;
68 std::string callbackName = "CapturerReadData";
69 napi_create_string_utf8(env, callbackName.c_str(), callbackName.length(), &cbName);
70 napi_create_threadsafe_function(env, nullptr, nullptr, cbName, 0, 1, nullptr,
71 CaptureReadDataTsfnFinalize, nullptr, SafeJsCallbackCapturerReadDataWork, &acReadDataTsfn_);
72 }
73
RemoveCallbackReference(napi_env env,napi_value callback)74 void NapiCapturerReadDataCallback::RemoveCallbackReference(napi_env env, napi_value callback)
75 {
76 std::lock_guard<std::mutex> lock(mutex_);
77 isCallbackInited_ = false;
78 bool isEquals = false;
79 napi_value copyValue = nullptr;
80
81 if (callback == nullptr) {
82 napi_status ret = napi_delete_reference(env, capturerReadDataCallback_->cb_);
83 CHECK_AND_RETURN_LOG(napi_ok == ret, "delete callback reference failed");
84 AUDIO_INFO_LOG("Remove Js Callback");
85 capturerReadDataCallback_->cb_ = nullptr;
86 return;
87 }
88
89 napi_get_reference_value(env, capturerReadDataCallback_->cb_, ©Value);
90 CHECK_AND_RETURN_LOG(copyValue != nullptr, "copyValue is nullptr");
91 CHECK_AND_RETURN_LOG(napi_strict_equals(env, callback, copyValue, &isEquals) == napi_ok,
92 "get napi_strict_equals failed");
93 if (isEquals) {
94 AUDIO_INFO_LOG("found JS Callback, delete it!");
95 napi_status status = napi_delete_reference(env, capturerReadDataCallback_->cb_);
96 CHECK_AND_RETURN_LOG(status == napi_ok, "deleting reference for callback failed");
97 capturerReadDataCallback_->cb_ = nullptr;
98 }
99 }
100
RemoveNapiCapturer()101 void NapiCapturerReadDataCallback::RemoveNapiCapturer()
102 {
103 std::lock_guard<std::mutex> lock(mutex_);
104 std::lock_guard<std::mutex> asyncLock(g_asynccallbackMutex);
105 napiCapturer_ = nullptr;
106 g_napiAudioCapturerIsNullptr = true;
107 }
108
OnReadData(size_t length)109 void NapiCapturerReadDataCallback::OnReadData(size_t length)
110 {
111 std::lock_guard<std::mutex> lock(mutex_);
112 CHECK_AND_RETURN_LOG(capturerReadDataCallback_ != nullptr, "Cannot find the reference of readData callback");
113
114 std::unique_ptr<CapturerReadDataJsCallback> cb = std::make_unique<CapturerReadDataJsCallback>();
115 cb->callback = capturerReadDataCallback_;
116 cb->callbackName = READ_DATA_CALLBACK_NAME;
117 cb->bufDesc.buffer = nullptr;
118 cb->capturerNapiObj = napiCapturer_;
119 cb->readDataCallbackPtr = this;
120
121 CHECK_AND_RETURN_LOG(napiCapturer_ != nullptr, "Cannot find the reference to audio capturer napi");
122 napiCapturer_->audioCapturer_->GetBufferDesc(cb->bufDesc);
123 if (cb->bufDesc.buffer == nullptr) {
124 return;
125 }
126 if (length > cb->bufDesc.bufLength) {
127 cb->bufDesc.dataLength = cb->bufDesc.bufLength;
128 } else {
129 cb->bufDesc.dataLength = length;
130 }
131
132 return OnJsCapturerReadDataCallback(cb);
133 }
134
OnJsCapturerReadDataCallback(std::unique_ptr<CapturerReadDataJsCallback> & jsCb)135 void NapiCapturerReadDataCallback::OnJsCapturerReadDataCallback(std::unique_ptr<CapturerReadDataJsCallback> &jsCb)
136 {
137 if (jsCb.get() == nullptr) {
138 AUDIO_ERR_LOG("OnJsCapturerReadDataCallback: jsCb.get() is null");
139 return;
140 }
141
142 auto obj = static_cast<NapiAudioCapturer *>(napiCapturer_);
143 NapiAudioCapturer *napiCapturer = ObjectRefMap<NapiAudioCapturer>::IncreaseRef(obj);
144 if (napiCapturer == nullptr) {
145 AUDIO_ERR_LOG("napiCapturer is null");
146 return;
147 }
148
149 CapturerReadDataJsCallback *event = jsCb.release();
150 CHECK_AND_RETURN_LOG((event != nullptr) && (event->callback != nullptr),
151 "OnJsCapturerReadDataCallback: event is nullptr.");
152
153 if (napiCapturer_ == nullptr) {
154 return;
155 }
156 napiCapturer_->isFrameCallbackDone_.store(false);
157
158 napi_acquire_threadsafe_function(acReadDataTsfn_);
159 napi_call_threadsafe_function(acReadDataTsfn_, event, napi_tsfn_blocking);
160
161 std::unique_lock<std::mutex> readCallbackLock(napiCapturer_->readCallbackMutex_);
162 bool isTimeout = !napiCapturer_->readCallbackCv_.wait_for(readCallbackLock,
163 std::chrono::milliseconds(READ_CALLBACK_TIMEOUT_IN_MS), [this] {
164 return napiCapturer_->isFrameCallbackDone_.load();
165 });
166 if (isTimeout) {
167 AUDIO_ERR_LOG("Client OnReadData operation timed out");
168 }
169 readCallbackLock.unlock();
170 }
171
CaptureReadDataTsfnFinalize(napi_env env,void * data,void * hint)172 void NapiCapturerReadDataCallback::CaptureReadDataTsfnFinalize(napi_env env, void *data, void *hint)
173 {
174 AUDIO_DEBUG_LOG("CaptureReadDataTsfnFinalize: safe thread resource release.");
175 }
176
SafeJsCallbackCapturerReadDataWork(napi_env env,napi_value js_cb,void * context,void * data)177 void NapiCapturerReadDataCallback::SafeJsCallbackCapturerReadDataWork(
178 napi_env env, napi_value js_cb, void *context, void *data)
179 {
180 CapturerReadDataJsCallback *event = reinterpret_cast<CapturerReadDataJsCallback *>(data);
181 CHECK_AND_RETURN_LOG(event != nullptr, "capturer read data event is nullptr");
182 std::shared_ptr<CapturerReadDataJsCallback> safeContext(
183 static_cast<CapturerReadDataJsCallback*>(data),
184 [](CapturerReadDataJsCallback *ptr) {
185 delete ptr;
186 });
187 SafeJsCallbackCapturerReadDataWorkInner(event);
188
189 CHECK_AND_RETURN_LOG(event->capturerNapiObj != nullptr, "NapiAudioCapturer object is nullptr");
190 event->capturerNapiObj->isFrameCallbackDone_.store(true);
191 event->capturerNapiObj->readCallbackCv_.notify_all();
192 auto napiObj = static_cast<NapiAudioCapturer *>(event->capturerNapiObj);
193 ObjectRefMap<NapiAudioCapturer>::DecreaseRef(napiObj);
194 }
195
SafeJsCallbackCapturerReadDataWorkInner(CapturerReadDataJsCallback * event)196 void NapiCapturerReadDataCallback::SafeJsCallbackCapturerReadDataWorkInner(CapturerReadDataJsCallback *event)
197 {
198 CHECK_AND_RETURN_LOG(event != nullptr, "capture read data event is nullptr");
199 CHECK_AND_RETURN_LOG(event->readDataCallbackPtr != nullptr, "CapturerReadDataCallback is already released");
200 CHECK_AND_RETURN_LOG(event->readDataCallbackPtr->isCallbackInited_, "the callback has been dereferenced");
201 std::string request = event->callbackName;
202 CHECK_AND_RETURN_LOG(event->callback != nullptr, "event is nullptr");
203 napi_env env = event->callback->env_;
204 napi_ref callback = event->callback->cb_;
205
206 napi_handle_scope scope = nullptr;
207 napi_open_handle_scope(env, &scope);
208 CHECK_AND_RETURN_LOG(scope != nullptr, "%{public}s scope is nullptr", request.c_str());
209 do {
210 napi_value jsCallback = nullptr;
211 napi_status nstatus = napi_get_reference_value(env, callback, &jsCallback);
212 CHECK_AND_BREAK_LOG(nstatus == napi_ok && jsCallback != nullptr, "%{public}s get reference value failed",
213 request.c_str());
214 napi_value args[ARGS_ONE] = { nullptr };
215 nstatus = napi_create_external_arraybuffer(env, event->bufDesc.buffer, event->bufDesc.dataLength,
216 [](napi_env env, void *data, void *hint) {}, nullptr, &args[PARAM0]);
217 CHECK_AND_BREAK_LOG(nstatus == napi_ok && args[PARAM0] != nullptr,
218 "%{public}s callback fail to create buffer", request.c_str());
219 const size_t argCount = 1;
220 napi_value result = nullptr;
221 nstatus = napi_call_function(env, nullptr, jsCallback, argCount, args, &result);
222 CHECK_AND_BREAK_LOG(nstatus == napi_ok, "fail to call %{public}s callback", request.c_str());
223
224 CHECK_AND_BREAK_LOG(event->capturerNapiObj != nullptr && event->capturerNapiObj->audioCapturer_ != nullptr,
225 "audioCapturer_ is null");
226 event->capturerNapiObj->audioCapturer_->Enqueue(event->bufDesc);
227 } while (0);
228 napi_close_handle_scope(env, scope);
229 }
230 } // namespace AudioStandard
231 } // namespace OHOS
232