返回

Flutter 视频播放角度与拉伸问题解决实战

Android

Flutter 视频播放角度及拉伸问题

在使用 Flutter 的 image_pickervideo_player 插件时,开发者有时会遇到视频显示角度不正确、视频被拉伸等问题。 这些问题主要来源于设备录制视频时产生的元数据信息与播放器默认处理方式之间的差异。视频本身可能以特定角度录制,同时含有方向信息。而video_player 默认可能并不会自动调整这些方向信息,进而导致最终显示出现偏差。

问题分析

核心问题在于 image_picker 获取的视频文件,包含旋转角度的元数据信息,但 video_player 默认不会读取或利用这些元数据。这种默认处理方式导致:

  • 角度不正确: 例如,竖屏录制的视频在播放时可能显示为横屏,或者发生其它不正常的旋转。
  • 视频拉伸: 旋转角度后,为了适应播放器的显示尺寸,视频被强行拉伸或压缩,画面失真。

解决方案一:利用 VideoPlayerController 的旋转处理

一种直接的解决方式是手动调整视频旋转。VideoPlayerController 并没有提供直接旋转视频角度的方法。但借助 Transform.rotate 组件可以手动对视频进行旋转。但是仅仅使用 Transform.rotate 并不足够。因为此方法可能会导致所有视频都旋转,也会可能导致拉伸。

此方法的核心在于获取视频原始角度,依据其角度进行旋转:

  1. 获取视频元数据: 使用诸如 flutter_media_metadata 插件来提取视频的旋转角度信息。
  2. 根据角度旋转视频: 使用 Transform.rotate 包裹 VideoPlayer,根据元数据中读取的旋转角度动态调整旋转值。
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:video_player/video_player.dart';
import 'package:flutter_media_metadata/flutter_media_metadata.dart';

class VideoPlayerWidget extends StatefulWidget {
  @override
  _VideoPlayerWidgetState createState() => _VideoPlayerWidgetState();
}

class _VideoPlayerWidgetState extends State<VideoPlayerWidget> {
  File? _videoFile;
  VideoPlayerController? _videoController;
  final ImagePicker _picker = ImagePicker();
  double _rotationAngle = 0.0; // 添加旋转角度

  Future<void> _pickVideo() async {
    final XFile? video = await _picker.pickVideo(source: ImageSource.gallery);
    if (video != null) {
      _videoFile = File(video.path);

        final metadata = await MetadataRetriever.fromFile(_videoFile!);

        int? rotation = metadata.trackRotation;
        _rotationAngle = rotation != null ? rotation *  (3.14159 / 180): 0.0;
       _videoController = VideoPlayerController.file(_videoFile!)..initialize().then((_) {
        setState(() {});
         _videoController?.play();
      });

    }
  }

    @override
    void dispose() {
     _videoController?.dispose();
        super.dispose();
    }

  @override
  Widget build(BuildContext context) {
      return Scaffold(
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                 ElevatedButton(
                     onPressed: _pickVideo,
                      child: const Text('Choose Video')
                 ),
                const SizedBox(height: 20),
                if (_videoFile != null)
                 _videoController!.value.isInitialized ? AspectRatio(
                  aspectRatio: _videoController!.value.aspectRatio,
                   child: Transform.rotate(
                     angle: _rotationAngle, // 使用计算出的旋转角度
                      child:  VideoPlayer(_videoController!),
                   )
                 ) : Container()
                   else
                     const Text("Click on pick video to selecte video"),
                ]
           ),
          )
     );
  }
}

操作步骤:

  1. 添加 flutter_media_metadata 依赖到 pubspec.yaml: flutter pub add flutter_media_metadata.
  2. 安装依赖 flutter pub get.
  3. 将上述代码集成到 Flutter 应用中。
  4. 在选择视频后,视频将自动旋转到正确的角度。

这种方式更加精确,不会出现由于视频默认方向不同造成的错误。

解决方案二:自定义播放器容器尺寸

部分情况下,简单的旋转并不能完全解决问题, 还需要结合 AspectRatio 来配合工作, 确保播放器控件的比例是正常的,这样旋转之后的显示效果才是准确的。 结合计算出的角度,动态调整容器大小:

 // 修改后的 AspectRatio

 AspectRatio(
      aspectRatio: calculateAspectRatio(_videoController!.value.aspectRatio, _rotationAngle), //动态计算宽高比
       child: Transform.rotate(
        angle: _rotationAngle,
         child: VideoPlayer(_videoController!),
    ),
)


double calculateAspectRatio(double videoAspectRatio, double angle) {

    bool isVertical = angle != 0 && (angle / 90) % 2 != 0;

    return isVertical? 1 / videoAspectRatio : videoAspectRatio;
}

操作步骤:

  1. 确保使用方法一, 可以获得正确的旋转角度。
  2. 使用 calculateAspectRatio() 函数来动态调整宽高比。
  3. 在播放器初始化后,计算宽高比和应用旋转角度。

额外安全建议

  • 资源释放: 在组件销毁 (dispose) 时,释放 VideoPlayerController 以避免内存泄漏。
  • 错误处理: 在获取视频元数据或初始化 VideoPlayerController 时添加错误处理,提高应用健壮性。
  • UI提示: 在视频加载或初始化时,添加 loading 指示器,改善用户体验。

总结,通过结合元数据分析,角度处理, 和自定义显示容器比例, 可以解决大部分常见的视频播放方向和拉伸问题,提升用户体验, 并可以使 image_pickervideo_player 在复杂的使用场景下表现的更加优秀。