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 "NapiRendererDataRequestCallback"
17 #endif
18
19 #include "js_native_api.h"
20 #include "napi_renderer_data_request_callback.h"
21
22 #include "audio_errors.h"
23 #include "audio_renderer_log.h"
24
25 namespace OHOS {
26 namespace AudioStandard {
27 namespace {
28 const std::string RENDERER_DATA_REQUEST_CALLBACK_NAME = "dataRequest";
29 }
30
NapiRendererDataRequestCallback(napi_env env,NapiAudioRenderer * napiRenderer)31 NapiRendererDataRequestCallback::NapiRendererDataRequestCallback(napi_env env, NapiAudioRenderer *napiRenderer)
32 : env_(env), napiRenderer_(napiRenderer)
33 {
34 AUDIO_INFO_LOG("instance create");
35 }
36
~NapiRendererDataRequestCallback()37 NapiRendererDataRequestCallback::~NapiRendererDataRequestCallback()
38 {
39 if (regArDataReqTsfn_) {
40 napi_release_threadsafe_function(arDataReqTsfn_, napi_tsfn_abort);
41 }
42 AUDIO_INFO_LOG("instance destroy");
43 }
44
SaveCallbackReference(const std::string & callbackName,napi_value args)45 void NapiRendererDataRequestCallback::SaveCallbackReference(const std::string &callbackName, napi_value args)
46 {
47 std::lock_guard<std::mutex> lock(mutex_);
48 // create function that will operate while save callback reference success.
49 std::function<void(std::shared_ptr<AutoRef> generatedCallback)> successed =
50 [this](std::shared_ptr<AutoRef> generatedCallback) {
51 rendererDataRequestCallback_ = generatedCallback;
52 };
53 NapiAudioRendererCallbackInner::SaveCallbackReferenceInner(callbackName, args, successed);
54 AUDIO_DEBUG_LOG("SaveAudioRendererDataRequestCallback sucessful");
55 }
56
GetCallback(const std::string & callbackName)57 std::shared_ptr<AutoRef> NapiRendererDataRequestCallback::GetCallback(const std::string &callbackName)
58 {
59 return rendererDataRequestCallback_;
60 }
61
RemoveCallbackReference(const std::string & callbackName,napi_env env,napi_value callback,napi_value args)62 void NapiRendererDataRequestCallback::RemoveCallbackReference(
63 const std::string &callbackName, napi_env env, napi_value callback, napi_value args)
64 {
65 std::lock_guard<std::mutex> lock(mutex_);
66 //create function that will operate while save callback reference success.
67 std::function<void()> successed = [this]() {
68 rendererDataRequestCallback_ = nullptr;
69 };
70 RemoveCallbackReferenceInner(callbackName, env, callback, successed);
71 }
72
GetEnv()73 napi_env &NapiRendererDataRequestCallback::GetEnv()
74 {
75 return env_;
76 }
77
CheckIfTargetCallbackName(const std::string & callbackName)78 bool NapiRendererDataRequestCallback::CheckIfTargetCallbackName(const std::string &callbackName)
79 {
80 return (callbackName == DATA_REQUEST_CALLBACK_NAME);
81 }
82
CreateWriteDataTsfn(napi_env env)83 void NapiRendererDataRequestCallback::CreateWriteDataTsfn(napi_env env)
84 {
85 regArDataReqTsfn_ = true;
86 std::string callbackName = "writeData";
87 napi_value cbName;
88 napi_create_string_utf8(env, callbackName.c_str(), callbackName.length(), &cbName);
89 napi_create_threadsafe_function(env, nullptr, nullptr, cbName, 0, 1, nullptr,
90 DataRequestTsfnFinalize, nullptr, SafeJsCallbackDataRequestWork, &arDataReqTsfn_);
91 }
92
OnWriteData(size_t length)93 void NapiRendererDataRequestCallback::OnWriteData(size_t length)
94 {
95 std::lock_guard<std::mutex> lock(mutex_);
96 AUDIO_DEBUG_LOG("onDataRequest enqueue added");
97 CHECK_AND_RETURN_LOG(rendererDataRequestCallback_ != nullptr, "Cannot find the reference of dataRequest callback");
98 CHECK_AND_RETURN_LOG(napiRenderer_ != nullptr, "Cannot find the reference to audio renderer napi");
99 std::unique_ptr<RendererDataRequestJsCallback> cb = std::make_unique<RendererDataRequestJsCallback>();
100 CHECK_AND_RETURN_LOG(cb != nullptr, "No memory");
101 cb->callback = rendererDataRequestCallback_;
102 cb->callbackName = DATA_REQUEST_CALLBACK_NAME;
103 size_t reqLen = length;
104 cb->bufDesc_.buffer = nullptr;
105 cb->rendererNapiObj = napiRenderer_;
106 napiRenderer_->audioRenderer_->GetBufferDesc(cb->bufDesc_);
107 if (cb->bufDesc_.buffer == nullptr) {
108 return;
109 }
110 if (reqLen > cb->bufDesc_.bufLength) {
111 cb->bufDesc_.dataLength = cb->bufDesc_.bufLength;
112 } else {
113 cb->bufDesc_.dataLength = reqLen;
114 }
115 AudioRendererDataInfo audioRendererDataInfo = {};
116 audioRendererDataInfo.buffer = cb->bufDesc_.buffer;
117 audioRendererDataInfo.flag = cb->bufDesc_.bufLength;
118 cb->audioRendererDataInfo = audioRendererDataInfo;
119 return OnJsRendererDataRequestCallback(cb);
120 }
121
SafeJsCallbackDataRequestWork(napi_env env,napi_value js_cb,void * context,void * data)122 void NapiRendererDataRequestCallback::SafeJsCallbackDataRequestWork(
123 napi_env env, napi_value js_cb, void *context, void *data)
124 {
125 RendererDataRequestJsCallback *event = reinterpret_cast<RendererDataRequestJsCallback *>(data);
126 CHECK_AND_RETURN_LOG((event != nullptr) && (event->callback != nullptr),
127 "OnJsRendererDataRequestCallback: no memory");
128 std::shared_ptr<RendererDataRequestJsCallback> safeContext(
129 static_cast<RendererDataRequestJsCallback*>(data),
130 [](RendererDataRequestJsCallback *ptr) {
131 delete ptr;
132 });
133 std::string request = event->callbackName;
134 napi_ref callback = event->callback->cb_;
135 napi_handle_scope scope = nullptr;
136 napi_open_handle_scope(env, &scope);
137 CHECK_AND_RETURN_LOG(scope != nullptr, "scope is nullptr");
138 AUDIO_INFO_LOG("SafeJsCallbackDataRequestWork: safe js callback working.");
139
140 do {
141 napi_value jsCallback = nullptr;
142 napi_status nstatus = napi_get_reference_value(env, callback, &jsCallback);
143 CHECK_AND_BREAK_LOG(nstatus == napi_ok && jsCallback != nullptr, "%{public}s get reference value fail",
144 request.c_str());
145
146 napi_value args[ARGS_ONE] = { nullptr };
147 NapiParamUtils::SetNativeAudioRendererDataInfo(env, event->audioRendererDataInfo, args[0]);
148 CHECK_AND_BREAK_LOG(nstatus == napi_ok && args[PARAM0] != nullptr,
149 "%{public}s fail to create position callback", request.c_str());
150 const size_t argCount = 1;
151 napi_value result = nullptr;
152 nstatus = napi_call_function(env, nullptr, jsCallback, argCount, args, &result);
153 event->rendererNapiObj->audioRenderer_->Enqueue(event->bufDesc_);
154 CHECK_AND_BREAK_LOG(nstatus == napi_ok, "%{public}s fail to call position callback", request.c_str());
155 } while (0);
156 napi_close_handle_scope(env, scope);
157 }
158
DataRequestTsfnFinalize(napi_env env,void * data,void * hint)159 void NapiRendererDataRequestCallback::DataRequestTsfnFinalize(napi_env env, void *data, void *hint)
160 {
161 AUDIO_INFO_LOG("DataRequestTsfnFinalize: safe thread resource release.");
162 }
163
OnJsRendererDataRequestCallback(std::unique_ptr<RendererDataRequestJsCallback> & jsCb)164 void NapiRendererDataRequestCallback::OnJsRendererDataRequestCallback(
165 std::unique_ptr<RendererDataRequestJsCallback> &jsCb)
166 {
167 if (jsCb.get() == nullptr) {
168 AUDIO_ERR_LOG("OnJsRendererDataRequestCallback: jsCb.get() is null");
169 return;
170 }
171
172 RendererDataRequestJsCallback *event = jsCb.release();
173 CHECK_AND_RETURN_LOG((event != nullptr) && (event->callback != nullptr), "event is nullptr.");
174
175 napi_acquire_threadsafe_function(arDataReqTsfn_);
176 napi_call_threadsafe_function(arDataReqTsfn_, event, napi_tsfn_blocking);
177 }
178 } // namespace AudioStandard
179 } // namespace OHOS