ai-videovideo-formats

Veo and Sora Lock AI Video Aspect Ratio. Crop 4:5 and 1:1.

·Javid Jamae·10 min read
Veo and Sora Lock AI Video Aspect Ratio. Crop 4:5 and 1:1.

You asked Veo for a 4:5 clip for Instagram feed and got 16:9 back. You tried "aspectRatio": "4:5" and the Gemini API rejected it. The parameter isn't broken and your prompt isn't the problem: the models only render two shapes, and every other format in your ad set is a job for the step after generation.

Quick answer: Every AI video aspect ratio available today from Google's Veo models on the Gemini API and OpenAI's Sora is either 16:9 or 9:16. Veo accepts exactly those two aspectRatio values, and Sora's size options (1280x720, 720x1280, plus 1920x1080 and 1080x1920 on sora-2-pro) resolve to the same two shapes. To get 4:5 for Instagram feed, 1:1 for square placements, or a 16:9 ad master, crop or pad the generated file afterward with FFmpeg (crop=1080:1350:0:285 gives 4:5 from a 1080x1920 master), or send one generation to FFmpeg Micro and get all four variants back from a single API call, no encoder to host.

Why Veo and Sora only render two aspect ratios

Veo and Sora are trained and served at fixed output geometries, so the aspect ratio is a serving constraint rather than a creative control. The Gemini API's video generation config exposes aspectRatio with two accepted values, 16:9 and 9:16, across current Veo variants. Sora doesn't expose a ratio at all. It exposes a size string, and each valid size is one of two shapes: 1280x720 or 720x1280 on the standard model, 1920x1080 or 1080x1920 on sora-2-pro.

Prompt Architects' 2026 aspect ratio reference for AI video says the quiet part directly: other ratios "have to be handled outside the model, not inside the parameter." That's correct, and it's where most guides stop. Nobody publishes the expressions.

Meta's two highest-performing feed formats, 4:5 and 1:1, can't be generated. They can only be derived. If your pipeline produces AI ad creative, an FFmpeg step isn't optional infrastructure, it's what makes the output usable.

Pad, crop, or blurred fill: decide before you generate

Padding, cropping, and blurred fill each give up something different, and picking between them is a creative decision you encode as a rule once, not per clip. Padding keeps every pixel and adds bars. Cropping keeps full resolution and throws away part of the frame. Blurred fill keeps every pixel and hides the bars behind a scaled, blurred copy of the same video.

That maps onto a 1080x1920 master from Veo or Sora like this:

TargetOutput sizeMethodWhat it costs
9:16 (Reels, TikTok, Shorts)1080x1920native, no changenothing
4:5 (Instagram feed)1080x1350center crop, y offset 28529.7% of the frame height
1:1 (square placements)1080x1080center crop, y offset 42043.75% of the frame height
16:9 (YouTube, Amazon-safe)1920x1080blurred fill, 608px subjectno pixels lost, 68% of the canvas is blur

Crop wins for 4:5 and 1:1 because both targets are close enough to 9:16 that the surviving frame still fills the placement at full 1080px width. Nobody wants pillarboxed bars in an Instagram feed ad. For 16:9 the math flips: cropping a 9:16 master to 16:9 leaves you 1080x607, which means discarding 68% of the rows and then upscaling 1.78x to reach 1920 wide. That's a visibly soft file. Use a blurred fill there, or generate 16:9 natively as a second call, which costs one more generation but zero quality.

The exact FFmpeg expressions for 4:5 and 1:1

All three derivations below assume a 1080x1920 master, which is what Veo returns at 9:16 and what sora-2-pro returns at 1080x1920. If your master is 720x1280, scale the offsets by 0.667 or scale the file to 1080 wide first.

4:5 for Instagram feed

The 4:5 crop keeps the full 1080px width and removes 285 rows from the top and 285 from the bottom:

ffmpeg -i veo_9x16.mp4 \
  -vf "crop=1080:1350:0:285" \
  -c:v libx264 -crf 20 -preset medium \
  -c:a aac -b:a 128k -movflags +faststart \
  ad_4x5.mp4

