32blogby Studio Mitsu

FFmpeg Social Media Video: 9:16 for Reels, Shorts & TikTok

Convert any video to 9:16 vertical format for Instagram Reels, YouTube Shorts, TikTok, and X with FFmpeg. Covers crop, pad, blur background, text overlays, subtitles, and batch processing.

by omitsu15 min read
On this page

You've got a perfectly good 16:9 video and now every platform wants 9:16 vertical. Manually re-exporting from a GUI editor for each platform is tedious — especially when the specs are almost identical across the board. If you've been searching for "ffmpeg reels format" or "ffmpeg convert to tiktok," you're in the right place.

The good news: all major short-form platforms accept the same base format — 1080×1920, H.264, AAC, MP4. One FFmpeg command covers YouTube Shorts, Instagram Reels, TikTok, X, and Facebook Reels. The differences are in duration limits and bitrate sweet spots, not the container or codec.

This guide shows you how to convert any video to vertical format, add text and subtitles, and batch-process an entire folder — all from the command line.

What you'll learn

  • Platform specs for Shorts, Reels, TikTok, X, and Facebook Reels
  • A single FFmpeg command that works for all platforms
  • Three methods to convert 16:9 to 9:16 (crop, pad, blur background)
  • How to add text overlays and burn in subtitles
  • Platform-specific encoding tweaks
  • Batch processing with shell scripts

Platform Video Specs at a Glance

Every platform has its own help page, but the core requirements are remarkably similar. Here's the condensed version:

PlatformAspect RatioMax ResolutionCodecMax DurationFile Size Limit
YouTube Shorts9:161080×1920H.264 + AAC3 min256 GB
Instagram Reels9:161080×1920H.264 + AAC20 min (90s recommended)4 GB
TikTok9:161080×1920H.264 + AACRecording 10 min / Upload 60 min72–500 MB
X (Twitter)9:16, 16:9, 1:11920×1080H.264 + AAC2 min 20s (free) / 4 hr (Premium)512 MB (free) / 16 GB (Premium)
Facebook Reels9:161080×1920H.264 + AACNo limit (≤90s recommended)4 GB

The pattern: 1080×1920, H.264, AAC, MP4. That's your target for everything. Platform specs change frequently — always check the linked official pages for the latest limits before a major upload run.

Bitrate determines quality vs. file size. Higher isn't always better — platforms re-encode your upload anyway, so going beyond their processing ceiling wastes bandwidth.

PlatformVideo BitrateAudio BitrateNotes
YouTube Shorts5–10 Mbps128 kbpsYouTube recommends 8+ Mbps for 1080p
Instagram Reels3.5–5 Mbps128 kbpsLower bitrate is fine — Meta re-encodes aggressively
TikTok2–4 Mbps128 kbpsOptimal file size for fast upload
X2.5–5 Mbps128 kbps5 Mbps for 1080p, 2.5 Mbps for 720p
Facebook Reels4–6 Mbps128 kbpsHigher bitrate for text-heavy content

The Universal FFmpeg Preset

This single command produces a file that works on every platform listed above:

bash
ffmpeg -i input.mp4 \
  -vf "scale=1080:1920:force_original_aspect_ratio=decrease,pad=1080:1920:(ow-iw)/2:(oh-ih)/2:black,setsar=1" \
  -c:v libx264 -crf 20 -preset slow -profile:v high -level 4.2 \
  -pix_fmt yuv420p -r 30 \
  -c:a aac -b:a 128k -ar 44100 -ac 2 \
  -movflags +faststart \
  -y output_vertical.mp4

What each part does:

FlagPurpose
scale=1080:1920:force_original_aspect_ratio=decreaseScales to fit within 1080×1920, keeping aspect ratio
pad=1080:1920:(ow-iw)/2:(oh-ih)/2:blackAdds black bars to fill the remaining space
setsar=1Ensures square pixels (prevents stretching)
-crf 20Quality level — lower = better quality, larger file. 18–23 is the sweet spot
-preset slowBetter compression at the cost of encoding time
-profile:v high -level 4.2Maximum H.264 compatibility across devices
-pix_fmt yuv420pRequired for broad player compatibility
-movflags +faststartMoves the moov atom to the beginning — crucial for web playback

