ffmpegai-videoautomation

Your AI video generator stops at 8 seconds. The workflow doesn't.

·Javid Jamae·10 min read
Your AI video generator stops at 8 seconds. The workflow doesn't.

Your text-to-video model gives you eight seconds. The video you actually want is ninety. Everything between those two numbers is file plumbing, and it's the part the prompt-engineering posts leave out.

Quick answer: An AI video generator workflow ends where the model stops. Text-to-video tools return clips of a few seconds each, so assembly is on you: extract the last frame of each clip with ffmpeg -sseof -0.1 -i clip.mp4 -update 1 frame.jpg to seed the next generation, scale and pad every clip to one canvas and one frame rate before concatenating, then add music and burn captions once on the finished cut. If you'd rather not host an encoder or babysit renders that outlive your workflow's timeout, send the clip URLs to FFmpeg Micro and get the assembled video back from one API call.

The bottleneck in AI video isn't the model, it's the seams

Conventional wisdom says longer AI video is a prompting problem. Write a better scene description, chain your prompts, keep the character consistent, and the model will carry you. That's partly true, and continuity prompting genuinely matters. But the thing that breaks a ten-clip sequence isn't the prose. It's that clip 3 came back at 1280x720 at 24 fps with no audio track, clip 4 came back at 1080x1920 at 30 fps with a silent stereo track, and FFmpeg refuses to join them.

Generation is a solved, purchasable step. Assembly is the half that nobody sells you, so builders wire it by hand: a dev.to writeup on Sora 2 and n8n walks through exactly this, and there's a visible cluster of Sora and FFmpeg MCP servers on glama.ai doing the same job for agents. Four operations cover almost all of it.

Extract the last frame to seed the next clip

Continuity between AI clips comes from the image, not the prompt. Feed the final frame of clip N into clip N+1 as the image conditioning input, and the cut reads as a camera move instead of a jump.

ffmpeg -sseof -0.1 -i clip_03.mp4 -update 1 -q:v 2 seed_04.jpg

-sseof -0.1 seeks to 0.1 seconds before the end of the file, and -update 1 tells the image muxer to keep overwriting one output file rather than writing a numbered sequence. Use -q:v 2 for near-lossless JPEG, or write PNG if your generation endpoint accepts it. Don't seek to exactly the end. Many encoders leave a partial or duplicated final frame, and a 0.1 second offset skips past it.

Two things degrade over a long chain. Color drifts a little with each generation, so by clip eight the grade has wandered. And motion blur baked into the seed frame gets interpreted as scene content by the next model. Pull the seed from a frame with less motion when you can.

Normalize before you concatenate, never `-c copy`

Mixed-source concatenation is the single most common failure in this pipeline. The concat demuxer with -c copy only works when every input shares codec, resolution, pixel format, sample aspect ratio, and time base. AI-generated clips almost never do, and the error is blunt:

Input link in1:v0 parameters (size 1280x720, SAR 1:1) do not match
the corresponding output link in0:v0 parameters (1080x1920, SAR 1:1)

The fix is to scale and pad every input to one canvas, force a constant frame rate, and reset SAR, all inside a single filter_complex so you re-encode exactly once:

ffmpeg -i clip_01.mp4 -i clip_02.mp4 -i clip_03.mp4 -filter_complex "\
[0:v]scale=1080:1920:force_original_aspect_ratio=decrease,\
pad=1080:1920:(ow-iw)/2:(oh-ih)/2:color=black,fps=30,setsar=1[v0];\
[1:v]scale=1080:1920:force_original_aspect_ratio=decrease,\
pad=1080:1920:(ow-iw)/2:(oh-ih)/2:color=black,fps=30,setsar=1[v1];\
[2:v]scale=1080:1920:force_original_aspect_ratio=decrease,\
pad=1080:1920:(ow-iw)/2:(oh-ih)/2:color=black,fps=30,setsar=1[v2];\
[v0][v1][v2]concat=n=3:v=1:a=0[outv]" \
-map "[outv]" -c:v libx264 -crf 20 -preset medium -pix_fmt yuv420p sequence.mp4

Note a=0. Discard the model's audio entirely at this stage. Generated clips are inconsistent about whether an audio track exists at all, and mixing present and absent streams into one concat filter throws Stream specifier ':a' in filtergraph description matches no streams. If you truly need per-clip audio, synthesize silence for the gaps with -f lavfi -t 8 -i anullsrc=channel_layout=stereo:sample_rate=48000. For the deeper version of this problem, including when the demuxer path is still worth using, see FFmpeg concat different resolutions.

If you want the cuts softer than a hard splice, xfade replaces concat for the transitions, with the caveat that its offset is measured from the start of the first input and the output is shorter than the sum of the parts. That's covered in crossfade between clips with xfade.

Lay one audio bed across the seams

A continuous music track is what makes a stitched sequence feel like one video. Apply it to the concatenated file, not to the individual clips, because per-clip audio restarts the waveform at every cut and the listener hears every seam.

ffmpeg -i sequence.mp4 -stream_loop -1 -i bed.mp3 \
-filter_complex "[1:a]volume=0.22,afade=t=out:st=85:d=3[a]" \
-map 0:v -map "[a]" -shortest -c:v copy -c:a aac -b:a 128k final.mp4

-stream_loop -1 has to appear before the input it applies to, so a short loop covers a long sequence. -shortest cuts the music at the end of the video. -c:v copy matters here: the video was already encoded in the concat step, and re-encoding it a second time for an audio change costs quality for nothing. Voiceover mixes on top the same way with amix, covered in add background music to video with FFmpeg.

Caption the finished cut, not each clip

