1 /*
2  * Copyright (c) 2020-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 #include "acelite_config.h"
16 #if (FEATURE_COMPONENT_VIDEO == 1)
17 
18 #include "video_view.h"
19 #include "video_state_callback.h"
20 
21 namespace OHOS {
22 namespace ACELite {
23 pthread_mutex_t VideoView::rebuildPlayerlock_ = PTHREAD_MUTEX_INITIALIZER;
24 pthread_cond_t VideoView::rebuildPlayerCondition_ = PTHREAD_COND_INITIALIZER;
25 
VideoView()26 VideoView::VideoView()
27     : panelView_(nullptr),
28       videoPlayer_(nullptr),
29       surfaceView_(nullptr),
30       playEventListener_(nullptr),
31       playStateCallback_(nullptr),
32       preparedSyncCallBackFunc_(UNDEFINED),
33       startSyncCallBackFunc_(UNDEFINED),
34       pauseSyncCallBackFunc_(UNDEFINED),
35       timeUpdateSyncCallBackFunc_(UNDEFINED),
36       videoSrc_(nullptr),
37       mutedFlag_(false),
38       autoPlayFlag_(false) {}
39 
CreateVideoView()40 bool VideoView::CreateVideoView()
41 {
42     videoPlayer_ = new Media::Player();
43     surfaceView_ = new UISurfaceView();
44     panelView_ = new PanelView();
45     if ((videoPlayer_ == nullptr) || (surfaceView_ == nullptr) ||
46         (panelView_ == nullptr) || !(panelView_->InitView())) {
47         return false;
48     }
49     Add(surfaceView_);
50     Add(panelView_);
51     SetLayoutDirection(LAYOUT_VER);
52     SetMajorAxisAlign(OHOS::ALIGN_END);
53     LayoutChildren();
54     return true;
55 }
SetVideoView()56 void VideoView::SetVideoView()
57 {
58     SetSurfaceView();
59     SetVideoPanel();
60 }
61 
SetSurfaceView()62 void VideoView::SetSurfaceView()
63 {
64     if (surfaceView_ == nullptr) {
65         HILOG_DEBUG(HILOG_MODULE_ACE, "native VideoView get surfaceView is nullptr");
66         return;
67     }
68     surfaceView_->SetPosition(0, 0);
69     surfaceView_->SetWidth(GetWidth());
70     surfaceView_->SetHeight(GetHeight() - PanelView::DEFAULT_PANEL_HEIGHT);
71 }
72 
PanelRefreshLayout()73 void VideoView::PanelRefreshLayout()
74 {
75     if (panelView_ == nullptr) {
76         return;
77     }
78     panelView_->LayoutChildren();
79     panelView_->Invalidate();
80     LayoutChildren();
81 }
82 
~VideoView()83 VideoView::~VideoView()
84 {
85     if (!DestroyPlayer()) {
86         HILOG_ERROR(HILOG_MODULE_ACE, "videoView destroy player failed");
87     }
88     ACE_DELETE(panelView_);
89     ACE_DELETE(surfaceView_);
90     jerry_release_value(preparedSyncCallBackFunc_);
91     jerry_release_value(startSyncCallBackFunc_);
92     jerry_release_value(pauseSyncCallBackFunc_);
93     jerry_release_value(timeUpdateSyncCallBackFunc_);
94     (void)pthread_mutex_destroy(&rebuildPlayerlock_);
95     (void)pthread_cond_destroy(&rebuildPlayerCondition_);
96 }
97 
GetVideoPlayer() const98 const Media::Player *VideoView::GetVideoPlayer() const
99 {
100     return videoPlayer_;
101 }
102 
103 /* player info */
Prepare()104 int32_t VideoView::Prepare()
105 {
106     if ((videoPlayer_ != nullptr) && (surfaceView_ != nullptr) && (videoPlayer_->Prepare() == 0)) {
107         Surface *surface = surfaceView_->GetSurface();
108         if (surface == nullptr) {
109             HILOG_DEBUG(HILOG_MODULE_ACE, "surfaceView get surface failed");
110             return -1;
111         }
112         videoPlayer_->SetVideoSurface(surface);
113         /* call js prepare sync callback function */
114         if (jerry_value_is_function(preparedSyncCallBackFunc_)) {
115             CallJSFunctionAutoRelease(preparedSyncCallBackFunc_, UNDEFINED, nullptr, 0);
116         }
117         HILOG_DEBUG(HILOG_MODULE_ACE, "native VideoView prepare");
118         return 0;
119     }
120     return -1;
121 }
122 
Play()123 int32_t VideoView::Play()
124 {
125     if ((videoPlayer_ != nullptr) && (videoPlayer_->Play() == 0)) {
126         /* call js start play sync callback function */
127         if (jerry_value_is_function(startSyncCallBackFunc_)) {
128             CallJSFunctionAutoRelease(startSyncCallBackFunc_, UNDEFINED, nullptr, 0);
129         }
130         UpdatePlayState(VideoPlayState::STATE_PLAYING);
131         HILOG_DEBUG(HILOG_MODULE_ACE, "native VideoView play");
132         return 0;
133     }
134     return -1;
135 }
136 
Pause()137 int32_t VideoView::Pause()
138 {
139     if ((videoPlayer_ != nullptr) && (videoPlayer_->Pause() == 0)) {
140         /* call js pause play sync callback function */
141         if (jerry_value_is_function(pauseSyncCallBackFunc_)) {
142             CallJSFunctionAutoRelease(pauseSyncCallBackFunc_, UNDEFINED, nullptr, 0);
143         }
144         UpdatePlayState(VideoPlayState::STATE_PAUSED);
145         HILOG_DEBUG(HILOG_MODULE_ACE, "native VideoView Pause");
146         return 0;
147     }
148     return -1;
149 }
150 
Stop()151 int32_t VideoView::Stop()
152 {
153     if ((videoPlayer_ != nullptr) && (videoPlayer_->Stop() == 0)) {
154         HILOG_DEBUG(HILOG_MODULE_ACE, "native VideoView Stop");
155         UpdatePlayState(VideoPlayState::STATE_STOPPED);
156         return 0;
157     }
158     return -1;
159 }
160 
WaitRebuildPlayerFinish()161 bool VideoView::WaitRebuildPlayerFinish()
162 {
163     int retCode = pthread_mutex_lock(&rebuildPlayerlock_);
164     if (retCode != 0) {
165         HILOG_ERROR(HILOG_MODULE_ACE, "WaitRebuildPlayerFinish mutex lock failed: %{public}d", retCode);
166         return false;
167     }
168     PrepareRebuildPlayerThread();
169     (void)pthread_cond_wait(&rebuildPlayerCondition_, &rebuildPlayerlock_);
170     retCode = pthread_mutex_unlock(&rebuildPlayerlock_);
171     if (retCode != 0) {
172         HILOG_ERROR(HILOG_MODULE_ACE, "WaitRebuildPlayerFinish mutex unlock failed: %{public}d", retCode);
173         return false;
174     }
175     return true;
176 }
177 
PrepareRebuildPlayerThread()178 void VideoView::PrepareRebuildPlayerThread()
179 {
180     pthread_t threadId;
181     pthread_attr_t attr;
182     pthread_attr_init(&attr);
183     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
184     int retCode = pthread_create(&threadId, &attr, VideoView::RebuildPlayerHandler, this);
185     if (retCode != 0) {
186         HILOG_ERROR(HILOG_MODULE_ACE, "fork play complete thread failed, %{public}d", retCode);
187         return;
188     }
189 }
190 
RebuildPlayerHandler(void * arg)191 void *VideoView::RebuildPlayerHandler(void *arg)
192 {
193     VideoView *videoView = static_cast<VideoView *>(arg);
194     HILOG_DEBUG(HILOG_MODULE_ACE, "rebuild player handler");
195     prctl(PR_SET_NAME, "VideoPlayerRebuildHandler");
196     if (!videoView->ResetPlayer()) {
197         HILOG_ERROR(HILOG_MODULE_ACE, "videoView reset player failed");
198     }
199     return nullptr;
200 }
201 
ResetPlayer()202 bool VideoView::ResetPlayer()
203 {
204     (void)pthread_mutex_lock(&rebuildPlayerlock_);
205     if (!DestroyPlayer()) {
206         HILOG_ERROR(HILOG_MODULE_ACE, "videoView destroy player failed");
207     }
208     if (!CreatePlayer()) {
209         HILOG_ERROR(HILOG_MODULE_ACE, "videoView create player failed");
210     }
211     (void)pthread_mutex_unlock(&rebuildPlayerlock_);
212     int retCode = pthread_cond_signal(&rebuildPlayerCondition_);
213     if (retCode != 0) {
214         HILOG_ERROR(HILOG_MODULE_ACE, "mutex unlock failed: %{public}d", retCode);
215         return false;
216     }
217     return true;
218 }
219 
DestroyPlayer()220 bool VideoView::DestroyPlayer()
221 {
222     if (videoPlayer_ == nullptr) {
223         return false;
224     }
225     if (videoPlayer_->IsPlaying() && videoPlayer_->Stop() != 0) {
226         HILOG_ERROR(HILOG_MODULE_ACE, "DestroyPlayer fail to stop videoplayer");
227     }
228     if (videoPlayer_->Reset() != 0) {
229         HILOG_ERROR(HILOG_MODULE_ACE, "DestroyPlayer fail to reset videoplayer");
230     }
231     if (videoPlayer_->Release() != 0) {
232         HILOG_ERROR(HILOG_MODULE_ACE, "DestroyPlayer fail to release videoplayer");
233     }
234     delete videoPlayer_;
235     videoPlayer_ = nullptr;
236     HILOG_DEBUG(HILOG_MODULE_ACE, "delete player");
237     return true;
238 }
239 
CreatePlayer()240 bool VideoView::CreatePlayer()
241 {
242     videoPlayer_ = new Media::Player();
243     if (videoPlayer_ == nullptr) {
244         HILOG_ERROR(HILOG_MODULE_ACE, "fail to create videoplayer");
245         return false;
246     }
247     videoPlayer_->SetPlayerCallback(playEventListener_);
248     const float volumeValue = mutedFlag_ ? 0 : PanelView::DEFAULT_VOLUME_VALUE;
249     SetVolume(volumeValue);
250     if (SetSource(videoSrc_) != 0) {
251         HILOG_ERROR(HILOG_MODULE_ACE, "video view SetSource failed");
252         return false;
253     }
254     if (Prepare() != 0) {
255         HILOG_ERROR(HILOG_MODULE_ACE, "video view prepare failed");
256         return false;
257     }
258     HILOG_DEBUG(HILOG_MODULE_ACE, "reCreate player success");
259     return true;
260 }
261 
UpdatePlayState(VideoPlayState newState)262 void VideoView::UpdatePlayState(VideoPlayState newState)
263 {
264     if (playStateCallback_ != nullptr) {
265         playStateCallback_->OnPlayStateChanged(newState);
266     }
267 }
268 
SeekTo(int64_t currentTime)269 int32_t VideoView::SeekTo(int64_t currentTime)
270 {
271     if ((videoPlayer_ != nullptr) &&
272         videoPlayer_->Rewind(currentTime, Media::PlayerSeekMode::PLAYER_SEEK_CLOSEST_SYNC) == 0) {
273         HILOG_DEBUG(HILOG_MODULE_ACE, "native VideoView SeekTo %{public}lld", currentTime);
274         return 0;
275     }
276     return -1;
277 }
278 
SetVolume(float volumeValue)279 int32_t VideoView::SetVolume(float volumeValue)
280 {
281     if ((videoPlayer_ != nullptr) && videoPlayer_->SetVolume(volumeValue, volumeValue) == 0) {
282         HILOG_DEBUG(HILOG_MODULE_ACE, "native VideoView setVolume %{public}f", volumeValue);
283         return 0;
284     }
285     return -1;
286 }
287 
GetCurrentPosition(int64_t & currentPosition)288 int32_t VideoView::GetCurrentPosition(int64_t &currentPosition)
289 {
290     if ((videoPlayer_ != nullptr) && videoPlayer_->GetCurrentTime(currentPosition) == 0) {
291         HILOG_DEBUG(HILOG_MODULE_ACE, "native VideoView GetCurrentTime %{public}lld", currentPosition);
292         return 0;
293     }
294     return -1;
295 }
296 
297 /* get video total time */
GetDuration(int64_t & duration)298 int32_t VideoView::GetDuration(int64_t &duration)
299 {
300     if ((videoPlayer_ != nullptr) && videoPlayer_->GetDuration(duration) == 0) {
301         HILOG_DEBUG(HILOG_MODULE_ACE, "native VideoView GetDuration %{public}lld", duration);
302         return 0;
303     }
304     return -1;
305 }
306 
SetSource(const char * const videoSourceUrl)307 int32_t VideoView::SetSource(const char * const videoSourceUrl)
308 {
309     if ((videoPlayer_ == nullptr) || (videoSourceUrl == nullptr)) {
310         HILOG_DEBUG(HILOG_MODULE_ACE, "native VideoView videoSourceUrl is nullptr");
311         return -1;
312     }
313     std::string uri(videoSourceUrl);
314     std::map<std::string, std::string> header;
315     Media::Source source(uri, header);
316     if (videoPlayer_->SetSource(source) == 0) {
317         HILOG_DEBUG(HILOG_MODULE_ACE, "native VideoView set source success, %{public}s", videoSourceUrl);
318         return 0;
319     }
320     return -1;
321 }
322 
323 /* panel info */
HideVideoPanel()324 bool VideoView::HideVideoPanel()
325 {
326     if (panelView_ == nullptr) {
327         return false;
328     }
329     UILabel* curPlayTime = const_cast<UILabel *>(panelView_->GetCurTimeText());
330     if (curPlayTime != nullptr) {
331         curPlayTime->SetVisible(false);
332     }
333     UIImageView* playImage = const_cast<UIImageView *>(panelView_->GetVideoPlayImage());
334     if (playImage != nullptr) {
335         playImage->SetVisible(false);
336     }
337     UISlider* slider = const_cast<UISlider *>(panelView_->GetVideoSlider());
338     if (slider != nullptr) {
339         slider->SetVisible(false);
340     }
341     UILabel* totalTimeLabel = const_cast<UILabel *>(panelView_->GetVideoTotalTimeText());
342     if (totalTimeLabel != nullptr) {
343         totalTimeLabel->SetVisible(false);
344     }
345     UIImageView* mutedImage = const_cast<UIImageView *>(panelView_->GetVideoMutedImage());
346     if (mutedImage != nullptr) {
347         mutedImage->SetVisible(false);
348     }
349 
350     panelView_->SetVisible(false);
351     PanelRefreshLayout();
352     HILOG_DEBUG(HILOG_MODULE_ACE, "native VideoView hide control panel");
353     return true;
354 }
355 
SetVideoPanel()356 void VideoView::SetVideoPanel()
357 {
358     if (panelView_ == nullptr) {
359         HILOG_DEBUG(HILOG_MODULE_ACE, "native VideoView get panel view is nullptr");
360         return;
361     }
362     const int16_t panelViewWidth = GetWidth();
363     const int16_t panelViewHeight = PanelView::DEFAULT_PANEL_HEIGHT;
364     const int16_t imageWidth = 48;
365     const int16_t imageHeight = 48;
366     /* set panelView layout children from left to right in one row */
367     (reinterpret_cast<FlexLayout *>(panelView_))->SetLayoutDirection(LAYOUT_HOR);
368     SetMajorAxisAlign(OHOS::ALIGN_CENTER);
369     panelView_->SetWidth(panelViewWidth);
370     panelView_->SetHeight(panelViewHeight);
371     /* set video current play time label in video panel */
372 
373     UILabel* curTimeLabel = const_cast<UILabel *>(panelView_->GetCurTimeText());
374     if (curTimeLabel != nullptr) {
375         panelView_->SetTextInPanel(curTimeLabel);
376     }
377     UIImageView* playImage = const_cast<UIImageView *>(panelView_->GetVideoPlayImage());
378     if (playImage != nullptr) {
379         panelView_->SetImageInPanel(playImage, imageWidth, imageHeight, GetVideoPlayImageOnInfo());
380     }
381     /* set video total time label in video panel */
382     UILabel* totalTimeLabel = const_cast<UILabel *>(panelView_->GetVideoTotalTimeText());
383     if (totalTimeLabel != nullptr) {
384         panelView_->SetTextInPanel(totalTimeLabel);
385     }
386     /* set muted image in video panel */
387     UIImageView* mutedImage = const_cast<UIImageView *>(panelView_->GetVideoMutedImage());
388     if (mutedImage != nullptr) {
389         panelView_->SetImageInPanel(mutedImage, imageWidth, imageHeight, GetVideoMutedImageOffInfo());
390     }
391     /* video slider set last in video panel, because slider width depend others */
392     panelView_->SetVideoSlider();
393 }
394 
UpdatePanelProgress()395 void VideoView::UpdatePanelProgress()
396 {
397     // update current play position time
398     UpdatePanelTimeText(true);
399 }
400 
SetCurrentPlayTimeText(int32_t inSeconds)401 void VideoView::SetCurrentPlayTimeText(int32_t inSeconds)
402 {
403     if (panelView_ == nullptr || inSeconds <= 0 || inSeconds >= (PanelView::MAX_HOURS * PanelView::SECONDS_PER_HOUR)) {
404         HILOG_ERROR(HILOG_MODULE_ACE, "input duration inSeconds time exception");
405         return;
406     }
407     UILabel *curTimeLabel = const_cast<UILabel *>(panelView_->GetCurTimeText());
408     if (curTimeLabel == nullptr) {
409         return;
410     }
411     const uint8_t timeStrLength = 16;
412     char timeStr[timeStrLength] = {0};
413     FormatTime(inSeconds, timeStr, timeStrLength);
414     curTimeLabel->SetText(timeStr);
415     curTimeLabel->Invalidate();
416 }
417 
UpdatePanelTimeText(bool currentTime)418 void VideoView::UpdatePanelTimeText(bool currentTime)
419 {
420     if (panelView_ == nullptr) {
421         return;
422     }
423     UILabel *timeLabel = currentTime ?
424         const_cast<UILabel *>(panelView_->GetCurTimeText()) :
425         const_cast<UILabel *>(panelView_->GetVideoTotalTimeText());
426     if (timeLabel == nullptr) {
427         return;
428     }
429 
430     int64_t duration = 0;
431     if (currentTime) {
432         if (GetCurrentPosition(duration) != 0) {
433             HILOG_ERROR(HILOG_MODULE_ACE, "UpdatePanelTimeText GetCurrentPosition failed");
434             return;
435         }
436     } else {
437         if (GetDuration(duration) != 0) {
438             HILOG_ERROR(HILOG_MODULE_ACE, "UpdatePanelTimeText GetDuration failed");
439             return;
440         }
441     }
442     if (duration < 0 || duration >= PanelView::MAX_SHOW_TIME) {
443         HILOG_ERROR(HILOG_MODULE_ACE, "input duration exception");
444         duration = 0;
445     }
446     const uint8_t timeStrLength = 16;
447     char timeStr[timeStrLength] = {0};
448     FormatTime((duration / PanelView::MILLIONS_PER_SECOND), timeStr, timeStrLength);
449     HILOG_DEBUG(HILOG_MODULE_ACE, "--- set time =%{public}s -----", timeStr);
450     timeLabel->SetText(timeStr);
451     timeLabel->Invalidate();
452     PanelRefreshLayout();
453     // update video slider status when play changed
454     UISlider* videoSlider = const_cast<UISlider *>(panelView_->GetVideoSlider());
455     if (videoSlider != nullptr && currentTime) {
456         videoSlider->SetValue(duration / PanelView::MILLIONS_PER_SECOND);
457         videoSlider->Invalidate();
458         // call js timeupdate callback function
459         if (!jerry_value_is_function(timeUpdateSyncCallBackFunc_)) {
460             return;
461         }
462         CallJSFunctionWithOnePara(timeUpdateSyncCallBackFunc_, duration / PanelView::MILLIONS_PER_SECOND);
463     }
464 }
465 
UpdateMutedStatus()466 void VideoView::UpdateMutedStatus()
467 {
468     if (panelView_ == nullptr) {
469         HILOG_ERROR(HILOG_MODULE_ACE, "video view get mpanelView_ is nullptr");
470         return;
471     }
472     UIImageView *mutedImageView = const_cast<UIImageView *>(panelView_->GetVideoMutedImage());
473     if (mutedImageView == nullptr) {
474         HILOG_ERROR(HILOG_MODULE_ACE, "video view get muted image view is nullptr");
475         return;
476     }
477     float volumeValue = mutedFlag_ ? 0 : PanelView::DEFAULT_VOLUME_VALUE;
478     const ImageInfo* mutedImageSrc = mutedFlag_ ? GetVideoMutedImageOffInfo() : GetVideoMutedImageOnInfo();
479     if (SetVolume(volumeValue) != 0) {
480         HILOG_ERROR(HILOG_MODULE_ACE, "video set volume failed");
481         return;
482     }
483     mutedImageView->SetSrc(mutedImageSrc);
484 }
485 
FormatTime(uint32_t inSeconds,char * outBuffer,uint8_t bufferLength)486 void VideoView::FormatTime(uint32_t inSeconds, char *outBuffer, uint8_t bufferLength)
487 {
488     if (inSeconds >= (PanelView::MAX_HOURS * PanelView::SECONDS_PER_HOUR)) {
489         HILOG_ERROR(HILOG_MODULE_ACE, "input duration time exception");
490         return;
491     }
492     const uint8_t minBufferLength = 6;  // xx:xx
493     const uint8_t minBufferLength2 = 9; // xx:xx:xx
494     if (outBuffer == nullptr) {
495         return;
496     }
497     uint8_t hours = inSeconds / PanelView::SECONDS_PER_HOUR;
498     uint32_t remainSeconds = inSeconds % PanelView::SECONDS_PER_HOUR;
499     uint8_t minutes = remainSeconds / PanelView::SECONDS_PER_MINUTE;
500     uint8_t seconds = remainSeconds % PanelView::SECONDS_PER_MINUTE;
501     if (hours >= PanelView::MAX_HOURS) {
502         HILOG_ERROR(HILOG_MODULE_ACE, "input video length more than the max length");
503         return;
504     }
505     if (hours == 0) {
506         if (sprintf_s(outBuffer, bufferLength, "%02d:%02d", minutes, seconds) < 0) {
507             HILOG_ERROR(HILOG_MODULE_ACE, "xx:xx transfer error");
508             return;
509         }
510         outBuffer[minBufferLength - 1] = '\0';
511     } else {
512         if (sprintf_s(outBuffer, bufferLength, "%02d:%02d:%02d", hours, minutes, seconds) < 0) {
513             HILOG_ERROR(HILOG_MODULE_ACE, "xx:xx:xx transfer error");
514             return;
515         }
516         outBuffer[minBufferLength2 - 1] = '\0';
517     }
518 }
519 
CallJSFunctionWithOnePara(const jerry_value_t & callBackFunc,const int64_t inputValue)520 void VideoView::CallJSFunctionWithOnePara(const jerry_value_t &callBackFunc, const int64_t inputValue)
521 {
522     if (!jerry_value_is_function(callBackFunc)) {
523         return;
524     }
525     jerry_value_t value = jerry_create_number(inputValue);
526     jerry_value_t args[1];
527     args[0] = jerry_create_object();
528     jerry_value_t result = jerryx_set_property_str(args[0], "currenttime", value);
529     if (!jerry_value_is_error(result)) {
530         CallJSFunctionAutoRelease(callBackFunc, jerry_create_undefined(), args, 1);
531     }
532     ReleaseJerryValue(result, args[0], value, VA_ARG_END_FLAG);
533 }
534 } // namespace ACELite
535 } // namespace OHOS
536 
537 #endif // FEATURE_COMPONENT_VIDEO
538