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