前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >Flutter 中 视频封面 视频的压缩 上传 播放

Flutter 中 视频封面 视频的压缩 上传 播放

作者头像
心安事随
发布2024-12-29 09:28:08
发布2024-12-29 09:28:08
11700
代码可运行
举报
文章被收录于专栏:前端大合集前端大合集
运行总次数:0
代码可运行

需求分析

  1. 用户选择视频之后,可以生成一个视频的封面图
  2. 点击封面图可以播放视频
  3. 用户发表视频之前进行视频的校验(大于1080p 即像素点大于 1920 * 1080 = 2073600 像素点 或者 视频的内存大小 大于50mb) 进行压缩, 否则直接上传即可.
  4. 上传之后,进行视频的播放.(这里包括一系列视频的操作方法)

涉及到的库

video_thumbnail : 用于从视频文件中生成缩略图。

video_player : 是 Flutter 中用于播放视频的重要库。它提供了一套完整的 API 来处理视频播放相关的功能,支持多种视频格式,能够在 Android 和 iOS 平台上实现流畅的视频播放体验

ideo_compress : 是一个在 Flutter 应用中用于视频压缩的库。它帮助开发者方便地减小视频文件的大小,同时在一定程度上保持视频的质量,这在应用开发中对于优化存储、减少网络传输带宽等场景非常有用

1. 封装视频工具类

我们封装一个视频的工具类, 里面包含了一些对视频的操作的方法, 包括获取视频的大小, 视频的像素, 获取视频的封面图...

video_utils

代码语言:javascript
代码运行次数:0
复制
import 'dart:io';
import 'package:path_provider/path_provider.dart';
import 'package:video_player/video_player.dart';
import 'package:video_thumbnail/video_thumbnail.dart';
class VideoUtils {
 
}

视频的大小

传入视频的路径, 创建了一个File对象, 通过lengthSync()方法 拿到字节大小 ,然后 / 1024 / 1024 就可以得到 mb的大小.

代码语言:javascript
代码运行次数:0
复制
import 'dart:io';
import 'package:path_provider/path_provider.dart';
import 'package:video_player/video_player.dart';
import 'package:video_thumbnail/video_thumbnail.dart';
class VideoUtils {
  // 获取视频的大小(以 MB 为单位)
  static Future<double> getVideoFileSize(String videoPath) async {
    final File file = File(videoPath);
    return file.lengthSync() / 1024 / 1024;
  }

视频的像素大小

通过创建 VideoPlayerController 来初始化指定视频文件,获取其宽度和高度并相乘,最后释放控制器资源,以此实现获取视频像素大小(宽度乘高度)的功能,函数定义为静态异步方法,接收视频路径作为参数并返回像素大小的双精度值

代码语言:javascript
代码运行次数:0
复制
  // 获取视频的像素大小(宽度 * 高度)
  static Future<double> getVideoPixelSize(String videoPath) async {
    final VideoPlayerController _controller = VideoPlayerController.file(File(videoPath));
    await _controller.initialize();
    final double width = _controller.value.size.width;
    final double height = _controller.value.size.height;
    await _controller.dispose();
    return width * height;
  }

视频的封面

调用 VideoThumbnail.thumbnailFile 来基于指定视频路径临时目录路径设定的图片格式(JPEG)、最大高度(128)以及质量(75)等参数生成视频封面图,并返回该封面图的路径

代码语言:javascript
代码运行次数:0
复制
  // 获取视频的封面图
 static Future<String?> getVideoThumbnail(String videoPath) async {
   final thumbnailPath = await VideoThumbnail.thumbnailFile(
     video: videoPath,
     thumbnailPath: (await getTemporaryDirectory()).path,
     imageFormat: ImageFormat.JPEG,
     maxHeight: 128,  // 设置最大高度
     quality: 75, // 设置质量
   );
   return thumbnailPath;
 }

2. 封装视频压缩类.

在这个类里面 我们定义了 是否需要压缩, 压缩视频 这两个静态方法.

compress:这是一个异步方法,用于压缩指定路径的视频。

首先订阅视频压缩进度,在每次进度更新时,将进度赋值给_progress变量,并且如果onProgress回调函数不为空,则将进度传递给外部。

然后调用VideoCompress类的compressVideo方法来实际执行视频压缩操作,传入视频路径、指定压缩质量VideoQuality.Res1280x720Quality)以及是否删除原始视频(此处为false)等参数,

