exportVideo method

Future<void> exportVideo({
  1. required void onCompleted(
    1. File file
    ),
  2. void onError(
    1. Object,
    2. StackTrace
    )?,
  3. String? name,
  4. String? outDir,
  5. String format = "mp4",
  6. double scale = 1.0,
  7. String? customInstruction,
  8. void onProgress(
    1. Statistics,
    2. double
    )?,
  9. VideoExportPreset preset = VideoExportPreset.none,
  10. bool isFiltersEnabled = true,
})

Export the video using this edition parameters and return a File.

The onCompleted param must be set to return the exported File video.

The onError function provides the Exception and StackTrace that causes the exportation error.

If the name is null, then it uses this video filename.

If the outDir is null, then it uses TemporaryDirectory.

The format of the video to be exported, by default mp4.

The scale is scale=width*scale:height*scale and reduce or increase video size.

The customInstruction param can be set to add custom commands to the FFmpeg eexecution (i.e. -an to mute the generated video), some commands require the GPL package

The onProgress is called while the video is exporting. This argument is usually used to update the export progress percentage. This function return Statistics from FFmpeg session and the double progress value between 0.0 and 1.0.

The preset is the compress quality (Only available on GPL package). A slower preset will provide better compression (compression is quality per filesize). More info about presets

Set isFiltersEnabled to false if you do not want to apply any changes

Implementation

Future<void> exportVideo({
  required void Function(File file) onCompleted,
  void Function(Object, StackTrace)? onError,
  String? name,
  String? outDir,
  String format = "mp4",
  double scale = 1.0,
  String? customInstruction,
  void Function(Statistics, double)? onProgress,
  VideoExportPreset preset = VideoExportPreset.none,
  bool isFiltersEnabled = true,
}) async {
  final String tempPath = outDir ?? (await getTemporaryDirectory()).path;
  final String videoPath = file.path;
  name ??= path.basenameWithoutExtension(videoPath);
  final int epoch = DateTime.now().millisecondsSinceEpoch;
  final String outputPath = "$tempPath/${name}_$epoch.$format";

  // CALCULATE FILTERS
  final String gif = format != "gif" ? "" : "fps=10 -loop 0";
  final String trim = minTrim >= _min.dx && maxTrim <= _max.dx
      ? "-ss $_trimStart -to $_trimEnd"
      : "";
  final String crop = minCrop >= _min && maxCrop <= _max ? _getCrop() : "";
  final String rotation =
      _rotation >= 360 || _rotation <= 0 ? "" : _getRotation();
  final String scaleInstruction =
      scale == 1.0 ? "" : "scale=iw*$scale:ih*$scale";

  // VALIDATE FILTERS
  final List<String> filters = [crop, scaleInstruction, rotation, gif];
  filters.removeWhere((item) => item.isEmpty);
  final String filter = filters.isNotEmpty && isFiltersEnabled
      ? "-filter:v ${filters.join(",")}"
      : "";
  final String execute =
      // ignore: unnecessary_string_escapes
      " -i \'$videoPath\' ${customInstruction ?? ""} $filter ${_getPreset(preset)} $trim -y \"$outputPath\"";

  // PROGRESS CALLBACKS
  FFmpegKit.executeAsync(
    execute,
    (session) async {
      final state =
          FFmpegKitConfig.sessionStateToString(await session.getState());
      final code = await session.getReturnCode();

      if (ReturnCode.isSuccess(code)) {
        onCompleted(File(outputPath));
      } else {
        if (onError != null) {
          onError(
            Exception(
                'FFmpeg process exited with state $state and return code $code.\n${await session.getOutput()}'),
            StackTrace.current,
          );
        }
        return;
      }
    },
    null,
    onProgress != null
        ? (stats) {
            // Progress value of encoded video
            double progressValue =
                stats.getTime() / (_trimEnd - _trimStart).inMilliseconds;
            onProgress(stats, progressValue.clamp(0.0, 1.0));
          }
        : null,
  );
}