Captions belong at the end of the pipeline, on the assembled file. Transcribing and burning per clip means you re-encode every clip an extra time and then have to shift every subtitle's timestamps by the running offset of its position in the sequence, which is where drift creeps in.

ffmpeg -i final.mp4 -vf "subtitles=captions.srt:force_style=\
'FontName=Inter,FontSize=18,Outline=2,MarginV=90'" \
-c:a copy -c:v libx264 -crf 20 -pix_fmt yuv420p captioned.mp4

MarginV keeps the text off the platform UI at the bottom of a 9:16 frame. If the burn-in lands late by a growing amount as the video plays, the source is variable frame rate, not the subtitle file. That's what the fps=30 in the concat step prevents. If the offset is constant instead of growing, it's a sync problem, and `-itsoffset` is the correct lever.

Running this yourself vs. one API call

Every command above works on your laptop. The question is what happens when the same chain runs forty times a night inside n8n, Make, or a Zapier task, which is what a faceless channel actually looks like in production.

StepSelf-hosted FFmpegFFmpeg Micro
Install and version pinningYour container, your libx264 buildNone
10-clip render at 1080x1920Minutes of CPU you're paying for idleOne job, webhook on completion
Workflow timeoutSync HTTP calls die firstSubmit, poll or webhook, download
Large file memoryn8n loads bytes into the instancePass URLs, nothing routes through n8n
Scaling to 40 renders/nightQueue, workers, retriesConcurrent jobs

The self-hosted route has real gotchas that only show up in production. n8n disabled the Execute Command node by default in v2.0 and later, because arbitrary shell execution is a risk in shared environments, so the old "just shell out to FFmpeg" trick is gone on most instances. The free NCA Toolkit is the community's usual answer, and its own README recommends Google Cloud Run while documenting that Cloud Run is "best for processing under 5 minutes," which a ten-clip assembly can exceed. Moving the bytes through n8n at all is its own failure class, covered in n8n running out of memory on large video files.

This is the step the FFmpeg API exists for. You post the clip URLs and the target canvas, the job runs on managed encoders, and a webhook tells your workflow where the finished MP4 is. No binaries, no workers, no timeout to design around, and a free tier to test the whole chain before you commit to it.

Pitfalls that only appear after clip five

Skipping -pix_fmt yuv420p produces a file that plays in VLC and shows a black rectangle in Safari and on iOS. libx264 will happily pick yuv444p from some inputs, and hardware decoders won't touch it.

Padding to a canvas that doesn't divide by 2 throws height not divisible by 2. AI models return odd dimensions more often than phones do, so force the target size explicitly instead of using -1 on both axes. The full fix is here.

Re-encoding at every step compounds. A ten-clip video that gets encoded for concat, again for music, again for captions, and again for a platform-specific export has been through libx264 four times. Encode once at concat, then copy the video stream everywhere it isn't being changed.

And watch the -sseof value. On clips shorter than about a second, 0.1 seconds from the end can land before the last keyframe and give you a blurry seed frame.

When this pipeline is the wrong tool

Programmatic assembly is right when the sequence is generated and repeatable. It's wrong when a human needs to make taste decisions per cut. If someone is going to watch each transition and nudge it, you want a timeline editor, not an API, and no amount of filter_complex will replace that judgment. FFmpeg Micro is an API, not an editor.

It's also the wrong tool if your output is one hero video a month. The setup cost only pays back across volume. The break-even is somewhere around the point where you're assembling several videos a week and have stopped enjoying it.

FAQ

How do I make a longer video from an AI video generator?

You make a longer AI video by generating a sequence of short clips and concatenating them, because current text-to-video models cap a single generation at a handful of seconds. The continuity trick is to extract the last frame of each clip and pass it as the image seed for the next generation, then normalize all the clips to one resolution and frame rate before joining them.

Why does my concatenated AI video have black frames or audio drift?

Black frames and audio drift in a concatenated AI video come from joining clips with mismatched parameters using -c copy, which preserves each clip's original time base and pixel format instead of unifying them. Normalizing with scale, pad, fps, and setsar inside one filter_complex and re-encoding once fixes both symptoms.

Can n8n stitch AI-generated clips together?

n8n can orchestrate the stitching but shouldn't do the encoding itself. The Execute Command node is disabled by default in n8n v2.0 and later, and routing large video files through the instance is a known memory failure, so the working pattern is to have n8n pass clip URLs to a video processing API and receive a webhook when the render finishes. A complete example lives in the n8n clip workflow walkthrough.

Should I burn captions on each clip or on the final video?

Burn captions on the final assembled video. Captioning each clip separately forces an extra encode per clip and requires shifting every subtitle timestamp by that clip's offset in the sequence, which is the usual source of captions that drift later and later as the video plays.

Do AI-generated clips need the same frame rate to concatenate?

Clips do need a uniform frame rate for a clean concatenation, and the fps filter is what enforces it. Generated clips commonly come back at 24, 25, or 30 fps, and mixing them without normalizing produces stuttering at the seams plus caption timing that slips over the length of the video.

If your assembly step is an agent rather than a workflow, the MCP server exposes the same jobs as tool calls, so Claude can concatenate, add the audio bed, and burn captions without you writing the filtergraph at all. Sign up free and run your next ten-clip sequence through it.

About Javid Jamae

Founder & CEO at FFmpeg Micro

Javid is a software engineer, author, and entrepreneur with over 25 years of professional software development experience across enterprise, startup, and consulting environments. He founded FFmpeg Micro to make video processing accessible to developers through a simple, automation-first REST API.

Software EngineeringVideo ProcessingFFmpegCloud ArchitectureAPI DesignAutomation

Ready to process videos at scale?

Start using FFmpeg Micro's simple API today. No infrastructure required.

Get Started Free