• 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 #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