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 
16 #include "avscreen_capture_callback.h"
17 #include "scope_guard.h"
18 #include "media_log.h"
19 
20 namespace {
21     constexpr OHOS::HiviewDFX::HiLogLabel LABEL = {LOG_CORE, LOG_DOMAIN_SCREENCAPTURE, "AVScreenCaptureCallback"};
22 }
23 
24 namespace OHOS {
25 namespace Media {
AVScreenCaptureCallback(napi_env env)26 AVScreenCaptureCallback::AVScreenCaptureCallback(napi_env env) : env_(env)
27 {
28     MEDIA_LOGI("0x%{public}06" PRIXPTR "Instances create", FAKE_POINTER(this));
29 }
30 
~AVScreenCaptureCallback()31 AVScreenCaptureCallback::~AVScreenCaptureCallback()
32 {
33     MEDIA_LOGI("0x%{public}06" PRIXPTR "Instances destroy", FAKE_POINTER(this));
34 }
35 
SendErrorCallback(int32_t errCode,const std::string & msg)36 void AVScreenCaptureCallback::SendErrorCallback(int32_t errCode, const std::string &msg)
37 {
38     std::lock_guard<std::mutex> lock(mutex_);
39     if (refMap_.find(AVScreenCaptureEvent::EVENT_ERROR) == refMap_.end()) {
40         MEDIA_LOGW("can not find error callback!");
41         return;
42     }
43 
44     AVScreenCaptureJsCallback *cb = new(std::nothrow) AVScreenCaptureJsCallback();
45     CHECK_AND_RETURN_LOG(cb != nullptr, "cb is nullptr");
46     cb->autoRef = refMap_.at(AVScreenCaptureEvent::EVENT_ERROR);
47     cb->callbackName = AVScreenCaptureEvent::EVENT_ERROR;
48     cb->errorCode = errCode;
49     cb->errorMsg = msg;
50     return OnJsErrorCallBack(cb);
51 }
52 
SendStateCallback(AVScreenCaptureStateCode stateCode)53 void AVScreenCaptureCallback::SendStateCallback(AVScreenCaptureStateCode stateCode)
54 {
55     std::lock_guard<std::mutex> lock(mutex_);
56     if (refMap_.find(AVScreenCaptureEvent::EVENT_STATE_CHANGE) == refMap_.end()) {
57         MEDIA_LOGW("can not find stateChange callback!");
58         return;
59     }
60 
61     AVScreenCaptureJsCallback *cb = new(std::nothrow) AVScreenCaptureJsCallback();
62     CHECK_AND_RETURN_LOG(cb != nullptr, "cb is nullptr");
63     cb->autoRef = refMap_.at(AVScreenCaptureEvent::EVENT_STATE_CHANGE);
64     cb->callbackName = AVScreenCaptureEvent::EVENT_STATE_CHANGE;
65     cb->stateCode = stateCode;
66     return OnJsStateChangeCallBack(cb);
67 }
68 
SaveCallbackReference(const std::string & name,std::weak_ptr<AutoRef> ref)69 void AVScreenCaptureCallback::SaveCallbackReference(const std::string &name, std::weak_ptr<AutoRef> ref)
70 {
71     std::lock_guard<std::mutex> lock(mutex_);
72     refMap_[name] = ref;
73     MEDIA_LOGI("Set callback type: %{public}s", name.c_str());
74 }
75 
CancelCallbackReference(const std::string & name)76 void AVScreenCaptureCallback::CancelCallbackReference(const std::string &name)
77 {
78     std::lock_guard<std::mutex> lock(mutex_);
79     auto iter = refMap_.find(name);
80     if (iter != refMap_.end()) {
81         refMap_.erase(iter);
82     }
83     MEDIA_LOGI("Cancel callback type: %{public}s", name.c_str());
84 }
85 
ClearCallbackReference()86 void AVScreenCaptureCallback::ClearCallbackReference()
87 {
88     std::lock_guard<std::mutex> lock(mutex_);
89     refMap_.clear();
90     MEDIA_LOGI("ClearCallback!");
91 }
92 
OnError(ScreenCaptureErrorType errorType,int32_t errorCode)93 void AVScreenCaptureCallback::OnError(ScreenCaptureErrorType errorType, int32_t errorCode)
94 {
95     MEDIA_LOGI("OnError is called, name: %{public}d, error message: %{public}d", errorType, errorCode);
96     SendErrorCallback(errorCode, "Screen capture failed.");
97 }
98 
OnStateChange(AVScreenCaptureStateCode stateCode)99 void AVScreenCaptureCallback::OnStateChange(AVScreenCaptureStateCode stateCode)
100 {
101     MEDIA_LOGI("OnStateChange() is called, stateCode: %{public}d", stateCode);
102     SendStateCallback(stateCode);
103 }
104 
OnJsErrorCallBack(AVScreenCaptureJsCallback * jsCb) const105 void AVScreenCaptureCallback::OnJsErrorCallBack(AVScreenCaptureJsCallback *jsCb) const
106 {
107     ON_SCOPE_EXIT(0) {
108         delete jsCb;
109     };
110 
111     uv_loop_s *loop = nullptr;
112     napi_get_uv_event_loop(env_, &loop);
113     CHECK_AND_RETURN_LOG(loop != nullptr, "Fail to get uv event loop");
114 
115     uv_work_t *work = new(std::nothrow) uv_work_t;
116     CHECK_AND_RETURN_LOG(work != nullptr, "fail to new uv_work_t");
117     ON_SCOPE_EXIT(1) {
118         delete work;
119     };
120 
121     work->data = reinterpret_cast<void *>(jsCb);
122     // async callback, jsWork and jsWork->data should be heap object.
123     int ret = uv_queue_work_with_qos(loop, work, [] (uv_work_t *work) {
124         MEDIA_LOGD("OnJsErrorCallBack uv_queue_work_with_qos");
125     }, OnJsErrorCallBackWork, uv_qos_user_initiated);
126     CHECK_AND_RETURN_LOG(ret == 0, "fail to uv_queue_work_with_qos task");
127 
128     CANCEL_SCOPE_EXIT_GUARD(0);
129     CANCEL_SCOPE_EXIT_GUARD(1);
130 }
131 
OnJsErrorCallBackWork(uv_work_t * work,int status)132 void AVScreenCaptureCallback::OnJsErrorCallBackWork(uv_work_t *work, int status)
133 {
134     // Js Thread
135     CHECK_AND_RETURN_LOG(work != nullptr, "work is nullptr");
136     if (work->data == nullptr) {
137         delete work;
138         MEDIA_LOGE("workdata is nullptr error");
139         return;
140     }
141     AVScreenCaptureJsCallback *event = reinterpret_cast<AVScreenCaptureJsCallback *>(work->data);
142     std::string request = event->callbackName;
143     MEDIA_LOGI("uv_queue_work_with_qos start, errorcode:%{public}d , errormessage:%{public}s:",
144         event->errorCode, event->errorMsg.c_str());
145     do {
146         CHECK_AND_BREAK_LOG(status != UV_ECANCELED, "%{public}s canceled", request.c_str());
147         std::shared_ptr<AutoRef> ref = event->autoRef.lock();
148         CHECK_AND_BREAK_LOG(ref != nullptr, "%{public}s AutoRef is nullptr", request.c_str());
149 
150         napi_handle_scope scope = nullptr;
151         napi_open_handle_scope(ref->env_, &scope);
152         CHECK_AND_BREAK_LOG(scope != nullptr, "%{public}s scope is nullptr", request.c_str());
153         ON_SCOPE_EXIT(0) {
154             napi_close_handle_scope(ref->env_, scope);
155         };
156 
157         napi_value jsCallback = nullptr;
158         napi_status nstatus = napi_get_reference_value(ref->env_, ref->cb_, &jsCallback);
159         CHECK_AND_BREAK_LOG(nstatus == napi_ok && jsCallback != nullptr, "%{public}s get reference value fail",
160             request.c_str());
161 
162         napi_value msgValStr = nullptr;
163         nstatus = napi_create_string_utf8(ref->env_, event->errorMsg.c_str(), NAPI_AUTO_LENGTH, &msgValStr);
164         CHECK_AND_BREAK_LOG(nstatus == napi_ok && msgValStr != nullptr, "create error message str fail");
165 
166         napi_value args[1] = { nullptr };
167         nstatus = napi_create_error(ref->env_, nullptr, msgValStr, &args[0]);
168         CHECK_AND_BREAK_LOG(nstatus == napi_ok && args[0] != nullptr, "create error callback fail");
169 
170         nstatus = CommonNapi::FillErrorArgs(ref->env_, event->errorCode, args[0]);
171         CHECK_AND_BREAK_LOG(nstatus == napi_ok, "create error callback fail");
172 
173         // Call back function
174         napi_value result = nullptr;
175         nstatus = napi_call_function(ref->env_, nullptr, jsCallback, 1, args, &result);
176         CHECK_AND_BREAK_LOG(nstatus == napi_ok, "%{public}s fail to napi call function", request.c_str());
177     } while (0);
178     delete event;
179     delete work;
180 }
181 
OnJsStateChangeCallBack(AVScreenCaptureJsCallback * jsCb) const182 void AVScreenCaptureCallback::OnJsStateChangeCallBack(AVScreenCaptureJsCallback *jsCb) const
183 {
184     ON_SCOPE_EXIT(0) {
185         delete jsCb;
186     };
187 
188     uv_loop_s *loop = nullptr;
189     napi_get_uv_event_loop(env_, &loop);
190     CHECK_AND_RETURN_LOG(loop != nullptr, "Fail to get uv event loop");
191 
192     uv_work_t *work = new(std::nothrow) uv_work_t;
193     CHECK_AND_RETURN_LOG(work != nullptr, "fail to new uv_work_t");
194     ON_SCOPE_EXIT(1) {
195         delete work;
196     };
197 
198     work->data = reinterpret_cast<void *>(jsCb);
199     int ret = uv_queue_work_with_qos(loop, work, [] (uv_work_t *work) {
200         MEDIA_LOGD("OnJsStateChangeCallBack uv_queue_work_with_qos");
201     }, [] (uv_work_t *work, int status) {
202         // Js Thread
203         CHECK_AND_RETURN_LOG(work != nullptr, "work is nullptr");
204         AVScreenCaptureJsCallback *event = reinterpret_cast<AVScreenCaptureJsCallback *>(work->data);
205         std::string request = event->callbackName;
206         do {
207             CHECK_AND_BREAK_LOG(status != UV_ECANCELED, "%{public}s canceled", request.c_str());
208             std::shared_ptr<AutoRef> ref = event->autoRef.lock();
209             CHECK_AND_BREAK_LOG(ref != nullptr, "%{public}s AutoRef is nullptr", request.c_str());
210 
211             napi_handle_scope scope = nullptr;
212             napi_open_handle_scope(ref->env_, &scope);
213             CHECK_AND_BREAK_LOG(scope != nullptr, "%{public}s scope is nullptr", request.c_str());
214             ON_SCOPE_EXIT(0) {
215                 napi_close_handle_scope(ref->env_, scope);
216             };
217 
218             napi_value jsCallback = nullptr;
219             napi_status nstatus = napi_get_reference_value(ref->env_, ref->cb_, &jsCallback);
220             CHECK_AND_BREAK_LOG(nstatus == napi_ok && jsCallback != nullptr, "%{public}s get reference value fail",
221                 request.c_str());
222 
223             napi_value args[1] = { nullptr };
224             nstatus = napi_create_int32(ref->env_, event->stateCode, &args[0]);
225             CHECK_AND_BREAK_LOG(nstatus == napi_ok && args[0] != nullptr,
226                 "%{public}s fail to create callback", request.c_str());
227 
228             const size_t argCount = 1;
229             napi_value result = nullptr;
230             nstatus = napi_call_function(ref->env_, nullptr, jsCallback, argCount, args, &result);
231             CHECK_AND_BREAK_LOG(nstatus == napi_ok, "%{public}s fail to napi call function", request.c_str());
232         } while (0);
233         delete event;
234         delete work;
235     }, uv_qos_user_initiated);
236     CHECK_AND_RETURN_LOG(ret == 0, "fail to uv_queue_work_with_qos task");
237 
238     CANCEL_SCOPE_EXIT_GUARD(0);
239     CANCEL_SCOPE_EXIT_GUARD(1);
240 }
241 
242 } // namespace Media
243 } // namespace OHOS