diff --git a/example/lib/widgets/preview_asset_widget.dart b/example/lib/widgets/preview_asset_widget.dart index ca21f49..6e37037 100644 --- a/example/lib/widgets/preview_asset_widget.dart +++ b/example/lib/widgets/preview_asset_widget.dart @@ -5,7 +5,7 @@ import 'dart:io'; import 'package:flutter/material.dart'; -import 'package:video_player/video_player.dart'; +import 'package:better_player_plus/better_player_plus.dart'; import 'package:wechat_camera_picker/wechat_camera_picker.dart'; class PreviewAssetWidget extends StatefulWidget { @@ -20,7 +20,7 @@ class PreviewAssetWidget extends StatefulWidget { class _PreviewAssetWidgetState extends State { bool get _isVideo => widget.asset.type == AssetType.video; Object? _error; - VideoPlayerController? _playerController; + BetterPlayerController? _playerController; @override void initState() { @@ -40,21 +40,36 @@ class _PreviewAssetWidgetState extends State { final String? url = await widget.asset.getMediaUrl(); if (url == null) { _error = StateError('The media URL of the preview asset is null.'); + if (mounted) { + setState(() {}); + } return; } - final VideoPlayerController controller; - final Uri uri = Uri.parse(url); - if (Platform.isAndroid) { - controller = VideoPlayerController.contentUri(uri); - } else { - controller = VideoPlayerController.networkUrl(uri); - } - _playerController = controller; try { - await controller.initialize(); - controller - ..setLooping(true) - ..play(); + final betterPlayerDataSource = BetterPlayerDataSource( + BetterPlayerDataSourceType.network, + url, + videoFormat: BetterPlayerVideoFormat.other, + ); + final betterPlayerConfiguration = BetterPlayerConfiguration( + autoPlay: true, + looping: true, + controlsConfiguration: BetterPlayerControlsConfiguration( + showControls: false, + ), + errorBuilder: (context, errorMessage) { + return Center( + child: Text( + errorMessage ?? 'Failed to load video', + style: const TextStyle(color: Colors.white), + ), + ); + }, + ); + _playerController = BetterPlayerController( + betterPlayerConfiguration, + betterPlayerDataSource: betterPlayerDataSource, + ); } catch (e) { _error = e; } finally { @@ -69,13 +84,12 @@ class _PreviewAssetWidgetState extends State { } Widget _buildVideo(BuildContext context) { - final VideoPlayerController? controller = _playerController; + final BetterPlayerController? controller = _playerController; if (controller == null) { return const CircularProgressIndicator(); } - return AspectRatio( - aspectRatio: controller.value.aspectRatio, - child: VideoPlayer(controller), + return BetterPlayer( + controller: controller, ); } diff --git a/lib/src/states/camera_picker_viewer_state.dart b/lib/src/states/camera_picker_viewer_state.dart index 5bc5158..3dabc89 100644 --- a/lib/src/states/camera_picker_viewer_state.dart +++ b/lib/src/states/camera_picker_viewer_state.dart @@ -5,6 +5,7 @@ import 'dart:async'; import 'dart:io'; +import 'package:better_player_plus/better_player_plus.dart'; import 'package:flutter/material.dart'; import 'package:flutter/semantics.dart'; import 'package:path/path.dart' as path; @@ -12,10 +13,10 @@ import 'package:video_player/video_player.dart'; import 'package:wechat_picker_library/wechat_picker_library.dart'; import '../constants/config.dart'; -import '../internals/singleton.dart'; import '../constants/enums.dart'; import '../constants/type_defs.dart'; import '../internals/methods.dart'; +import '../internals/singleton.dart'; import '../widgets/camera_picker.dart'; import '../widgets/camera_picker_viewer.dart'; @@ -35,11 +36,11 @@ class CameraPickerViewerState extends State { /// Controller for the video player. /// 视频播放的控制器 - late final videoController = VideoPlayerController.file(previewFile); + BetterPlayerController? _controller; /// Whether the controller is playing. /// 播放控制器是否在播放 - bool get isControllerPlaying => videoController.value.isPlaying; + bool get isControllerPlaying => _controller?.isPlaying() ?? false; /// Whether the controller has initialized. /// 控制器是否已初始化 @@ -64,22 +65,58 @@ class CameraPickerViewerState extends State { @override void dispose() { - videoController - ..removeListener(videoControllerListener) - ..pause() - ..dispose(); + _controller?.removeEventsListener(betterPlayerListener); + _controller?.pause(); + _controller?.dispose(); super.dispose(); } Future initializeVideoPlayerController() async { try { + final betterPlayerDataSource = BetterPlayerDataSource( + BetterPlayerDataSourceType.file, + previewFile.path, + videoFormat: BetterPlayerVideoFormat.other, + ); + + // 使用 VideoPlayerController 获取宽高比 + final videoController = + VideoPlayerController.file(File(previewFile.path)); await videoController.initialize(); - videoController.addListener(videoControllerListener); - hasLoaded = true; - if (pickerConfig.shouldAutoPreviewVideo) { - videoController.play(); - videoController.setLooping(true); + double aspectRatio = videoController.value.aspectRatio; + // 检查旋转角度 + final orientation = videoController.value.rotationCorrection; // 如果可用 + if (orientation == 90 || orientation == 270) { + aspectRatio = 1 / aspectRatio; } + debugPrint('Error when initializing video controller:2222 $aspectRatio'); + + videoController.dispose(); // 释放临时控制器 + final betterPlayerConfiguration = BetterPlayerConfiguration( + autoPlay: pickerConfig.shouldAutoPreviewVideo, + looping: pickerConfig.shouldAutoPreviewVideo, + aspectRatio: aspectRatio, + controlsConfiguration: BetterPlayerControlsConfiguration( + showControls: false, + ), + errorBuilder: (context, errorMessage) { + hasErrorWhenInitializing = true; + safeSetState(() {}); + return Center( + child: Text( + Singleton.textDelegate.loadFailed, + style: const TextStyle(inherit: false), + ), + ); + }, + ); + _controller = BetterPlayerController( + betterPlayerConfiguration, + betterPlayerDataSource: betterPlayerDataSource, + ); + + _controller!.addEventsListener(betterPlayerListener); + hasLoaded = true; } catch (e, s) { hasErrorWhenInitializing = true; realDebugPrint('Error when initializing video controller: $e'); @@ -91,9 +128,19 @@ class CameraPickerViewerState extends State { /// Listener for the video player. /// 播放器的监听方法 - void videoControllerListener() { - if (isControllerPlaying != isPlaying.value) { - isPlaying.value = isControllerPlaying; + void betterPlayerListener(BetterPlayerEvent event) { + if (event.betterPlayerEventType == BetterPlayerEventType.initialized) { + debugPrint( + "_controller?.getAspectRatio(); ${_controller?.getAspectRatio()}"); + } + if (event.betterPlayerEventType == BetterPlayerEventType.play) { + if (!isPlaying.value) { + isPlaying.value = true; + } + } else if (event.betterPlayerEventType == BetterPlayerEventType.pause) { + if (isPlaying.value) { + isPlaying.value = false; + } } } @@ -106,14 +153,18 @@ class CameraPickerViewerState extends State { Future playButtonCallback() async { try { if (isPlaying.value) { - videoController.pause(); + _controller?.pause(); } else { - if (videoController.value.duration == videoController.value.position) { - videoController.seekTo(Duration.zero); + if (_controller != null && + _controller!.videoPlayerController!.value.position >= + (_controller!.videoPlayerController!.value?.duration ?? + Duration.zero)) { + _controller + ?..seekTo(Duration.zero) + ..play(); + } else { + _controller?.play(); } - videoController - ..play() - ..setLooping(true); } } catch (e, s) { handleErrorWithHandler(e, s, onError); @@ -260,10 +311,9 @@ class CameraPickerViewerState extends State { builder = Stack( children: [ Center( - child: AspectRatio( - aspectRatio: videoController.value.aspectRatio, - child: VideoPlayer(videoController), - ), + child: _controller != null + ? BetterPlayer(controller: _controller!) + : const SizedBox.shrink(), ), buildPlayControlButton(context), ], diff --git a/pubspec.yaml b/pubspec.yaml index 5961b40..006fb91 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -34,6 +34,8 @@ dependencies: photo_manager_image_provider: ^2.0.0 sensors_plus: '>=4.0.0 <7.0.0' video_player: ^2.7.0 + better_player_plus: ^1.0.8 + dev_dependencies: flutter_lints: any