Merge and Combine Inputs
Send multiple inputs in one job to replace an audio track, or combine separate video and audio files.
Follow along with a live API key. 100 free minutes, no credit card.
Get free API keyOne job, many inputs
Every job so far has taken a single input. But the inputs array accepts up to ten items, and FFmpeg Micro feeds them all into the same command. That's how you replace an audio track, combine a separate video and audio file, or mix a soundtrack over silent footage, all in one request, without stitching files together yourself first.
You already have createTranscode, waitForJob, and getDownloadUrl from Lesson 3. In this lesson you just call createTranscode with more than one input.
Attach an audio track to a video
Pass the video first and the audio second. The API maps the first video stream and the first audio stream into the output by default, so order is what tells it which input is which. Copy the video through untouched and encode the new audio track:
const job = await createTranscode({
inputs: [
{ url: videoUrl },
{ url: audioUrl },
],
outputFormat: 'mp4',
options: [
{ option: '-c:v', argument: 'copy' },
{ option: '-c:a', argument: 'aac' },
],
})-c:v copyavoids re-encoding the video. It's fast and lossless. The argument valuecopyis allowed.-c:a aacencodes the new audio track into the MP4.- The first video stream and first audio stream are mapped by default, so you don't have to.
A reusable helper
Wrap it up so you can merge any video and audio pair. Adding -shortest is a safe default: it ends the output when the shorter input runs out, which keeps mismatched lengths from producing trailing silence or a truncated track. Then compose the lifecycle you already know. Submit, poll, download:
// merge.mjs
async function mergeAudioVideo(videoUrl, audioUrl) {
const job = await createTranscode({
inputs: [
{ url: videoUrl },
{ url: audioUrl },
],
outputFormat: 'mp4',
options: [
{ option: '-c:v', argument: 'copy' },
{ option: '-c:a', argument: 'aac' },
{ option: '-shortest' },
],
})
const done = await waitForJob(job.id)
return getDownloadUrl(done.id)
}
const url = await mergeAudioVideo(
'https://example.com/silent-clip.mp4',
'https://example.com/soundtrack.mp3',
)
console.log('Merged file:', url)Run it the same way as every script in this course:
node --env-file=.env merge.mjsChoosing streams explicitly
The defaults are right most of the time, but when a source has multiple streams, or you want the audio from input 1 over the video from input 0, reach for -map. It takes an argument like 0:v:0 (first video stream of the first input) or 1:a:0 (first audio stream of the second input) and puts exactly that stream into the output:
options: [
{ option: '-map', argument: '0:v:0' },
{ option: '-map', argument: '1:a:0' },
{ option: '-c:v', argument: 'copy' },
{ option: '-c:a', argument: 'aac' },
]What could go wrong: If the audio and video lengths differ, you'll get trailing silence when the audio is shorter, or a truncated audio track when the video is shorter. Add -shortest or trim the inputs to match. If a source has multiple streams and the defaults pick the wrong one, use -map to select the stream explicitly. And remember inputs is capped at ten items.
Key takeaways
- Pass multiple items in
inputsto combine sources in one job, up to ten. -c:v copyskips re-encoding the video: fast and lossless.-c:a aacencodes the new audio track into the output.-shortestends the output at the shorter input, handling length mismatches.-mapgives you explicit control over which input and stream lands in the output.
Next up: run many jobs at once with batch processing at scale, a concurrency limiter, and per-item error handling.
Build this into your app. No FFmpeg install
One call kicks off a job. No local FFmpeg, no servers, no worker queue to run.
const res = await fetch('https://api.ffmpeg-micro.com/v1/transcodes', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.FFMPEG_MICRO_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
inputs: [{ url: 'gs://your-bucket/input.mp4' }],
outputFormat: 'mp4',
preset: { quality: 'medium', resolution: '1080p' },
}),
})
const job = await res.json() // { id, status: 'pending' }