Flutter实现视频压缩功能的示例代码

来自:网络
时间:2023-07-25
阅读:
目录

为什么Flutter应用需要视频压缩功能

移动应用程序中,视频占用了大量的存储空间和带宽。这在一定程度上影响了应用程序的性能和用户体验。因此,在许多应用程序中,我们需要对视频文件进行压缩以优化其大小和质量。通过压缩视频,可以有效地减小视频文件的体积,并且可在保证画质的前提下,大幅降低视频传输和播放时所需的带宽。对于 Flutter 应用程序而言,视频压缩同样也是非常重要的。

常见的视频压缩算法和格式

视频压缩算法可以分为有损压缩和无损压缩两种方式。有损压缩是一种在保证视觉质量的情况下,通过舍弃冗余数据来压缩视频的方式。相反,无损压缩则可保留所有视频数据,但通常需要更大的存储空间。

在实际应用中,我们常用以下几种视频压缩格式:H.264,HEVC,AV1 等等,其中 H.264 是目前移动应用程序中最主流的压缩格式之一,支持广泛,文件体积和清晰度较为平衡。以 Flutter 应用程序为例,我们可以使用 FFmpeg 库进行视频压缩,以支持各种流行的视频压缩格式。接下来,我们将介绍如何使用 FFmpeg 压缩 Flutter 应用程序的视频。

使用FFmpeg库压缩Flutter应用中的视频

如何在Flutter应用中集成FFmpeg库

若需使用FFmpeg进行视频压缩,我们首先需要将 FFmpeg 库集成到 Flutter 应用程序中。下面是一些基本操作步骤:

  • 从FFmpeg官网下载 FFmpeg 库并解压文件。
  • 在 Flutter 应用程序的 Android 端目录中添加 FFmpeg 的 gradle 依赖项。
  • 在 Flutter 应用程序的 iOS 端目录中添加 FFmpeg 的pod依赖项。
  • 在 Flutter 应用程序使用 FFmpeg 库时初始化所需配置和参数。

以下是一些关键代码步骤和教程供您参考:

1.下载和解压 FFmpeg 库

$ curl -LO http://ffmpeg.org/releases/ffmpeg-<version>.tar.bz2
$ tar jxvf ffmpeg-<version>.tar.bz2

这里需要您自行选择您需要下载的对应版本。

2.添加 FFmpeg 的 gradle 依赖项

在应用程序的 android/app/build.gradle 文件中,添加以下依赖项:

repositories {
    jcenter()
}
dependencies {
    implementation 'com.arthenica:mobile-ffmpeg-full:4.4.LTS'
}

使用上面的依赖项后,我们就可以在应用程序中使用 FFmpeg 库了。

3.在 iOS 端目录中添加 FFmpeg 的pod依赖项

在应用程序的 ios/Podfile 文件中,添加以下依赖项:

target 'Runner' do
  use_frameworks!
  # Pods for Runner
  # Add the following line:
  pod 'MobileFFmpeg', '4.4.LTS'
end

添加依赖项后,我们可以使用 CocoaPods 更新我们的依赖:

cd ios
pod install

4.初始化FFmpeg库配置和参数

在使用 FFmpeg 进行任何操作之前,我们需要通过一些设置来初始化 FFmpeg 库的基本配置和参数。这一步需要在启动应用程序时进行,根据以下代码执行即可:

import 'package:flutter_video_compress/flutter_video_compress.dart';
FlutterVideoCompress flutterVideoCompress = FlutterVideoCompress();
await flutterVideoCompress.getFFmpegVersion();
flutterVideoCompress.setLogLevel(LogLevel.AV_LOG_ERROR);
await flutterVideoCompress.loadFFmpeg();

以上是Flutter中集成 FFmpeg 库的基本代码,这为后续的视频压缩操作打下了必要的基础。接下来,我们将介绍 FFmpeg 库的基本视频压缩操作。

使用FFmpeg库进行视频压缩的基本步骤

在对视频进行压缩之前,我们需要对视频进行解码,并根据要求对其进行重新编码。这个过程需要借助于 FFmpeg 库,并完成以下几个步骤:

  • 打开输入文件;
  • 解码输入文件;
  • 进行需要的操作(如压缩、转换等);
  • 将操作后的输出写入到输出文件中;
  • 关闭输入文件和输出文件。
