• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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