DEV Community

Cover image for Why your screen recording is 500 MB: containers, codecs, and bitrate math
Zao
Zao

Posted on

Why your screen recording is 500 MB: containers, codecs, and bitrate math

If you've ever recorded a demo or a bug reproduction, you've probably met the 500 MB .mov that refuses to attach to a ticket, upload to a code review, or survive a Slack message. The fix isn't a bigger cloud drive — it's understanding what's actually inside a video file. Once you do, shrinking it becomes a deliberate, repeatable operation instead of guesswork.

Container vs codec: two different things in one file

The first confusion to clear up: .mp4, .mov, and .mkv are containers — they're boxes that hold a compressed video stream, an audio stream, and metadata. They don't determine the file size.

The size is mostly decided by the codec — the compression algorithm applied to the video stream (H.264, H.265/HEVC, VP9, AV1) — and by the bitrate: how many bits per second that codec was allowed to spend.

So "convert to MP4" doesn't make a file smaller. Re-encoding the stream inside the container does.

The only size math you need

Uncompressed video is enormous, but compressed file size follows one simple relationship:

file size (bytes) ≈ bitrate (bits/s) × duration (s) / 8
Enter fullscreen mode Exit fullscreen mode

A 10-minute recording at 8 Mbps is roughly:

8,000,000 × 600 / 8 = 600,000,000 bytes ≈ 600 MB
Enter fullscreen mode Exit fullscreen mode

This is why a 4K screen recording explodes: the recorder grabbed a huge bitrate because it was told to preserve "4K quality," even though screen content doesn't need it. If you want a 20 MB file for a 90-second clip, invert the formula: you can afford roughly 1.7 Mbps.

Why screen recordings are their own problem

Screen content behaves differently from camera footage, and this is where most default settings let you down:

  • Flat regions and text: a screen is mostly static UI with sharp text edges. Camera-oriented encoders spend bits modeling "motion" that isn't there, then soften exactly what your viewer needs to read.
  • Frame rate you don't need: camera footage wants 30–60 fps; a cursor moving across an IDE doesn't. Recording or re-encoding at 15 fps often looks identical for screencasts and cuts size significantly.
  • Audio you can shrink too: voice narration at 128 kbps AAC is transparent; many recorders capture 320 kbps or uncompressed audio out of habit. For a clip with no narration, dropping the audio track entirely (-an) is free space.

Inspecting a file before you touch it

Don't guess — measure. ffprobe (ships with ffmpeg) tells you what's in the box:

ffprobe -v error -show_entries format=duration,size,bit_rate \
  -of default=noprint_wrappers=1 input.mov
Enter fullscreen mode Exit fullscreen mode

For stream-level detail (codec, resolution, pixel format, frame rate):

ffprobe -v error -select_streams v:0 \
  -show_entries stream=codec_name,width,height,pix_fmt,r_frame_rate \
  -of default=noprint_wrappers=1 input.mov
Enter fullscreen mode Exit fullscreen mode

Two things to look for: the codec (H.264 is universally playable; H.265 support outside Apple ecosystems is patchy in browsers) and the pixel format — screen recorders sometimes produce unusual formats that inflate size or break compatibility.

Compressing on purpose with ffmpeg

The workhorse is a single re-encode with a CRF (constant rate factor) value. CRF is quality-targeted: lower = better quality and bigger file. For libx264, the range 18–28 is the useful band, and 23 is the default starting point:

ffmpeg -i input.mov \
  -c:v libx264 -crf 23 -preset slow \
  -pix_fmt yuv420p \
  -c:a aac -b:a 96k \
  output.mp4
Enter fullscreen mode Exit fullscreen mode

What each flag buys you:

  • -preset slow: the encoder tries harder to find compression; slower encode, smaller file at the same quality.
  • -pix_fmt yuv420p: the compatibility-safe pixel format. If your compressed video plays locally but not in a browser or on a colleague's machine, this is usually why.
  • -c:a aac -b:a 96k: re-encodes audio at a sane rate for voice.

For screen recordings, add a frame-rate cap and, if the capture is larger than needed, a downscale:

ffmpeg -i input.mov \
  -c:v libx264 -crf 23 -preset slow \
  -vf "scale=-2:1080,fps=15" \
  -pix_fmt yuv420p -c:a aac -b:a 96k \
  output.mp4
Enter fullscreen mode Exit fullscreen mode

scale=-2:1080 means "fit height 1080, keep aspect ratio, round width to an even number" (odd dimensions break many players). If H.265 is an option, libx265 -crf 28 targets roughly the same visual quality as libx264 -crf 23 at a smaller size — at the cost of slower encoding and weaker browser support. Add -tag:v hvc1 so QuickTime-based players on macOS accept the file.

Verifying the result

Never trust the process — check it:

# size and duration actually changed as expected?
ffprobe -v error -show_entries format=size,duration -of default=noprint_wrappers=1 output.mp4

# pixel format and codec are what you asked for?
ffprobe -v error -select_streams v:0 -show_entries stream=codec_name,pix_fmt -of default=noprint_wrappers=1 output.mp4
Enter fullscreen mode Exit fullscreen mode

Then watch the clip once, ideally where the text is smallest. Compression artifacts show up first in fine detail — small font sizes, thin borders, fast cursor movement. If text has gone fuzzy, drop CRF by 2–3 and re-encode; don't ship something your reviewer has to squint at.

A repeatable recipe

For "I need this under N MB in M minutes":

  1. Compute your bitrate budget: N × 8 / M kbps, minus a safety margin.
  2. Probe the source: codec, resolution, frame rate.
  3. Re-encode with libx264, CRF 23, yuv420p, capped fps, voice-appropriate audio.
  4. Probe and watch the output.
  5. Adjust CRF (and only then fps or scale) until it fits.

Ten minutes of deliberate re-encoding beats every "upload failed, retry" loop — and the same commands work the same way in CI if you ever need to compress artifacts automatically.


Top comments (1)

Collapse
 
demovexa profile image
DemoVexa •

The bitrate budget formula is the part I'd pin above every recorder's settings. Once you see size as bitrate times duration, the 500 MB file stops being a mystery and becomes a choice someone made. One thing I'd add for UI footage: the 15 fps cap works well for IDE and settings screens, but it hurts anything with scrolling or a dragged element, because smooth motion turns into visible steps. For those clips I keep 30 fps and lean on CRF and the 1080p downscale instead. Your final check is the one people skip. Watching the output where the text is smallest catches fuzzy labels that a file size never will. Have you tried the stillimage or animation tunes on mostly static captures?