1 /*
2 * Copyright (c) 2021-2022 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
16 #include "renderer_data_request_callback_napi.h"
17
18 #include <uv.h>
19
20 #include "audio_errors.h"
21 #include "audio_log.h"
22
23 namespace {
24 const std::string RENDERER_DATA_REQUEST_CALLBACK_NAME = "dataRequest";
25 }
26
27 namespace OHOS {
28 namespace AudioStandard {
RendererDataRequestCallbackNapi(napi_env env,AudioRendererNapi * rendererNapi)29 RendererDataRequestCallbackNapi::RendererDataRequestCallbackNapi(napi_env env, AudioRendererNapi *rendererNapi)
30 : env_(env),
31 rendererNapi_(rendererNapi)
32 {
33 AUDIO_INFO_LOG("RendererDataRequestCallbackNapi: instance create");
34 }
35
~RendererDataRequestCallbackNapi()36 RendererDataRequestCallbackNapi::~RendererDataRequestCallbackNapi()
37 {
38 AUDIO_INFO_LOG("RendererDataRequestCallbackNapi: instance destroy");
39 }
40
SaveCallbackReference(const std::string & callbackName,napi_value args)41 void RendererDataRequestCallbackNapi::SaveCallbackReference(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,
48 "RendererDataRequestCallbackNapi: creating reference for callback fail");
49
50 std::shared_ptr<AutoRef> cb = std::make_shared<AutoRef>(env_, callback);
51 if (callbackName == RENDERER_DATA_REQUEST_CALLBACK_NAME) {
52 rendererDataRequestCallback_ = cb;
53 } else {
54 AUDIO_ERR_LOG("RendererDataRequestCallbackNapi: Unknown callback type: %{public}s", callbackName.c_str());
55 }
56 }
57
OnWriteData(size_t length)58 void RendererDataRequestCallbackNapi::OnWriteData(size_t length)
59 {
60 std::lock_guard<std::mutex> lock(mutex_);
61 AUDIO_DEBUG_LOG("RendererDataRequestCallbackNapi: onDataRequest enqueue added");
62 CHECK_AND_RETURN_LOG(rendererDataRequestCallback_ != nullptr, "Cannot find the reference of dataRequest callback");
63 CHECK_AND_RETURN_LOG(rendererNapi_ != nullptr, "Cannot find the reference to audio renderer napi");
64 std::unique_ptr<RendererDataRequestJsCallback> cb = std::make_unique<RendererDataRequestJsCallback>();
65 CHECK_AND_RETURN_LOG(cb != nullptr, "No memory");
66 cb->callback = rendererDataRequestCallback_;
67 cb->callbackName = RENDERER_DATA_REQUEST_CALLBACK_NAME;
68 size_t reqLen = length;
69 cb->bufDesc_.buffer = nullptr;
70 cb->rendererNapiObj = rendererNapi_;
71 rendererNapi_->audioRenderer_->GetBufferDesc(cb->bufDesc_);
72 if (cb->bufDesc_.buffer == nullptr) {
73 return;
74 }
75 if (reqLen > cb->bufDesc_.bufLength) {
76 cb->bufDesc_.dataLength = cb->bufDesc_.bufLength;
77 } else {
78 cb->bufDesc_.dataLength = reqLen;
79 }
80 AudioRendererDataInfo audioRendererDataInfo = {};
81 audioRendererDataInfo.buffer = cb->bufDesc_.buffer;
82 audioRendererDataInfo.flag = cb->bufDesc_.bufLength;
83 cb->audioRendererDataInfo = audioRendererDataInfo;
84 return OnJsRendererDataRequestCallback(cb);
85 }
86
SetValueInt32(const napi_env & env,const std::string & fieldStr,const int intValue,napi_value & result)87 static void SetValueInt32(const napi_env& env, const std::string& fieldStr, const int intValue, napi_value& result)
88 {
89 napi_value value = nullptr;
90 napi_create_int32(env, intValue, &value);
91 napi_set_named_property(env, result, fieldStr.c_str(), value);
92 }
93
CreateArrayBuffer(const napi_env & env,const std::string & fieldStr,size_t bufferLen,uint8_t * bufferData,napi_value & result)94 static void CreateArrayBuffer(const napi_env& env, const std::string& fieldStr, size_t bufferLen,
95 uint8_t* bufferData, napi_value& result)
96 {
97 napi_value value = nullptr;
98
99 napi_create_arraybuffer(env, bufferLen, (void**)&bufferData, &value);
100 napi_set_named_property(env, result, fieldStr.c_str(), value);
101 }
102
NativeAudioRendererDataInfoToJsObj(const napi_env & env,napi_value & jsObj,const AudioRendererDataInfo & audioRendererDataInfo)103 static void NativeAudioRendererDataInfoToJsObj(const napi_env& env, napi_value& jsObj,
104 const AudioRendererDataInfo &audioRendererDataInfo)
105 {
106 napi_create_object(env, &jsObj);
107
108 SetValueInt32(env, "flag", static_cast<int32_t>(audioRendererDataInfo.flag), jsObj);
109 CreateArrayBuffer(env, "buffer", audioRendererDataInfo.flag, audioRendererDataInfo.buffer, jsObj);
110 }
111
OnJsRendererDataRequestCallback(std::unique_ptr<RendererDataRequestJsCallback> & jsCb)112 void RendererDataRequestCallbackNapi::OnJsRendererDataRequestCallback(
113 std::unique_ptr<RendererDataRequestJsCallback> &jsCb)
114 {
115 uv_loop_s *loop = nullptr;
116 napi_get_uv_event_loop(env_, &loop);
117 if (loop == nullptr) {
118 return;
119 }
120
121 uv_work_t *work = new(std::nothrow) uv_work_t;
122 if (work == nullptr) {
123 AUDIO_ERR_LOG("RendererDataRequestCallbackNapi: OnJsRendererPeriodPositionCallback: No memory");
124 return;
125 }
126 if (jsCb.get() == nullptr) {
127 AUDIO_ERR_LOG("RendererDataRequestCallbackNapi: OnJsRendererPeriodPositionCallback: jsCb.get() is null");
128 delete work;
129 return;
130 }
131 work->data = reinterpret_cast<void *>(jsCb.get());
132
133 int ret = uv_queue_work(loop, work, [] (uv_work_t *work) {}, [] (uv_work_t *work, int status) {
134 // Js Thread
135 RendererDataRequestJsCallback *event = reinterpret_cast<RendererDataRequestJsCallback *>(work->data);
136 std::string request = event->callbackName;
137 napi_env env = event->callback->env_;
138 napi_ref callback = event->callback->cb_;
139 AUDIO_DEBUG_LOG("RendererDataRequestCallbackNapi: JsCallBack %{public}s, uv_queue_work start",
140 request.c_str());
141 do {
142 CHECK_AND_BREAK_LOG(status != UV_ECANCELED, "%{public}s canceled", request.c_str());
143
144 napi_value jsCallback = nullptr;
145 napi_status nstatus = napi_get_reference_value(env, callback, &jsCallback);
146 CHECK_AND_BREAK_LOG(nstatus == napi_ok && jsCallback != nullptr, "%{public}s get reference value fail",
147 request.c_str());
148
149 napi_value args[1] = { nullptr };
150 NativeAudioRendererDataInfoToJsObj(env, args[0], event->audioRendererDataInfo);
151 CHECK_AND_BREAK_LOG(nstatus == napi_ok && args[0] != nullptr,
152 "%{public}s fail to create position callback", request.c_str());
153 const size_t argCount = 1;
154 napi_value result = nullptr;
155 nstatus = napi_call_function(env, nullptr, jsCallback, argCount, args, &result);
156 event->rendererNapiObj->audioRenderer_->Enqueue(event->bufDesc_);
157 CHECK_AND_BREAK_LOG(nstatus == napi_ok, "%{public}s fail to call position callback", request.c_str());
158 } while (0);
159 delete event;
160 delete work;
161 });
162 if (ret != 0) {
163 AUDIO_ERR_LOG("Failed to execute libuv work queue");
164 delete work;
165 } else {
166 jsCb.release();
167 }
168 }
169 } // namespace AudioStandard
170 } // namespace OHOS
171