ffmpeg - the media conversion toolbox

46 readers
2 users here now

Everything about the powerful open source media recording, conversion and streaming tool!

Scripting, encoding, remuxing, filters, choosing the right parameters...

Be civil and on-topic

founded 1 week ago
MODERATORS
1
 
 

I own an old junky PC that can encode but it is slow and energy intense. Also a laptop that I dont wanna use.

This is a horrible time to buy hardware with soldered memory, but used Mac Minis are available.

I am looking for raw and efficient CPU power because afaik GPU acceleration for encoding always sucks.

I want to use ffmpeg with svt-av1 and a few filters for artificial grain generation or noise reduction. With my PC, these filters totally kill performance, from 1,5x speed to 0,1-0,05x speed easily.

Anyone here use a mac mini for software encoding? Even better, with Linux, and with svt-av1?

I wonder what might cause these slowdowns and if unified and crazy fast memory might be able to improve this.

Also, how is memory used here? For my usual ffmpeg encodes I dont need more than 16GB of memory, and that is VERY widely available (used market; shitty low spec hardware is way more frequent).

I would try and get NixOS with Asahi kernel and patches to run there, maybe also just Asahi? Never used Arch, at least it doesnt have version upgrades like Fedora that break.

Maybe run it as a headless encoding server and connect over ssh, access my files from my server and control via my laptop

2
 
 

I used this today to try and find a frame showing a blinking error dialog, but wasnt successful, anyways here it is!

ffmpeg -i video.mp4 -r 30 frame%d.png
  • -r is the FPS
  • the %d is a nice iterator, works in fish and bash apparently

Images to video

Useful for timelapses!

#!/bin/sh

fps=10

ffmpeg \
	-framerate $fps \
	-pattern_type glob \
	-i '*.jpg' \
	-c:v libsvtav1 \
	-crf 20 -preset 6 \
	-vf "scale=-2:1080" \
	timelapse.mp4
3
 
 

I write this to convert my old, multiple GiB, multimedia files to formats that are smaller in size and royalty free.

remove_extension() {
  sed -E 's/^(.+)\.[^.]+$/\1/'
}

get_extension() {
  sed -E '/^\.?[^.]+$/d; s/^.+\.([^.]*)$/\1/'
}

ffmpeg_av1_opus() {
  if [ "$#" -lt 4 ]; then
    return
  fi
  ffmpeg -n -i "$4" -c:v libsvtav1 -preset "$1" -crf "$2" -c:a libopus -vbr:a 1 -b:a "$3" "${5:-"$(echo "$4" | remove_extension)_ffmpeg_av1_$1_$2_opus_$3.mkv"}"
}

ffmpeg_av1_lossless_flac() {
  if [ "$#" -eq 0 ]; then
    return
  fi
  ffmpeg -n -i "$1" -c:v libsvtav1 -svtav1-params lossless=1 -preset -2 -c:a flac "${2:-"$(echo "$1" | remove_extension)_ffmpeg_av1_lossless_flac.mkv"}"
}

ffmpeg_opus() {
  if [ "$#" -lt 2 ]; then
    return
  fi
  ffmpeg -n -i "$2" -c:a libopus -vbr:a 1 -b:a "$1" "${3:-"$(echo "$2" | remove_extension)_ffmpeg_opus_$1.opus"}"
}

ffmpeg_flac() {
  if [ "$#" -eq 0 ]; then
    return
  fi
  ffmpeg -n -i "$1" -c:a flac "${2:-"$(echo "$1" | remove_extension)_ffmpeg_flac.flac"}"
}

ffmpeg_segment() {
  if [ "$#" -lt 2 ]; then
    return
  fi
  ffmpeg -n -i "$2" -c copy -map 0 -segment_time "$1" -f segment -reset_timestamps 1 "${3:-"$(echo "$2" | remove_extension)_ffmpeg_%04d.$(echo "$2" | get_extension)"}"
}

ffmpeg_concat_auto() {
  local files=()
  if [ "$#" -eq 0 ]; then
    for f in *; do
      test -f "$f" && files+=("$f")
    done
  else
    files=("$@")
  fi
  ffmpeg -n -f concat -safe 0 -i <(printf "file '$PWD/%s'\n" "${files[@]}") -c copy "$(echo "${files[0]}" | remove_extension | sed -E 's/_ffmpeg_[0-9]+$//').$(echo "${files[0]}" | get_extension)"
}

ffmpeg_concat() {
  if [ "$#" -lt 2 ]; then
    return
  fi
  ffmpeg -n -f concat -safe 0 -i <(printf "file '$PWD/%s'\n" "${@:2}") -c copy "$1"
}

