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