Update
This commit is contained in:
@@ -14,11 +14,13 @@ const useVideoExport = ({ timelineElements, dimensions, totalDuration }) => {
|
||||
showConsoleLogs && console.log('🎬 STARTING FFmpeg generation');
|
||||
|
||||
const videos = timelineElements.filter((el) => el.type === 'video');
|
||||
const images = timelineElements.filter((el) => el.type === 'image');
|
||||
const texts = timelineElements.filter((el) => el.type === 'text');
|
||||
|
||||
showConsoleLogs && console.log('Videos found:', videos.length);
|
||||
showConsoleLogs && console.log('Images found:', images.length);
|
||||
|
||||
if (videos.length === 0) {
|
||||
if (videos.length === 0 && images.length === 0) {
|
||||
if (is_string) {
|
||||
return 'ffmpeg -f lavfi -i color=black:size=450x800:duration=1 -c:v libx264 -t 1 output.mp4';
|
||||
} else {
|
||||
@@ -27,17 +29,31 @@ const useVideoExport = ({ timelineElements, dimensions, totalDuration }) => {
|
||||
}
|
||||
|
||||
let inputArgs = [];
|
||||
let inputIndex = 0;
|
||||
|
||||
// Add video inputs
|
||||
videos.forEach((v, i) => {
|
||||
inputArgs.push('-i');
|
||||
inputArgs.push(useLocalFiles ? `input${i}.webm` : v.source);
|
||||
inputArgs.push(useLocalFiles ? `input_video_${i}.webm` : v.source_webm);
|
||||
inputIndex++;
|
||||
});
|
||||
|
||||
// Add image inputs with loop and duration
|
||||
images.forEach((img, i) => {
|
||||
inputArgs.push('-loop', '1', '-t', img.duration.toString(), '-i');
|
||||
inputArgs.push(useLocalFiles ? `input_image_${i}.jpg` : img.source);
|
||||
inputIndex++;
|
||||
});
|
||||
|
||||
let filters = [];
|
||||
filters.push(`color=black:size=${dimensions.width}x${dimensions.height}:duration=${totalDuration}[base]`);
|
||||
|
||||
let videoLayer = 'base';
|
||||
let currentInputIndex = 0;
|
||||
|
||||
// Process video elements
|
||||
videos.forEach((v, i) => {
|
||||
filters.push(`[${i}:v]trim=start=${v.inPoint}:duration=${v.duration},setpts=PTS-STARTPTS[v${i}_trim]`);
|
||||
filters.push(`[${currentInputIndex}:v]trim=start=${v.inPoint}:duration=${v.duration},setpts=PTS-STARTPTS[v${i}_trim]`);
|
||||
filters.push(`[v${i}_trim]scale=${Math.round(v.width)}:${Math.round(v.height)}[v${i}_scale]`);
|
||||
filters.push(
|
||||
`[${videoLayer}][v${i}_scale]overlay=${Math.round(v.x)}:${Math.round(v.y)}:enable='between(t,${v.startTime},${
|
||||
@@ -45,6 +61,20 @@ const useVideoExport = ({ timelineElements, dimensions, totalDuration }) => {
|
||||
})'[v${i}_out]`,
|
||||
);
|
||||
videoLayer = `v${i}_out`;
|
||||
currentInputIndex++;
|
||||
});
|
||||
|
||||
// Process image elements
|
||||
images.forEach((img, i) => {
|
||||
const imgInputIndex = currentInputIndex;
|
||||
filters.push(`[${imgInputIndex}:v]scale=${Math.round(img.width)}:${Math.round(img.height)}[img${i}_scale]`);
|
||||
filters.push(
|
||||
`[${videoLayer}][img${i}_scale]overlay=${Math.round(img.x)}:${Math.round(img.y)}:enable='between(t,${img.startTime},${
|
||||
img.startTime + img.duration
|
||||
})'[img${i}_out]`,
|
||||
);
|
||||
videoLayer = `img${i}_out`;
|
||||
currentInputIndex++;
|
||||
});
|
||||
|
||||
showConsoleLogs && console.log('🎵 PROCESSING AUDIO FOR', videos.length, 'VIDEOS');
|
||||
@@ -103,7 +133,18 @@ const useVideoExport = ({ timelineElements, dimensions, totalDuration }) => {
|
||||
];
|
||||
|
||||
if (is_string) {
|
||||
const inputs = videos.map((v, i) => `-i "${useLocalFiles ? `input${i}.webm` : v.source}"`).join(' ');
|
||||
let inputStrings = [];
|
||||
let inputIdx = 0;
|
||||
|
||||
videos.forEach((v, i) => {
|
||||
inputStrings.push(`-i "${useLocalFiles ? `input_video_${i}.webm` : v.source_webm}"`);
|
||||
});
|
||||
|
||||
images.forEach((img, i) => {
|
||||
inputStrings.push(`-loop 1 -t ${img.duration} -i "${useLocalFiles ? `input_image_${i}.jpg` : img.source}"`);
|
||||
});
|
||||
|
||||
const inputs = inputStrings.join(' ');
|
||||
const audioMap = audioArgs.length > 0 ? ` ${audioArgs.join(' ')}` : '';
|
||||
const command = `ffmpeg ${inputs} -filter_complex "${filterComplex}" -map "[${videoLayer}]"${audioMap} -c:v libx264 -pix_fmt yuv420p -r 30 -t ${totalDuration} output.mp4`;
|
||||
|
||||
@@ -172,12 +213,25 @@ const useVideoExport = ({ timelineElements, dimensions, totalDuration }) => {
|
||||
showConsoleLogs && console.log('Font loaded!');
|
||||
setExportProgress(30);
|
||||
|
||||
setExportStatus('Downloading videos...');
|
||||
setExportStatus('Downloading media...');
|
||||
const videos = timelineElements.filter((el) => el.type === 'video');
|
||||
const images = timelineElements.filter((el) => el.type === 'image');
|
||||
const totalMedia = videos.length + images.length;
|
||||
|
||||
let mediaProgress = 0;
|
||||
|
||||
// Download videos
|
||||
for (let i = 0; i < videos.length; i++) {
|
||||
await ffmpeg.writeFile(`input${i}.webm`, await fetchFile(videos[i].source));
|
||||
setExportProgress(30 + Math.round(((i + 1) / videos.length) * 30));
|
||||
await ffmpeg.writeFile(`input_video_${i}.webm`, await fetchFile(videos[i].source_webm));
|
||||
mediaProgress++;
|
||||
setExportProgress(30 + Math.round((mediaProgress / totalMedia) * 30));
|
||||
}
|
||||
|
||||
// Download images
|
||||
for (let i = 0; i < images.length; i++) {
|
||||
await ffmpeg.writeFile(`input_image_${i}.jpg`, await fetchFile(images[i].source));
|
||||
mediaProgress++;
|
||||
setExportProgress(30 + Math.round((mediaProgress / totalMedia) * 30));
|
||||
}
|
||||
|
||||
setExportStatus('Processing video...');
|
||||
|
||||
Reference in New Issue
Block a user