The Complete Guide to FFmpeg | Improve Your Video Automation Skills

ffmpeg

FFmpeg is the Swiss army knife of media processing: it can inspect, convert, trim, resize, mix, overlay, and package audio/video in almost any format. It’s used everywhere from simple “convert this file” tasks to fully automated pipelines that generate clips, subtitles, thumbnails, and platform ready exports. The challenge is that FFmpeg’s power is front loaded into syntax and concepts once you understand how inputs, streams, filters, and encoding fit together, you can reliably build commands instead of trial and error hacking.

At a high level, think of FFmpeg as doing three things in sequence. It reads one or more inputs (each input can contain multiple streams such as video, audio, subtitles). It optionally transforms streams through filters (scaling, padding, fades, overlays, mixing). Then it writes an output container, either by copying existing streams (fast, no quality loss) or by encoding new streams (slower, but required when you change the media).


Build intuition: containers, codecs, streams, and when “copy” is your best friend

A common early confusion is mixing up containers and codecs. MP4, MKV, and MOV are containers: they hold streams. H.264 (libx264) and H.265 (libx265) are video codecs: they define how video is compressed. AAC, MP3, and Opus are audio codecs. You can often “remux” between containers (MP4 MKV, MP4 MOV) without touching the underlying audio/video compression. That’s where stream copy shines.

Using -c copy tells FFmpeg to copy streams exactly as is into a new container. This is typically instant and avoids quality loss because there’s no re encoding step. It’s the right default whenever you’re not applying filters, not changing codecs, and not doing frame accurate cuts. The moment you scale, overlay, burn subtitles, change speed, mix audio, or do anything that modifies samples/frames, you’re in re encode territory.


Learn the “shape” of an FFmpeg command

Most commands follow the same pattern: inputs first, then any filters, then mapping, then output settings. The mental model that makes complex commands manageable is stream selection and mapping. FFmpeg labels inputs starting at 0, so the first file is input 0, the second is input 1, and so on. You’ll see selectors like 0:v (video stream from input 0) and 1:a (audio stream from input 1). For simple one input conversions you can often ignore mapping entirely, but as soon as you introduce multiple inputs (audio replacement, overlays, intros/outros) mapping is how you stay in control of what ends up in the final file.

Filters come in two main forms. -vf applies a video filter chain to a single video stream and is great for simple operations like scaling or changing frame rate. -filter_complex is for anything multi stream or multi step: split a video into branches, combine multiple videos, mix multiple audios, overlay images, or coordinate audio and video processing together. When you see bracketed labels like [v0] or [out], that’s just naming intermediate results in a filter graph so you can reuse them later.


Start with safe, everyday operations: inspect, remux, and convert

Before changing anything, you’ll save time by inspecting media. FFprobe is designed for that: it prints structured metadata about streams, codecs, durations, and formats. This is how you confirm whether a file is H.264 or H.265, whether it has multiple audio tracks, what the frame rate is, and whether there are subtitle streams.

From there, the cleanest “first win” is remuxing with stream copy. If you’re simply changing the container, -c copy is typically all you need. A true conversion like writing AVI from MP4-often triggers re encoding because the target format expects different defaults. When you re encode, you should choose codecs intentionally rather than relying on defaults, because defaults vary by build and platform support.


Resizing for platforms: scale, pad, and compatibility settings that prevent surprises

The most common automation task is creating consistent dimensions for a platform: YouTube landscape, Shorts/Reels vertical, or a fixed player size. The reliable approach is “scale to fit, then pad to fill.” Scaling with aspect ratio preservation avoids distortion; padding fills the remaining space with a background colour (often black) to reach an exact target canvas.

Two practical details matter here. First, video encoders often prefer even numbered dimensions; using -2 for one dimension is a handy way to keep results divisible by two. Second, playback compatibility can break if the pixel format isn’t widely supported. When you generate video from images or do filter heavy workflows, forcing a widely compatible pixel format (commonly yuv420p) prevents the “plays on my machine but not on QuickTime/iOS” problem.


Trimming and seeking: fast versus accurate, and why keyframes matter

Trimming feels simple until you care about accuracy. The core issue is keyframes: inter frame codecs (H.264/H.265) store full frames periodically (keyframes) and store the rest as deltas. If you “copy” streams and cut in the middle of a GOP (group of pictures), the decoder may not have the reference data it needs for those first frames, which can manifest as glitches or black frames.

