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