jbilcke-hf HF staff commited on
Commit
f26d0ef
·
1 Parent(s): 8f8b601

trying a better imageToVideo converter

Browse files
src/bug-in-bun/aitube_ffmpeg/overlay/imageToVideoBase64.ts CHANGED
@@ -58,48 +58,65 @@ export async function imageToVideoBase64({
58
  outputVideoFormat,
59
  }, null, 2)}`)
60
 
 
 
61
  // Decode the Base64 image and write it to a temporary file.
62
  const base64Data = inputImageInBase64.substring(inputImageInBase64.indexOf(',') + 1);
63
  const buffer = Buffer.from(base64Data, 'base64');
64
- const inputImagePath = path.join(outputDir, `${uuidv4()}.png`)
65
  await writeFile(inputImagePath, buffer);
66
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
  // Set the path for the output video.
68
  outputFilePath = outputFilePath || path.join(outputDir, `output_${uuidv4()}.${outputVideoFormat}`);
69
-
 
70
  const durationInSeconds = outputVideoDurationInMs / 1000;
 
 
 
 
 
71
 
72
  // Process the image to video conversion using ffmpeg.
73
- await new Promise<void>((resolve, reject) => {
74
 
75
- let ffmpegCommand = ffmpeg(inputImagePath)
76
- .inputOptions(['-loop 1']) // Loop the input image
 
77
  .outputOptions([
78
  `-t ${durationInSeconds}`,
79
  `-r ${fps}`,
80
- `-s ${width}x${height}`, // set frame size
81
- `-c:v ${codec}`, // set the codec
82
  '-tune stillimage',
83
  '-pix_fmt yuv420p'
84
  ])
85
-
86
- if (zoomInRatePerSecond > 0) {
87
- const zoomIncreasePerSecond = zoomInRatePerSecond / 100;
88
- const totalZoomFactor = 1 + (zoomIncreasePerSecond * durationInSeconds);
89
- const framesTotal = durationInSeconds * fps;
90
- const zoomPerFrame = zoomIncreasePerSecond / fps;
91
-
92
- const zoomFormula = `if(lte(zoom\\,${totalZoomFactor}),zoom+${zoomPerFrame}\\,zoom)`;
93
-
94
- ffmpegCommand = ffmpegCommand.videoFilters(`zoompan=z='${zoomFormula}':d=${framesTotal}:x='iw/2-(iw/zoom/2)':y='ih/2-(ih/zoom/2)'`);
95
- }
96
-
97
- return ffmpegCommand
98
- .on('start', function(commandLine) {
99
- console.log('imageToVideoBase64: Spawned Ffmpeg with command: ' + commandLine);
100
- })
101
  .on('end', () => resolve())
102
- .on('error', (err) => reject(err))
103
  .save(outputFilePath);
104
  });
105
 
 
58
  outputVideoFormat,
59
  }, null, 2)}`)
60
 
61
+ outputDir = outputDir || await getRandomDirectory();
62
+
63
  // Decode the Base64 image and write it to a temporary file.
64
  const base64Data = inputImageInBase64.substring(inputImageInBase64.indexOf(',') + 1);
65
  const buffer = Buffer.from(base64Data, 'base64');
66
+ const inputImagePath = path.join(outputDir, `${uuidv4()}.png`);
67
  await writeFile(inputImagePath, buffer);
68
 
69
+ const inputImageDetails = await new Promise<ffmpeg.FfprobeData>((resolve, reject) => {
70
+ ffmpeg.ffprobe(inputImagePath, (err, data) => {
71
+ if (err) reject(err);
72
+ else resolve(data);
73
+ });
74
+ });
75
+
76
+ const originalWidth = inputImageDetails.streams[0].width;
77
+ const originalHeight = inputImageDetails.streams[0].height;
78
+ const originalAspect = originalWidth / originalHeight;
79
+ const targetAspect = width / height;
80
+ let cropWidth, cropHeight;
81
+
82
+ if (originalAspect > targetAspect) {
83
+ // Crop width to match target aspect
84
+ cropHeight = originalHeight;
85
+ cropWidth = Math.floor(cropHeight * targetAspect);
86
+ } else {
87
+ // Crop height to match target aspect
88
+ cropWidth = originalWidth;
89
+ cropHeight = Math.floor(cropWidth / targetAspect);
90
+ }
91
+
92
  // Set the path for the output video.
93
  outputFilePath = outputFilePath || path.join(outputDir, `output_${uuidv4()}.${outputVideoFormat}`);
94
+
95
+ // we want to create a smooth Ken Burns effect
96
  const durationInSeconds = outputVideoDurationInMs / 1000;
97
+ const framesTotal = durationInSeconds * fps;
98
+ const startZoom = 1;
99
+ const endZoom = 1 + zoomInRatePerSecond * durationInSeconds;
100
+ const xCenter = `iw/2-(iw/zoom/2)`;
101
+ const yCenter = `ih/2-(ih/zoom/2)`;
102
 
103
  // Process the image to video conversion using ffmpeg.
 
104
 
105
+ await new Promise<void>((resolve, reject) => {
106
+ ffmpeg(inputImagePath)
107
+ .inputOptions(['-loop 1'])
108
  .outputOptions([
109
  `-t ${durationInSeconds}`,
110
  `-r ${fps}`,
111
+ `-s ${width}x${height}`,
112
+ `-c:v ${codec}`,
113
  '-tune stillimage',
114
  '-pix_fmt yuv420p'
115
  ])
116
+ .videoFilters(`zoompan=z='zoom+${(endZoom - startZoom) / framesTotal}':x='${xCenter}':y='${yCenter}':d=1`)
117
+ .on('start', commandLine => console.log('imageToVideoBase64: Spawned Ffmpeg with command: ' + commandLine))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
118
  .on('end', () => resolve())
119
+ .on('error', err => reject(err))
120
  .save(outputFilePath);
121
  });
122