applyTextAndEffects static method

Future<String> applyTextAndEffects({
  1. required String inputPath,
  2. required List<VideoEditorClip> clips,
  3. required List<VideoTextOverlay> textOverlays,
  4. required List<VideoEffectOverlay> effectOverlays,
})

Burns clip effects, effect overlays and text into an existing video.

Implementation

static Future<String> applyTextAndEffects({
  required String inputPath,
  required List<VideoEditorClip> clips,
  required List<VideoTextOverlay> textOverlays,
  required List<VideoEffectOverlay> effectOverlays,
}) async {
  final hasClipEffects = clips.any((c) => c.effect != VideoEditorEffect.none);
  if (!hasClipEffects && textOverlays.isEmpty && effectOverlays.isEmpty) {
    return inputPath;
  }

  final directory = await getTemporaryDirectory();
  final timestamp = DateTime.now().millisecondsSinceEpoch;
  final outputPath = '${directory.path}/prixa_effects_$timestamp.mp4';
  final fontPath = await RenderFilterBuilder.getFontPath();

  String between(Duration start, Duration end) =>
      "'between(t,${(start.inMilliseconds / 1000).toStringAsFixed(3)},"
      "${(end.inMilliseconds / 1000).toStringAsFixed(3)})'";

  final filters = <String>[];
  var accumulated = Duration.zero;
  for (final clip in clips) {
    final end = accumulated + clip.effectiveDuration;
    for (final f in RenderFilterBuilder.effectFilterChain(clip.effect)) {
      filters.add('$f:enable=${between(accumulated, end)}');
    }
    accumulated = end;
  }
  for (final overlay in effectOverlays) {
    for (final f in RenderFilterBuilder.effectFilterChain(overlay.effect)) {
      filters.add(
        '$f:enable=${between(overlay.startTime, overlay.effectiveEndTime)}',
      );
    }
  }
  for (final overlay in textOverlays) {
    filters.add(
      RenderFilterBuilder.textOverlayToDrawText(overlay, fontPath ?? ''),
    );
  }

  final session = await FFmpegKit.executeWithArguments([
    '-y',
    '-i', inputPath,
    '-vf', filters.join(','),
    '-map', '0:v:0',
    '-map', '0:a?',
    ...videoCodecArgs(
      canvas: const Size(1080, 1920),
      fps: 30,
      quality: ExportQuality.standard,
      preview: false,
    ),
    '-pix_fmt', 'yuv420p',
    '-c:a', 'aac',
    '-b:a', '192k',
    '-movflags', '+faststart',
    outputPath,
  ]);
  final returnCode = await session.getReturnCode();

  if (!ReturnCode.isSuccess(returnCode)) {
    final logs = await session.getLogsAsString();
    throw RenderException('Text/effects export failed', logs);
  }

  return outputPath;
}