await flutterVideoCompress.executeWithArguments([
    '-y',
    '-i',
    'input.mp4',
    '-c:v',
    'libx264',
    '-crf',
    '18',
    '-preset',
    'superfast',
    '-c:a',
    'aac',
    '-b:a',
    '128k',
    '-strict',
    '-2',
    'output.mp4',
]);

该示例中,input.mp4 是我们需要压缩的文件名,通过指定 -c:v libx264 -crf 18 -preset superfast 对视频进行了压缩处理,并且 -c:a aac -b:a 128k 对音频进行了编码,保证音频的质量,最终生成 output.mp4 文件。

这就是使用 FFmpeg 进行视频压缩的基本流程,接下来,我们将详细讲解如何使用 Dart 语言封装 FFmpeg 命令。

使用Dart语言封装FFmpeg命令

在 Flutter 应用程序中使用 FFmpeg 库进行视频压缩时,我们通常需要输入大量的参数才能开始命令执行。为了方便操作,我们常常会使用 Dart 语言的特性来封装 FFmpeg 命令。

通常,我们使用 Process.run 方法执行命令:

import 'dart:convert';
import 'dart:io';
await Process
    .run('ffmpeg', ['-i', 'input.mp4', '-vf', 'scale=-1:360', '-c:v', 'libx264', '-preset', 'fast', '-crf', '23', '-ac', '1', '-ar', '44100', '-acodec', 'aac', 'output.mp4'])
    .then((ProcessResult result) {
      print('standard out:\n${result.stdout}\n');
      print('standard in:\n${result.stderr}\n');
      String message = result.stderr.toString();
      if (message.contains('Cannot find ffmpeg')) {
        throw ('${message.toString()}');
      }
    });

以上示例中,我们使用 Process.run 方法执行 FFmpeg 命令, -i input.mp4指定需要压缩的文件名,-vf scale=-1:360 指定视频框的大小为 360,-c:v libx264 -preset fast -crf 23 是视频压缩参数,-ac 1 -ar 44100 -acodec aac 是音频编码参数,最终我们通过 output.mp4 输出压缩后的视频文件。

然而,对于多次执行相似操作的情况,我们并不希望每次都输入一大堆长串的命令参数。这时候,我们可以使用 Dart 语言中的类来对 FFmpeg 命令进行封装,方便测试和重用。

在以下示例中,我们将基本的 FFmpeg 命令封装在 FFmpegCommands 类中,并通过 toCommand 方法将参数转换为一条命令行命令:

class FFmpegCommands {
  String _inputPath;
  String _outputPath;
  List<String> _videoFilters;
  List<String> _audioFilters;
  String _crf;
  String _bitrate;
  FFmpegCommands({
    @required String inputPath,
    @required String outputPath,
    List<String> videoFilters,
    List<String> audioFilters,
    String crf = '23',
    String bitrate = '1024k',
  })  : assert(inputPath != null),
        assert(outputPath != null) {
    this._inputPath = inputPath;
    this._outputPath = outputPath;
    this._videoFilters = videoFilters;
    this._audioFilters = audioFilters;
    this._crf = crf;
    this._bitrate = bitrate;
  }
  String toCommand() {
    List<String> commands = [];
    commands.addAll([
      'ffmpeg',
      '-i',
      this._inputPath,
    ]);
    if (this._videoFilters != null) {
      commands.addAll(['-vf', this._videoFilters.join(',')]);
    }
    if (this._crf != null) {
      commands.addAll(['-crf', this._crf]);
    }
    if (this._bitrate != null) {
      commands.addAll(['-b:a', this._bitrate]);
    }
    commands.addAll(['-y', this._outputPath]);
    return commands.join(' ');
  }
}

使用 FFmpeg 命令封装类虽然不会改变最终的操作,但能提高重用率和可维护性。下面,我们以例子,具体展示如何使用它进行视频压缩。

void main() async {
  FFmpegCommands ffmpegCommands = FFmpegCommands(
    inputPath: 'input.mp4',
    outputPath: 'output.mp4',
    videoFilters: ['scale=-1:360'],
    crf: '23',
    bitrate: '1024k',
  );
  await Process
      .run(ffmpegCommands.toCommand(), [])
      .then((ProcessResult result) {
        print(result.stdout);
        print(result.stderr);
      });
}

在上面这个例子中,我们首先构造了一个 FFmpegCommands 对象,其中包含了全部参数,然后通过 ffmpegCommands.toCommand() 方法生成可执行的命令行命令,最终通过 Process.run 方法执行压缩操作。

