Skip to content

Commit ba5f87b

Browse files
committed
Added FFmpeg / sharp / vercel and updated mint.json
1 parent 2b5771f commit ba5f87b

File tree

5 files changed

+509
-50
lines changed

5 files changed

+509
-50
lines changed
File renamed without changes.
Lines changed: 328 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,328 @@
1+
---
2+
title: "Video processing with FFmpeg"
3+
sidebarTitle: "FFmpeg video processing"
4+
description: "These examples show you how to process videos in various ways using FFmpeg with Trigger.dev."
5+
---
6+
7+
## Adding the FFmpeg build extension
8+
9+
To use these example tasks, you'll first need to add our FFmpeg extension to your project configuration like this:
10+
11+
```ts trigger.config.ts
12+
import { ffmpeg } from "@trigger.dev/build/extensions/core";
13+
import { defineConfig } from "@trigger.dev/sdk/v3";
14+
15+
export default defineConfig({
16+
project: "<project ref>",
17+
// Your other config settings...
18+
build: {
19+
extensions: [ffmpeg()],
20+
},
21+
});
22+
```
23+
24+
<Note>
25+
[Build extensions](../guides/build-extensions) allow you to hook into the build system and
26+
customize the build process or the resulting bundle and container image (in the case of
27+
deploying). You can use pre-built extensions or create your own.
28+
</Note>
29+
30+
## Compress a video using FFmpeg
31+
32+
This task demonstrates how to use FFmpeg to compress a video, reducing its file size while maintaining reasonable quality, and upload the compressed video to R2 storage.
33+
34+
### Key Features:
35+
36+
- Fetches a video from a given URL
37+
- Compresses the video using FFmpeg with various compression settings
38+
- Uploads the compressed video to R2 storage
39+
40+
### Task code
41+
42+
```ts trigger/ffmpeg-compress-video.ts
43+
import { PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
44+
import { logger, task } from "@trigger.dev/sdk/v3";
45+
import ffmpeg from "fluent-ffmpeg";
46+
import fs from "fs/promises";
47+
import fetch from "node-fetch";
48+
import { Readable } from "node:stream";
49+
import os from "os";
50+
import path from "path";
51+
52+
// Initialize S3 client for R2 storage
53+
const s3Client = new S3Client({
54+
region: "auto",
55+
endpoint: process.env.S3_ENDPOINT,
56+
credentials: {
57+
accessKeyId: process.env.R2_ACCESS_KEY_ID ?? "",
58+
secretAccessKey: process.env.R2_SECRET_ACCESS_KEY ?? "",
59+
},
60+
});
61+
62+
export const ffmpegCompressVideo = task({
63+
id: "ffmpeg-compress-video",
64+
run: async (payload: { videoUrl: string }) => {
65+
const { videoUrl } = payload;
66+
67+
// Generate output file name with a timestamp
68+
const tempDirectory = os.tmpdir();
69+
const outputPath = path.join(tempDirectory, `output_${Date.now()}.mp4`);
70+
71+
// Fetch the video from the provided URL
72+
const response = await fetch(videoUrl);
73+
74+
// Compress the video using FFmpeg
75+
await new Promise((resolve, reject) => {
76+
if (!response.body) {
77+
return reject(new Error("Failed to fetch video"));
78+
}
79+
80+
ffmpeg(Readable.from(response.body))
81+
.outputOptions([
82+
"-c:v libx264", // Use H.264 codec
83+
"-crf 28", // Higher CRF for more compression (28 is near the upper limit for acceptable quality)
84+
"-preset veryslow", // Slowest preset for best compression
85+
"-vf scale=iw/2:ih/2", // Reduce resolution to 50% of original width and height
86+
"-c:a aac", // Use AAC for audio
87+
"-b:a 64k", // Reduce audio bitrate to 64k
88+
"-ac 1", // Convert to mono audio
89+
])
90+
.output(outputPath)
91+
.on("end", resolve)
92+
.on("error", reject)
93+
.run();
94+
});
95+
96+
// Read the compressed video into a buffer
97+
const compressedVideo = await fs.readFile(outputPath);
98+
99+
// Get the compressed video size
100+
const compressedSize = compressedVideo.length;
101+
102+
// Log compression results for debugging purposes
103+
logger.log(`Compressed video size: ${compressedSize} bytes`);
104+
logger.log(`Compressed video saved at: ${outputPath}`);
105+
106+
// Generate the S3 key for the uploaded video file
107+
const s3Key = `processed-videos/${path.basename(outputPath)}`;
108+
109+
// Set up the parameters for uploading the video to R2
110+
const uploadParams = {
111+
Bucket: process.env.S3_BUCKET,
112+
Key: s3Key,
113+
Body: compressedVideo,
114+
};
115+
116+
// Upload the video to R2 and get the public URL
117+
await s3Client.send(new PutObjectCommand(uploadParams));
118+
const s3Url = `https://${process.env.S3_BUCKET}.s3.amazonaws.com/${s3Key}`;
119+
logger.log("Compressed video uploaded to R2", { url: s3Url });
120+
121+
// Delete the temporary compressed video file
122+
await fs.unlink(outputPath);
123+
124+
// Return the compressed video file path, compressed size, and R2 URL
125+
return {
126+
compressedVideoPath: outputPath,
127+
compressedSize,
128+
s3Url,
129+
};
130+
},
131+
});
132+
```
133+
134+
## Extract audio from a video using FFmpeg
135+
136+
This task demonstrates how to use FFmpeg to extract audio from a video, convert it to WAV format, and upload it to R2 storage.
137+
138+
### Key Features:
139+
140+
- Fetches a video from a given URL
141+
- Extracts the audio from the video using FFmpeg
142+
- Converts the extracted audio to WAV format
143+
- Uploads the extracted audio to R2 storage
144+
145+
### Task code
146+
147+
<Warning>
148+
When testing, make sure to provide a video URL that contains audio. If the video does not have
149+
audio, the task will fail.
150+
</Warning>
151+
152+
```ts trigger/ffmpeg-extract-audio.ts
153+
import { PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
154+
import { logger, task } from "@trigger.dev/sdk/v3";
155+
import ffmpeg from "fluent-ffmpeg";
156+
import fs from "fs/promises";
157+
import fetch from "node-fetch";
158+
import { Readable } from "node:stream";
159+
import os from "os";
160+
import path from "path";
161+
162+
// Initialize S3 client for R2 storage
163+
const s3Client = new S3Client({
164+
region: "auto",
165+
endpoint: process.env.S3_ENDPOINT,
166+
credentials: {
167+
accessKeyId: process.env.R2_ACCESS_KEY_ID ?? "",
168+
secretAccessKey: process.env.R2_SECRET_ACCESS_KEY ?? "",
169+
},
170+
});
171+
172+
export const ffmpegExtractAudio = task({
173+
id: "ffmpeg-extract-audio",
174+
run: async (payload: { videoUrl: string }) => {
175+
const { videoUrl } = payload;
176+
177+
// Generate output file name with a timestamp
178+
const tempDirectory = os.tmpdir();
179+
const outputPath = path.join(tempDirectory, `output_${Date.now()}.wav`);
180+
181+
// Fetch the video from the provided URL
182+
const response = await fetch(videoUrl);
183+
184+
// Convert the video to WAV format using FFmpeg
185+
await new Promise((resolve, reject) => {
186+
if (!response.body) {
187+
return reject(new Error("Failed to fetch video"));
188+
}
189+
ffmpeg(Readable.from(response.body))
190+
.toFormat("wav")
191+
.save(outputPath)
192+
.on("end", () => {
193+
logger.log(`WAV file saved to ${outputPath}`);
194+
resolve(outputPath);
195+
})
196+
.on("error", (err) => {
197+
reject(err);
198+
});
199+
});
200+
201+
// Read the WAV file into a buffer
202+
const wavBuffer = await fs.readFile(outputPath);
203+
204+
// Log the output file path for debugging purposes
205+
logger.log(`Converted video saved at: ${outputPath}`);
206+
207+
// Generate the S3 key for the uploaded audio file
208+
const s3Key = `processed-audio/${path.basename(outputPath)}`;
209+
210+
// Set up the parameters for uploading the audio to R2
211+
const uploadParams = {
212+
Bucket: process.env.S3_BUCKET,
213+
Key: s3Key,
214+
Body: wavBuffer,
215+
};
216+
217+
// Upload the audio to R2 and get the public URL
218+
await s3Client.send(new PutObjectCommand(uploadParams));
219+
const s3Url = `https://${process.env.S3_BUCKET}.s3.amazonaws.com/${s3Key}`;
220+
logger.log("Extracted audio uploaded to R2", { url: s3Url });
221+
222+
// Delete the temporary output file
223+
await fs.unlink(outputPath);
224+
225+
// Return the WAV buffer, file path, and R2 URL
226+
return {
227+
wavBuffer,
228+
wavFilePath: outputPath,
229+
s3Url,
230+
};
231+
},
232+
});
233+
```
234+
235+
## Generate a thumbnail from a video using FFmpeg
236+
237+
This task demonstrates how to use FFmpeg to generate a thumbnail from a video at a specific time and upload the generated thumbnail to R2 storage.
238+
239+
### Key Features:
240+
241+
- Fetches a video from a given URL
242+
- Generates a thumbnail from the video at the 5-second mark
243+
- Uploads the generated thumbnail to R2 storage
244+
245+
### Task code
246+
247+
```ts trigger/ffmpeg-generate-thumbnail.ts
248+
import { PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
249+
import { logger, task } from "@trigger.dev/sdk/v3";
250+
import ffmpeg from "fluent-ffmpeg";
251+
import fs from "fs/promises";
252+
import fetch from "node-fetch";
253+
import { Readable } from "node:stream";
254+
import os from "os";
255+
import path from "path";
256+
257+
// Initialize S3 client for R2 storage
258+
const s3Client = new S3Client({
259+
region: "auto",
260+
endpoint: process.env.S3_ENDPOINT,
261+
credentials: {
262+
accessKeyId: process.env.R2_ACCESS_KEY_ID ?? "",
263+
secretAccessKey: process.env.R2_SECRET_ACCESS_KEY ?? "",
264+
},
265+
});
266+
267+
export const ffmpegGenerateThumbnail = task({
268+
id: "ffmpeg-generate-thumbnail",
269+
run: async (payload: { videoUrl: string }) => {
270+
const { videoUrl } = payload;
271+
272+
// Generate output file name with a timestamp
273+
const tempDirectory = os.tmpdir();
274+
const outputPath = path.join(tempDirectory, `thumbnail_${Date.now()}.jpg`);
275+
276+
// Fetch the video from the provided URL
277+
const response = await fetch(videoUrl);
278+
279+
// Generate the thumbnail using FFmpeg
280+
await new Promise((resolve, reject) => {
281+
if (!response.body) {
282+
return reject(new Error("Failed to fetch video"));
283+
}
284+
ffmpeg(Readable.from(response.body))
285+
.screenshots({
286+
count: 1,
287+
folder: "/tmp",
288+
filename: path.basename(outputPath),
289+
size: "320x240",
290+
timemarks: ["5"], // 5 seconds
291+
})
292+
.on("end", resolve)
293+
.on("error", reject);
294+
});
295+
296+
// Read the generated thumbnail into a buffer
297+
const thumbnail = await fs.readFile(outputPath);
298+
299+
// Generate the S3 key for the uploaded thumbnail file
300+
const s3Key = `thumbnails/${path.basename(outputPath)}`;
301+
302+
// Set up the parameters for uploading the thumbnail to R2
303+
const uploadParams = {
304+
Bucket: process.env.S3_BUCKET,
305+
Key: s3Key,
306+
Body: thumbnail,
307+
};
308+
309+
// Upload the thumbnail to R2 and get the public URL
310+
await s3Client.send(new PutObjectCommand(uploadParams));
311+
const s3Url = `https://${process.env.S3_BUCKET}.s3.amazonaws.com/${s3Key}`;
312+
logger.log("Thumbnail uploaded to R2", { url: s3Url });
313+
314+
// Delete the temporary thumbnail file
315+
await fs.unlink(outputPath);
316+
317+
// Log thumbnail generation results for debugging purposes
318+
logger.log(`Thumbnail uploaded to S3: ${s3Url}`);
319+
320+
// Return the thumbnail buffer, file path, and R2 URL
321+
return {
322+
thumbnailBuffer: thumbnail,
323+
thumbnailPath: outputPath,
324+
s3Url,
325+
};
326+
},
327+
});
328+
```

0 commit comments

Comments
 (0)