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