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 "napi_audio_renderer_write_data_callback.h"
20 #include "audio_renderer_log.h"
21 #include "napi_audio_enum.h"
22
23 namespace OHOS {
24 namespace AudioStandard {
25 static const int32_t WRITE_CALLBACK_TIMEOUT_IN_MS = 1000; // 1s
26
NapiRendererWriteDataCallback(napi_env env,NapiAudioRenderer * napiRenderer)27 NapiRendererWriteDataCallback::NapiRendererWriteDataCallback(napi_env env, NapiAudioRenderer *napiRenderer)
28 : env_(env), napiRenderer_(napiRenderer)
29 {
30 AUDIO_DEBUG_LOG("instance create");
31 }
32
~NapiRendererWriteDataCallback()33 NapiRendererWriteDataCallback::~NapiRendererWriteDataCallback()
34 {
35 AUDIO_DEBUG_LOG("instance destroy");
36 if (napiRenderer_ != nullptr) {
37 napiRenderer_->writeCallbackCv_.notify_all();
38 }
39 }
40
AddCallbackReference(const std::string & callbackName,napi_value args)41 void NapiRendererWriteDataCallback::AddCallbackReference(const std::string &callbackName, napi_value args)
42 {
43 std::lock_guard<std::mutex> lock(mutex_);
44 napi_ref callback = nullptr;
45 const int32_t refCount = 1;
46 napi_status status = napi_create_reference(env_, args, refCount, &callback);
47 CHECK_AND_RETURN_LOG(status == napi_ok && callback != nullptr, "creating reference for callback failed");
48
49 std::shared_ptr<AutoRef> cb = std::make_shared<AutoRef>(env_, callback);
50 if (callbackName == WRITE_DATA_CALLBACK_NAME) {
51 rendererWriteDataCallback_ = cb;
52 callback_ = callback;
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 NapiRendererWriteDataCallback::RemoveCallbackReference(napi_env env, napi_value callback)
59 {
60 std::lock_guard<std::mutex> lock(mutex_);
61 bool isEquals = false;
62 napi_value copyValue = nullptr;
63
64 if (callback == nullptr) {
65 napi_status ret = napi_delete_reference(env, callback_);
66 CHECK_AND_RETURN_LOG(napi_ok == ret, "delete callback reference failed");
67 rendererWriteDataCallback_->cb_ = nullptr;
68 AUDIO_INFO_LOG("Remove Js Callback");
69 return;
70 }
71
72 napi_get_reference_value(env, callback_, ©Value);
73 CHECK_AND_RETURN_LOG(copyValue != nullptr, "copyValue is nullptr");
74 CHECK_AND_RETURN_LOG(napi_strict_equals(env, callback, copyValue, &isEquals) == napi_ok,
75 "get napi_strict_equals failed");
76 if (isEquals) {
77 AUDIO_INFO_LOG("found Js Callback, delete it!");
78 napi_status status = napi_delete_reference(env, rendererWriteDataCallback_->cb_);
79 CHECK_AND_RETURN_LOG(status == napi_ok, "deleting reference for callback failed");
80 rendererWriteDataCallback_->cb_ = nullptr;
81 }
82 }
83
OnWriteData(size_t length)84 void NapiRendererWriteDataCallback::OnWriteData(size_t length)
85 {
86 std::lock_guard<std::mutex> lock(mutex_);
87 CHECK_AND_RETURN_LOG(rendererWriteDataCallback_ != nullptr, "Cannot find the reference of writeData callback");
88
89 std::unique_ptr<RendererWriteDataJsCallback> cb = std::make_unique<RendererWriteDataJsCallback>();
90 cb->callback = rendererWriteDataCallback_;
91 cb->callbackName = WRITE_DATA_CALLBACK_NAME;
92 cb->bufDesc.buffer = nullptr;
93 cb->rendererNapiObj = napiRenderer_;
94
95 CHECK_AND_RETURN_LOG(napiRenderer_ != nullptr, "Cannot find the reference to audio renderer napi");
96 napiRenderer_->audioRenderer_->GetBufferDesc(cb->bufDesc);
97 if (cb->bufDesc.buffer == nullptr) {
98 return;
99 }
100 if (length > cb->bufDesc.bufLength) {
101 cb->bufDesc.dataLength = cb->bufDesc.bufLength;
102 } else {
103 cb->bufDesc.dataLength = length;
104 }
105
106 return OnJsRendererWriteDataCallback(cb);
107 }
108
OnJsRendererWriteDataCallback(std::unique_ptr<RendererWriteDataJsCallback> & jsCb)109 void NapiRendererWriteDataCallback::OnJsRendererWriteDataCallback(std::unique_ptr<RendererWriteDataJsCallback> &jsCb)
110 {
111 uv_loop_s *loop = nullptr;
112 napi_get_uv_event_loop(env_, &loop);
113 CHECK_AND_RETURN_LOG(loop != nullptr, "loop is nullptr");
114
115 uv_work_t *work = new(std::nothrow) uv_work_t;
116 CHECK_AND_RETURN_LOG(work != nullptr, "writeData Js Callback: No memory");
117
118 if (jsCb.get() == nullptr) {
119 AUDIO_ERR_LOG("writeData Js Callback is null");
120 delete work;
121 return;
122 }
123
124 auto obj = static_cast<NapiAudioRenderer *>(napiRenderer_);
125 ObjectRefMap<NapiAudioRenderer>::IncreaseRef(obj);
126 work->data = reinterpret_cast<void *>(jsCb.get());
127
128 int ret = uv_queue_work_with_qos(loop, work, [](uv_work_t *work) {},
129 WorkCallbackRendererWriteData, uv_qos_default);
130 if (ret != 0) {
131 AUDIO_ERR_LOG("Failed to execute uv work queue");
132 delete work;
133 } else {
134 jsCb.release();
135 }
136
137 if (napiRenderer_ == nullptr) {
138 return;
139 }
140 std::unique_lock<std::mutex> writeCallbackLock(napiRenderer_->writeCallbackMutex_);
141 std::cv_status cvStatus = napiRenderer_->writeCallbackCv_.wait_for(writeCallbackLock,
142 std::chrono::milliseconds(WRITE_CALLBACK_TIMEOUT_IN_MS));
143 if (cvStatus == std::cv_status::timeout) {
144 AUDIO_ERR_LOG("Client OnWriteData operation timed out");
145 }
146 writeCallbackLock.unlock();
147 }
148
CheckWriteDataCallbackResult(napi_env env,BufferDesc & bufDesc,napi_value result)149 void NapiRendererWriteDataCallback::CheckWriteDataCallbackResult(napi_env env, BufferDesc &bufDesc, napi_value result)
150 {
151 napi_valuetype resultType = napi_undefined;
152 napi_typeof(env, result, &resultType);
153 if (resultType == napi_number) {
154 int32_t resultIntValue;
155 napi_get_value_int32(env, result, &resultIntValue);
156 auto resultValue = static_cast<NapiAudioEnum::AudioDataCallbackResult>(resultIntValue);
157 if (resultValue == NapiAudioEnum::CALLBACK_RESULT_INVALID) {
158 AUDIO_DEBUG_LOG("Data callback returned invalid, data will not be used.");
159 bufDesc.dataLength = 0; // Ensure that the invalid data is not used.
160 }
161 }
162 }
163
WorkCallbackRendererWriteData(uv_work_t * work,int status)164 void NapiRendererWriteDataCallback::WorkCallbackRendererWriteData(uv_work_t *work, int status)
165 {
166 // Js Thread
167 std::shared_ptr<RendererWriteDataJsCallback> context(
168 static_cast<RendererWriteDataJsCallback*>(work->data),
169 [work](RendererWriteDataJsCallback* ptr) {
170 delete ptr;
171 delete work;
172 });
173 WorkCallbackRendererWriteDataInner(work, status);
174
175 CHECK_AND_RETURN_LOG(work != nullptr, "renderer write data work is nullptr");
176 RendererWriteDataJsCallback *event = reinterpret_cast<RendererWriteDataJsCallback *>(work->data);
177 CHECK_AND_RETURN_LOG(event != nullptr, "renderer write data event is nullptr");
178 CHECK_AND_RETURN_LOG(event->rendererNapiObj != nullptr, "NapiAudioRenderer object is nullptr");
179 event->rendererNapiObj->writeCallbackCv_.notify_all();
180 auto napiObj = static_cast<NapiAudioRenderer *>(event->rendererNapiObj);
181 ObjectRefMap<NapiAudioRenderer>::DecreaseRef(napiObj);
182 }
183
WorkCallbackRendererWriteDataInner(uv_work_t * work,int status)184 void NapiRendererWriteDataCallback::WorkCallbackRendererWriteDataInner(uv_work_t *work, int status)
185 {
186 CHECK_AND_RETURN_LOG(work != nullptr, "renderer write data work is nullptr");
187 RendererWriteDataJsCallback *event = reinterpret_cast<RendererWriteDataJsCallback *>(work->data);
188 CHECK_AND_RETURN_LOG(event != nullptr, "renderer write data event is nullptr");
189 std::string request = event->callbackName;
190 CHECK_AND_RETURN_LOG(event->callback != nullptr, "event is nullptr");
191 napi_env env = event->callback->env_;
192 napi_ref callback = event->callback->cb_;
193
194 napi_handle_scope scope = nullptr;
195 napi_open_handle_scope(env, &scope);
196 CHECK_AND_RETURN_LOG(scope != nullptr, "%{public}s scope is nullptr", request.c_str());
197 do {
198 CHECK_AND_BREAK_LOG(status != UV_ECANCELED, "%{public}s canceled", request.c_str());
199
200 napi_value jsCallback = nullptr;
201 napi_status nstatus = napi_get_reference_value(env, callback, &jsCallback);
202 CHECK_AND_BREAK_LOG(nstatus == napi_ok && jsCallback != nullptr, "%{public}s get reference value failed",
203 request.c_str());
204 napi_value args[ARGS_ONE] = { nullptr };
205 nstatus = napi_create_external_arraybuffer(env, event->bufDesc.buffer, event->bufDesc.dataLength,
206 [](napi_env env, void *data, void *hint) {}, nullptr, &args[PARAM0]);
207 CHECK_AND_BREAK_LOG(nstatus == napi_ok && args[PARAM0] != nullptr,
208 "%{public}s callback fail to create buffer", request.c_str());
209 const size_t argCount = 1;
210 napi_value result = nullptr;
211 nstatus = napi_call_function(env, nullptr, jsCallback, argCount, args, &result);
212 CHECK_AND_BREAK_LOG(nstatus == napi_ok, "fail to call %{public}s callback", request.c_str());
213 CheckWriteDataCallbackResult(env, event->bufDesc, result);
214 event->rendererNapiObj->audioRenderer_->Enqueue(event->bufDesc);
215 } while (0);
216 napi_close_handle_scope(env, scope);
217 }
218 } // namespace AudioStandard
219 } // namespace OHOS
220