That’s why seeking has two flavours. Putting -ss before an input is fast because FFmpeg seeks to nearby keyframes and starts decoding from there, but it may not land exactly where you asked. Putting -ss after the input is accurate because it decodes up to the exact timestamp and discards frames until it reaches the point you want, but it’s slower. For dependable, frame accurate trimming, prefer output seeking and re encode the result. Stream copy trimming is best reserved for rough cuts at keyframe boundaries where speed matters more than precision.


Audio workflows you’ll use constantly: replace, extract, mix, and normalise

Audio manipulation is where stream mapping becomes instantly useful. Replacing audio is just choosing the video stream from one input and the audio stream from another, then deciding how to handle duration mismatches. If your new audio is shorter than the video, you can either end the output at the audio’s end or keep the video and accept silence after the audio finishes. Making that choice explicitly is what separates predictable automation from “why did it cut early?”

Extraction is another everyday task. If you only need the audio, you can output to an audio container and let FFmpeg encode as needed. If the audio stream is already in a codec you want (for example AAC), you can copy it directly without re encoding, which is faster and lossless. When you’re preparing audio for transcription or analysis pipelines, you’ll often downsample (for example to 16 kHz) and convert to mono; those operations require re encoding, so pick a codec and bitrate that match the downstream tool’s expectations.

Mixing audio is best handled via -filter_complex so you can control volume, mixing behaviour, and how to treat different track lengths. In real pipelines like adding background music under speech you’ll nearly always want to reduce the music volume, sometimes fade it in/out, and ensure it doesn’t stop abruptly. If you’re combining tracks from different sources, basic normalisation filters can help smooth the loud/quiet swings, but treat them as finishing steps: it’s better to set sensible levels first, then normalise gently.


Timing and pace: change speed without ruining speech

Changing playback speed is deceptively hard because video timing and audio pitch are different problems. For video, you change timestamps. For audio, you change tempo while preserving pitch. The practical takeaway is that you usually handle both in a single filter graph so they stay in sync: one branch adjusts video PTS, the other uses an audio tempo filter. If you only change FPS, you’re changing how many frames are shown per second, not the underlying audio duration, so it’s a different tool for a different goal. This distinction matters in automation because “make this 60fps” is not the same request as “make this play faster”


Social media transformations: crops, reframes, and “jump cut” assembly

For vertical content, you’re often adapting horizontal footage. The simplest approach is scale and pad into a vertical canvas, but if you want a tighter, more engaging result you’ll crop into a region of interest and then upscale. When your crop window moves over time because you’re effectively reframing the shot you’re building a multi step filter graph: split the input into segments, crop each segment differently, scale, then concatenate. The detail to respect is timestamps: trimming segments creates streams that start at different times, so resetting timestamps before concatenation keeps audio/video alignment sane.

Jump cuts are the same principle applied to time rather than space. Instead of cropping different regions, you select different time intervals and stitch them together. Doing this cleanly requires resetting timestamps after selection so the final output doesn’t contain discontinuities that break sync. It’s a powerful pattern for silence removal, tightening dialogue, or building highlight reels, but it’s also a reminder that FFmpeg is happiest when you treat edits as explicit timeline operations, not “magic trimming”


Overlays, text, subtitles, and why some changes must be “burned in”

Overlaying an image (logos, watermarks, banners) is one of FFmpeg’s strengths. Once you understand that overlays are just another input layered onto video, you can position them with simple expressions and enable them only during certain time ranges. If your overlay image already has transparency (alpha), it will blend naturally; if it doesn’t, you can still create transparency in the filter graph, but that’s more advanced and easiest to reserve for cases where you truly need FFmpeg to control opacity dynamically.

Text overlays are useful for automated captions, titles, or callouts. In practice, they become more reliable when the text comes from a file rather than being embedded directly in the command line escaping special characters across shells is a recurring source of subtle breakage. You can also control timing so text appears only within specific segments, and you can animate opacity to create simple fade ins.

