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
16 #include "audio_haptic_player_callback_napi.h"
17
18 #include <uv.h>
19
20 #include "media_errors.h"
21 #include "audio_haptic_log.h"
22
23 namespace {
24 const std::string AUDIO_INTERRUPT_CALLBACK_NAME = "audioInterrupt";
25 const std::string END_OF_STREAM_CALLBACK_NAME = "endOfStream";
26
27 constexpr OHOS::HiviewDFX::HiLogLabel LABEL = {LOG_CORE, LOG_DOMAIN_AUDIO_NAPI, "AudioHapticPlayerCallbackNapi"};
28 }
29
30 namespace OHOS {
31 namespace Media {
AudioHapticPlayerCallbackNapi(napi_env env)32 AudioHapticPlayerCallbackNapi::AudioHapticPlayerCallbackNapi(napi_env env)
33 : env_(env)
34 {
35 MEDIA_LOGI("AudioHapticPlayerCallbackNapi: instance create");
36 }
37
~AudioHapticPlayerCallbackNapi()38 AudioHapticPlayerCallbackNapi::~AudioHapticPlayerCallbackNapi()
39 {
40 MEDIA_LOGI("~AudioHapticPlayerCallbackNapi: instance destroy");
41 }
42
SaveCallbackReference(const std::string & callbackName,napi_value args)43 void AudioHapticPlayerCallbackNapi::SaveCallbackReference(const std::string &callbackName, napi_value args)
44 {
45 std::lock_guard<std::mutex> lock(cbMutex_);
46
47 napi_ref callback = nullptr;
48 const int32_t refCount = 1;
49 napi_status status = napi_create_reference(env_, args, refCount, &callback);
50 CHECK_AND_RETURN_LOG(status == napi_ok && callback != nullptr,
51 "SaveCallbackReference: creating reference for callback fail");
52
53 std::shared_ptr<AutoRef> cb = std::make_shared<AutoRef>(env_, callback);
54 if (callbackName == AUDIO_INTERRUPT_CALLBACK_NAME) {
55 audioInterruptCb_ = cb;
56 } else if (callbackName == END_OF_STREAM_CALLBACK_NAME) {
57 endOfStreamCb_ = cb;
58 } else {
59 MEDIA_LOGE("SaveCallbackReference: Unknown callback type: %{public}s", callbackName.c_str());
60 }
61 }
62
RemoveCallbackReference(const std::string & callbackName)63 void AudioHapticPlayerCallbackNapi::RemoveCallbackReference(const std::string &callbackName)
64 {
65 std::lock_guard<std::mutex> lock(cbMutex_);
66
67 if (callbackName == AUDIO_INTERRUPT_CALLBACK_NAME) {
68 audioInterruptCb_ = nullptr;
69 } else if (callbackName == END_OF_STREAM_CALLBACK_NAME) {
70 endOfStreamCb_ = nullptr;
71 } else {
72 MEDIA_LOGE("RemoveCallbackReference: Unknown callback type: %{public}s", callbackName.c_str());
73 }
74 }
75
SetValueInt32(const napi_env & env,const std::string & fieldStr,const int intValue,napi_value & result)76 static void SetValueInt32(const napi_env& env, const std::string& fieldStr, const int intValue, napi_value& result)
77 {
78 napi_handle_scope scope = nullptr;
79 napi_open_handle_scope(env, &scope);
80
81 napi_value value = nullptr;
82 napi_create_int32(env, intValue, &value);
83 napi_set_named_property(env, result, fieldStr.c_str(), value);
84
85 napi_close_handle_scope(env, scope);
86 }
87
NativeInterruptEventToJsObj(const napi_env & env,napi_value & jsObj,const AudioStandard::InterruptEvent & interruptEvent)88 static void NativeInterruptEventToJsObj(const napi_env& env, napi_value& jsObj,
89 const AudioStandard::InterruptEvent& interruptEvent)
90 {
91 napi_create_object(env, &jsObj);
92 SetValueInt32(env, "eventType", static_cast<int32_t>(interruptEvent.eventType), jsObj);
93 SetValueInt32(env, "forceType", static_cast<int32_t>(interruptEvent.forceType), jsObj);
94 SetValueInt32(env, "hintType", static_cast<int32_t>(interruptEvent.hintType), jsObj);
95 }
96
OnInterrupt(const AudioStandard::InterruptEvent & interruptEvent)97 void AudioHapticPlayerCallbackNapi::OnInterrupt(const AudioStandard::InterruptEvent &interruptEvent)
98 {
99 std::lock_guard<std::mutex> lock(cbMutex_);
100 MEDIA_LOGI("OnInterrupt: hintType: %{public}d for js", interruptEvent.hintType);
101 CHECK_AND_RETURN_LOG(audioInterruptCb_ != nullptr, "Cannot find the reference of interrupt callback");
102
103 std::unique_ptr<AudioHapticPlayerJsCallback> cb = std::make_unique<AudioHapticPlayerJsCallback>();
104 CHECK_AND_RETURN_LOG(cb != nullptr, "No memory");
105 cb->callback = audioInterruptCb_;
106 cb->callbackName = AUDIO_INTERRUPT_CALLBACK_NAME;
107 cb->interruptEvent = interruptEvent;
108 return OnInterruptJsCallback(cb);
109 }
110
OnInterruptJsCallback(std::unique_ptr<AudioHapticPlayerJsCallback> & jsCb)111 void AudioHapticPlayerCallbackNapi::OnInterruptJsCallback(std::unique_ptr<AudioHapticPlayerJsCallback> &jsCb)
112 {
113 uv_loop_s *loop = nullptr;
114 napi_get_uv_event_loop(env_, &loop);
115 CHECK_AND_RETURN(loop != nullptr);
116
117 uv_work_t *work = new(std::nothrow) uv_work_t;
118 CHECK_AND_RETURN_LOG(work != nullptr, "OnInterruptJsCallback: No memory");
119 if (jsCb.get() == nullptr) {
120 MEDIA_LOGE("OnInterruptJsCallback: jsCb.get() is null");
121 delete work;
122 return;
123 }
124 work->data = reinterpret_cast<void *>(jsCb.get());
125
126 int ret = uv_queue_work(loop, work, [] (uv_work_t *work) {}, [] (uv_work_t *work, int status) {
127 // Js Thread
128 AudioHapticPlayerJsCallback *event = reinterpret_cast<AudioHapticPlayerJsCallback *>(work->data);
129 std::string request = event->callbackName;
130 napi_env env = event->callback->env_;
131 napi_ref callback = event->callback->cb_;
132 napi_handle_scope scope = nullptr;
133 napi_open_handle_scope(env, &scope);
134 MEDIA_LOGI("OnInterruptJsCallback: %{public}s JsCallBack, uv_queue_work start", request.c_str());
135 do {
136 CHECK_AND_BREAK_LOG(status != UV_ECANCELED, "%{public}s cancelled", request.c_str());
137
138 napi_value jsCallback = nullptr;
139 napi_status napiStatus = napi_get_reference_value(env, callback, &jsCallback);
140 CHECK_AND_BREAK_LOG(napiStatus == napi_ok && jsCallback != nullptr,
141 "%{public}s get reference value fail", request.c_str());
142
143 // Call back function
144 napi_value args[1] = { nullptr };
145 NativeInterruptEventToJsObj(env, args[0], event->interruptEvent);
146 CHECK_AND_BREAK_LOG(napiStatus == napi_ok && args[0] != nullptr,
147 "%{public}s fail to create Interrupt callback", request.c_str());
148
149 const size_t argCount = 1;
150 napi_value result = nullptr;
151 napiStatus = napi_call_function(env, nullptr, jsCallback, argCount, args, &result);
152 CHECK_AND_BREAK_LOG(napiStatus == napi_ok, "%{public}s fail to send interrupt callback", request.c_str());
153 } while (0);
154 napi_close_handle_scope(env, scope);
155 delete event;
156 delete work;
157 });
158 if (ret != 0) {
159 MEDIA_LOGE("OnInterruptJsCallback: Failed to execute libuv work queue");
160 delete work;
161 } else {
162 jsCb.release();
163 }
164 }
165
OnEndOfStream(void)166 void AudioHapticPlayerCallbackNapi::OnEndOfStream(void)
167 {
168 std::lock_guard<std::mutex> lock(cbMutex_);
169 MEDIA_LOGI("OnEndOfStream in succeed");
170 CHECK_AND_RETURN_LOG(endOfStreamCb_ != nullptr, "Cannot find the reference of endOfStream callback");
171
172 std::unique_ptr<AudioHapticPlayerJsCallback> cb = std::make_unique<AudioHapticPlayerJsCallback>();
173 CHECK_AND_RETURN_LOG(cb != nullptr, "No memory");
174 cb->callback = endOfStreamCb_;
175 cb->callbackName = END_OF_STREAM_CALLBACK_NAME;
176 return OnEndOfStreamJsCallback(cb);
177 }
178
OnError(int32_t errorCode)179 void AudioHapticPlayerCallbackNapi::OnError(int32_t errorCode)
180 {
181 MEDIA_LOGI("OnError from audio haptic player. errorCode %{public}d", errorCode);
182 }
183
OnEndOfStreamJsCallback(std::unique_ptr<AudioHapticPlayerJsCallback> & jsCb)184 void AudioHapticPlayerCallbackNapi::OnEndOfStreamJsCallback(std::unique_ptr<AudioHapticPlayerJsCallback> &jsCb)
185 {
186 uv_loop_s *loop = nullptr;
187 napi_get_uv_event_loop(env_, &loop);
188 CHECK_AND_RETURN(loop != nullptr);
189
190 uv_work_t *work = new(std::nothrow) uv_work_t;
191 CHECK_AND_RETURN_LOG(work != nullptr, "OnEndOfStreamJsCallback: No memory");
192 if (jsCb.get() == nullptr) {
193 MEDIA_LOGE("OnEndOfStreamJsCallback: jsCb.get() is null");
194 delete work;
195 return;
196 }
197 work->data = reinterpret_cast<void *>(jsCb.get());
198
199 int ret = uv_queue_work(loop, work, [] (uv_work_t *work) {}, [] (uv_work_t *work, int status) {
200 // Js Thread
201 AudioHapticPlayerJsCallback *event = reinterpret_cast<AudioHapticPlayerJsCallback *>(work->data);
202 std::string request = event->callbackName;
203 napi_env env = event->callback->env_;
204 napi_ref callback = event->callback->cb_;
205 napi_handle_scope scope = nullptr;
206 napi_open_handle_scope(env, &scope);
207 MEDIA_LOGI("OnEndOfStreamJsCallback: %{public}s JsCallBack, uv_queue_work start", request.c_str());
208 do {
209 CHECK_AND_BREAK_LOG(status != UV_ECANCELED, "%{public}s cancelled", request.c_str());
210
211 napi_value jsCallback = nullptr;
212 napi_status napiStatus = napi_get_reference_value(env, callback, &jsCallback);
213 CHECK_AND_BREAK_LOG(napiStatus == napi_ok && jsCallback != nullptr,
214 "%{public}s get reference value fail", request.c_str());
215
216 // Call back function
217 napi_value result = nullptr;
218 napiStatus = napi_call_function(env, nullptr, jsCallback, 0, nullptr, &result);
219 CHECK_AND_BREAK_LOG(napiStatus == napi_ok, "%{public}s fail to send interrupt callback", request.c_str());
220 } while (0);
221 napi_close_handle_scope(env, scope);
222 delete event;
223 delete work;
224 });
225 if (ret != 0) {
226 MEDIA_LOGE("OnEndOfStreamJsCallback: Failed to execute libuv work queue");
227 delete work;
228 } else {
229 jsCb.release();
230 }
231 }
232 } // namespace Media
233 } // namespace OHOS
234