以上就是使用 Dart 语言封装 FFmpeg 命令的方法,接下来,我们将结合实例,讲解如何在 Flutter 应用程序中使用 FFmpeg 库进行视频压缩。

Flutter应用中视频压缩的最佳实践

我们将通过实例,介绍在 Flutter 应用程序中如何使用 FFmpeg 库进行视频压缩的最佳实践。

选择最合适的视频压缩算法和格式

在实际应用中,我们根据视频压缩的需求,选择最适合的压缩算法和格式。

尺寸压缩:对于需要压缩视频大小的需求,可以使用 FFmpeg 库中的 scale 滤镜来改变视频的分辨率,从而减小视频文件大小,例如:

String command = 'ffmpeg -i input.mp4 -vf scale=-1:360 output.mp4';

视频编码压缩:在保证视频质量的前提下,可以选择压缩视频的编码格式,例如使用 H.264 或者 HEVC 等。

音频编码压缩:选择合适的音频编码格式也可以减小视频文件大小。

综合考虑需求,我们需要结合实际进行选择。

配置FFmpeg工具进行压缩

在选择完合适的压缩算法和格式后,便可以使用 FFmpeg 工具进行压缩。

String command = 'ffmpeg -i input.mp4 -c:v libx264 -crf 23 -preset fast -c:a aac -b:a 128k output.mp4';
await Process.run(command, []);

在执行命令前,我们需要确保我们已经按照前面所述集成了 FFmpeg 库。除此之外,根据具体需要,我们可以传递不同的参数来实现不同效果的压缩。

例如,如果我们在压缩过程中想要保留视频的宽高比例,我们可以将压缩命令改为:

String command = 'ffmpeg -i input.mp4 -c:v libx264 -filter:v "scale=-1:360:force_original_aspect_ratio=decrease,pad=360:360:(ow-iw)/2:(oh-ih)/2" -crf 23 -preset fast -c:a aac -b:a 128k output.mp4';

这里使用了 pad 滤镜来保持宽高比例,同时使用 force_original_aspect_ratio=decrease 保证视频宽高比不变。

使用Dart语言封装FFmpeg命令

使用 Dart 语言封装 FFmpeg 命令可以让我们更好地进行参数的组合和维护,以下是使用 FFmpeg 命令封装类进行视频压缩的示例:

class FFmpegCommands {
  String _inputPath;
  String _outputPath;
  List<String> _videoFilters;
  List<String> _audioFilters;
  String _crf;
  String _bitrate;
  FFmpegCommands({
    @required String inputPath,
    @required String outputPath,
    List<String> videoFilters,
    List<String> audioFilters,
    String crf = '23',
    String bitrate = '1024k',
  })  : assert(inputPath != null),
        assert(outputPath != null) {
    this._inputPath = inputPath;
    this._outputPath = outputPath;
    this._videoFilters = videoFilters;
    this._audioFilters = audioFilters;
    this._crf = crf;
    this._bitrate = bitrate;
  }
  String toCommand() {
    List<String> commands = [];
    commands.addAll([
      'ffmpeg',
      '-i',
      this._inputPath,
    ]);
    if (this._videoFilters != null) {
      commands.addAll(['-vf', this._videoFilters.join(',')]);
    }
    if (this._crf != null) {
      commands.addAll(['-crf', this._crf]);
    }
    if (this._bitrate != null) {
      commands.addAll(['-b:a', this._bitrate]);
    }
    commands.addAll(['-y', this._outputPath]);
    return commands.join(' ');
  }
}
void main() async {
  FFmpegCommands ffmpegCommands = FFmpegCommands(
    inputPath: 'input.mp4',
    outputPath: 'output.mp4',
    videoFilters: ['scale=-1:360', 'pad=360:360:(ow-iw)/2:(oh-ih)/2:color=black'],
    crf: '23',
    bitrate: '1024k',
  );
  await Process
      .run(ffmpegCommands.toCommand(), [])
      .then((ProcessResult result) {
        print(result.stdout);
        print(result.stderr);
      });
}

在上述示例中,我们首先定义了 FFmpegCommands 类,然后构造了一个对象来指定 FFmpeg 命令的各个参数,最后通过 toCommand 方法生成可执行的命令行命令,并通过 Process.run 方法执行视频压缩。整个过程中,我们可以自由设置 FFmpeg 命令中的各个参数来实现不同的视频压缩效果。

