buildSegmentJoin static method
Joins cached segments listed in concatListPath (FFmpeg concat demuxer
format) into the preview MP4. The video is copied as is unless timed
effects need it re-encoded; the audio is re-timed, mixed with the audio
tracks and encoded to AAC.
Implementation
static RenderPlan buildSegmentJoin({
required String concatListPath,
required Duration duration,
required List<VideoEditorAudioTrack> audioTracks,
required List<VideoEffectOverlay> effectOverlays,
required String outputPath,
required List<String> videoCodecArgs,
int fps = 30,
String audioBitrate = '96k',
}) {
final inputs = <String>['-f', 'concat', '-safe', '0', '-i', concatListPath];
final filters = <String>[
// Segments are whole frames long, so audio and video already line up;
// any tiny gap left is filled at once (not slowly corrected), so the
// voice can never drift from the lips across many cuts.
'[0:a]aresample=async=1000:min_hard_comp=0.01:first_pts=0,$_audioFormat[acat]',
];
final hasEffects = effectOverlays
.any((o) => RenderFilterBuilder.effectFilterChain(o.effect).isNotEmpty);
if (hasEffects) {
final last = _addEffects(filters, '0:v', effectOverlays);
filters.add('[$last]format=yuv420p[vout]');
}
_addAudioMix(
inputs: inputs,
filters: filters,
audioTracks: audioTracks,
projectMs: duration.inMilliseconds,
firstInput: 1,
base: 'acat',
);
return RenderPlan(
arguments: [
'-y',
...inputs,
'-filter_complex', filters.join(';'),
'-map', hasEffects ? '[vout]' : '0:v',
'-map', '[aout]',
if (hasEffects) ...[
'-r', '$fps',
...videoCodecArgs,
if (!videoCodecArgs.contains('-pix_fmt')) ...['-pix_fmt', 'yuv420p'],
] else ...['-c:v', 'copy'],
'-c:a', 'aac',
'-b:a', audioBitrate,
'-ar', '44100',
'-movflags', '+faststart',
'-t', _sec(duration.inMilliseconds / 1000),
outputPath,
],
duration: duration,
canvas: Size.zero,
);
}