1 /*
2 * Copyright (C) 2021 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 "video_callback_napi.h"
17 #include "media_errors.h"
18 #include "media_log.h"
19 #include "scope_guard.h"
20
21 namespace {
22 constexpr OHOS::HiviewDFX::HiLogLabel LABEL = {LOG_CORE, LOG_DOMAIN_PLAYER, "VideoCallbackNapi"};
23 const std::string START_RENDER_FRAME_CALLBACK_NAME = "startRenderFrame";
24 const std::string VIDEO_SIZE_CHANGED_CALLBACK_NAME = "videoSizeChanged";
25 const std::string PLAYBACK_COMPLETED_CALLBACK_NAME = "playbackCompleted";
26 const std::string BITRATE_COLLECTED_CALLBACK_NAME = "availableBitratesCollect";
27 }
28
29 namespace OHOS {
30 namespace Media {
VideoCallbackNapi(napi_env env)31 VideoCallbackNapi::VideoCallbackNapi(napi_env env)
32 : PlayerCallbackNapi(env), env_(env)
33 {
34 MEDIA_LOGD("0x%{public}06" PRIXPTR " Instances create", FAKE_POINTER(this));
35 contextMap_.clear();
36 }
37
~VideoCallbackNapi()38 VideoCallbackNapi::~VideoCallbackNapi()
39 {
40 MEDIA_LOGD("0x%{public}06" PRIXPTR " Instances destroy", FAKE_POINTER(this));
41 }
42
QueueAsyncWork(VideoPlayerAsyncContext * context)43 void VideoCallbackNapi::QueueAsyncWork(VideoPlayerAsyncContext *context)
44 {
45 std::lock_guard<std::mutex> lock(mutex_);
46 if (contextMap_.find(context->asyncWorkType) == contextMap_.end()) {
47 std::queue<VideoPlayerAsyncContext *> contextQue;
48 contextQue.push(context);
49 contextMap_[context->asyncWorkType] = contextQue;
50 } else {
51 contextMap_.at(context->asyncWorkType).push(context);
52 }
53 }
54
ClearAsyncWork(bool error,const std::string & msg)55 void VideoCallbackNapi::ClearAsyncWork(bool error, const std::string &msg)
56 {
57 ClearAsyncWorkWithErrorCode(MSERR_EXT_OPERATE_NOT_PERMIT, error, msg);
58 }
59
ClearAsyncWorkWithErrorCode(MediaServiceExtErrCode extErrCode,bool error,const std::string & msg)60 void VideoCallbackNapi::ClearAsyncWorkWithErrorCode(MediaServiceExtErrCode extErrCode,
61 bool error, const std::string &msg)
62 {
63 MEDIA_LOGD("%{public}s", msg.c_str());
64 std::lock_guard<std::mutex> lock(mutex_);
65 for (auto it = contextMap_.begin(); it != contextMap_.end(); it++) {
66 auto &contextQue = it->second;
67 while (!contextQue.empty()) {
68 VideoPlayerAsyncContext *context = contextQue.front();
69 contextQue.pop();
70 if (error) {
71 context->SignError(extErrCode, msg);
72 }
73 VideoCallbackNapi::OnJsCallBack(context);
74 }
75 }
76 contextMap_.clear();
77 }
78
OnInfo(PlayerOnInfoType type,int32_t extra,const Format & infoBody)79 void VideoCallbackNapi::OnInfo(PlayerOnInfoType type, int32_t extra, const Format &infoBody)
80 {
81 std::lock_guard<std::mutex> lock(mutex_);
82 MEDIA_LOGI("OnInfo is called, PlayerOnInfoType: %{public}d", type);
83 switch (type) {
84 case INFO_TYPE_MESSAGE:
85 if (extra == PlayerMessageType::PLAYER_INFO_VIDEO_RENDERING_START) {
86 VideoCallbackNapi::OnStartRenderFrameCb();
87 }
88 break;
89 case INFO_TYPE_RESOLUTION_CHANGE:
90 VideoCallbackNapi::OnVideoSizeChangedCb(infoBody);
91 break;
92 case INFO_TYPE_STATE_CHANGE:
93 VideoCallbackNapi::OnStateChangeCb(static_cast<PlayerStates>(extra));
94 break;
95 case INFO_TYPE_SEEKDONE:
96 VideoCallbackNapi::OnSeekDoneCb(extra);
97 break;
98 case INFO_TYPE_SPEEDDONE:
99 VideoCallbackNapi::OnSpeedDoneCb(extra);
100 break;
101 case INFO_TYPE_BITRATEDONE:
102 VideoCallbackNapi::OnBitRateDoneCb(extra);
103 break;
104 case INFO_TYPE_VOLUME_CHANGE:
105 VideoCallbackNapi::OnVolumeDoneCb();
106 break;
107 case INFO_TYPE_BITRATE_COLLECT:
108 VideoCallbackNapi::OnBitRateCollectedCb(infoBody);
109 break;
110 default:
111 // video + audio common info
112 PlayerCallbackNapi::OnInfo(type, extra, infoBody);
113 break;
114 }
115 MEDIA_LOGD("send OnInfo callback success");
116 }
117
OnError(int32_t errorCode,const std::string & errorMsg)118 void VideoCallbackNapi::OnError(int32_t errorCode, const std::string &errorMsg)
119 {
120 MediaServiceExtErrCode extErrCode = MSErrorToExtError(static_cast<MediaServiceErrCode>(errorCode));
121 if (extErrCode == MSERR_EXT_UNKNOWN) {
122 extErrCode = MSERR_EXT_OPERATE_NOT_PERMIT;
123 }
124 ClearAsyncWorkWithErrorCode(extErrCode, true,
125 "The request was aborted because en error occurred, please check event(error)");
126 return PlayerCallbackNapi::OnError(errorCode, errorMsg);
127 }
128
OnSeekDoneCb(int32_t position)129 void VideoCallbackNapi::OnSeekDoneCb(int32_t position)
130 {
131 if (contextMap_.find(AsyncWorkType::ASYNC_WORK_SEEK) == contextMap_.end() ||
132 contextMap_.at(AsyncWorkType::ASYNC_WORK_SEEK).empty()) {
133 MEDIA_LOGE("OnSeekDoneCb is called, But context is empty");
134 return;
135 }
136
137 VideoPlayerAsyncContext *context = contextMap_.at(AsyncWorkType::ASYNC_WORK_SEEK).front();
138 CHECK_AND_RETURN_LOG(context != nullptr, "context is nullptr");
139 contextMap_.at(AsyncWorkType::ASYNC_WORK_SEEK).pop();
140
141 context->JsResult = std::make_unique<MediaJsResultInt>(position);
142 // Switch Napi threads
143 VideoCallbackNapi::OnJsCallBack(context);
144 }
145
OnSpeedDoneCb(int32_t speedMode)146 void VideoCallbackNapi::OnSpeedDoneCb(int32_t speedMode)
147 {
148 if (speedMode < SPEED_FORWARD_0_75_X || speedMode > SPEED_FORWARD_2_00_X) {
149 MEDIA_LOGE("OnSpeedDoneCb mode:%{public}d error", speedMode);
150 }
151
152 if (contextMap_.find(AsyncWorkType::ASYNC_WORK_SPEED) == contextMap_.end() ||
153 contextMap_.at(AsyncWorkType::ASYNC_WORK_SPEED).empty()) {
154 MEDIA_LOGE("OnSpeedDoneCb is called, But context is empty");
155 return;
156 }
157
158 VideoPlayerAsyncContext *context = contextMap_.at(AsyncWorkType::ASYNC_WORK_SPEED).front();
159 CHECK_AND_RETURN_LOG(context != nullptr, "context is nullptr");
160 contextMap_.at(AsyncWorkType::ASYNC_WORK_SPEED).pop();
161
162 context->JsResult = std::make_unique<MediaJsResultInt>(context->speedMode);
163 // Switch Napi threads
164 VideoCallbackNapi::OnJsCallBack(context);
165 }
166
OnBitRateDoneCb(int32_t bitRate)167 void VideoCallbackNapi::OnBitRateDoneCb(int32_t bitRate)
168 {
169 if (contextMap_.find(AsyncWorkType::ASYNC_WORK_BITRATE) == contextMap_.end() ||
170 contextMap_.at(AsyncWorkType::ASYNC_WORK_BITRATE).empty()) {
171 MEDIA_LOGE("OnBitRateDoneCb is called, But context is empty");
172 return;
173 }
174
175 VideoPlayerAsyncContext *context = contextMap_.at(AsyncWorkType::ASYNC_WORK_BITRATE).front();
176 CHECK_AND_RETURN_LOG(context != nullptr, "context is nullptr");
177 contextMap_.at(AsyncWorkType::ASYNC_WORK_BITRATE).pop();
178
179 context->JsResult = std::make_unique<MediaJsResultInt>(bitRate);
180 // Switch Napi threads
181 VideoCallbackNapi::OnJsCallBack(context);
182 }
183
OnVolumeDoneCb()184 void VideoCallbackNapi::OnVolumeDoneCb()
185 {
186 if (contextMap_.find(AsyncWorkType::ASYNC_WORK_VOLUME) == contextMap_.end() ||
187 contextMap_.at(AsyncWorkType::ASYNC_WORK_VOLUME).empty()) {
188 MEDIA_LOGE("OnVolumeDoneCb is called, But context is empty");
189 return;
190 }
191
192 VideoPlayerAsyncContext *context = contextMap_.at(AsyncWorkType::ASYNC_WORK_VOLUME).front();
193 CHECK_AND_RETURN_LOG(context != nullptr, "context is nullptr");
194 contextMap_.at(AsyncWorkType::ASYNC_WORK_VOLUME).pop();
195
196 // Switch Napi threads
197 VideoCallbackNapi::OnJsCallBack(context);
198 }
199
OnStartRenderFrameCb() const200 void VideoCallbackNapi::OnStartRenderFrameCb() const
201 {
202 MEDIA_LOGD("OnStartRenderFrameCb is called");
203 if (refMap_.find(START_RENDER_FRAME_CALLBACK_NAME) == refMap_.end()) {
204 MEDIA_LOGW("can not find start render frame callback!");
205 return;
206 }
207 PlayerJsCallback *cb = new(std::nothrow) PlayerJsCallback();
208 CHECK_AND_RETURN_LOG(cb != nullptr, "No memory");
209 cb->callback = refMap_.at(START_RENDER_FRAME_CALLBACK_NAME);
210 cb->callbackName = START_RENDER_FRAME_CALLBACK_NAME;
211 return PlayerCallbackNapi::OnJsCallBack(cb);
212 }
213
OnVideoSizeChangedCb(const Format & infoBody)214 void VideoCallbackNapi::OnVideoSizeChangedCb(const Format &infoBody)
215 {
216 (void)infoBody.GetIntValue(PlayerKeys::PLAYER_WIDTH, width_);
217 (void)infoBody.GetIntValue(PlayerKeys::PLAYER_HEIGHT, height_);
218 MEDIA_LOGD("OnVideoSizeChangedCb is called, width = %{public}d, height = %{public}d", width_, height_);
219 if (refMap_.find(VIDEO_SIZE_CHANGED_CALLBACK_NAME) == refMap_.end()) {
220 MEDIA_LOGW("can not find video size changed callback!");
221 return;
222 }
223 PlayerJsCallback *cb = new(std::nothrow) PlayerJsCallback();
224 CHECK_AND_RETURN_LOG(cb != nullptr, "No memory");
225 cb->callback = refMap_.at(VIDEO_SIZE_CHANGED_CALLBACK_NAME);
226 cb->callbackName = VIDEO_SIZE_CHANGED_CALLBACK_NAME;
227 cb->valueVec.push_back(width_);
228 cb->valueVec.push_back(height_);
229 return PlayerCallbackNapi::OnJsCallBackIntVec(cb);
230 }
231
OnPlaybackCompleteCb() const232 void VideoCallbackNapi::OnPlaybackCompleteCb() const
233 {
234 MEDIA_LOGD("OnPlaybackCompleteCb is called");
235 if (refMap_.find(PLAYBACK_COMPLETED_CALLBACK_NAME) == refMap_.end()) {
236 MEDIA_LOGW("can not find completed callback!");
237 return;
238 }
239
240 PlayerJsCallback *cb = new(std::nothrow) PlayerJsCallback();
241 CHECK_AND_RETURN_LOG(cb != nullptr, "No memory");
242 cb->callback = refMap_.at(PLAYBACK_COMPLETED_CALLBACK_NAME);
243 cb->callbackName = PLAYBACK_COMPLETED_CALLBACK_NAME;
244 return PlayerCallbackNapi::OnJsCallBack(cb);
245 }
246
GetCurrentState() const247 PlayerStates VideoCallbackNapi::GetCurrentState() const
248 {
249 return currentState_;
250 }
251
DequeueAsyncWork()252 void VideoCallbackNapi::DequeueAsyncWork()
253 {
254 AsyncWorkType asyncWork = AsyncWorkType::ASYNC_WORK_INVALID;
255 switch (currentState_) {
256 case PLAYER_PREPARED:
257 asyncWork = AsyncWorkType::ASYNC_WORK_PREPARE;
258 break;
259 case PLAYER_STARTED:
260 asyncWork = AsyncWorkType::ASYNC_WORK_PLAY;
261 break;
262 case PLAYER_PAUSED:
263 asyncWork = AsyncWorkType::ASYNC_WORK_PAUSE;
264 break;
265 case PLAYER_STOPPED:
266 asyncWork = AsyncWorkType::ASYNC_WORK_STOP;
267 break;
268 case PLAYER_IDLE:
269 asyncWork = AsyncWorkType::ASYNC_WORK_RESET;
270 break;
271 default:
272 break;
273 }
274
275 if (contextMap_.find(asyncWork) == contextMap_.end() ||
276 contextMap_.at(asyncWork).empty()) {
277 MEDIA_LOGE("OnStateChanged(%{public}d) is called, But contextState is empty", currentState_);
278 return;
279 }
280
281 VideoPlayerAsyncContext *context = contextMap_.at(asyncWork).front();
282 CHECK_AND_RETURN_LOG(context != nullptr, "context is nullptr");
283
284 contextMap_.at(asyncWork).pop();
285 VideoCallbackNapi::OnJsCallBack(context);
286 }
287
OnStateChangeCb(PlayerStates state)288 void VideoCallbackNapi::OnStateChangeCb(PlayerStates state)
289 {
290 MEDIA_LOGD("OnStateChanged is called, current state: %{public}d", state);
291 currentState_ = state;
292
293 switch (state) {
294 case PLAYER_PREPARED:
295 case PLAYER_STARTED:
296 case PLAYER_PAUSED:
297 case PLAYER_STOPPED:
298 case PLAYER_IDLE:
299 return DequeueAsyncWork();
300 case PLAYER_PLAYBACK_COMPLETE:
301 return OnPlaybackCompleteCb();
302 default:
303 break;
304 }
305 }
306
OnBitRateCollectedCb(const Format & infoBody) const307 void VideoCallbackNapi::OnBitRateCollectedCb(const Format &infoBody) const
308 {
309 if (refMap_.find(BITRATE_COLLECTED_CALLBACK_NAME) == refMap_.end()) {
310 MEDIA_LOGW("can not find bitrate collected callback!");
311 return;
312 }
313
314 PlayerJsCallback *cb = new(std::nothrow) PlayerJsCallback();
315 CHECK_AND_RETURN_LOG(cb != nullptr, "No memory");
316 cb->callback = refMap_.at(BITRATE_COLLECTED_CALLBACK_NAME);
317 cb->callbackName = BITRATE_COLLECTED_CALLBACK_NAME;
318
319 if (infoBody.ContainKey(std::string(PlayerKeys::PLAYER_AVAILABLE_BITRATES))) {
320 uint8_t *addr = nullptr;
321 size_t size = 0;
322 infoBody.GetBuffer(std::string(PlayerKeys::PLAYER_AVAILABLE_BITRATES), &addr, size);
323 if (addr == nullptr) {
324 delete cb;
325 return;
326 }
327
328 MEDIA_LOGD("bitrate size = %{public}zu", size / sizeof(uint32_t));
329 while (size > 0) {
330 if ((size - sizeof(uint32_t)) < 0) {
331 break;
332 }
333
334 uint32_t bitrate = *(static_cast<uint32_t *>(static_cast<void *>(addr)));
335 MEDIA_LOGD("bitrate = %{public}u", bitrate);
336 addr += sizeof(uint32_t);
337 size -= sizeof(uint32_t);
338 cb->valueVec.push_back(static_cast<int32_t>(bitrate));
339 }
340 }
341
342 return PlayerCallbackNapi::OnJsCallBackIntArray(cb);
343 }
344
UvWorkCallBack(uv_work_t * work,int status)345 void VideoCallbackNapi::UvWorkCallBack(uv_work_t *work, int status)
346 {
347 napi_status nstatus = napi_generic_failure;
348 switch (status) {
349 case 0:
350 nstatus = napi_ok;
351 break;
352 case UV_EINVAL:
353 nstatus = napi_invalid_arg;
354 break;
355 case UV_ECANCELED:
356 nstatus = napi_cancelled;
357 break;
358 default:
359 nstatus = napi_generic_failure;
360 break;
361 }
362
363 auto asyncContext = reinterpret_cast<MediaAsyncContext *>(work->data);
364 if (asyncContext != nullptr) {
365 napi_handle_scope scope = nullptr;
366 napi_env env = asyncContext->env_;
367 napi_open_handle_scope(env, &scope);
368 CHECK_AND_RETURN_LOG(scope != nullptr, "scope is nullptr");
369 ON_SCOPE_EXIT(0) {
370 napi_close_handle_scope(env, scope);
371 };
372 MediaAsyncContext::CompleteCallback(asyncContext->env_, nstatus, work->data);
373 }
374 delete work;
375 }
376
OnJsCallBack(VideoPlayerAsyncContext * context) const377 void VideoCallbackNapi::OnJsCallBack(VideoPlayerAsyncContext *context) const
378 {
379 uv_loop_s *loop = nullptr;
380 napi_get_uv_event_loop(env_, &loop);
381 if (loop != nullptr) {
382 uv_work_t *work = new(std::nothrow) uv_work_t;
383 if (work != nullptr) {
384 work->data = reinterpret_cast<void *>(context);
385 int ret = uv_queue_work(loop, work, [] (uv_work_t *work) {}, VideoCallbackNapi::UvWorkCallBack);
386 if (ret != 0) {
387 MEDIA_LOGE("Failed to execute libuv work queue");
388 delete context;
389 delete work;
390 }
391 } else {
392 delete context;
393 }
394 } else {
395 delete context;
396 }
397 }
398 } // namespace Media
399 } // namespace OHOS
400