实现视频压缩进度的更新与回调

在视频压缩过程中,我们通常希望能够实时显示压缩的进度,并提供进度条供用户观察操作进度。为了实现这一目标,我们可以通过监听 FFmpeg 工具的输出流来更新压缩进度。

以下是实现视频压缩进度更新和回调的示例代码:

class VideoCompressUtil {
  static final FlutterVideoCompress _flutterVideoCompress = FlutterVideoCompress();
  static StreamSubscription _subscription;
  static int _prevProgress;
  static Future<void> compressVideo({
    @required String inputPath,
    @required String outputPath,
    List<String> videoFilters,
    List<String> audioFilters,
    String crf = '23',
    String bitrate = '1024k',
    Function(int) onProgress,
  }) async {
    if (_subscription != null) {
      throw 'Another FFmpeg compression is already in progress.';
    }
    int totalDuration = await _flutterVideoCompress.getMediaInformation(inputPath: inputPath).then((info) => info
        .duration
        .inMilliseconds);
    int previousPercent = 0;
    final Completer completer = Completer();
    FFmpegCommands cmd = FFmpegCommands(
      inputPath: inputPath,
      outputPath: outputPath,
      videoFilters: videoFilters,
      audioFilters: audioFilters,
      crf: crf,
      bitrate: bitrate,
    );
    String command = cmd.toCommand();
    print(command);
    _prevProgress = 0;
    _subscription = _flutterVideoCompress.pipe(command).listen((RetrieveData stdout) async {
      String data = utf8.decode(stdout.data);
      if (data.contains('frame=')) {
        String progressString = data.split('frame=')[1].split('fps=')[0].trim();
        int progress = int.parse(progressString);
        if (previousPercent != ((progress * 100) ~/ totalDuration)) {
          previousPercent = ((progress * 100) ~/ totalDuration);
          onProgress(previousPercent);
        }
      }
      if (data.contains('Stream mapping')) {
        _prevProgress = null;
      }
      if (data.contains('size=')) {
        String durString = data.split("Duration:")[1].split(",")[0].trim();
        Duration duration = Duration(
            hours: int.parse(durString.split(":")[0]),
            minutes: int.parse(durString.split(":")[1]),
            seconds: int.parse(durString.split(":")[2].split(".")[0]),
            milliseconds: int.parse(durString.split(":")[2].split(".")[1]));
        int totalDuration = duration.inSeconds;
        double _progress = 0;
        RegExp timeRegExp = new RegExp(r"(?:(\d+):)?(\d{2}):(\d{2})\.(\d{1,3})");
        String lastMatch;
        data.split('\n').forEach((line) {
          lastMatch = line;
          if (line.contains('time=')) {
            final match = timeRegExp.allMatches(line).elementAt(0);
            final hours = match.group(1) != null ? int.parse(match.group(1)) : 0;
            final minutes = int.parse(match.group(2));
            final seconds = int.parse(match.group(3));
            final videoDurationInSeconds = (hours * 3600) + (minutes * 60) + seconds;
            _progress = (videoDurationInSeconds / totalDuration) * 100;
            if ((_progress - _prevProgress).abs()>= 1.0) {
              _prevProgress = _progress.toInt();
              onProgress(_prevProgress);
            }
          }
        });
        completer.complete();
      }
      // Output FFmpeg log to console.
      print(data);
    });
    await completer.future;
    await _subscription.cancel();
    _subscription = null;
    _prevProgress = 0;
  }
}

在上述代码中,我们定义了 VideoCompressUtil 类,通过内部的 FFmpegCommands 类封装了 FFmpeg 命令,并监听 FFmpeg 工具输出流来实现视频压缩进度的更新和回调。在进行压缩过程中,我们通过传递 onProgress 参数来实现压缩进度的实时更新。

总结

本文介绍了使用 Flutter 开发视频压缩功能的方法,包括集成 FFmpeg 库、使用 FFmpeg 命令进行视频压缩、封装 FFmpeg 命令并实现压缩进度更新和回调等。

在实际开发中,视频压缩是一项经常用到的技术,通过掌握本文所述的方法和技巧,我们可以轻易地实现视频压缩功能,并让用户更好地体验应用的功能和服务,兄弟们赶紧去敲一遍试试。

以上就是Flutter实现视频压缩功能的示例代码的详细内容,更多关于Flutter视频压缩的资料请关注其它相关文章!

返回顶部
顶部