echo_non_ffmpeg() {
  while IFS= read -r -d '' f; do
    case "$f" in
      *ffmpeg_*) ;;
      *)
        echo "$f"
        ;;
    esac
  done < <(find . -type f -print0)
}

remove_non_ffmpeg() {
  while IFS= read -r -d '' f; do
    case "$f" in
      *ffmpeg_*) ;;
      *)
        rm -f -- "$f"
        ;;
    esac
  done < <(find . -type f -print0)
}

echo_ffmpeg() {
  while IFS= read -r -d '' f; do
    case "$f" in
      *ffmpeg_*)
        echo "$f"
        ;;
      *) ;;
    esac
  done < <(find . -type f -print0)
}

remove_ffmpeg() {
  while IFS= read -r -d '' f; do
    case "$f" in
      *ffmpeg_*)
        rm -f -- "$f"
        ;;
      *) ;;
    esac
  done < <(find . -type f -print0)
}

jxl_lossy() {
  if [ "$#" -lt 2 ]; then
    return
  fi
  cjxl -j 0 -e 10 -d "$1" "$2" "${3:-"$(echo "$2" | remove_extension).jxl"}"
}

jxl_lossless() {
  if [ "$#" -eq 0 ]; then
    return
  fi
  cjxl -j 1 -e 10 -d 0 "$1" "${2:-"$(echo "$1" | remove_extension).jxl"}"
}

