A collection of frequently used ffmpeg commands.

Video to Video

Convert .flv to .mp4 without re-encode (fast)

ffmpeg -i input.flv -c copy -copyts output.mp4

Convert unplayable .mp4 to Samsung supported .mp4

ffmpeg -n -nostdin -i input.mp4 -c:v h264_nvenc -preset slow -crf 23 -c:a aac -b:a 192k -vf format=yuv420p -movflags +faststart output.mp4
batch version
#!/bin/env bash

# My Video Converter
# Convert unplayable mp4 to playable mp4

# File with spaces. Ref: https://unix.stackexchange.com/a/9499
find . -maxdepth 1 -type f \( -iname "*.mp4" ! -iname "*.new.mp4" \) -print0 | while IFS= read -r -d '' file; do
    ext="${file##*.}"
    filename="${file%.*}"
    new_file="${filename}.new.${ext}"
    echo $new_file
    ffmpeg -n -nostdin -i "$file" -c:v h264_nvenc -preset slow \
        -crf 23 -c:a aac -b:a 192k -vf format=yuv420p \
        -movflags +faststart "$new_file"
done

Video Concat

Concat w/o re-encode (fast)

  • Create the list.txt file manually
$ cat list.txt
file A.mp4
file B.mp4
file C.mp4
$ ffmpeg -safe 0 -f concat -i list.txt -c copy output.mp3
  • Create a file list using the find command (not compatible with PowerShell)
ffmpeg -safe 0 -f concat -i <(find . -type f -name '*.mp4' -printf "file '$PWD/%p'\n" | sort) -c copy output.mp4

Concat w/ re-encode (slow)

  • remove -c:v h264_nvenc if there is no GPU driver
ffmpeg -i A.mp4 -i B.mp4 -filter_complex "concat=n=2:v=1:a=1" -c:v h264_nvenc output.mp4

if error when different SAR

[Parsed_concat_4 @ 0x55c55ae4d400] Input link in0:v0 parameters (size 1280x720, SAR 1:1) do not match the corresponding output link in0:v0 parameters (1280x720, SAR 8001:8000)
[Parsed_concat_4 @ 0x55c55ae4d400] Failed to configure output pad on Parsed_concat_4
Error reinitializing filters!

Correct the aspect of one of videos first by: ffmpeg -i wrong_A.mp4 -aspect 16:9 -c copy A.mp4

Reduce video file size

  • -crf N: N: 0-51, where 0 is lossless, 23 is the default, and 51 is worst quality possible
  • -b 800k: limit the bitrate (optional)
ffmpeg -i input.flv -c:v h264_nvenc -crf 30 -c:a aac -b:a 192k -vf format=yuv420p -movflags +faststart output.mp4

Combine video and audio

ffmpeg -i video.mp4 -i audio.m4a -c copy -map 0:v:0 -map 1:a:0 output.mp4

Video to .gif

  • scale and max_colors impact the output gif’s size
ffmpeg -y -i input.flv -filter_complex "fps=4,scale=1920:-1:flags=lanczos,split[s0][s1];[s0]palettegen=max_colors=256[p];[s1][p]paletteuse=dither=bayer" output.gif

Convert .jpeg to .webp

ffmpeg -i input.jpg -c:v libwebp output.webp

Convert .webp to .jpeg

ffmpeg -i input.webp output.jpg

Batch version with find

  • keep original input file
find . -iname '*.webp' -exec bash -c 'ffmpeg -y -hide_banner -loglevel error -i "$1" -q:v 1 -qmin 1 -qmax 1 "${1%.*}.jpg"; printf "$1\n"' _ {} \;
  • remove original input file
find . -iname '*.webp' -exec bash -c 'ffmpeg -y -hide_banner -loglevel error -i "$1" -q:v 1 -qmin 1 -qmax 1 "${1%.*}.jpg"; printf "$1\n" && rm "$1"' _ {} \;

PIL offers a significantly faster performance when processing images in batches

  • tested in Windows PowerShell with python 3.12.5
python script
# -*- coding: utf8 -*-

# pip install Pillow tqdm
# python -X utf8 .\webp2jpg.py --dir ../

from pathlib import Path
from multiprocessing import Pool, cpu_count
import argparse
import os, sys
from tqdm import tqdm
from PIL import Image


def process(file):
    ori_dir = str(str(file.parent.resolve()).encode("utf8"), encoding="utf8")
    new_dir = ori_dir + "_jpg"
    os.makedirs(new_dir, exist_ok=True)

    ori_file = str(file.resolve())
    new_file = ori_file.replace(file.suffix, ".jpg").replace(ori_dir, new_dir)
    ori_file = str(bytes(ori_file, encoding="utf8"), encoding="utf8")
    new_file = str(bytes(new_file, encoding="utf8"), encoding="utf8")

    if not os.path.isfile(new_file):
        im = Image.open(ori_file).convert("RGB")
        im.save(new_file, "jpeg", subsampling=0, quality=95)


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("--dir", type=str, required=True)
    parser.add_argument("--count", action="store_true")
    args = parser.parse_args()

    pwd = Path(rf"{args.dir}")
    files = [Path(file) for file in pwd.glob("**/*.webp")]

    if args.count:
        print(f"total: {len(files)}")
        sys.exit(0)

    with Pool(cpu_count()) as p:
        with tqdm(total=len(files), ascii=True, ncols=60) as pbar:
            for _ in p.imap_unordered(process, files):
                pbar.update()

    print(f"total: {len(files)}")


if __name__ == "__main__":
    main()
  • ⊛ Back to top
  • ⊛ Go to bottom