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 "avmetadatahelper_callback.h"
17 #include <uv.h>
18 #include "media_errors.h"
19 #include "media_log.h"
20 #include "scope_guard.h"
21 #include "event_queue.h"
22 
23 namespace {
24 constexpr OHOS::HiviewDFX::HiLogLabel LABEL = {LOG_CORE, LOG_DOMAIN_METADATA, "AVMetadataHelperCallback"};
25 }
26 
27 namespace OHOS {
28 namespace Media {
29 class NapiCallback {
30 public:
31     struct Base {
32         std::weak_ptr<AutoRef> callback;
33         std::string callbackName = "unknown";
34         Base() = default;
35         virtual ~Base() = default;
UvWorkOHOS::Media::NapiCallback::Base36         virtual void UvWork()
37         {
38             std::shared_ptr<AutoRef> ref = callback.lock();
39             CHECK_AND_RETURN_LOG(ref != nullptr,
40                 "%{public}s AutoRef is nullptr", callbackName.c_str());
41 
42             napi_handle_scope scope = nullptr;
43             napi_open_handle_scope(ref->env_, &scope);
44             CHECK_AND_RETURN_LOG(scope != nullptr,
45                 "%{public}s scope is nullptr", callbackName.c_str());
46             ON_SCOPE_EXIT(0) {
47                 napi_close_handle_scope(ref->env_, scope);
48             };
49 
50             napi_value jsCallback = nullptr;
51             napi_status status = napi_get_reference_value(ref->env_, ref->cb_, &jsCallback);
52             CHECK_AND_RETURN_LOG(status == napi_ok && jsCallback != nullptr,
53                 "%{public}s failed to napi_get_reference_value", callbackName.c_str());
54 
55             // Call back function
56             napi_value result = nullptr;
57             status = napi_call_function(ref->env_, nullptr, jsCallback, 0, nullptr, &result);
58             CHECK_AND_RETURN_LOG(status == napi_ok,
59                 "%{public}s failed to napi_call_function", callbackName.c_str());
60         }
JsCallbackOHOS::Media::NapiCallback::Base61         virtual void JsCallback()
62         {
63             UvWork();
64             delete this;
65         }
66     };
67 
68     struct Error : public Base {
69         std::string errorMsg = "unknown";
70         MediaServiceExtErrCodeAPI9 errorCode = MSERR_EXT_API9_UNSUPPORT_FORMAT;
UvWorkOHOS::Media::NapiCallback::Error71         void UvWork() override
72         {
73             std::shared_ptr<AutoRef> ref = callback.lock();
74             CHECK_AND_RETURN_LOG(ref != nullptr,
75                 "%{public}s AutoRef is nullptr", callbackName.c_str());
76 
77             napi_handle_scope scope = nullptr;
78             napi_open_handle_scope(ref->env_, &scope);
79             CHECK_AND_RETURN_LOG(scope != nullptr,
80                 "%{public}s scope is nullptr", callbackName.c_str());
81             ON_SCOPE_EXIT(0) {
82                 napi_close_handle_scope(ref->env_, scope);
83             };
84 
85             napi_value jsCallback = nullptr;
86             napi_status status = napi_get_reference_value(ref->env_, ref->cb_, &jsCallback);
87             CHECK_AND_RETURN_LOG(status == napi_ok && jsCallback != nullptr,
88                 "%{public}s failed to napi_get_reference_value", callbackName.c_str());
89 
90             napi_value args[1] = {nullptr};
91             (void)CommonNapi::CreateError(ref->env_, errorCode, errorMsg, args[0]);
92 
93             // Call back function
94             napi_value result = nullptr;
95             status = napi_call_function(ref->env_, nullptr, jsCallback, 1, args, &result);
96             CHECK_AND_RETURN_LOG(status == napi_ok,
97                 "%{public}s failed to napi_call_function", callbackName.c_str());
98         }
99     };
100 
CompleteCallback(napi_env env,NapiCallback::Base * jsCb)101     static void CompleteCallback(napi_env env, NapiCallback::Base *jsCb)
102     {
103         ON_SCOPE_EXIT(0) {
104             delete jsCb;
105         };
106 
107         uv_loop_s *loop = nullptr;
108         napi_get_uv_event_loop(env, &loop);
109         CHECK_AND_RETURN_LOG(loop != nullptr, "Fail to napi_get_uv_event_loop");
110 
111         uv_work_t *work = new(std::nothrow) uv_work_t;
112         CHECK_AND_RETURN_LOG(work != nullptr, "Fail to new uv_work_t");
113 
114         work->data = reinterpret_cast<void *>(jsCb);
115         // async callback, jsWork and jsWork->data should be heap object.
116         int ret = uv_queue_work(loop, work, [] (uv_work_t *work) {}, [] (uv_work_t *work, int status) {
117             CHECK_AND_RETURN_LOG(work != nullptr, "Work thread is nullptr");
118             (void)status;
119             NapiCallback::Base *cb = reinterpret_cast<NapiCallback::Base *>(work->data);
120             if (cb != nullptr) {
121                 MEDIA_LOGI("JsCallBack %{public}s, uv_queue_work start", cb->callbackName.c_str());
122                 cb->UvWork();
123                 delete cb;
124             }
125             delete work;
126         });
127         if (ret != 0) {
128             MEDIA_LOGE("Failed to execute libuv work queue");
129             delete jsCb;
130             delete work;
131         }
132         CANCEL_SCOPE_EXIT_GUARD(0);
133     }
134 };
135 
AVMetadataHelperCallback(napi_env env,AVMetadataHelperNotify * listener)136 AVMetadataHelperCallback::AVMetadataHelperCallback(napi_env env, AVMetadataHelperNotify *listener)
137     : env_(env), listener_(listener)
138 {
139     onInfoFuncs_[HELPER_INFO_TYPE_STATE_CHANGE] =
140         [this](const int32_t extra, const Format &infoBody) { OnStateChangeCb(extra, infoBody); };
141     MEDIA_LOGI("0x%{public}06" PRIXPTR " Instances create", FAKE_POINTER(this));
142 }
143 
~AVMetadataHelperCallback()144 AVMetadataHelperCallback::~AVMetadataHelperCallback()
145 {
146     MEDIA_LOGI("0x%{public}06" PRIXPTR " Instances destroy", FAKE_POINTER(this));
147 }
148 
OnError(int32_t errorCode,const std::string & errorMsg)149 void AVMetadataHelperCallback::OnError(int32_t errorCode, const std::string &errorMsg)
150 {
151     if (errorCode == HelperErrorType::INVALID_RESULT) {
152         MEDIA_LOGE("OnError: errorCode %{public}d, errorMsg %{public}s", errorCode, errorMsg.c_str());
153         AVMetadataHelperCallback::OnErrorCb(MSERR_EXT_API9_OPERATE_NOT_PERMIT, errorMsg);
154         return;
155     }
156     MediaServiceExtErrCodeAPI9 errorCodeApi9 = MSErrorToExtErrorAPI9(static_cast<MediaServiceErrCode>(errorCode));
157     AVMetadataHelperCallback::OnErrorCb(errorCodeApi9, errorMsg);
158 }
159 
OnErrorCb(MediaServiceExtErrCodeAPI9 errorCode,const std::string & errorMsg)160 void AVMetadataHelperCallback::OnErrorCb(MediaServiceExtErrCodeAPI9 errorCode, const std::string &errorMsg)
161 {
162     std::string message = MSExtAVErrorToString(errorCode) + errorMsg;
163     MEDIA_LOGE("OnErrorCb:errorCode %{public}d, errorMsg %{public}s", errorCode, message.c_str());
164     std::lock_guard<std::mutex> lock(mutex_);
165     if (refMap_.find(AVMetadataHelperEvent::EVENT_ERROR) == refMap_.end()) {
166         MEDIA_LOGW("can not find error callback!");
167         return;
168     }
169 
170     NapiCallback::Error *cb = new(std::nothrow) NapiCallback::Error();
171     CHECK_AND_RETURN_LOG(cb != nullptr, "failed to new Error");
172 
173     cb->callback = refMap_.at(AVMetadataHelperEvent::EVENT_ERROR);
174     cb->callbackName = AVMetadataHelperEvent::EVENT_ERROR;
175     cb->errorCode = errorCode;
176     cb->errorMsg = message;
177     NapiCallback::CompleteCallback(env_, cb);
178 }
179 
OnInfo(HelperOnInfoType type,int32_t extra,const Format & infoBody)180 void AVMetadataHelperCallback::OnInfo(HelperOnInfoType type, int32_t extra, const Format &infoBody)
181 {
182     std::lock_guard<std::mutex> lock(mutex_);
183     MEDIA_LOGI("OnInfo is called, OnInfoType: %{public}d", type);
184     if (onInfoFuncs_.count(type) > 0) {
185         onInfoFuncs_[type](extra, infoBody);
186     } else {
187         MEDIA_LOGI("OnInfo: no member func supporting, %{public}d", type);
188     }
189 }
190 
OnStateChangeCb(const int32_t extra,const Format & infoBody)191 void AVMetadataHelperCallback::OnStateChangeCb(const int32_t extra, const Format &infoBody)
192 {
193     HelperStates state = static_cast<HelperStates>(extra);
194     MEDIA_LOGI("OnStateChanged is called, current state: %{public}d", state);
195 
196     if (listener_ != nullptr) {
197         listener_->NotifyState(state);
198     }
199 }
200 
SaveCallbackReference(const std::string & name,std::weak_ptr<AutoRef> ref)201 void AVMetadataHelperCallback::SaveCallbackReference(const std::string &name, std::weak_ptr<AutoRef> ref)
202 {
203     std::lock_guard<std::mutex> lock(mutex_);
204     refMap_[name] = ref;
205 }
206 
ClearCallbackReference()207 void AVMetadataHelperCallback::ClearCallbackReference()
208 {
209     std::lock_guard<std::mutex> lock(mutex_);
210     refMap_.clear();
211 }
212 
ClearCallbackReference(const std::string & name)213 void AVMetadataHelperCallback::ClearCallbackReference(const std::string &name)
214 {
215     std::lock_guard<std::mutex> lock(mutex_);
216     refMap_.erase(name);
217 }
218 
Start()219 void AVMetadataHelperCallback::Start()
220 {
221     isloaded_ = true;
222 }
223 
Pause()224 void AVMetadataHelperCallback::Pause()
225 {
226     isloaded_ = false;
227 }
228 
Release()229 void AVMetadataHelperCallback::Release()
230 {
231     std::lock_guard<std::mutex> lock(mutex_);
232 
233     Format infoBody;
234     AVMetadataHelperCallback::OnStateChangeCb(HelperStates::HELPER_RELEASED, infoBody);
235     listener_ = nullptr;
236 }
237 } // namespace Media
238 } // namespace OHOS