• 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 "NapiRendererWriteDataCallback"
17 #endif
18 
19 #include "js_native_api.h"
20 #include "napi_audio_renderer_write_data_callback.h"
21 #include "audio_renderer_log.h"
22 #include "napi_audio_enum.h"
23 
24 namespace OHOS {
25 namespace AudioStandard {
26 static const int32_t WRITE_CALLBACK_TIMEOUT_IN_MS = 1000; // 1s
27 
28 #if defined(ANDROID_PLATFORM) || defined(IOS_PLATFORM)
29 vector<NapiAudioRenderer*> NapiRendererWriteDataCallback::activeRenderers_;
30 #endif
NapiRendererWriteDataCallback(napi_env env,NapiAudioRenderer * napiRenderer)31 NapiRendererWriteDataCallback::NapiRendererWriteDataCallback(napi_env env, NapiAudioRenderer *napiRenderer)
32     : env_(env), napiRenderer_(napiRenderer)
33 {
34     AUDIO_DEBUG_LOG("instance create");
35 #if defined(ANDROID_PLATFORM) || defined(IOS_PLATFORM)
36     activeRenderers_.emplace_back(napiRenderer_);
37 #endif
38 }
39 
~NapiRendererWriteDataCallback()40 NapiRendererWriteDataCallback::~NapiRendererWriteDataCallback()
41 {
42     AUDIO_DEBUG_LOG("instance destroy");
43 #if defined(ANDROID_PLATFORM) || defined(IOS_PLATFORM)
44     auto iter = std::find(activeRenderers_.begin(), activeRenderers_.end(), napiRenderer_);
45     if (iter != activeRenderers_.end()) {
46         activeRenderers_.erase(iter);
47     }
48 #endif
49     if (regArWriteDataTsfn_) {
50         napi_release_threadsafe_function(arWriteDataTsfn_, napi_tsfn_abort);
51     }
52 }
53 
AddCallbackReference(const std::string & callbackName,napi_value args)54 void NapiRendererWriteDataCallback::AddCallbackReference(const std::string &callbackName, napi_value args)
55 {
56     std::lock_guard<std::mutex> lock(mutex_);
57     napi_ref callback = nullptr;
58     const int32_t refCount = 1;
59     napi_status status = napi_create_reference(env_, args, refCount, &callback);
60     CHECK_AND_RETURN_LOG(status == napi_ok && callback != nullptr, "creating reference for callback failed");
61 
62     std::shared_ptr<AutoRef> cb = std::make_shared<AutoRef>(env_, callback);
63     if (callbackName == WRITE_DATA_CALLBACK_NAME) {
64         rendererWriteDataCallback_ = cb;
65     } else {
66         AUDIO_ERR_LOG("Unknown callback type: %{public}s", callbackName.c_str());
67     }
68 }
69 
CreateWriteDTsfn(napi_env env)70 void NapiRendererWriteDataCallback::CreateWriteDTsfn(napi_env env)
71 {
72     regArWriteDataTsfn_ = true;
73     std::string callbackName = "writeData";
74     napi_value cbName;
75     napi_create_string_utf8(env, callbackName.c_str(), callbackName.length(), &cbName);
76     napi_create_threadsafe_function(env, nullptr, nullptr, cbName, 0, 1, nullptr,
77         WriteDataTsfnFinalize, nullptr, SafeJsCallbackWriteDataWork, &arWriteDataTsfn_);
78 }
79 
GetWriteDTsfnFlag()80 bool NapiRendererWriteDataCallback::GetWriteDTsfnFlag()
81 {
82     return regArWriteDataTsfn_;
83 }
84 
RemoveCallbackReference(napi_env env,napi_value callback)85 void NapiRendererWriteDataCallback::RemoveCallbackReference(napi_env env, napi_value callback)
86 {
87     std::lock_guard<std::mutex> lock(mutex_);
88     bool isEquals = false;
89     napi_value copyValue = nullptr;
90 
91     if (callback == nullptr) {
92         napi_status ret = napi_delete_reference(env, rendererWriteDataCallback_->cb_);
93         CHECK_AND_RETURN_LOG(napi_ok == ret, "delete callback reference failed");
94         rendererWriteDataCallback_->cb_ = nullptr;
95         AUDIO_INFO_LOG("Remove Js Callback");
96         return;
97     }
98 
99     napi_get_reference_value(env, rendererWriteDataCallback_->cb_, &copyValue);
100     CHECK_AND_RETURN_LOG(copyValue != nullptr, "copyValue is nullptr");
101     CHECK_AND_RETURN_LOG(napi_strict_equals(env, callback, copyValue, &isEquals) == napi_ok,
102         "get napi_strict_equals failed");
103     if (isEquals) {
104         AUDIO_INFO_LOG("found Js Callback, delete it!");
105         napi_status status = napi_delete_reference(env, rendererWriteDataCallback_->cb_);
106         CHECK_AND_RETURN_LOG(status == napi_ok, "deleting reference for callback failed");
107         rendererWriteDataCallback_->cb_ = nullptr;
108     }
109 }
110 
OnWriteData(size_t length)111 void NapiRendererWriteDataCallback::OnWriteData(size_t length)
112 {
113     std::lock_guard<std::mutex> lock(mutex_);
114     CHECK_AND_RETURN_LOG(rendererWriteDataCallback_ != nullptr, "Cannot find the reference of writeData callback");
115 
116     std::unique_ptr<RendererWriteDataJsCallback> cb = std::make_unique<RendererWriteDataJsCallback>();
117     cb->callback = rendererWriteDataCallback_;
118     cb->callbackName = WRITE_DATA_CALLBACK_NAME;
119     cb->bufDesc.buffer = nullptr;
120     cb->rendererNapiObj = napiRenderer_;
121 
122     CHECK_AND_RETURN_LOG(napiRenderer_ != nullptr, "Cannot find the reference to audio renderer napi");
123     if (!napiRenderer_->audioRenderer_) {
124         AUDIO_INFO_LOG("OnWriteData audioRenderer_ is null.");
125         return;
126     }
127     napiRenderer_->audioRenderer_->GetBufferDesc(cb->bufDesc);
128     if (cb->bufDesc.buffer == nullptr) {
129         return;
130     }
131     if (length > cb->bufDesc.bufLength) {
132         cb->bufDesc.dataLength = cb->bufDesc.bufLength;
133     } else {
134         cb->bufDesc.dataLength = length;
135     }
136 
137     return OnJsRendererWriteDataCallback(cb);
138 }
139 
OnJsRendererWriteDataCallback(std::unique_ptr<RendererWriteDataJsCallback> & jsCb)140 void NapiRendererWriteDataCallback::OnJsRendererWriteDataCallback(std::unique_ptr<RendererWriteDataJsCallback> &jsCb)
141 {
142     if (jsCb.get() == nullptr) {
143         AUDIO_ERR_LOG("OnJsRendererWriteDataCallback: jsCb.get() is null");
144         return;
145     }
146 
147     RendererWriteDataJsCallback *event = jsCb.release();
148     CHECK_AND_RETURN_LOG((event != nullptr) && (event->callback != nullptr), "event is nullptr.");
149 
150     auto obj = static_cast<NapiAudioRenderer *>(napiRenderer_);
151     NapiAudioRenderer *napiRenderer = ObjectRefMap<NapiAudioRenderer>::IncreaseRef(obj);
152     if (napiRenderer == nullptr) {
153         AUDIO_ERR_LOG("napiRenderer is null");
154         delete event;
155         return;
156     }
157 
158     napi_acquire_threadsafe_function(arWriteDataTsfn_);
159     napi_call_threadsafe_function(arWriteDataTsfn_, event, napi_tsfn_blocking);
160 
161     if (napiRenderer_ == nullptr) {
162         return;
163     }
164     std::unique_lock<std::mutex> writeCallbackLock(napiRenderer_->writeCallbackMutex_);
165     bool ret = napiRenderer_->writeCallbackCv_.wait_for(writeCallbackLock,
166         std::chrono::milliseconds(WRITE_CALLBACK_TIMEOUT_IN_MS), [this] () {
167             return napiRenderer_->enqueued_;
168         });
169     if (!ret) {
170         AUDIO_ERR_LOG("Client OnWriteData operation timed out");
171     }
172     napiRenderer_->enqueued_ = false;
173     writeCallbackLock.unlock();
174 }
175 
CheckWriteDataCallbackResult(napi_env env,BufferDesc & bufDesc,napi_value result)176 void NapiRendererWriteDataCallback::CheckWriteDataCallbackResult(napi_env env, BufferDesc &bufDesc, napi_value result)
177 {
178     napi_valuetype resultType = napi_undefined;
179     napi_typeof(env, result, &resultType);
180     if (resultType == napi_number) {
181         int32_t resultIntValue;
182         napi_get_value_int32(env, result, &resultIntValue);
183         auto resultValue = static_cast<NapiAudioEnum::AudioDataCallbackResult>(resultIntValue);
184         if (resultValue == NapiAudioEnum::CALLBACK_RESULT_INVALID) {
185             AUDIO_DEBUG_LOG("Data callback returned invalid, data will not be used.");
186             bufDesc.dataLength = 0; // Ensure that the invalid data is not used.
187         }
188     }
189 }
190 
SafeJsCallbackWriteDataWork(napi_env env,napi_value js_cb,void * context,void * data)191 void NapiRendererWriteDataCallback::SafeJsCallbackWriteDataWork(
192     napi_env env, napi_value js_cb, void *context, void *data)
193 {
194     RendererWriteDataJsCallback *event = reinterpret_cast<RendererWriteDataJsCallback *>(data);
195     CHECK_AND_RETURN_LOG((event != nullptr) && (event->callback != nullptr), "event is nullptr.");
196     std::shared_ptr<RendererWriteDataJsCallback> safeContext(
197         static_cast<RendererWriteDataJsCallback*>(data),
198         [](RendererWriteDataJsCallback *ptr) {
199             delete ptr;
200     });
201     WorkCallbackRendererWriteDataInner(event);
202     CHECK_AND_RETURN_LOG(event->rendererNapiObj != nullptr, "NapiAudioRenderer object is nullptr");
203     std::unique_lock<std::mutex> writeCallbackLock(event->rendererNapiObj->writeCallbackMutex_);
204     event->rendererNapiObj->enqueued_ = true;
205     event->rendererNapiObj->writeCallbackCv_.notify_all();
206     writeCallbackLock.unlock();
207     auto napiObj = static_cast<NapiAudioRenderer *>(event->rendererNapiObj);
208     ObjectRefMap<NapiAudioRenderer>::DecreaseRef(napiObj);
209 }
210 
WriteDataTsfnFinalize(napi_env env,void * data,void * hint)211 void NapiRendererWriteDataCallback::WriteDataTsfnFinalize(napi_env env, void *data, void *hint)
212 {
213     AUDIO_DEBUG_LOG("WriteDataTsfnFinalize: safe thread resource release.");
214 }
215 
WorkCallbackRendererWriteDataInner(RendererWriteDataJsCallback * event)216 void NapiRendererWriteDataCallback::WorkCallbackRendererWriteDataInner(RendererWriteDataJsCallback *event)
217 {
218     CHECK_AND_RETURN_LOG((event != nullptr) && (event->callback != nullptr),
219         "OnJsRendererWriteDataCallback: no memory");
220     std::string request = event->callbackName;
221     napi_ref callback = event->callback->cb_;
222     napi_env env = event->callback->env_;
223     napi_handle_scope scope = nullptr;
224     napi_open_handle_scope(env, &scope);
225     CHECK_AND_RETURN_LOG(scope != nullptr, "%{public}s scope is nullptr", request.c_str());
226 
227     do {
228         napi_value jsCallback = nullptr;
229         napi_status nstatus = napi_get_reference_value(env, callback, &jsCallback);
230         CHECK_AND_BREAK_LOG(nstatus == napi_ok && jsCallback != nullptr, "%{public}s get reference value failed",
231             request.c_str());
232         napi_value args[ARGS_ONE] = { nullptr };
233         nstatus = napi_create_external_arraybuffer(env, event->bufDesc.buffer, event->bufDesc.dataLength,
234             [](napi_env env, void *data, void *hint) {}, nullptr, &args[PARAM0]);
235         CHECK_AND_BREAK_LOG(nstatus == napi_ok && args[PARAM0] != nullptr,
236             "%{public}s callback fail to create buffer", request.c_str());
237         const size_t argCount = 1;
238         napi_value result = nullptr;
239         nstatus = napi_call_function(env, nullptr, jsCallback, argCount, args, &result);
240         CHECK_AND_BREAK_LOG(nstatus == napi_ok, "fail to call %{public}s callback", request.c_str());
241         CheckWriteDataCallbackResult(env, event->bufDesc, result);
242 #if defined(ANDROID_PLATFORM) || defined(IOS_PLATFORM)
243         auto iter = std::find(activeRenderers_.begin(), activeRenderers_.end(), event->rendererNapiObj);
244         if (iter != activeRenderers_.end()) {
245             if (event->rendererNapiObj->audioRenderer_) {
246                 event->rendererNapiObj->audioRenderer_->Enqueue(event->bufDesc);
247             } else {
248                 AUDIO_INFO_LOG("WorkCallbackRendererWriteData audioRenderer_ is null");
249             }
250         } else {
251             AUDIO_INFO_LOG("NapiRendererWriteDataCallback is finalize.");
252         }
253 #else
254         event->rendererNapiObj->audioRenderer_->Enqueue(event->bufDesc);
255 #endif
256     } while (0);
257     napi_close_handle_scope(env, scope);
258 }
259 }  // namespace AudioStandard
260 }  // namespace OHOS
261