FFmpeg Scale Video: Resize to 4K, 1080p, 720p + API

April 8, 2026 · RenderIO

The scale filter, from the beginning

If you've ever tried to ffmpeg scale video and gotten a cryptic error about dimensions being not divisible by 2, you've met the scale filter. It's one of the most-used video filters in FFmpeg and also one of the easiest to get slightly wrong.

This guide covers the full range: basic syntax, how to preserve aspect ratios, platform-specific presets for TikTok, YouTube, and Instagram, letterboxing to avoid stretching, and how to run batch scaling jobs over an API when you have more than a handful of files. If you want a quick reference for common FFmpeg operations while you read, keep the FFmpeg cheat sheet open in another tab.

The basic command

ffmpeg -i input.mp4 -vf "scale=1280:720" output.mp4

That's it. -vf "scale=width:height" is the whole thing. This resizes the video to exactly 1280x720 pixels. The problem is that "exactly" part. If your input is 4K at 16:9 and you scale to 1280x720, everything is fine. If your input is some other aspect ratio, you get a stretched video.

Almost nobody wants stretched video. So in practice you almost never specify both dimensions directly.

Aspect ratio preservation: the -1 trick

The most important thing to know about scale is the -1 shorthand. When you set one dimension to -1, FFmpeg calculates the other automatically to maintain the original aspect ratio.

Scale to 1280 wide, keep the ratio:

ffmpeg -i input.mp4 -vf "scale=1280:-1" output.mp4

Scale to 720 tall, keep the ratio:

ffmpeg -i input.mp4 -vf "scale=-1:720" output.mp4

This works for most inputs. But some codecs (H.264 in particular) require dimensions divisible by 2. A -1 calculation can sometimes land on an odd number, which causes an encoder error. Use -2 instead of -1 to automatically round to the nearest even number:

ffmpeg -i input.mp4 -vf "scale=1280:-2" output.mp4

Use -2 by default and you'll rarely hit dimension errors.

The iw and ih variables

For more control, you can use iw (input width) and ih (input height) as variables in your scale expression. This is useful for scaling by a percentage rather than a fixed size:

Scale to 50% of original dimensions:

ffmpeg -i input.mp4 -vf "scale=iw/2:ih/2" output.mp4

Scale to exactly half width, height stays proportional:

ffmpeg -i input.mp4 -vf "scale=iw/2:-2" output.mp4

The iw*2:ih*2 pattern doubles the resolution, though upscaling rarely improves quality (more on that below). The expressions support basic arithmetic, so iw*0.75:-2 for 75% width works fine.

Downscaling for web delivery

The most common ffmpeg scale video use case: you have a large file and need to make it smaller for web delivery without destroying it.

1080p from 4K

ffmpeg -i input-4k.mp4 -vf "scale=1920:-2" -c:v libx264 -crf 23 -preset medium -c:a copy output-1080p.mp4

The scale filter handles the resize; the CRF controls the compression quality. For web delivery, CRF 23 is the standard starting point. The FFmpeg compress video guide covers the CRF scale in detail if you're trying to hit a specific file size.

720p for faster streaming

ffmpeg -i input.mp4 -vf "scale=1280:-2" -c:v libx264 -crf 26 -preset fast -c:a aac -b:a 128k output-720p.mp4

A slight quality bump to CRF 26 makes sense at 720p since the lower resolution already means less visual detail. You won't notice the difference.

Small thumbnails or preview clips

ffmpeg -i input.mp4 -vf "scale=640:-2" -c:v libx264 -crf 28 output-preview.mp4

For a preview or thumbnail-level clip, CRF 28 is fine.

Platform presets

Different social media platforms have different resolution requirements and aspect ratio expectations. Here's what actually works for each.

YouTube (16:9, 1080p)

YouTube accepts various resolutions but encodes uploaded content into multiple quality levels. Uploading native 1080p gives you the best result:

ffmpeg -i input.mp4 -vf "scale=1920:1080:force_original_aspect_ratio=decrease,pad=1920:1080:(ow-iw)/2:(oh-ih)/2" -c:v libx264 -crf 20 -preset slow -c:a aac -b:a 192k output-youtube.mp4

The force_original_aspect_ratio=decrease part shrinks the video to fit within 1920x1080 without cropping, then pad adds black bars to fill the remaining space. More on the pad filter below.