FFmpeg's crop takes w:h:x:y, and the y offset is (1920 - 1350) / 2 = 285. Write it as an expression if your master height varies: crop=iw:iw*5/4:0:(ih-iw*5/4)/2.

1:1 for square placements

The square crop is the same operation with a bigger bite, 420 rows off each end:

ffmpeg -i veo_9x16.mp4 \
  -vf "crop=1080:1080:0:420" \
  -c:v libx264 -crf 20 -preset medium \
  -c:a aac -b:a 128k -movflags +faststart \
  ad_1x1.mp4

16:9 with a blurred fill

The 16:9 variant scales the master to cover a 1920x1080 canvas, blurs that copy as a background bed, and overlays the correctly proportioned 608x1080 video on top:

ffmpeg -i veo_9x16.mp4 -filter_complex \
 "[0:v]split=2[bg][fg]; \
  [bg]scale=1920:-2,crop=1920:1080,gblur=sigma=24[bgb]; \
  [fg]scale=-2:1080[fgs]; \
  [bgb][fgs]overlay=(W-w)/2:0" \
  -c:v libx264 -crf 20 -preset medium \
  -c:a aac -b:a 128k -movflags +faststart \
  ad_16x9.mp4

scale=1920:-2 forces an even output height so libx264's yuv420p chroma subsampling doesn't fail, and sigma=24 is strong enough that the background reads as texture rather than a second, confusing video.

Keep the subject inside the 1:1 window while you prompt

Cropping moves every pixel's y coordinate, so text and faces that looked well placed in the 9:16 master can land outside the derived frames. The rule that prevents this: anything that must survive all four variants has to sit between y=420 and y=1500 on the 1080x1920 canvas, because that 1080px band is the intersection of the 1:1 crop and everything wider.

Work the failure case. Say you burn a caption baseline at y=1600, which clears TikTok's roughly 320px bottom UI margin on a 1080x1920 canvas. After the 4:5 crop it sits at y=1315 in a 1350px-tall frame, 35px from the bottom edge and effectively touching it. After the 1:1 crop it's at y=1180 in a 1080px frame, which means it isn't in the file at all. The caption didn't move. The frame did. Platform safe zones are a separate coordinate problem from crop offsets, and I wrote up the pixel conversion in TikTok safe zone margins aren't FFmpeg pixels.

Two things follow. First, prompt for a centered subject when you're deriving a square, because no crop filter can recover a subject the model parked at the top of the frame. Second, burn captions and hook text per variant, after the crop, not once on the master. FFmpeg has no subject tracking, so a fixed center crop is what you get unless you build detection yourself, which I covered in FFmpeg can't auto reframe video to vertical.

Generate once, produce all four variants in one pass

Re-prompting Veo or Sora per format is the expensive mistake: you'd burn four generations to get two shapes, and the four clips wouldn't match because the model reseeds every run. One decode, four encodes, one command:

ffmpeg -i veo_9x16.mp4 -filter_complex \
 "[0:v]split=4[v916][v45][v11][v169]; \
  [v45]crop=1080:1350:0:285[out45]; \
  [v11]crop=1080:1080:0:420[out11]; \
  [v169]split=2[bg][fg]; \
  [bg]scale=1920:-2,crop=1920:1080,gblur=sigma=24[bgb]; \
  [fg]scale=-2:1080[fgs]; \
  [bgb][fgs]overlay=(W-w)/2:0[out169]" \
 -map "[v916]" -map 0:a? -c:v libx264 -crf 20 -c:a aac -movflags +faststart out_9x16.mp4 \
 -map "[out45]" -map 0:a? -c:v libx264 -crf 20 -c:a aac -movflags +faststart out_4x5.mp4 \
 -map "[out11]" -map 0:a? -c:v libx264 -crf 20 -c:a aac -movflags +faststart out_1x1.mp4 \
 -map "[out169]" -map 0:a? -c:v libx264 -crf 20 -c:a aac -movflags +faststart out_16x9.mp4

