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