For 4K uploads:

ffmpeg -i input.mp4 -vf "scale=3840:2160:force_original_aspect_ratio=decrease,pad=3840:2160:(ow-iw)/2:(oh-ih)/2" -c:v libx264 -crf 17 -preset slow -c:a aac -b:a 320k output-4k.mp4

TikTok and Instagram Reels (9:16, 1080x1920)

Vertical video for mobile-first platforms. The tricky part is going from a landscape source to portrait. Cropping to 9:16 is usually better than letterboxing here since a narrow pillarbox looks bad on phones.

If your source is landscape and you want to crop to 9:16:

ffmpeg -i input.mp4 -vf "scale=1080:1920:force_original_aspect_ratio=increase,crop=1080:1920" -c:v libx264 -crf 23 -c:a aac output-tiktok.mp4

force_original_aspect_ratio=increase scales up until the video fills 1080x1920, then crop trims the overflow. The crop is center-aligned by default.

If your source is already vertical or close to it:

ffmpeg -i input.mp4 -vf "scale=1080:-2" -c:v libx264 -crf 23 -c:a aac output-tiktok.mp4

Instagram square (1:1, 1080x1080)

ffmpeg -i input.mp4 -vf "scale=1080:1080:force_original_aspect_ratio=decrease,pad=1080:1080:(ow-iw)/2:(oh-ih)/2:color=black" -c:v libx264 -crf 23 -c:a aac output-instagram.mp4

This letterboxes or pillarboxes as needed. For Instagram, most creators prefer cropping over black bars. To crop to square from a landscape source:

ffmpeg -i input.mp4 -vf "scale=1080:1080:force_original_aspect_ratio=increase,crop=1080:1080" -c:v libx264 -crf 23 -c:a aac output-instagram-crop.mp4

Scale + pad: letterboxing without stretching

When you need to hit exact dimensions without cropping or stretching, combine scale with pad. The pattern comes up constantly for broadcast deliverables, ad networks, and anywhere that specifies exact pixel dimensions.

ffmpeg -i input.mp4 \
  -vf "scale=1920:1080:force_original_aspect_ratio=decrease,pad=1920:1080:(ow-iw)/2:(oh-ih)/2:color=black" \
  -c:v libx264 -crf 22 output.mp4

Breaking it down:

  • scale=1920:1080:force_original_aspect_ratio=decrease — shrinks the input to fit within 1920x1080 while keeping its ratio

  • pad=1920:1080 — pads the result to exactly 1920x1080

  • (ow-iw)/2:(oh-ih)/2 — centers the video in the padded frame

  • color=black — sets the padding color (try white or 0x1a1a2e for a dark brand color)

You can also use this for custom padding colors:

ffmpeg -i input.mp4 \
  -vf "scale=1280:720:force_original_aspect_ratio=decrease,pad=1280:720:(ow-iw)/2:(oh-ih)/2:color=0xFFFFFF" \
  -c:v libx264 -crf 23 output.mp4

A note on upscaling

Scaling up (making a video larger than its source resolution) almost always looks worse, not better. FFmpeg will happily upscale a 720p video to 4K, but it can't invent detail that wasn't there. The result is a blurry, soft 4K video.

For most cases, upscaling is a mistake. The one exception is when a downstream platform re-encodes your video and applies heavy compression -- uploading a slightly larger file can sometimes give the platform's encoder more to work with. But even then, you're better off starting with higher-quality source footage.

If you genuinely need to upscale, the lanczos scaling algorithm is better than FFmpeg's default (bilinear):

ffmpeg -i input.mp4 -vf "scale=1920:-2:flags=lanczos" output.mp4

Batch scaling via API

Manually running FFmpeg on individual files doesn't scale. If you're processing user uploads, building a video pipeline, or generating multiple output sizes from one master file, the RenderIO API handles the infrastructure.

Resize a single video

curl -X POST https://renderio.dev/api/v1/run-ffmpeg-command \
  -H "Content-Type: application/json" \
  -H "X-API-KEY: your_api_key" \
  -d '{
    "ffmpeg_command": "-i {{input}} -vf scale=1280:-2 -c:v libx264 -crf 23 -c:a copy {{output}}",
    "input_files": {"input": "https://example.com/source.mp4"},
    "output_files": {"output": "resized-720p.mp4"}
  }'