Adjusting CRF for specific platforms

The -crf value controls the quality-size tradeoff. Here's a quick reference:

bash
# YouTube Shorts — higher quality (YouTube re-encodes, so start clean)
-crf 18

# Instagram Reels / TikTok — balanced (platforms compress anyway)
-crf 22

# X — keep file size small for the 512 MB limit
-crf 23

Three Ways to Convert 16:9 to 9:16

The universal preset uses padding (black bars). But that's not always what you want — sometimes you'd rather crop into the action, or add a blurred background for a polished look. Here are the three main approaches.

Method 1: Pad (black bars)

Best for: tutorials, screencasts, and talking-head videos where all content matters.

bash
ffmpeg -i input_16x9.mp4 \
  -vf "scale=1080:-2:force_original_aspect_ratio=decrease,pad=1080:1920:(ow-iw)/2:(oh-ih)/2:black" \
  -c:v libx264 -crf 20 -preset slow -pix_fmt yuv420p \
  -c:a aac -b:a 128k -movflags +faststart \
  output_padded.mp4

The video sits in the center with black bars above and below. Clean and simple, but it wastes 43% of the screen real estate on a 16:9 source.

Method 2: Crop (center cut)

Best for: action-focused content, landscapes, or when the subject is centered.

bash
ffmpeg -i input_16x9.mp4 \
  -vf "scale=-2:1920,crop=1080:1920" \
  -c:v libx264 -crf 20 -preset slow -pix_fmt yuv420p \
  -c:a aac -b:a 128k -movflags +faststart \
  output_cropped.mp4

This scales the height to 1920, then crops the width to 1080 from the center. You lose the left and right edges — about 43% of the original frame.

To crop from a specific position instead of center:

bash
# Crop from the left third (for subjects on the left side)
-vf "scale=-2:1920,crop=1080:1920:0:0"

# Crop from the right third
-vf "scale=-2:1920,crop=1080:1920:iw-1080:0"

Method 3: Blur background (the professional look)

Best for: repurposing YouTube videos into Shorts/Reels. This is what most content creators use — the original video plays in the center while a blurred, zoomed version fills the background.

bash
ffmpeg -i input_16x9.mp4 -filter_complex \
  "[0:v]scale=1080:1920:force_original_aspect_ratio=increase,crop=1080:1920,boxblur=20:5[bg]; \
   [0:v]scale=1080:1920:force_original_aspect_ratio=decrease,pad=1080:1920:(ow-iw)/2:(oh-ih)/2:color=0x00000000[fg]; \
   [bg][fg]overlay=0:0,format=yuv420p" \
  -c:v libx264 -crf 20 -preset slow \
  -c:a aac -b:a 128k -movflags +faststart \
  output_blur_bg.mp4

How the filter chain works:

  1. [bg]: Scales the input to cover 1080×1920, crops to fit, then applies a 20-pixel box blur
  2. [fg]: Scales the input to fit within 1080×1920, pads with transparent pixels
  3. overlay: Stacks the sharp foreground on top of the blurred background

Which method to choose?

MethodProsConsBest for
PadNo content loss, fast encodeWasted screen space, looks basicTutorials, screencasts
CropFills the frame, high impactLoses side contentAction, centered subjects
Blur backgroundProfessional look, no content lossSlower encode, larger fileRepurposed YouTube content

Adding Text Overlays and Burned-In Subtitles

Vertical videos for social media almost always benefit from text — whether it's a title, a call-to-action, or full captions. FFmpeg handles both static text overlays and subtitle burn-in.

Static text overlay with drawtext

bash
ffmpeg -i input.mp4 \
  -vf "scale=1080:1920:force_original_aspect_ratio=decrease,pad=1080:1920:(ow-iw)/2:(oh-ih)/2:black, \
       drawtext=text='Subscribe for more':fontsize=48:fontcolor=white:x=(w-text_w)/2:y=h-100: \
       box=1:boxcolor=black@0.6:boxborderw=10" \
  -c:v libx264 -crf 20 -preset slow -pix_fmt yuv420p \
  -c:a aac -b:a 128k -movflags +faststart \
  output_text.mp4

