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