Generate multiple sizes in parallel

The run-multiple-ffmpeg-commands endpoint submits up to 10 jobs at once:

curl -X POST https://renderio.dev/api/v1/run-multiple-ffmpeg-commands \
  -H "Content-Type: application/json" \
  -H "X-API-KEY: your_api_key" \
  -d '{
    "commands": [
      {
        "ffmpeg_command": "-i {{input}} -vf scale=1920:-2 -c:v libx264 -crf 20 -c:a copy {{output}}",
        "input_files": {"input": "https://example.com/source-4k.mp4"},
        "output_files": {"output": "output-1080p.mp4"}
      },
      {
        "ffmpeg_command": "-i {{input}} -vf scale=1280:-2 -c:v libx264 -crf 23 -c:a copy {{output}}",
        "input_files": {"input": "https://example.com/source-4k.mp4"},
        "output_files": {"output": "output-720p.mp4"}
      },
      {
        "ffmpeg_command": "-i {{input}} -vf scale=854:-2 -c:v libx264 -crf 26 -c:a copy {{output}}",
        "input_files": {"input": "https://example.com/source-4k.mp4"},
        "output_files": {"output": "output-480p.mp4"}
      }
    ]
  }'

All three encode in parallel. The response includes a command_id for each job. Poll individually or configure a webhook to fire when each completes. The API curl examples page has ready-to-copy patterns for the full submit-poll-download flow.

Social media export pipeline

TikTok, YouTube, and Instagram all want different specs. You can pipe a single source through three transforms:

curl -X POST https://renderio.dev/api/v1/run-multiple-ffmpeg-commands \
  -H "Content-Type: application/json" \
  -H "X-API-KEY: your_api_key" \
  -d '{
    "commands": [
      {
        "ffmpeg_command": "-i {{input}} -vf \"scale=1920:1080:force_original_aspect_ratio=decrease,pad=1920:1080:(ow-iw)/2:(oh-ih)/2\" -c:v libx264 -crf 20 -c:a aac -b:a 192k {{output}}",
        "input_files": {"input": "https://cdn.example.com/master.mp4"},
        "output_files": {"output": "youtube-1080p.mp4"}
      },
      {
        "ffmpeg_command": "-i {{input}} -vf \"scale=1080:1920:force_original_aspect_ratio=increase,crop=1080:1920\" -c:v libx264 -crf 23 -c:a aac {{output}}",
        "input_files": {"input": "https://cdn.example.com/master.mp4"},
        "output_files": {"output": "tiktok-9x16.mp4"}
      },
      {
        "ffmpeg_command": "-i {{input}} -vf \"scale=1080:1080:force_original_aspect_ratio=decrease,pad=1080:1080:(ow-iw)/2:(oh-ih)/2\" -c:v libx264 -crf 23 -c:a aac {{output}}",
        "input_files": {"input": "https://cdn.example.com/master.mp4"},
        "output_files": {"output": "instagram-square.mp4"}
      }
    ]
  }'

Three outputs from one master file, running at the same time.

Common issues and fixes

Error: "width not divisible by 2" -- Switch from -1 to -2 in your scale expression. -2 rounds to the nearest even number automatically.

Video looks stretched -- You're specifying both dimensions. Use -2 for one of them (e.g., scale=1280:-2) so the other is calculated automatically.

Output is blurry -- Either you're upscaling from a low-res source, or you're using a high CRF value. For upscaling, add :flags=lanczos to the scale filter. For quality, lower the CRF number.

Black bars in wrong position -- The pad offset values (ow-iw)/2:(oh-ih)/2 center the video. If you want it top-aligned instead, use (ow-iw)/2:0.

Filter string too long in a shell -- Escape the quotes or use a filter script file (-filter_script). The API avoids this issue since the command is a JSON string.


Scale is one of those FFmpeg building blocks you end up combining with nearly everything else. Resize before compressing. Scale before watermarking. Crop to ratio then scale to final dimensions. Once you're comfortable with scale, pad, and the -2 trick, most resolution problems become straightforward.

For compression settings to pair with your scaled output, the FFmpeg compress video guide covers CRF, presets, and codec choices in detail.