import 'dart:async'; import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:flutter/painting.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; import 'package:media_kit/media_kit.dart'; import 'package:media_kit_video/media_kit_video.dart'; import 'package:pilipala/plugin/pl_player/models/data_source.dart'; import 'package:pilipala/utils/feed_back.dart'; import 'package:pilipala/utils/storage.dart'; import 'package:screen_brightness/screen_brightness.dart'; import 'package:universal_platform/universal_platform.dart'; import 'package:volume_controller/volume_controller.dart'; import 'package:wakelock_plus/wakelock_plus.dart'; import 'models/data_status.dart'; import 'models/play_speed.dart'; import 'models/play_status.dart'; Box videoStorage = GStrorage.video; class PlPlayerController { Player? _videoPlayerController; VideoController? _videoController; // 流事件 监听播放状态变化 StreamSubscription? _playerEventSubs; /// [playerStatus] has a [status] observable final PlPlayerStatus playerStatus = PlPlayerStatus(); /// final PlPlayerDataStatus dataStatus = PlPlayerDataStatus(); bool controlsEnabled = true; /// 响应数据 // 播放位置 final Rx _position = Rx(Duration.zero); final Rx _sliderPosition = Rx(Duration.zero); final Rx _sliderTempPosition = Rx(Duration.zero); final Rx _duration = Rx(Duration.zero); final Rx _buffered = Rx(Duration.zero); final Rx _playbackSpeed = 1.0.obs; final Rx _currentVolume = 1.0.obs; final Rx _currentBrightness = 0.0.obs; final Rx _mute = false.obs; final Rx _showControls = false.obs; final Rx _showVolumeStatus = false.obs; final Rx _showBrightnessStatus = false.obs; final Rx _doubleSpeedStatus = false.obs; final Rx _controlsLock = false.obs; final Rx _isFullScreen = false.obs; final Rx _direction = 'horizontal'.obs; Rx videoFitChanged = false.obs; final Rx _videoFit = Rx(BoxFit.fill); /// Rx _isSliderMoving = false.obs; PlaylistMode _looping = PlaylistMode.none; bool _autoPlay = false; final bool _listenersInitialized = false; Timer? _timer; Timer? _timerForSeek; Timer? _timerForVolume; Timer? _timerForShowingVolume; Timer? _timerForGettingVolume; Timer? timerForTrackingMouse; Timer? videoFitChangedTimer; // final Durations durations; List fits = [ BoxFit.contain, BoxFit.cover, BoxFit.fill, BoxFit.fitHeight, BoxFit.fitWidth, BoxFit.scaleDown ]; PreferredSizeWidget? headerControl; /// 数据加载监听 Stream get onDataStatusChanged => dataStatus.status.stream; /// 播放状态监听 Stream get onPlayerStatusChanged => playerStatus.status.stream; /// 视频时长 Rx get duration => _duration; Stream get onDurationChanged => _duration.stream; /// 视频当前播放位置 Rx get position => _position; Stream get onPositionChanged => _position.stream; /// 视频播放速度 double get playbackSpeed => _playbackSpeed.value; /// 视频缓冲 Rx get buffered => _buffered; Stream get onBufferedChanged => _buffered.stream; // 视频静音 Rx get mute => _mute; Stream get onMuteChanged => _mute.stream; /// [videoPlayerController] instace of Player Player? get videoPlayerController => _videoPlayerController; /// [videoController] instace of Player VideoController? get videoController => _videoController; Rx get isSliderMoving => _isSliderMoving; /// 进度条位置及监听 Rx get sliderPosition => _sliderPosition; Stream get onSliderPositionChanged => _sliderPosition.stream; Rx get sliderTempPosition => _sliderTempPosition; // Stream get onSliderPositionChanged => _sliderPosition.stream; /// 是否展示控制条及监听 Rx get showControls => _showControls; Stream get onShowControlsChanged => _showControls.stream; /// 音量控制条展示/隐藏 Rx get showVolumeStatus => _showVolumeStatus; Stream get onShowVolumeStatusChanged => _showVolumeStatus.stream; /// 亮度控制条展示/隐藏 Rx get showBrightnessStatus => _showBrightnessStatus; Stream get onShowBrightnessStatusChanged => _showBrightnessStatus.stream; /// 音量控制条 Rx get volume => _currentVolume; Stream get onVolumeChanged => _currentVolume.stream; /// 亮度控制条 Rx get brightness => _currentBrightness; Stream get onBrightnessChanged => _currentBrightness.stream; /// 是否循环 PlaylistMode get looping => _looping; /// 是否自动播放 bool get autoplay => _autoPlay; /// 视频比例 Rx get videoFit => _videoFit; /// 是否长按倍速 Rx get doubleSpeedStatus => _doubleSpeedStatus; Rx isBuffering = true.obs; /// 屏幕锁 为true时,关闭控制栏 Rx get controlsLock => _controlsLock; /// 全屏状态 Rx get isFullScreen => _isFullScreen; /// 全屏方向 Rx get direction => _direction; PlPlayerController({ // 直播间 传false 关闭控制栏 this.controlsEnabled = true, this.fits = const [ BoxFit.contain, BoxFit.cover, BoxFit.fill, BoxFit.fitHeight, BoxFit.fitWidth, BoxFit.scaleDown ], }) { controlsEnabled = controlsEnabled; _playerEventSubs = onPlayerStatusChanged.listen((PlayerStatus status) { if (status == PlayerStatus.playing) { WakelockPlus.enable(); } else { WakelockPlus.enable(); } }); } // 初始化资源 Future setDataSource( DataSource dataSource, { bool autoplay = true, // 默认不循环 PlaylistMode looping = PlaylistMode.none, // 初始化播放位置 Duration seekTo = Duration.zero, // 初始化播放速度 double speed = 1.0, // 硬件加速 bool enableHA = true, double? width, double? height, Duration? duration, // 方向 String? direction, // 全屏模式 }) async { try { _autoPlay = autoplay; _looping = looping; // 初始化视频时长 _duration.value = duration ?? Duration.zero; // 初始化视频倍速 _playbackSpeed.value = speed; // 初始化数据加载状态 dataStatus.status.value = DataStatus.loading; // 初始化全屏方向 _direction.value = direction ?? 'horizontal'; if (_videoPlayerController != null && _videoPlayerController!.state.playing) { await pause(notify: false); } // 配置Player 音轨、字幕等等 _videoPlayerController = await _createVideoController( dataSource, _looping, enableHA, width, height); // 获取视频时长 00:00 _duration.value = _videoPlayerController!.state.duration; // 数据加载完成 dataStatus.status.value = DataStatus.loaded; await _initializePlayer(seekTo: seekTo); // listen the video player events if (!_listenersInitialized) { startListeners(); } } catch (err) { dataStatus.status.value = DataStatus.error; print('plPlayer err: $err'); } } // 配置播放器 Future _createVideoController( DataSource dataSource, PlaylistMode looping, bool enableHA, double? width, double? height, ) async { Player player = _videoPlayerController ?? Player( configuration: const PlayerConfiguration( // 默认缓存 5M 大小 bufferSize: 5 * 1024 * 1024, ), ); var pp = player.platform as NativePlayer; // 音轨 if (dataSource.audioSource != '' && dataSource.audioSource != null) { await pp.setProperty( 'audio-files', UniversalPlatform.isWindows ? dataSource.audioSource!.replaceAll(';', '\\;') : dataSource.audioSource!.replaceAll(':', '\\:'), ); } // 字幕 if (dataSource.subFiles != '' && dataSource.subFiles != null) { await pp.setProperty( 'sub-files', UniversalPlatform.isWindows ? dataSource.subFiles!.replaceAll(';', '\\;') : dataSource.subFiles!.replaceAll(':', '\\:'), ); await pp.setProperty("subs-with-matching-audio", "no"); await pp.setProperty("sub-forced-only", "yes"); await pp.setProperty("blend-subtitles", "video"); } _videoController = _videoController ?? VideoController( player, configuration: VideoControllerConfiguration( enableHardwareAcceleration: enableHA, ), ); player.setPlaylistMode(looping); if (dataSource.type == DataSourceType.asset) { final assetUrl = dataSource.videoSource!.startsWith("asset://") ? dataSource.videoSource! : "asset://${dataSource.videoSource!}"; player.open( Media(assetUrl, httpHeaders: dataSource.httpHeaders), play: false, ); } else if (dataSource.type == DataSourceType.network) { player.open( Media(dataSource.videoSource!, httpHeaders: dataSource.httpHeaders), play: false, ); // 音轨 // player.setAudioTrack( // AudioTrack.uri(dataSource.audioSource!), // ); } else { player.open( Media(dataSource.file!.path, httpHeaders: dataSource.httpHeaders), play: false, ); } return player; } // 开始播放 Future _initializePlayer({ Duration seekTo = Duration.zero, }) async { // 跳转播放 if (seekTo != Duration.zero) { await this.seekTo(seekTo); } // 设置倍速 if (_playbackSpeed.value != 1.0) { await setPlaybackSpeed(_playbackSpeed.value); } // if (_looping) { // await setLooping(_looping); // } // 自动播放 if (_autoPlay) { await play(); } } List subscriptions = []; /// 播放事件监听 void startListeners() { subscriptions.addAll( [ videoPlayerController!.stream.playing.listen((event) { if (event) { playerStatus.status.value = PlayerStatus.playing; } else { // playerStatus.status.value = PlayerStatus.paused; } }), videoPlayerController!.stream.completed.listen((event) { if (event) { playerStatus.status.value = PlayerStatus.completed; } else { // playerStatus.status.value = PlayerStatus.playing; } }), videoPlayerController!.stream.position.listen((event) { _position.value = event; if (!isSliderMoving.value) { _sliderPosition.value = event; } }), videoPlayerController!.stream.duration.listen((event) { duration.value = event; }), videoPlayerController!.stream.buffer.listen((event) { _buffered.value = event; }), videoPlayerController!.stream.buffering.listen((event) { isBuffering.value = event; }), // videoPlayerController!.stream.volume.listen((event) { // if (!mute.value && _volumeBeforeMute != event) { // _volumeBeforeMute = event / 100; // } // }), ], ); } /// 移除事件监听 void removeListeners() { for (final s in subscriptions) { s.cancel(); } } /// 跳转至指定位置 Future seekTo(Duration position) async { // if (position >= duration.value) { // position = duration.value - const Duration(milliseconds: 100); // } if (position < Duration.zero) { position = Duration.zero; } _position.value = position; if (duration.value.inSeconds != 0) { // await _videoPlayerController!.stream.buffer.first; await _videoPlayerController?.seek(position); // if (playerStatus.stopped) { // play(); // } } else { _timerForSeek?.cancel(); _timerForSeek = Timer.periodic(const Duration(milliseconds: 200), (Timer t) async { //_timerForSeek = null; if (duration.value.inSeconds != 0) { await _videoPlayerController?.seek(position); // if (playerStatus.stopped) { // play(); // } t.cancel(); //_timerForSeek = null; } }); } } /// 设置倍速 Future setPlaybackSpeed(double speed) async { await _videoPlayerController?.setRate(speed); _playbackSpeed.value = speed; } /// 设置倍速 Future togglePlaybackSpeed() async { List allowedSpeeds = PlaySpeed.values.map((e) => e.value).toList(); int index = allowedSpeeds.indexOf(_playbackSpeed.value); if (index < allowedSpeeds.length - 1) { setPlaybackSpeed(allowedSpeeds[index + 1]); } else { setPlaybackSpeed(allowedSpeeds[0]); } } /// 播放视频 Future play({bool repeat = false, bool hideControls = true}) async { // repeat为true,将从头播放 if (repeat) { await seekTo(Duration.zero); } await _videoPlayerController?.play(); await getCurrentVolume(); await getCurrentBrightness(); playerStatus.status.value = PlayerStatus.playing; // screenManager.setOverlays(false); // 播放时自动隐藏控制条 if (hideControls) { _hideTaskControls(); } } /// 暂停播放 Future pause({bool notify = true}) async { await _videoPlayerController?.pause(); playerStatus.status.value = PlayerStatus.paused; } /// 更改播放状态 Future togglePlay() async { feedBack(); if (playerStatus.playing) { pause(); } else { play(); } } /// 隐藏控制条 void _hideTaskControls() { _timer = Timer(const Duration(milliseconds: 3000), () { if (!isSliderMoving.value) { controls = false; } _timer = null; }); } /// 调整播放时间 onChangedSlider(double v) { _sliderPosition.value = Duration(seconds: v.floor()); } void onChangedSliderStart() { _isSliderMoving.value = true; } void onUodatedSliderProgress(Duration value) { _sliderTempPosition.value = value; _sliderPosition.value = value; } void onChangedSliderEnd() { feedBack(); _isSliderMoving.value = false; _hideTaskControls(); } /// 音量 Future getCurrentVolume() async { _currentVolume.value = await VolumeController().getVolume(); } Future setVolume(double volumeNew, {bool videoPlayerVolume = false}) async { if (volumeNew < 0.0) { volumeNew = 0.0; } else if (volumeNew > 1.0) { volumeNew = 1.0; } if (volume.value == volumeNew) { return; } volume.value = volumeNew; try { VolumeController().setVolume(volumeNew, showSystemUI: false); } catch (err) { print(err); } } void volumeUpdated() { showVolumeStatus.value = true; _timerForShowingVolume?.cancel(); _timerForShowingVolume = Timer(const Duration(seconds: 1), () { showVolumeStatus.value = false; }); } /// 亮度 Future getCurrentBrightness() async { try { _currentBrightness.value = await ScreenBrightness().current; } catch (e) { throw 'Failed to get current brightness'; //return 0; } } Future setBrightness(double brightnes) async { try { brightness.value = brightnes; ScreenBrightness().setScreenBrightness(brightnes); setVideoBrightness(); } catch (e) { throw 'Failed to set brightness'; } } Future resetBrightness() async { try { await ScreenBrightness().resetScreenBrightness(); } catch (e) { throw 'Failed to reset brightness'; } } /// Toggle Change the videofit accordingly void toggleVideoFit() { videoFitChangedTimer?.cancel(); videoFitChanged.value = true; if (fits.indexOf(_videoFit.value) < fits.length - 1) { _videoFit.value = fits[fits.indexOf(_videoFit.value) + 1]; } else { _videoFit.value = fits[0]; } videoFitChangedTimer = Timer(const Duration(seconds: 1), () { videoFitChangedTimer = null; videoFitChanged.value = false; }); print(_videoFit.value); } /// Change Video Fit accordingly void onVideoFitChange(BoxFit fit) { _videoFit.value = fit; } /// 缓存fit Future setVideoFit() async { videoStorage.put(VideoBoxKey.videoBrightness, _videoFit.value.name); } /// 读取fit Future getVideoFit() async { String fitValue = videoStorage.get(VideoBoxKey.videoBrightness, defaultValue: 'fitHeight'); _videoFit.value = fits.firstWhere((element) => element.name == fitValue); } /// 缓存亮度 Future setVideoBrightness() async {} /// 读取亮度 Future getVideoBrightness() async { double brightnessValue = videoStorage.get(VideoBoxKey.videoBrightness, defaultValue: 0.5); setBrightness(brightnessValue); } set controls(bool visible) { _showControls.value = visible; _timer?.cancel(); if (visible) { _hideTaskControls(); } } /// 设置长按倍速状态 void setDoubleSpeedStatus(bool val) { _doubleSpeedStatus.value = val; } /// 关闭控制栏 void onLockControl(bool val) { feedBack(); _controlsLock.value = val; showControls.value = !val; } void toggleFullScreen(bool val) { _isFullScreen.value = val; } /// 截屏 Future screenshot() async { final Uint8List? screenshot = await _videoPlayerController!.screenshot(format: 'image/png'); return screenshot; } Future videoPlayerClosed() async { _timer?.cancel(); _timerForVolume?.cancel(); _timerForGettingVolume?.cancel(); timerForTrackingMouse?.cancel(); _timerForSeek?.cancel(); videoFitChangedTimer?.cancel(); } Future dispose() async { _timer?.cancel(); _timerForVolume?.cancel(); _timerForGettingVolume?.cancel(); timerForTrackingMouse?.cancel(); _timerForSeek?.cancel(); videoFitChangedTimer?.cancel(); _position.close(); _playerEventSubs?.cancel(); _sliderPosition.close(); _sliderTempPosition.close(); _isSliderMoving.close(); _duration.close(); _buffered.close(); _showControls.close(); _controlsLock.close(); playerStatus.status.close(); dataStatus.status.close(); removeListeners(); await _videoPlayerController?.dispose(); _videoPlayerController = null; } }