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