Subtitles split into two categories: soft subtitles (stored as a track you can toggle on/off) and burned subtitles (rendered into the video pixels). Soft subtitles preserve flexibility and can be added without re encoding when the container supports them. Burned subtitles are required when you need guaranteed appearance across players, fonts, and platforms, or when you want precise styling. Styling is possible directly via subtitle filters, but if you need full control, ASS subtitles are often a better workflow because they’re built for styling and animation.


Asset generation: image to video, slideshows, Ken Burns, GIFs, thumbnails, and storyboards

Automation pipelines often generate media, not just transform it. Turning an image into a video is a standard building block for intros, placeholders, or simple visualisers. The key is remembering that a single image doesn’t contain frames, so FFmpeg must generate frames over time; that implies encoding work and can be slow if the image is remote and fetched repeatedly. In production automation, you usually download inputs locally first.

Slideshows and Ken Burns style motion are basically the same idea: generate frames from stills, then transition between them. Crossfades are created by overlapping segments, which means your final video duration may be slightly shorter than the sum of all still durations because transitions share time. Once you’re comfortable with that, you can intentionally design transitions rather than being surprised by timing changes.

GIFs are a special case: they’re widely supported, but they’re inefficient and quality sensitive. The practical approach is to reduce resolution, reduce frame rate or sample frames selectively, and accept that GIF is best for short loops. For discovery and review workflows, thumbnails and storyboards are extremely valuable. Extracting a single frame at a timestamp is straightforward; extracting representative frames based on scene change or keyframes is where FFmpeg becomes a real automation tool. Scene detection thresholds are adjustable, so you can tune sensitivity depending on the kind of content you process.


Encoding settings that matter in the real world: quality, speed, and “web ready” outputs

Once you re encode, you’re balancing three competing goals: quality, file size, and encode time. With H.264/H.265, CRF is the workhorse quality control. Lower CRF generally means higher quality and larger files; higher CRF means smaller files and lower quality. Presets control how much CPU time the encoder spends searching for compression efficiency: slower presets generally reduce file size for a given quality target.

For web delivery, one flag shows up repeatedly for good reason: moving metadata to the beginning of the file allows progressive playback to start sooner. This is one of those small details that makes automated outputs feel professional, especially when videos are hosted on typical HTTP servers.

For VP9 in WebM, the mental model is similar but the specifics differ: some “constant quality” modes require explicitly setting bitrate to zero to engage the intended behaviour. The broader lesson is that each encoder has its own rate control knobs, and reliable automation comes from knowing which knobs actually activate the mode you think you’re using.

Hardware encoding (NVIDIA NVENC, Intel QSV, AMD VAAPI) can drastically reduce encode time and is useful when throughput matters more than maximum compression efficiency. It’s often a good fit for bulk processing, previews, or fast turnarounds, while software encoders remain attractive for final exports where you care about squeezing file size without sacrificing quality.


A simple workflow for building correct commands without getting lost

The fastest path to confidence is to build commands in layers. Start by proving you can read the inputs and write an output with no filters. Add mapping explicitly when you introduce a second input. Add one filter step at a time, verifying the output at each stage. The moment you introduce -filter_complex, name intermediate streams so the graph stays readable and you can reason about what each step produces. When something goes wrong, inspect the inputs with FFprobe and confirm your assumptions about codecs, stream counts, durations, and frame rates FFmpeg is literal, and most “mystery” failures are mismatches between what you think is in the file and what’s actually there.

FFmpeg rewards you for being deliberate: choose when you’re copying versus encoding, be explicit about which streams you want, and treat timing (keyframes, timestamps, overlaps) as first class concerns. Once those foundations are in place, the “hard” commands social crops, jump cuts, intros/outros, subtitle burns, storyboards become variations on a small set of repeatable patterns.


Get The Blockchain Sector Newsletter, binge the YouTube channel and connect with me on Twitter

The Blockchain Sector newsletter goes out a few times a month when there is breaking news or interesting developments to discuss. All the content I produce is free, if you’d like to help please share this content on social media.

Thank you.

James Bachini

Disclaimer: Not a financial advisor, not financial advice. The content I create is to document my journey and for educational and entertainment purposes only. It is not under any circumstances investment advice. I am not an investment or trading professional and am learning myself while still making plenty of mistakes along the way. Any code published is experimental and not production ready to be used for financial transactions. Do your own research and do not play with funds you do not want to lose.


Posted

in

, , ,

by