• 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 "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_, &copyValue);
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