1 /*
2  * Copyright (C) 2024 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 #include "avtranscoder_callback.h"
16 #include <uv.h>
17 #include "media_errors.h"
18 #include "scope_guard.h"
19 #include "media_log.h"
20 
21 namespace {
22     constexpr OHOS::HiviewDFX::HiLogLabel LABEL = {LOG_CORE, LOG_DOMAIN_PLAYER, "AVTransCoderCallback"};
23 }
24 
25 namespace OHOS {
26 namespace Media {
AVTransCoderCallback(napi_env env)27 AVTransCoderCallback::AVTransCoderCallback(napi_env env) : env_(env)
28 {
29     MEDIA_LOGI("0x%{public}06" PRIXPTR "Instances create", FAKE_POINTER(this));
30 }
31 
~AVTransCoderCallback()32 AVTransCoderCallback::~AVTransCoderCallback()
33 {
34     MEDIA_LOGI("0x%{public}06" PRIXPTR "Instances destroy", FAKE_POINTER(this));
35 }
36 
SaveCallbackReference(const std::string & name,std::weak_ptr<AutoRef> ref)37 void AVTransCoderCallback::SaveCallbackReference(const std::string &name, std::weak_ptr<AutoRef> ref)
38 {
39     std::lock_guard<std::mutex> lock(mutex_);
40     refMap_[name] = ref;
41     MEDIA_LOGI("Set callback type: %{public}s", name.c_str());
42 }
43 
CancelCallbackReference(const std::string & name)44 void AVTransCoderCallback::CancelCallbackReference(const std::string &name)
45 {
46     std::lock_guard<std::mutex> lock(mutex_);
47     auto iter = refMap_.find(name);
48     if (iter != refMap_.end()) {
49         refMap_.erase(iter);
50     }
51     MEDIA_LOGI("Cancel callback type: %{public}s", name.c_str());
52 }
53 
ClearCallbackReference()54 void AVTransCoderCallback::ClearCallbackReference()
55 {
56     std::lock_guard<std::mutex> lock(mutex_);
57     refMap_.clear();
58     MEDIA_LOGI("ClearCallback!");
59 }
60 
SendErrorCallback(MediaServiceExtErrCodeAPI9 errCode,const std::string & msg)61 void AVTransCoderCallback::SendErrorCallback(MediaServiceExtErrCodeAPI9 errCode, const std::string &msg)
62 {
63     std::string message = MSExtAVErrorToString(errCode) + msg;
64     MEDIA_LOGE("SendErrorCallback:errorCode %{public}d, errorMsg %{public}s", errCode, message.c_str());
65     std::lock_guard<std::mutex> lock(mutex_);
66     if (refMap_.find(AVTransCoderEvent::EVENT_ERROR) == refMap_.end()) {
67         MEDIA_LOGW("can not find error callback!");
68         return;
69     }
70 
71     AVTransCoderJsCallback *cb = new(std::nothrow) AVTransCoderJsCallback();
72     CHECK_AND_RETURN_LOG(cb != nullptr, "cb is nullptr");
73     cb->autoRef = refMap_.at(AVTransCoderEvent::EVENT_ERROR);
74     cb->callbackName = AVTransCoderEvent::EVENT_ERROR;
75     cb->errorCode = errCode;
76     cb->errorMsg = message;
77     return OnJsErrorCallBack(cb);
78 }
79 
SendStateCallback(const std::string & state,const StateChangeReason & reason)80 void AVTransCoderCallback::SendStateCallback(const std::string &state, const StateChangeReason &reason)
81 {
82     std::lock_guard<std::mutex> lock(mutex_);
83     MEDIA_LOGI("StateChange, currentState: %{public}s to state: %{public}s", currentState_.c_str(), state.c_str());
84     currentState_ = state;
85 }
86 
SendCompleteCallback()87 void AVTransCoderCallback::SendCompleteCallback()
88 {
89     std::lock_guard<std::mutex> lock(mutex_);
90     if (refMap_.find(AVTransCoderEvent::EVENT_COMPLETE) == refMap_.end()) {
91         MEDIA_LOGW("can not find statechange callback!");
92         return;
93     }
94 
95     AVTransCoderJsCallback *cb = new(std::nothrow) AVTransCoderJsCallback();
96     CHECK_AND_RETURN_LOG(cb != nullptr, "cb is nullptr");
97     cb->autoRef = refMap_.at(AVTransCoderEvent::EVENT_COMPLETE);
98     cb->callbackName = AVTransCoderEvent::EVENT_COMPLETE;
99     return OnJsCompleteCallBack(cb);
100 }
101 
SendProgressUpdateCallback(int32_t progress)102 void AVTransCoderCallback::SendProgressUpdateCallback(int32_t progress)
103 {
104     std::lock_guard<std::mutex> lock(mutex_);
105     if (refMap_.find(AVTransCoderEvent::EVENT_PROGRESS_UPDATE) == refMap_.end()) {
106         MEDIA_LOGW("can not find progressupdate callback!");
107         return;
108     }
109     AVTransCoderJsCallback *cb = new(std::nothrow) AVTransCoderJsCallback();
110     CHECK_AND_RETURN_LOG(cb != nullptr, "cb is nullptr");
111     cb->autoRef = refMap_.at(AVTransCoderEvent::EVENT_PROGRESS_UPDATE);
112     cb->callbackName = AVTransCoderEvent::EVENT_PROGRESS_UPDATE;
113     cb->progress = progress;
114     return OnJsProgressUpdateCallback(cb);
115 }
116 
OnError(int32_t errCode,const std::string & errorMsg)117 void AVTransCoderCallback::OnError(int32_t errCode, const std::string &errorMsg)
118 {
119     MEDIA_LOGE("AVTransCoderCallback::OnError: %{public}d, %{public}s", errCode, errorMsg.c_str());
120     MediaServiceExtErrCodeAPI9 errorCodeApi9 = MSErrorToExtErrorAPI9(static_cast<MediaServiceErrCode>(errCode));
121     SendErrorCallback(errorCodeApi9, errorMsg);
122     SendStateCallback(AVTransCoderState::STATE_ERROR, StateChangeReason::BACKGROUND);
123 }
124 
OnInfo(int32_t type,int32_t extra)125 void AVTransCoderCallback::OnInfo(int32_t type, int32_t extra)
126 {
127     if (type == TransCoderOnInfoType::INFO_TYPE_TRANSCODER_COMPLETED) {
128         SendCompleteCallback();
129     } else if (type == TransCoderOnInfoType::INFO_TYPE_PROGRESS_UPDATE) {
130         SendProgressUpdateCallback(extra);
131     }
132 }
133 
OnJsCompleteCallBack(AVTransCoderJsCallback * jsCb) const134 void AVTransCoderCallback::OnJsCompleteCallBack(AVTransCoderJsCallback *jsCb) const
135 {
136     ON_SCOPE_EXIT(0) {
137         delete jsCb;
138     };
139 
140     uv_loop_s *loop = nullptr;
141     napi_get_uv_event_loop(env_, &loop);
142     CHECK_AND_RETURN_LOG(loop != nullptr, "Fail to get uv event loop");
143 
144     uv_work_t *work = new(std::nothrow) uv_work_t;
145     CHECK_AND_RETURN_LOG(work != nullptr, "fail to new uv_work_t");
146     ON_SCOPE_EXIT(1) {
147         delete work;
148     };
149 
150     work->data = reinterpret_cast<void *>(jsCb);
151     int ret = QueueCompleteWork(loop, work);
152     CHECK_AND_RETURN_LOG(ret == 0, "fail to uv_queue_work_with_qos task");
153 
154     CANCEL_SCOPE_EXIT_GUARD(0);
155     CANCEL_SCOPE_EXIT_GUARD(1);
156 }
157 
QueueCompleteWork(uv_loop_s * loop,uv_work_t * work) const158 int32_t AVTransCoderCallback::QueueCompleteWork(uv_loop_s *loop, uv_work_t *work) const
159 {
160     int ret = uv_queue_work_with_qos(loop, work, [] (uv_work_t *work) {}, [] (uv_work_t *work, int status) {
161         // Js Thread
162         CHECK_AND_RETURN_LOG(work != nullptr, "work is nullptr");
163         AVTransCoderJsCallback *event = reinterpret_cast<AVTransCoderJsCallback *>(work->data);
164         std::string request = event->callbackName;
165         MEDIA_LOGI("uv_queue_work_with_qos start, state changes to %{public}s", event->state.c_str());
166         do {
167             CHECK_AND_BREAK_LOG(status != UV_ECANCELED, "%{public}s canceled", request.c_str());
168             std::shared_ptr<AutoRef> ref = event->autoRef.lock();
169             CHECK_AND_BREAK_LOG(ref != nullptr, "%{public}s AutoRef is nullptr", request.c_str());
170 
171             napi_handle_scope scope = nullptr;
172             napi_open_handle_scope(ref->env_, &scope);
173             CHECK_AND_BREAK_LOG(scope != nullptr, "%{public}s scope is nullptr", request.c_str());
174             ON_SCOPE_EXIT(0) {
175                 napi_close_handle_scope(ref->env_, scope);
176             };
177 
178             napi_value jsCallback = nullptr;
179             napi_status nstatus = napi_get_reference_value(ref->env_, ref->cb_, &jsCallback);
180             CHECK_AND_BREAK_LOG(nstatus == napi_ok && jsCallback != nullptr, "%{public}s get reference value fail",
181                 request.c_str());
182 
183             napi_value args[2] = { nullptr };
184 
185             const size_t argCount = 0;
186             napi_value result = nullptr;
187             nstatus = napi_call_function(ref->env_, nullptr, jsCallback, argCount, args, &result);
188             CHECK_AND_BREAK_LOG(nstatus == napi_ok, "%{public}s fail to napi call function", request.c_str());
189         } while (0);
190         delete event;
191         delete work;
192     }, uv_qos_user_initiated);
193     return ret;
194 }
195 
OnJsProgressUpdateCallback(AVTransCoderJsCallback * jsCb) const196 void AVTransCoderCallback::OnJsProgressUpdateCallback(AVTransCoderJsCallback *jsCb) const
197 {
198     ON_SCOPE_EXIT(0) {
199         delete jsCb;
200     };
201 
202     uv_loop_s *loop = nullptr;
203     napi_get_uv_event_loop(env_, &loop);
204     CHECK_AND_RETURN_LOG(loop != nullptr, "Fail to get uv event loop");
205 
206     uv_work_t *work = new(std::nothrow) uv_work_t;
207     CHECK_AND_RETURN_LOG(work != nullptr, "fail to new uv_work_t");
208     ON_SCOPE_EXIT(1) {
209         delete work;
210     };
211 
212     work->data = reinterpret_cast<void *>(jsCb);
213     int ret = QueueProgressUpdateWork(loop, work);
214     CHECK_AND_RETURN_LOG(ret == 0, "fail to uv_queue_work_with_qos task");
215 
216     CANCEL_SCOPE_EXIT_GUARD(0);
217     CANCEL_SCOPE_EXIT_GUARD(1);
218 }
219 
QueueProgressUpdateWork(uv_loop_s * loop,uv_work_t * work) const220 int32_t AVTransCoderCallback::QueueProgressUpdateWork(uv_loop_s *loop, uv_work_t *work) const
221 {
222     int ret = uv_queue_work_with_qos(loop, work, [] (uv_work_t *work) {}, [] (uv_work_t *work, int status) {
223         // Js Thread
224         CHECK_AND_RETURN_LOG(work != nullptr, "work is nullptr");
225         if (work->data == nullptr) {
226             delete work;
227             MEDIA_LOGE("workdata is nullptr");
228             return;
229         }
230         AVTransCoderJsCallback *event = reinterpret_cast<AVTransCoderJsCallback *>(work->data);
231         std::string request = event->callbackName;
232         do {
233             CHECK_AND_BREAK_LOG(status != UV_ECANCELED, "%{public}s canceled", request.c_str());
234             std::shared_ptr<AutoRef> ref = event->autoRef.lock();
235             CHECK_AND_BREAK_LOG(ref != nullptr, "%{public}s AutoRef is nullptr", request.c_str());
236 
237             napi_handle_scope scope = nullptr;
238             napi_open_handle_scope(ref->env_, &scope);
239             CHECK_AND_BREAK_LOG(scope != nullptr, "%{public}s scope is nullptr", request.c_str());
240             ON_SCOPE_EXIT(0) {
241                 napi_close_handle_scope(ref->env_, scope);
242             };
243 
244             napi_value jsCallback = nullptr;
245             napi_status nstatus = napi_get_reference_value(ref->env_, ref->cb_, &jsCallback);
246             CHECK_AND_BREAK_LOG(nstatus == napi_ok && jsCallback != nullptr, "%{public}s get reference value fail",
247                 request.c_str());
248 
249             napi_value args[1] = { nullptr };
250             nstatus = napi_create_int32(ref->env_, event->progress, &args[0]);
251             CHECK_AND_BREAK_LOG(nstatus == napi_ok && args[0] != nullptr,
252                 "%{public}s fail to create callback", request.c_str());
253 
254             const size_t argCount = 1;
255             napi_value result = nullptr;
256             nstatus = napi_call_function(ref->env_, nullptr, jsCallback, argCount, args, &result);
257             CHECK_AND_BREAK_LOG(nstatus == napi_ok, "%{public}s fail to napi call function", request.c_str());
258         } while (0);
259         delete event;
260         delete work;
261     }, uv_qos_user_initiated);
262     return ret;
263 }
264 
GetState()265 std::string AVTransCoderCallback::GetState()
266 {
267     std::lock_guard<std::mutex> lock(mutex_);
268     return currentState_;
269 }
270 
OnJsErrorCallBack(AVTransCoderJsCallback * jsCb) const271 void AVTransCoderCallback::OnJsErrorCallBack(AVTransCoderJsCallback *jsCb) const
272 {
273     ON_SCOPE_EXIT(0) {
274         delete jsCb;
275     };
276 
277     uv_loop_s *loop = nullptr;
278     napi_get_uv_event_loop(env_, &loop);
279     CHECK_AND_RETURN_LOG(loop != nullptr, "Fail to get uv event loop");
280 
281     uv_work_t *work = new(std::nothrow) uv_work_t;
282     CHECK_AND_RETURN_LOG(work != nullptr, "fail to new uv_work_t");
283     ON_SCOPE_EXIT(1) {
284         delete work;
285     };
286 
287     work->data = reinterpret_cast<void *>(jsCb);
288     // async callback, jsWork and jsWork->data should be heap object.
289     int ret = QueueErrorWork(loop, work);
290     CHECK_AND_RETURN_LOG(ret == 0, "fail to uv_queue_work_with_qos task");
291 
292     CANCEL_SCOPE_EXIT_GUARD(0);
293     CANCEL_SCOPE_EXIT_GUARD(1);
294 }
295 
QueueErrorWork(uv_loop_s * loop,uv_work_t * work) const296 int32_t AVTransCoderCallback::QueueErrorWork(uv_loop_s *loop, uv_work_t *work) const
297 {
298     int ret = uv_queue_work_with_qos(loop, work, [] (uv_work_t *work) {}, [] (uv_work_t *work, int status) {
299         // Js Thread
300         CHECK_AND_RETURN_LOG(work != nullptr, "work is nullptr");
301         if (work->data == nullptr) {
302             delete work;
303             MEDIA_LOGE("workdata is nullptr");
304             return;
305         }
306         AVTransCoderJsCallback *event = reinterpret_cast<AVTransCoderJsCallback *>(work->data);
307         std::string request = event->callbackName;
308         MEDIA_LOGI("uv_queue_work_with_qos start, errorcode:%{public}d , errormessage:%{public}s:",
309             event->errorCode, event->errorMsg.c_str());
310         do {
311             CHECK_AND_BREAK_LOG(status != UV_ECANCELED, "%{public}s canceled", request.c_str());
312             std::shared_ptr<AutoRef> ref = event->autoRef.lock();
313             CHECK_AND_BREAK_LOG(ref != nullptr, "%{public}s AutoRef is nullptr", request.c_str());
314 
315             napi_handle_scope scope = nullptr;
316             napi_open_handle_scope(ref->env_, &scope);
317             CHECK_AND_BREAK_LOG(scope != nullptr, "%{public}s scope is nullptr", request.c_str());
318             ON_SCOPE_EXIT(0) {
319                 napi_close_handle_scope(ref->env_, scope);
320             };
321 
322             napi_value jsCallback = nullptr;
323             napi_status nstatus = napi_get_reference_value(ref->env_, ref->cb_, &jsCallback);
324             CHECK_AND_BREAK_LOG(nstatus == napi_ok && jsCallback != nullptr, "%{public}s get reference value fail",
325                 request.c_str());
326 
327             napi_value msgValStr = nullptr;
328             nstatus = napi_create_string_utf8(ref->env_, event->errorMsg.c_str(), NAPI_AUTO_LENGTH, &msgValStr);
329             CHECK_AND_BREAK_LOG(nstatus == napi_ok && msgValStr != nullptr, "create error message str fail");
330 
331             napi_value args[1] = { nullptr };
332             nstatus = napi_create_error(ref->env_, nullptr, msgValStr, &args[0]);
333             CHECK_AND_BREAK_LOG(nstatus == napi_ok && args[0] != nullptr, "create error callback fail");
334 
335             nstatus = CommonNapi::FillErrorArgs(ref->env_, event->errorCode, args[0]);
336             CHECK_AND_BREAK_LOG(nstatus == napi_ok, "create error callback fail");
337 
338             // Call back function
339             napi_value result = nullptr;
340             nstatus = napi_call_function(ref->env_, nullptr, jsCallback, 1, args, &result);
341             CHECK_AND_BREAK_LOG(nstatus == napi_ok, "%{public}s fail to napi call function", request.c_str());
342         } while (0);
343         delete event;
344         delete work;
345     }, uv_qos_user_initiated);
346     return ret;
347 }
348 } // namespace Media
349 } // namespace OHOS