1 /*
2 * Copyright (C) 2022 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 "avrecorder_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, "AVRecorderCallback"};
23 }
24
25 namespace OHOS {
26 namespace Media {
AVRecorderCallback(napi_env env)27 AVRecorderCallback::AVRecorderCallback(napi_env env) : env_(env)
28 {
29 MEDIA_LOGI("0x%{public}06" PRIXPTR "Instances create", FAKE_POINTER(this));
30 }
31
~AVRecorderCallback()32 AVRecorderCallback::~AVRecorderCallback()
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 AVRecorderCallback::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 AVRecorderCallback::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 AVRecorderCallback::ClearCallbackReference()
55 {
56 std::lock_guard<std::mutex> lock(mutex_);
57 refMap_.clear();
58 MEDIA_LOGI("ClearCallback!");
59 }
60
SendErrorCallback(int32_t errCode,const std::string & msg)61 void AVRecorderCallback::SendErrorCallback(int32_t errCode, const std::string &msg)
62 {
63 std::lock_guard<std::mutex> lock(mutex_);
64 if (refMap_.find(AVRecorderEvent::EVENT_ERROR) == refMap_.end()) {
65 MEDIA_LOGW("can not find error callback!");
66 return;
67 }
68
69 AVRecordJsCallback *cb = new(std::nothrow) AVRecordJsCallback();
70 CHECK_AND_RETURN_LOG(cb != nullptr, "cb is nullptr");
71 cb->autoRef = refMap_.at(AVRecorderEvent::EVENT_ERROR);
72 cb->callbackName = AVRecorderEvent::EVENT_ERROR;
73 cb->errorCode = errCode;
74 cb->errorMsg = msg;
75 return OnJsErrorCallBack(cb);
76 }
77
SendStateCallback(const std::string & state,const StateChangeReason & reason)78 void AVRecorderCallback::SendStateCallback(const std::string &state, const StateChangeReason &reason)
79 {
80 std::lock_guard<std::mutex> lock(mutex_);
81 currentState_ = state;
82 if (refMap_.find(AVRecorderEvent::EVENT_STATE_CHANGE) == refMap_.end()) {
83 MEDIA_LOGW("can not find statechange callback!");
84 return;
85 }
86
87 AVRecordJsCallback *cb = new(std::nothrow) AVRecordJsCallback();
88 CHECK_AND_RETURN_LOG(cb != nullptr, "cb is nullptr");
89 cb->autoRef = refMap_.at(AVRecorderEvent::EVENT_STATE_CHANGE);
90 cb->callbackName = AVRecorderEvent::EVENT_STATE_CHANGE;
91 cb->reason = reason;
92 cb->state = state;
93 return OnJsStateCallBack(cb);
94 }
95
GetState()96 std::string AVRecorderCallback::GetState()
97 {
98 std::lock_guard<std::mutex> lock(mutex_);
99 return currentState_;
100 }
101
OnError(RecorderErrorType errorType,int32_t errCode)102 void AVRecorderCallback::OnError(RecorderErrorType errorType, int32_t errCode)
103 {
104 MEDIA_LOGI("OnError is called, name: %{public}d, error message: %{public}d", errorType, errCode);
105 if (errCode == MSERR_DATA_SOURCE_IO_ERROR) {
106 SendErrorCallback(MSERR_EXT_API9_TIMEOUT,
107 "The video input stream timed out. Please confirm that the input stream is normal.");
108 } else if (errCode == MSERR_DATA_SOURCE_OBTAIN_MEM_ERROR) {
109 SendErrorCallback(MSERR_EXT_API9_TIMEOUT,
110 "Read data from audio timeout, please confirm whether the audio module is normal.");
111 } else if (errCode == MSERR_DATA_SOURCE_ERROR_UNKNOWN) {
112 SendErrorCallback(MSERR_EXT_API9_IO, "Video input data is abnormal."
113 " Please confirm that the pts, width, height, size and other data are normal.");
114 } else {
115 SendErrorCallback(MSERR_EXT_API9_IO, "IO error happened.");
116 }
117 SendStateCallback(AVRecorderState::STATE_ERROR, StateChangeReason::BACKGROUND);
118 }
119
OnInfo(int32_t type,int32_t extra)120 void AVRecorderCallback::OnInfo(int32_t type, int32_t extra)
121 {
122 MEDIA_LOGI("OnInfo() is called, type: %{public}d, extra: %{public}d", type, extra);
123 }
124
OnJsStateCallBack(AVRecordJsCallback * jsCb) const125 void AVRecorderCallback::OnJsStateCallBack(AVRecordJsCallback *jsCb) const
126 {
127 ON_SCOPE_EXIT(0) {
128 delete jsCb;
129 };
130
131 uv_loop_s *loop = nullptr;
132 napi_get_uv_event_loop(env_, &loop);
133 CHECK_AND_RETURN_LOG(loop != nullptr, "Fail to get uv event loop");
134
135 uv_work_t *work = new(std::nothrow) uv_work_t;
136 CHECK_AND_RETURN_LOG(work != nullptr, "fail to new uv_work_t");
137 ON_SCOPE_EXIT(1) {
138 delete work;
139 };
140
141 work->data = reinterpret_cast<void *>(jsCb);
142 int ret = uv_queue_work(loop, work, [] (uv_work_t *work) {}, [] (uv_work_t *work, int status) {
143 // Js Thread
144 CHECK_AND_RETURN_LOG(work != nullptr, "work is nullptr");
145 AVRecordJsCallback *event = reinterpret_cast<AVRecordJsCallback *>(work->data);
146 std::string request = event->callbackName;
147 MEDIA_LOGI("uv_queue_work start, state changes to %{public}s", event->state.c_str());
148 do {
149 CHECK_AND_BREAK_LOG(status != UV_ECANCELED, "%{public}s canceled", request.c_str());
150 std::shared_ptr<AutoRef> ref = event->autoRef.lock();
151 CHECK_AND_BREAK_LOG(ref != nullptr, "%{public}s AutoRef is nullptr", request.c_str());
152
153 napi_value jsCallback = nullptr;
154 napi_status nstatus = napi_get_reference_value(ref->env_, ref->cb_, &jsCallback);
155 CHECK_AND_BREAK_LOG(nstatus == napi_ok && jsCallback != nullptr, "%{public}s get reference value fail",
156 request.c_str());
157
158 napi_value args[2] = { nullptr };
159 nstatus = napi_create_string_utf8(ref->env_, event->state.c_str(), NAPI_AUTO_LENGTH, &args[0]);
160 CHECK_AND_BREAK_LOG(nstatus == napi_ok && args[0] != nullptr,
161 "%{public}s fail to create callback", request.c_str());
162
163 nstatus = napi_create_int32(ref->env_, event->reason, &args[1]);
164 CHECK_AND_BREAK_LOG(nstatus == napi_ok && args[1] != nullptr,
165 "%{public}s fail to create callback", request.c_str());
166
167 const size_t argCount = 2;
168 napi_value result = nullptr;
169 nstatus = napi_call_function(ref->env_, nullptr, jsCallback, argCount, args, &result);
170 CHECK_AND_BREAK_LOG(nstatus == napi_ok, "%{public}s fail to napi call function", request.c_str());
171 } while (0);
172 delete event;
173 delete work;
174 });
175 CHECK_AND_RETURN_LOG(ret == 0, "fail to uv_queue_work task");
176
177 CANCEL_SCOPE_EXIT_GUARD(0);
178 CANCEL_SCOPE_EXIT_GUARD(1);
179 }
180
OnJsErrorCallBack(AVRecordJsCallback * jsCb) const181 void AVRecorderCallback::OnJsErrorCallBack(AVRecordJsCallback *jsCb) const
182 {
183 ON_SCOPE_EXIT(0) {
184 delete jsCb;
185 };
186
187 uv_loop_s *loop = nullptr;
188 napi_get_uv_event_loop(env_, &loop);
189 CHECK_AND_RETURN_LOG(loop != nullptr, "Fail to get uv event loop");
190
191 uv_work_t *work = new(std::nothrow) uv_work_t;
192 CHECK_AND_RETURN_LOG(work != nullptr, "fail to new uv_work_t");
193 ON_SCOPE_EXIT(1) {
194 delete work;
195 };
196
197 work->data = reinterpret_cast<void *>(jsCb);
198 // async callback, jsWork and jsWork->data should be heap object.
199 int ret = uv_queue_work(loop, work, [] (uv_work_t *work) {}, [] (uv_work_t *work, int status) {
200 // Js Thread
201 CHECK_AND_RETURN_LOG(work != nullptr, "work is nullptr");
202 CHECK_AND_RETURN_LOG(work->data != nullptr, "workdata is nullptr");
203 AVRecordJsCallback *event = reinterpret_cast<AVRecordJsCallback *>(work->data);
204 std::string request = event->callbackName;
205 MEDIA_LOGI("uv_queue_work start, errorcode:%{public}d , errormessage:%{public}s:",
206 event->errorCode, event->errorMsg.c_str());
207 do {
208 CHECK_AND_BREAK_LOG(status != UV_ECANCELED, "%{public}s canceled", request.c_str());
209 std::shared_ptr<AutoRef> ref = event->autoRef.lock();
210 CHECK_AND_BREAK_LOG(ref != nullptr, "%{public}s AutoRef is nullptr", request.c_str());
211
212 napi_value jsCallback = nullptr;
213 napi_status nstatus = napi_get_reference_value(ref->env_, ref->cb_, &jsCallback);
214 CHECK_AND_BREAK_LOG(nstatus == napi_ok && jsCallback != nullptr, "%{public}s get reference value fail",
215 request.c_str());
216
217 napi_value msgValStr = nullptr;
218 nstatus = napi_create_string_utf8(ref->env_, event->errorMsg.c_str(), NAPI_AUTO_LENGTH, &msgValStr);
219 CHECK_AND_BREAK_LOG(nstatus == napi_ok && msgValStr != nullptr, "create error message str fail");
220
221 napi_value args[1] = { nullptr };
222 nstatus = napi_create_error(ref->env_, nullptr, msgValStr, &args[0]);
223 CHECK_AND_BREAK_LOG(nstatus == napi_ok && args[0] != nullptr, "create error callback fail");
224
225 nstatus = CommonNapi::FillErrorArgs(ref->env_, event->errorCode, args[0]);
226 CHECK_AND_RETURN_LOG(nstatus == napi_ok, "create error callback fail");
227
228 // Call back function
229 napi_value result = nullptr;
230 nstatus = napi_call_function(ref->env_, nullptr, jsCallback, 1, 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 });
236 CHECK_AND_RETURN_LOG(ret == 0, "fail to uv_queue_work task");
237
238 CANCEL_SCOPE_EXIT_GUARD(0);
239 CANCEL_SCOPE_EXIT_GUARD(1);
240 }
241 } // namespace Media
242 } // namespace OHOS