• 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 
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