最后返回压缩后的视频相关信息(类型为MediaInfo

代码语言:javascript
代码运行次数:0
复制
import 'package:chat/src/utils/video_uploader.dart';
import 'package:chat/src/utils/video_utils.dart';
import 'package:video_compress/video_compress.dart';
import 'dart:async';

class VideoCompressor {

  // 是否压缩 (压缩前的校验)
  // 视频像素大小 小于 720p || 视频大小 小于 50mb --> 压缩
  static Future<bool> shouldCompress(String videoPath) async {
    double p = await VideoUtils.getVideoPixelSize(videoPath);
    double s = await VideoUtils.getVideoFileSize(videoPath);
    if (p > VideoUploader.maxVideoPixel || s >= VideoUploader.maxVideoSize ) {
      return true;
    }else{
      return false;
    }
  }

  // 压缩视频的方法
  Future<MediaInfo?> compress(String videoPath) async {
    // 订阅压缩进度
    _subscription = VideoCompress.compressProgress$.subscribe((progress) {
      _progress = progress;
      // 回调进度给外部
      if (onProgress != null) {
        onProgress!(progress);
      }
    });

    MediaInfo? info = await VideoCompress.compressVideo(
      videoPath,
      quality: VideoQuality.Res1280x720Quality,
      deleteOrigin: false,
    );
    return info;
  }

  // 释放资源
  void dispose() {
    _subscription.unsubscribe(); // 取消订阅
  }
}

3. 视频上传

  1. 通过传入视频的地址, 通过VideoCompressor.shouldCompress方法判断是否需要压缩, 他会返回一个bool值.
  2. 如果需要压缩, 调用videoCompressor.compress(path) 进行压缩 并返回压缩后的文件.
  3. 如果不为空, 那么就进行接口上传, 然后释放压缩的实例.
  4. 如果不需要压缩的话,直接上传即可.
代码语言:javascript
代码运行次数:0
复制
  static Future<String> compressAndUploadVideo(String path) async {
    bool shouldCompress = await VideoCompressor.shouldCompress(path);
    if (shouldCompress) {
      VideoCompressor videoCompressor = VideoCompressor(onProgress: (progress) {
        // print('当前压缩进度: ${progress.toStringAsFixed(1)}%');
        // 可以考虑对于进度进行一下展示
      });
      MediaInfo? info = await videoCompressor.compress(path); // 等待压缩,返回压缩后的信息
      if (info != null){
        final res = await VideoUploader.uploadVideoFile(info.file!);
        videoCompressor.dispose();
        return res;
      } else {
        throw Exception('Failed to compress video');
      }
    } else {
      return await VideoUploader.uploadVideoFile(File(path));
    }
  }

5. 视频播放

  1. 初始化视频播放器
代码语言:javascript
代码运行次数:0
复制
 late VideoPlayerController _videoController;

  @override
  void initState() {
    // print("widget.videoPath: ${widget.videoPath}");
    super.initState();
    _videoController = VideoPlayerController.file(File(widget.videoPath))
      ..initialize().then((_) {
        setState(() {});
        _videoController.play();
      }).catchError((error) {
        print('Error initializing video: $error');
      });
  }
  1. 创建视频显示组件

判断是否初始化完毕 _videoController.value.isInitialized

AspectRatio组件来保持视频的原始宽高比,_controller.value.aspectRatio获取视频的宽高比,VideoPlayer(_controller)则用于显示视频。

代码语言:javascript
代码运行次数:0
复制
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.black,
      body: Center(
        child: _videoController.value.isInitialized
            ? GestureDetector(
              onTap: () {
                // 点击暂停/播放
                if (_videoController.value.isPlaying) {
                  _videoController.pause();
                } else {
                  _videoController.play();
                }
                setState(() {});
              },
              child: AspectRatio(
                aspectRatio: _videoController.value.aspectRatio,
                child: VideoPlayer(_videoController),
              ),
            )
            : CircularProgressIndicator(color: TColors.instance.primaryColor),
      ),
    );
  }
}
  1. 释放播放器资源
代码语言:javascript
代码运行次数:0
复制
@override
void dispose() {
    super.dispose();
    _controller.dispose();
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-12-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 需求分析
  • 涉及到的库
  • 1. 封装视频工具类
  • 2. 封装视频压缩类.
  • 3. 视频上传
  • 5. 视频播放
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档