Key parameters for drawtext:

ParameterDescription
textThe text string to display
fontsizeFont size in pixels (48–72 works well for 1080p vertical)
fontcolorText color (white, yellow, #FF5733)
x, yPosition — (w-text_w)/2 centers horizontally
box=1Enable background box
boxcolor=black@0.6Semi-transparent black background
fontfilePath to a .ttf font file for custom fonts
enable='between(t,2,8)'Show text only between seconds 2 and 8

Burning in SRT subtitles

Burned-in (hardcoded) subtitles are standard for social media — most viewers watch on mute, and platform auto-captions are often inaccurate.

bash
ffmpeg -i input.mp4 \
  -vf "scale=1080:1920:force_original_aspect_ratio=decrease,pad=1080:1920:(ow-iw)/2:(oh-ih)/2:black, \
       subtitles=captions.srt:force_style='FontSize=20,FontName=Arial,PrimaryColour=&H00FFFFFF,OutlineColour=&H00000000,Outline=2,MarginV=60'" \
  -c:v libx264 -crf 20 -preset slow -pix_fmt yuv420p \
  -c:a aac -b:a 128k -movflags +faststart \
  output_subtitled.mp4

The force_style parameter uses ASS subtitle styling syntax. Key style options:

  • FontSize=20 — size in the ASS coordinate system (different from drawtext's pixel-based size)
  • PrimaryColour=&H00FFFFFF — white text (format: &HAABBGGRR)
  • OutlineColour=&H00000000 — black outline
  • Outline=2 — outline thickness
  • MarginV=60 — vertical margin from the bottom (keeps subtitles above platform UI elements)

ASS subtitles for advanced styling

For word-by-word highlighting (karaoke style) or precise positioning, use ASS format:

bash
ffmpeg -i input.mp4 \
  -vf "scale=1080:1920:force_original_aspect_ratio=decrease,pad=1080:1920:(ow-iw)/2:(oh-ih)/2:black, \
       ass=styled_captions.ass" \
  -c:v libx264 -crf 20 -preset slow -pix_fmt yuv420p \
  -c:a aac -b:a 128k -movflags +faststart \
  output_ass.mp4

To generate SRT from audio automatically, see FFmpeg + Whisper: Auto-Generate Subtitles.

Platform-Specific Tweaks

The universal preset covers 90% of cases, but sometimes you need to optimize for a specific platform.

YouTube Shorts

YouTube keeps your original quality longer when you upload at higher bitrate, since it stores multiple quality tiers.

bash
ffmpeg -i input.mp4 \
  -vf "scale=1080:1920:force_original_aspect_ratio=decrease,pad=1080:1920:(ow-iw)/2:(oh-ih)/2:black,setsar=1" \
  -c:v libx264 -crf 18 -preset slow -profile:v high -level 4.2 \
  -pix_fmt yuv420p -r 60 \
  -c:a aac -b:a 192k -ar 48000 -ac 2 \
  -movflags +faststart \
  -t 180 \
  output_shorts.mp4

Differences from the universal preset:

  • -crf 18 — higher quality, since YouTube re-encodes
  • -r 60 — 60 fps for smoother playback
  • -b:a 192k -ar 48000 — higher audio quality
  • -t 180 — hard limit at 3 minutes (Shorts max duration)

Instagram Reels

Meta's servers re-encode aggressively, so there's no point uploading a massive file. Optimize for a smaller, cleaner source.

bash
ffmpeg -i input.mp4 \
  -vf "scale=1080:1920:force_original_aspect_ratio=decrease,pad=1080:1920:(ow-iw)/2:(oh-ih)/2:black,setsar=1" \
  -c:v libx264 -crf 22 -preset slow -profile:v high -level 4.0 \
  -pix_fmt yuv420p -r 30 \
  -c:a aac -b:a 128k -ar 44100 -ac 2 \
  -movflags +faststart \
  -t 90 \
  output_reels.mp4
  • -crf 22 — slightly more compression (Meta re-encodes anyway)
  • -t 90 — Instagram recommends keeping Reels under 90 seconds for reach

TikTok

TikTok's algorithm favors videos in the 21–34 second sweet spot. Keep file sizes small for faster upload processing.

bash
ffmpeg -i input.mp4 \
  -vf "scale=1080:1920:force_original_aspect_ratio=decrease,pad=1080:1920:(ow-iw)/2:(oh-ih)/2:black,setsar=1" \
  -c:v libx264 -crf 23 -preset slow -profile:v high -level 4.0 \
  -maxrate 4M -bufsize 8M \
  -pix_fmt yuv420p -r 30 \
  -c:a aac -b:a 128k -ar 44100 -ac 2 \
  -movflags +faststart \
  output_tiktok.mp4
  • -maxrate 4M -bufsize 8M — caps bitrate to keep file size under TikTok's limits
  • -crf 23 — good enough quality at a smaller size

X (Twitter)

X has the strictest file size limits for free users (512 MB) and duration limits (2 min 20s).

bash
ffmpeg -i input.mp4 \
  -vf "scale=1080:1920:force_original_aspect_ratio=decrease,pad=1080:1920:(ow-iw)/2:(oh-ih)/2:black,setsar=1" \
  -c:v libx264 -crf 23 -preset slow -profile:v high -level 4.0 \
  -maxrate 5M -bufsize 10M \
  -pix_fmt yuv420p -r 30 \
  -c:a aac -b:a 128k -ar 44100 -ac 2 \
  -movflags +faststart \
  -t 140 \
  output_x.mp4
  • -t 140 — hard limit at 2 minutes 20 seconds
  • -maxrate 5M — prevents bitrate spikes that could push the file over 512 MB

Batch Processing

When you have dozens of videos to convert, a shell script saves hours.

Convert all videos in a folder

bash
#!/bin/bash
# batch-vertical.sh — Convert all MP4s to 9:16 vertical format
INPUT_DIR="$1"
OUTPUT_DIR="$2"
METHOD="${3:-pad}"  # pad, crop, or blur

mkdir -p "$OUTPUT_DIR"

for f in "$INPUT_DIR"/*.mp4; do
  filename=$(basename "$f" .mp4)
  echo "Processing: $filename"

  case "$METHOD" in
    pad)
      VF="scale=1080:1920:force_original_aspect_ratio=decrease,pad=1080:1920:(ow-iw)/2:(oh-ih)/2:black,setsar=1"
      ;;
    crop)
      VF="scale=-2:1920,crop=1080:1920,setsar=1"
      ;;
    blur)
      # Blur background requires filter_complex — handled separately
      ffmpeg -i "$f" -filter_complex \
        "[0:v]scale=1080:1920:force_original_aspect_ratio=increase,crop=1080:1920,boxblur=20:5[bg]; \
         [0:v]scale=1080:1920:force_original_aspect_ratio=decrease,pad=1080:1920:(ow-iw)/2:(oh-ih)/2:color=0x00000000[fg]; \
         [bg][fg]overlay=0:0,format=yuv420p" \
        -c:v libx264 -crf 20 -preset slow \
        -c:a aac -b:a 128k -movflags +faststart \
        -y "$OUTPUT_DIR/${filename}_vertical.mp4"
      continue
      ;;
  esac

  ffmpeg -i "$f" \
    -vf "$VF" \
    -c:v libx264 -crf 20 -preset slow -pix_fmt yuv420p \
    -c:a aac -b:a 128k -movflags +faststart \
    -y "$OUTPUT_DIR/${filename}_vertical.mp4"
done

echo "Done. Output files in $OUTPUT_DIR"

Usage:

bash
chmod +x batch-vertical.sh
./batch-vertical.sh ./raw_videos ./vertical_output pad
./batch-vertical.sh ./raw_videos ./vertical_output blur

Parallel processing for speed

The script above processes one video at a time. For large batches, use GNU Parallel or xargs to encode multiple files simultaneously:

bash
# Process 4 videos in parallel using xargs
ls raw_videos/*.mp4 | xargs -P 4 -I {} bash -c '
  filename=$(basename "{}" .mp4)
  ffmpeg -i "{}" \
    -vf "scale=1080:1920:force_original_aspect_ratio=decrease,pad=1080:1920:(ow-iw)/2:(oh-ih)/2:black,setsar=1" \
    -c:v libx264 -crf 20 -preset slow -pix_fmt yuv420p \
    -c:a aac -b:a 128k -movflags +faststart \
    -y "vertical_output/${filename}_vertical.mp4"
'

For more advanced automation (Python-based, progress tracking, error handling), see Automating FFmpeg with Python.

FAQ

What's the best aspect ratio for Shorts, Reels, and TikTok?

9:16 (1080×1920) for all of them. Square (1:1) also works but gets less screen real estate and lower engagement on platforms that prioritize full-screen vertical content.

Can I upload 4K (2160×3840) vertical video?

YouTube Shorts supports up to 4K. Instagram and TikTok will re-encode to 1080p regardless, so uploading 4K to those platforms just wastes upload time and bandwidth. Stick with 1080×1920.

How do I convert a square (1:1) video to 9:16?

Same approach as 16:9 — pad, crop, or blur background:

bash
# Pad a 1:1 video to 9:16
ffmpeg -i square.mp4 \
  -vf "scale=1080:-2:force_original_aspect_ratio=decrease,pad=1080:1920:(ow-iw)/2:(oh-ih)/2:black" \
  -c:v libx264 -crf 20 -pix_fmt yuv420p -c:a copy \
  output_vertical.mp4

Should I use H.265 (HEVC) instead of H.264?

No, at least not for social media uploads. Every platform re-encodes your upload to their own format (often VP9 or AV1 internally). H.264 has universal upload compatibility, while HEVC may cause processing errors on some platforms. For archiving your masters, H.265 or AV1 gives better compression — see AV1 vs H.265 vs H.264 comparison.

How do I trim a video to a specific duration before converting?

Use -ss (start time) and -t (duration) or -to (end time):

bash
# Extract 30 seconds starting at 1:15
ffmpeg -ss 01:15 -i input.mp4 -t 30 \
  -vf "scale=1080:1920:force_original_aspect_ratio=decrease,pad=1080:1920:(ow-iw)/2:(oh-ih)/2:black" \
  -c:v libx264 -crf 20 -pix_fmt yuv420p -c:a aac -b:a 128k \
  -movflags +faststart output_clip.mp4

Place -ss before -i for fast seeking (input seeking), or after -i for frame-accurate seeking (slower). For more details, see FFmpeg Usage Guide — trimming and cutting.

How can I speed up encoding with GPU?

Replace libx264 with a hardware encoder:

bash
# NVIDIA NVENC
-c:v h264_nvenc -preset p4 -cq 22

# Intel Quick Sync
-c:v h264_qsv -global_quality 22

# Apple VideoToolbox (macOS)
-c:v h264_videotoolbox -q:v 60

GPU encoding is 3–10× faster but produces slightly larger files at the same visual quality. See FFmpeg GPU Encoding Guide for setup instructions.

Do I need to normalize audio before uploading to social media?

It's strongly recommended. Platforms apply their own loudness normalization (typically around -14 LUFS), and if your audio is too quiet or too loud, the result may sound compressed or distorted. Pre-normalizing gives you control over the final sound. See FFmpeg Audio Normalization Guide for details.

What's the ideal video length for each platform?

There's no single answer — it depends on content type and audience. But here's what the algorithms tend to favor:

PlatformSweet SpotMax Length
YouTube Shorts30–60s3 min
Instagram Reels15–30s20 min
TikTok21–34s60 min (upload)
X15–45s2 min 20s (free) / 4 hr (Premium)
Facebook Reels15–30sNo limit (≤90s recommended)

Wrapping Up

Converting video for social media with FFmpeg comes down to three things: the right resolution (1080×1920), the right codec (H.264 + AAC in MP4), and the right conversion method for your content (crop, pad, or blur background).

The universal preset at the top of this article handles most cases. If you need to process a backlog, the batch script scales to hundreds of files. And if your workflow includes audio normalization or subtitle generation, chain it with the tools from the loudnorm guide and Whisper subtitle guide.

For a full reference of FFmpeg commands beyond social media conversion, see the FFmpeg Usage Guide.