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