split duplicates the decoded stream so FFmpeg parses the input once, and the four -map blocks write four files from that single pass. The ? on -map 0:a? makes the audio mapping optional, which matters because a silent generation would otherwise abort the whole job.

If you'd rather not host an encoder to run that graph, this exact fan-out is what the Listing Kit blueprint does: post one video, get back TikTok/Reels 9:16, 1:1, 4:5, and an Amazon-safe 16:9. Same filter math, one API call, no binary to install, and it drops into an n8n, Make, or Zapier branch right after your Veo or Sora node.

Common pitfalls

Errors in a reframing pipeline are mostly coordinate mistakes and stream assumptions.

  • Invalid value for aspectRatio from the Gemini API means you passed something other than 16:9 or 9:16. No retry or prompt rewrite changes that. The two-value list is the whole list.
  • height not divisible by 2 shows up when you compute a crop or scale height from an odd ratio. 1080 * 4 / 5 is fine at 1350, but derived widths like 759.375 are not. Use -2 in scale and round crop expressions to even numbers.
  • Stream map '0:a' matches no streams happens when the generated clip has no audio track. That's an input fact, not a bad file, and I unpacked it in Stream map matches no streams means no audio.
  • Cropping before burning captions silently destroys text. Always burn per variant.
  • Assuming the 4:5 crop is safe for Reels. 4:5 is a feed format. Instagram Reels wants 9:16, and uploading the 4:5 file there gets it letterboxed by the platform.

When reframing is the wrong call

Reframing has a real limit: it can't invent framing the model never captured. If the generated shot is a wide landscape with the subject at the left third, a center crop to 1:1 delivers a square of empty background. Re-generate at 9:16 instead and derive downward, because a 9:16 master is the only shape the two-ratio menu offers that can produce all four targets without upscaling. That's the single decision that makes this pipeline cheap.

Don't hard-code one generator either. OpenAI's deprecations page schedules the Sora API for removal on September 24, 2026, so a pipeline that assumes Sora's size strings will break on a date you can already read. Keep generation behind an interface and the reframing step generator-agnostic. It takes a video file, not a vendor. The same separation is why AI video pipelines usually fail at assembly rather than generation, which I argued in AI avatar video automation breaks at assembly.

FAQ

Can Veo 3 generate a 4:5 aspect ratio?

Veo 3 cannot generate 4:5. The Gemini API's aspectRatio parameter accepts 16:9 and 9:16 only, so a 4:5 Instagram feed video has to be cropped or padded from one of those two outputs after generation.

What aspect ratio options does Sora 2 support?

Sora 2 supports two shapes through its size parameter: 1280x720 (16:9) and 720x1280 (9:16), with sora-2-pro adding 1920x1080 and 1080x1920. There is no square or 4:5 size, and no ratio parameter separate from size.

Should I crop or pad an AI-generated video to change its aspect ratio?

Crop when the target ratio is close to the source, such as 9:16 to 4:5 or 1:1, because cropping keeps the full 1080px width and avoids bars. Pad or use a blurred fill when the target is far from the source, such as 9:16 to 16:9, where cropping would discard about 68% of the frame and force an upscale.

Does cropping to 1:1 reduce video quality?

Cropping a 1080x1920 master to 1080x1080 does not reduce quality, because it removes rows rather than resampling them. The output stays at native 1080px width. Quality only drops when a crop is followed by an upscale, which is what happens if you try to reach 1920x1080 from a vertical master.

How do I make all four ad formats from one AI generation?

Generate once at 9:16, then run a single FFmpeg pass that splits the decoded stream four ways and writes 9:16, 4:5, 1:1, and 16:9 outputs from it. One decode, four encodes, and all four files share identical footage so the ad set stays visually consistent.

Point your Veo or Sora node at the reframing step instead of re-prompting for formats the models don't have, and the four-variant fan-out stops being a script you maintain. The free tier covers enough jobs to run a full ad set through it: sign up free and send one generation through the blueprints catalog to see the four files come back.

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

Skip the command line

The Listing Video Kit blueprint runs the same job for you: upload, done.

Run it (free)