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