sojorn/sojorn_app/lib/services/video_stitching_service.dart

55 lines
2.1 KiB
Dart

import 'dart:io';
import 'package:ffmpeg_kit_flutter_new/ffmpeg_kit.dart';
import 'package:ffmpeg_kit_flutter_new/ffmpeg_session.dart';
import 'package:ffmpeg_kit_flutter_new/return_code.dart';
import 'package:path_provider/path_provider.dart';
class VideoStitchingService {
/// Stitches multiple video files into a single video file using FFmpeg.
///
/// Returns the stitched file, or null if stitching failed or input is empty.
static Future<File?> stitchVideos(List<File> segments) async {
if (segments.isEmpty) return null;
if (segments.length == 1) return segments.first;
try {
// 1. Create a temporary file listing all segments for FFmpeg concat demuxer
final tempDir = await getTemporaryDirectory();
final listFile = File('${tempDir.path}/segments_list.txt');
final buffer = StringBuffer();
for (final segment in segments) {
// FFmpeg requires safe paths (escaping special chars might be needed, but usually basic paths are fine)
// IMPORTANT: pathways in list file for concat demuxer must be absolute.
buffer.writeln("file '${segment.path}'");
}
await listFile.writeAsString(buffer.toString());
// 2. Define output path
final outputFile = File('${tempDir.path}/stitched_${DateTime.now().millisecondsSinceEpoch}.mp4');
// 3. Execute FFmpeg command
// -f concat: format
// -safe 0: allow unsafe paths (required for absolute paths)
// -i listFile: input list
// -c copy: stream copy (fast, no re-encoding)
final command = "-f concat -safe 0 -i '${listFile.path}' -c copy '${outputFile.path}'";
final session = await FFmpegKit.execute(command);
final returnCode = await session.getReturnCode();
if (ReturnCode.isSuccess(returnCode)) {
return outputFile;
} else {
// Fallback: return the last segment or first one to at least save something?
// For strict correctness, return null or throw.
// Let's print logs.
final logs = await session.getOutput();
return null;
}
} catch (e) {
return null;
}
}
}