multimedia_convert() {
  (
    shopt -s globstar nullglob
    for f in **/*; do
      [[ -f $f ]] || continue
      local file=${f##*/}
      local dir=${f%/*}
      [[ $dir == "$f" ]] && dir=.
      case ${file,,} in
        *.gif)
          if [[ ${file##*.} != gif ]]; then
            if [[ -e "${f%.*}.gif" ]]; then
              printf 'Skipping: output exists: %s\n' "$f" >&2
              continue
            fi
            mv -- "$f" "${f%.*}.gif"
          fi
          ;;
        *.flac)
          if [[ ${file##*.} != flac ]]; then
            if [[ -e "${f%.*}.flac" ]]; then
              printf 'Skipping: output exists: %s\n' "$f" >&2
              continue
            fi
            mv -- "$f" "${f%.*}.flac"
          fi
          ;;
        *.jpg | *.jpeg | *.png)
          local jxl="${file%.*}.jxl"
          if [[ -e "$dir/$jxl" ]]; then
            printf 'Skipping: output exists: %s\n' "$f" >&2
            continue
          fi
          (
            cd "$dir" &&
              jxl_lossy 1 "./$file" "./$jxl" &&
              rm -- "$file"
          )
          ;;
        *.avif | *.bmp | *.heic | *.ico | *.jp2 | *.tif | *.webp)
          local png="${file%.*}.png"
          if [[ -e "$dir/$png" ]]; then
            printf 'Skipping: intermediate png exists: %s\n' "$f" >&2
            continue
          fi
          local jxl="${file%.*}.jxl"
          if [[ -e "$dir/$jxl" ]]; then
            printf 'Skipping: output exists: %s\n' "$f" >&2
            continue
          fi
          (
            cd "$dir" &&
              magick "./$file" "./$png" && {
              jxl_lossy 1 "./$png" "./$jxl" &&
                rm -- "$file" ||
                true
            } &&
              rm -f -- "$png"
          )
          ;;
        *.3gp | *.mov | *.vob | *.wmv)
          (
            cd "$dir" &&
              ffmpeg_av1_opus 4 40 24k "./$file" &&
              rm -- "$file"
          )
          ;;
        *.avi | *.mkv | *.mp4)
          (
            cd "$dir" &&
              ffmpeg_av1_opus 4 32 72k "./$file" &&
              rm -- "$file"
          )
          ;;
        *.aac | *.mp3 | *.m4a | *.ogg)
          (
            cd "$dir" &&
              ffmpeg_opus 96k "./$file" &&
              rm -- "$file"
          )
          ;;
        *.wav)
          (
            cd "$dir" &&
              ffmpeg_flac "./$file" &&
              rm -- "$file"
          )
          ;;
        *)
          continue
          ;;
      esac
    done
  )
}
4
10
submitted 1 week ago* (last edited 1 week ago) by Willie169 to c/ffmpeg@programming.dev
 
 
remove_extension() {
  sed -E 's/^(.+)\.[^.]+$/\1/'
}

get_extension() {
  sed -E '/^\.?[^.]+$/d; s/^.+\.([^.]*)$/\1/'
}

ffmpeg_av1_opus() {
  if [ "$#" -lt 4 ]; then
    return
  fi
  ffmpeg -i "$4" -c:v libsvtav1 -preset "$1" -crf "$2" -c:a libopus -vbr:a 1 -b:a "$3" "${5:-"$(echo "$4" | remove_extension)_ffmpeg_av1_$1_$2_opus_$3.mkv"}"
}

ffmpeg_av1_lossless_flac() {
  if [ "$#" -eq 0 ]; then
    return
  fi
  ffmpeg -i "$1" -c:v libsvtav1 -svtav1-params lossless=1 -preset -2 -c:a flac "${2:-"$(echo "$1" | remove_extension)_ffmpeg_av1_lossless_flac.mkv"}"
}

ffmpeg_opus() {
  if [ "$#" -lt 2 ]; then
    return
  fi
  ffmpeg -i "$2" -c:a libopus -vbr:a 1 -b:a "$1" "${3:-"$(echo "$2" | remove_extension)_ffmpeg_opus_$1.opus"}"
}

ffmpeg_flac() {
  if [ "$#" -eq 0 ]; then
    return
  fi
  ffmpeg -i "$1" -c:a flac "${2:-"$(echo "$1" | remove_extension)_ffmpeg_flac.flac"}"
}

ffmpeg_segment() {
  if [ "$#" -lt 2 ]; then
    return
  fi
  ffmpeg -i "$2" -c copy -map 0 -segment_time "$1" -f segment -reset_timestamps 1 "${3:-"$(echo "$2" | remove_extension)_ffmpeg_%04d.$(echo "$2" | get_extension)"}"
}

ffmpeg_concat_auto() {
  local files=()
  if [ "$#" -eq 0 ]; then
    for f in *; do
      test -f "$f" && files+=("$f")
    done
  else
    files=("$@")
  fi
  ffmpeg -f concat -safe 0 -i <(printf "file '$PWD/%s'\n" "${files[@]}") -c copy "$(echo "${files[0]}" | remove_extension | sed -E 's/_ffmpeg_[0-9]+$//').$(echo "${files[0]}" | get_extension)"
}

ffmpeg_concat() {
  if [ "$#" -lt 2 ]; then
    return
  fi
  ffmpeg -f concat -safe 0 -i <(printf "file '$PWD/%s'\n" "${@:2}") -c copy "$1"
}

echo_non_ffmpeg() {
  while IFS= read -r -d '' f; do
    case "$f" in
      *ffmpeg_*) ;;
      *)
        echo "$f"
        ;;
    esac
  done < <(find . -type f -print0)
}

remove_non_ffmpeg() {
  while IFS= read -r -d '' f; do
    case "$f" in
      *ffmpeg_*) ;;
      *)
        rm -f "$f"
        ;;
    esac
  done < <(find . -type f -print0)
}

echo_ffmpeg() {
  while IFS= read -r -d '' f; do
    case "$f" in
      *ffmpeg_*)
        echo "$f"
        ;;
      *) ;;
    esac
  done < <(find . -type f -print0)
}

remove_ffmpeg() {
  while IFS= read -r -d '' f; do
    case "$f" in
      *ffmpeg_*)
        rm -f "$f"
        ;;
      *) ;;
    esac
  done < <(find . -type f -print0)
}

mv_space_underscore() {
  while IFS= read -r -d '' f; do
    new="${f// /_}"
    [[ -e "$new" ]] && {
      echo "skipping '$f', '$new' exists"
      continue
    }
    mv -- "$f" "$new"
  done < <(find . -depth -name '* *' -print0)
}

mv_space_hyphen() {
  while IFS= read -r -d '' f; do
    new="${f// /-}"
    [[ -e "$new" ]] && {
      echo "skipping '$f', '$new' exists"
      continue
    }
    mv -- "$f" "$new"
  done < <(find . -depth -name '* *' -print0)
}

Edit: Add -vbr:a 1 to opus, thanks to exdor.

5
6
Welcome! Introduction (programming.dev)
submitted 1 week ago* (last edited 1 week ago) by exdor@programming.dev to c/ffmpeg@programming.dev
 
 

Hi all!

Only after I made an account on this instance I saw that there already is a community on lemmy.world. many instances might defederate, so this one is useful.

My motivation: I use / have used ffmpeg for

  • encoding any audio to opus, variable bitrate (there are 2 modes, not sure why and mode 1 seems to be better)
  • converting stereo audio to mono, duplicated to 2 channels
  • cutting audio and video
  • encoding any kind of video to AV1, using svt_av1 (the software encoder which sadly produces way better results than qsv_av1 on my Intel Arc)
  • extracting screenshots from video
  • converting multiple images into a video for timelapse recordings
  • converting videos to animated images (webp)
  • add metadata to videos and audios
  • concatenate audiobooks in several folders to a single file including chapter metadata
  • ...

I will post all my scripts here in some time and hope for an engaging community to exchange tips, parameters etc! Ffmpeg is not very easy to use but incredibly efficient!