diff --git a/lib/src/controllers/pod_base_controller.dart b/lib/src/controllers/pod_base_controller.dart index 4edc604c..4b2a0afb 100644 --- a/lib/src/controllers/pod_base_controller.dart +++ b/lib/src/controllers/pod_base_controller.dart @@ -34,6 +34,9 @@ class _PodBaseController extends GetxController { int doubleTapForwardSeconds = 10; String? playingVideoUrl; + /// The video view type (textureView or platformView) + VideoViewType _videoViewType = VideoViewType.textureView; + late BuildContext mainContext; late BuildContext fullScreenContext; @@ -44,7 +47,7 @@ class _PodBaseController extends GetxController { await _videoCtr!.initialize(); } if (_videoCtr!.value.isInitialized) { - // _listneToVideoState(); + _listenToVideoState(); _listneToVideoPosition(); _listneToVolume(); if (kIsWeb && autoPlay && isMute && !_isWebAutoPlayDone) _webAutoPlay(); @@ -78,6 +81,34 @@ class _PodBaseController extends GetxController { // : PodVideoState.paused, // ); // } + void _listenToVideoState() { + // Handle the uninitialized state + if (!_videoCtr!.value.isInitialized) { + podVideoStateChanger(PodVideoState.loading); + return; + } + + // Handle the buffering state + if (!_videoCtr!.value.isPlaying && _videoCtr!.value.isBuffering) { + podVideoStateChanger(PodVideoState.loading); + return; + } + + // Handle the playing and paused states + if (_videoCtr!.value.isPlaying) { + podVideoStateChanger(PodVideoState.playing); + } else { + // Ensure that the video is really paused + // It was observed that sometimes the "isPlaying" value comes as FALSE then immediately comes as TRUE + // If we update the Pod Video state to paused right away, the player UI enters in a play/pause loop + Future.delayed(const Duration(milliseconds: 200)).then((value) { + // If after a small delay the video is still paused, then we update the Pod Video state + if (!_videoCtr!.value.isPlaying) { + podVideoStateChanger(PodVideoState.paused); + } + }); + } + } ///updates state with id `_podVideoState` void podVideoStateChanger(PodVideoState? val, {bool updateUi = true}) { @@ -96,8 +127,7 @@ class _PodBaseController extends GetxController { update(['video-progress']); update(['update-all']); } else { - if (_videoPosition.inSeconds != - (_videoCtr?.value.position ?? Duration.zero).inSeconds) { + if (_videoPosition.inSeconds != (_videoCtr?.value.position ?? Duration.zero).inSeconds) { _videoPosition = _videoCtr?.value.position ?? Duration.zero; update(['video-progress']); update(['update-all']); diff --git a/lib/src/controllers/pod_getx_video_controller.dart b/lib/src/controllers/pod_getx_video_controller.dart index 7a46d30d..258c9ae9 100644 --- a/lib/src/controllers/pod_getx_video_controller.dart +++ b/lib/src/controllers/pod_getx_video_controller.dart @@ -45,6 +45,7 @@ class PodGetXVideoController extends _PodGesturesController { }) { this.playVideoFrom = playVideoFrom; _videoPlayerType = playVideoFrom.playerType; + _videoViewType = playVideoFrom.viewType ?? VideoViewType.textureView; podPlayerConfig = playerConfig; autoPlay = playerConfig.autoPlay; isLooping = playerConfig.isLooping; @@ -91,6 +92,7 @@ class PodGetXVideoController extends _PodGesturesController { formatHint: playVideoFrom.formatHint, videoPlayerOptions: playVideoFrom.videoPlayerOptions, httpHeaders: playVideoFrom.httpHeaders, + viewType: playVideoFrom.viewType ?? VideoViewType.textureView, ); playingVideoUrl = playVideoFrom.dataSource; break; @@ -107,6 +109,7 @@ class PodGetXVideoController extends _PodGesturesController { formatHint: playVideoFrom.formatHint, videoPlayerOptions: playVideoFrom.videoPlayerOptions, httpHeaders: playVideoFrom.httpHeaders, + viewType: playVideoFrom.viewType ?? VideoViewType.textureView, ); playingVideoUrl = url; @@ -128,6 +131,7 @@ class PodGetXVideoController extends _PodGesturesController { formatHint: playVideoFrom.formatHint, videoPlayerOptions: playVideoFrom.videoPlayerOptions, httpHeaders: playVideoFrom.httpHeaders, + viewType: playVideoFrom.viewType ?? VideoViewType.textureView, ); playingVideoUrl = url; @@ -148,6 +152,7 @@ class PodGetXVideoController extends _PodGesturesController { formatHint: playVideoFrom.formatHint, videoPlayerOptions: playVideoFrom.videoPlayerOptions, httpHeaders: playVideoFrom.httpHeaders, + viewType: playVideoFrom.viewType ?? VideoViewType.textureView, ); playingVideoUrl = url; @@ -160,6 +165,7 @@ class PodGetXVideoController extends _PodGesturesController { closedCaptionFile: playVideoFrom.closedCaptionFile, package: playVideoFrom.package, videoPlayerOptions: playVideoFrom.videoPlayerOptions, + viewType: playVideoFrom.viewType ?? VideoViewType.textureView, ); playingVideoUrl = playVideoFrom.dataSource; @@ -174,6 +180,7 @@ class PodGetXVideoController extends _PodGesturesController { playVideoFrom.file!, closedCaptionFile: playVideoFrom.closedCaptionFile, videoPlayerOptions: playVideoFrom.videoPlayerOptions, + viewType: playVideoFrom.viewType ?? VideoViewType.textureView, ); break; @@ -193,6 +200,7 @@ class PodGetXVideoController extends _PodGesturesController { formatHint: playVideoFrom.formatHint, videoPlayerOptions: playVideoFrom.videoPlayerOptions, httpHeaders: playVideoFrom.httpHeaders, + viewType: playVideoFrom.viewType ?? VideoViewType.textureView, ); playingVideoUrl = url; @@ -207,6 +215,11 @@ class PodGetXVideoController extends _PodGesturesController { required String tag, }) { if (kIsWeb) { + // If keyboard shortcuts are disabled, don't handle any keyboard events + if (podPlayerConfig.disableKeyboardShortcuts) { + return; + } + if (event.isKeyPressed(LogicalKeyboardKey.space)) { togglePlayPauseVideo(); return; diff --git a/lib/src/controllers/pod_player_controller.dart b/lib/src/controllers/pod_player_controller.dart index 229ce884..8005ce99 100644 --- a/lib/src/controllers/pod_player_controller.dart +++ b/lib/src/controllers/pod_player_controller.dart @@ -185,8 +185,17 @@ class PodPlayerController { ); //Change double tap duration - void setDoubeTapForwarDuration(int seconds) => - _ctr.doubleTapForwardSeconds = seconds; + void setDoubeTapForwarDuration(int seconds) => _ctr.doubleTapForwardSeconds = seconds; + + // Change the video playback speed + Future setVideoPlayBack(double speed) async { + await _ctr.setVideoPlayBackSpeed(speed); + } + + // Change the video looping status + Future setVideoLooping(bool looping) async { + await _ctr.setLooping(looping); + } ///Jumps to specific position of the video Future videoSeekTo(Duration moment) async { diff --git a/lib/src/controllers/pod_video_controller.dart b/lib/src/controllers/pod_video_controller.dart index 0a0d4ea6..e76b750c 100644 --- a/lib/src/controllers/pod_video_controller.dart +++ b/lib/src/controllers/pod_video_controller.dart @@ -147,6 +147,10 @@ class _PodVideoController extends _PodUiController { _videoCtr?.setPlaybackSpeed(pickedSpeed); } + Future setVideoPlayBackSpeed(double speed) async { + await _videoCtr?.setPlaybackSpeed(speed); + } + Future setLooping(bool isLooped) async { isLooping = isLooped; await _videoCtr?.setLooping(isLooping); @@ -234,8 +238,7 @@ class _PodVideoController extends _PodUiController { tag: tag, ), reverseTransitionDuration: const Duration(milliseconds: 400), - transitionsBuilder: (context, animation, secondaryAnimation, child) => - FadeTransition( + transitionsBuilder: (context, animation, secondaryAnimation, child) => FadeTransition( opacity: animation, child: child, ), @@ -248,10 +251,7 @@ class _PodVideoController extends _PodUiController { String calculateVideoDuration(Duration duration) { final totalHour = duration.inHours == 0 ? '' : '${duration.inHours}:'; final totalMinute = duration.toString().split(':')[1]; - final totalSeconds = (duration - Duration(minutes: duration.inMinutes)) - .inSeconds - .toString() - .padLeft(2, '0'); + final totalSeconds = (duration - Duration(minutes: duration.inMinutes)).inSeconds.toString().padLeft(2, '0'); final String videoLength = '$totalHour$totalMinute:$totalSeconds'; return videoLength; } diff --git a/lib/src/controllers/pod_video_quality_controller.dart b/lib/src/controllers/pod_video_quality_controller.dart index f841e018..93327541 100644 --- a/lib/src/controllers/pod_video_quality_controller.dart +++ b/lib/src/controllers/pod_video_quality_controller.dart @@ -127,7 +127,10 @@ class _PodVideoQualityController extends _PodVideoController { podVideoStateChanger(PodVideoState.paused); podVideoStateChanger(PodVideoState.loading); playingVideoUrl = _videoQualityUrl; - _videoCtr = VideoPlayerController.networkUrl(Uri.parse(_videoQualityUrl)); + _videoCtr = VideoPlayerController.networkUrl( + Uri.parse(_videoQualityUrl), + viewType: _videoViewType, + ); await _videoCtr?.initialize(); _videoDuration = _videoCtr?.value.duration ?? Duration.zero; _videoCtr?.addListener(videoListner); diff --git a/lib/src/models/play_video_from.dart b/lib/src/models/play_video_from.dart index d3927bbb..76413a4e 100644 --- a/lib/src/models/play_video_from.dart +++ b/lib/src/models/play_video_from.dart @@ -14,6 +14,7 @@ class PlayVideoFrom { final VideoPlayerOptions? videoPlayerOptions; final Map httpHeaders; final bool live; + final VideoViewType? viewType; const PlayVideoFrom._({ required this.playerType, @@ -27,6 +28,7 @@ class PlayVideoFrom { this.closedCaptionFile, this.videoPlayerOptions, this.httpHeaders = const {}, + this.viewType, }); factory PlayVideoFrom.network( @@ -35,6 +37,7 @@ class PlayVideoFrom { Future? closedCaptionFile, VideoPlayerOptions? videoPlayerOptions, Map httpHeaders = const {}, + VideoViewType? viewType, }) { return PlayVideoFrom._( playerType: PodVideoPlayerType.network, @@ -43,6 +46,7 @@ class PlayVideoFrom { closedCaptionFile: closedCaptionFile, videoPlayerOptions: videoPlayerOptions, httpHeaders: httpHeaders, + viewType: viewType, ); } @@ -51,6 +55,7 @@ class PlayVideoFrom { String? package, Future? closedCaptionFile, VideoPlayerOptions? videoPlayerOptions, + VideoViewType? viewType, }) { return PlayVideoFrom._( playerType: PodVideoPlayerType.asset, @@ -58,6 +63,7 @@ class PlayVideoFrom { package: package, closedCaptionFile: closedCaptionFile, videoPlayerOptions: videoPlayerOptions, + viewType: viewType, ); } @@ -67,12 +73,14 @@ class PlayVideoFrom { File file, { Future? closedCaptionFile, VideoPlayerOptions? videoPlayerOptions, + VideoViewType? viewType, }) { return PlayVideoFrom._( file: file, playerType: PodVideoPlayerType.file, closedCaptionFile: closedCaptionFile, videoPlayerOptions: videoPlayerOptions, + viewType: viewType, ); } @@ -83,6 +91,7 @@ class PlayVideoFrom { Future? closedCaptionFile, VideoPlayerOptions? videoPlayerOptions, Map httpHeaders = const {}, + VideoViewType? viewType, }) { return PlayVideoFrom._( playerType: PodVideoPlayerType.vimeo, @@ -92,6 +101,7 @@ class PlayVideoFrom { closedCaptionFile: closedCaptionFile, videoPlayerOptions: videoPlayerOptions, httpHeaders: httpHeaders, + viewType: viewType, ); } @@ -101,6 +111,7 @@ class PlayVideoFrom { Future? closedCaptionFile, VideoPlayerOptions? videoPlayerOptions, Map httpHeaders = const {}, + VideoViewType? viewType, }) { return PlayVideoFrom._( playerType: PodVideoPlayerType.vimeoPrivateVideos, @@ -109,6 +120,7 @@ class PlayVideoFrom { closedCaptionFile: closedCaptionFile, videoPlayerOptions: videoPlayerOptions, httpHeaders: httpHeaders, + viewType: viewType, ); } factory PlayVideoFrom.youtube( @@ -118,6 +130,7 @@ class PlayVideoFrom { Future? closedCaptionFile, VideoPlayerOptions? videoPlayerOptions, Map httpHeaders = const {}, + VideoViewType? viewType, }) { return PlayVideoFrom._( live: live, @@ -127,6 +140,7 @@ class PlayVideoFrom { closedCaptionFile: closedCaptionFile, videoPlayerOptions: videoPlayerOptions, httpHeaders: httpHeaders, + viewType: viewType, ); } factory PlayVideoFrom.networkQualityUrls({ @@ -135,6 +149,7 @@ class PlayVideoFrom { Future? closedCaptionFile, VideoPlayerOptions? videoPlayerOptions, Map httpHeaders = const {}, + VideoViewType? viewType, }) { return PlayVideoFrom._( playerType: PodVideoPlayerType.networkQualityUrls, @@ -143,6 +158,7 @@ class PlayVideoFrom { closedCaptionFile: closedCaptionFile, videoPlayerOptions: videoPlayerOptions, httpHeaders: httpHeaders, + viewType: viewType, ); } } diff --git a/lib/src/models/pod_player_config.dart b/lib/src/models/pod_player_config.dart index f44c7918..077f6305 100644 --- a/lib/src/models/pod_player_config.dart +++ b/lib/src/models/pod_player_config.dart @@ -4,6 +4,11 @@ class PodPlayerConfig { final bool forcedVideoFocus; final bool wakelockEnabled; + /// Disable keyboard shortcuts on web (Space, M, F, Esc, Arrow keys). + /// When true, keyboard shortcuts are disabled to prevent interference with form inputs. + /// Default value is true (shortcuts disabled). + final bool disableKeyboardShortcuts; + /// Initial video quality priority. The first available option will be used, /// from start to the end of this list. If all options informed are not /// available or if nothing is provided, 360p is used. @@ -16,6 +21,7 @@ class PodPlayerConfig { this.isLooping = false, this.forcedVideoFocus = false, this.wakelockEnabled = true, + this.disableKeyboardShortcuts = true, this.videoQualityPriority = const [1080, 720, 360], }); @@ -24,6 +30,7 @@ class PodPlayerConfig { bool? isLooping, bool? forcedVideoFocus, bool? wakelockEnabled, + bool? disableKeyboardShortcuts, List? videoQualityPriority, }) { return PodPlayerConfig( @@ -31,6 +38,7 @@ class PodPlayerConfig { isLooping: isLooping ?? this.isLooping, forcedVideoFocus: forcedVideoFocus ?? this.forcedVideoFocus, wakelockEnabled: wakelockEnabled ?? this.wakelockEnabled, + disableKeyboardShortcuts: disableKeyboardShortcuts ?? this.disableKeyboardShortcuts, videoQualityPriority: videoQualityPriority ?? this.videoQualityPriority, ); }