Originally published May 14, 2019 @ 1:59 pm

All that crap I’ve been saving from YouTube, Facebook and whatnot tends to add up. As quality is not a huge concern here (not that it was very high to begin with), optimizing those video files can recover a surprisingly significant amount of disk space. If you have CPU cycles to spare that is. The main advantage here is really not the disk space, which is cheap nowadays, but faster network transfer rates.

All of this has been done in Windows 10 Pro running Ubuntu 18.04.1 LTS installed from the Microsoft store. The ffmpeg utility was installed like so:

sudo su -
cd /tmp && wget https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz
tar xf ffmpeg-release-amd64-static.tar.xz
mv ffmpe*amd64-static/ /opt/ffmpeg/
ln -s /opt/ffmpeg/ffmpeg /usr/bin/ffmpeg
ln -s /opt/ffmpeg/ffprobe /usr/bin/ffprobe
ln -s /opt/ffmpeg/qt-faststart /usr/bin/qt-faststart

The script below does a few things:

  1. Finds video files in the source folder. You can control the depth of search.
  2. Identifies the original encoding (this is if you want to stay with it).
  3. Calculates the optimal bitrate based on the duration of the video.
  4. Re-encodes the file using H.265 (or you can stick with the original codec).
  5. Tells you how much disk space you saved.

So let me break this down a bit. This line will find “video” files in the specified folder, without descending into subfolders. You can control this by modifying the -maxdepth option.

find "${in_dir}" -mindepth 1 -maxdepth 1 -type f -exec file -N -i -- {} + | sed -n 's!: video/[^:]*$!!p'

The two lines below will attempt to get the name of the codec used by the original file and to identify the appropriate encoder available on your system. This does not always work as expected, unfortunately, so feel free to make improvements.

codec="$(ffprobe "${p}/${n}.${e}" 2>&1 >/dev/null | grep -oP "(?<=Video: )[a0-z9]{1,}(?= )")"
encoder="$(ffmpeg -codecs 2>/dev/null| grep "decoders: ${codec}" | grep -oP "(?<=encoders: )[a0-z9]{1,}(?= )")"

The nested ffmpeg in the command below identifies the duration of the video in seconds. It then divides 10^9 (1GB) by that value to get the optimal bitrate and passes that to the -b option.

ffmpeg -i "${p}/${n}.${e}" -vcodec ${target_encoder} -crf 20 \
-b $(echo "scale=0; 10^9 / $(ffmpeg -i "${p}/${n}.${e}" 2>&1 | grep -oP -m1 "(?<=ion: )([0-9]{2}(:)?){3}(?=\.[0-9]{2},)" | \
awk '{split($1,A,":"); split(A[3],B,".");print 3600*A[1]+60*A[2]+B[1]}')"|bc) \
"${p}/${n}_${target_encoder}.${e}" >/dev/null 2>&1

The script is below and is also available on my GitHub.

Das Skript
#!/bin/bash
in_dir="/var/tmp"; target_encoder="libx265"
find "${in_dir}" -mindepth 1 -maxdepth 1 -type f -exec file -N -i -- {} + | sed -n 's!: video/[^:]*$!!p' | while read i
do
  p="$(dirname "${i}")"
  n="$(basename "${i}" | awk -F. '{$NF=""; print $0}'| sed 's/ $//g')"
  e="$(basename "${i}" | awk -F. '{print $NF}')"
  b=$(echo "scale=0;$(stat --printf="%s" "${p}/${n}.${e}")/1024/1024"|bc)
  codec="$(ffprobe "${p}/${n}.${e}" 2>&1 >/dev/null | grep -oP "(?<=Video: )[a0-z9]{1,}(?= )")"
  encoder="$(ffmpeg -codecs 2>/dev/null| grep "decoders: ${codec}" | grep -oP "(?<=encoders: )[a0-z9]{1,}(?= )")"
  echo "Converting ${p}/${n}.${e} (${b}MB, ${codec}) to ${p}/${n}_${target_encoder}.${e}"
  ffmpeg -i "${p}/${n}.${e}" -vcodec ${target_encoder} -crf 20 \
  -b $(echo "scale=0; 10^9 / $(ffmpeg -i "${p}/${n}.${e}" 2>&1 | grep -oP -m1 "(?<=ion: )([0-9]{2}(:)?){3}(?=\.[0-9]{2},)" | \
  awk '{split($1,A,":"); split(A[3],B,".");print 3600*A[1]+60*A[2]+B[1]}')"|bc) \
  "${p}/${n}_${target_encoder}.${e}" >/dev/null 2>&1
  a=$(echo "scale=0;$(stat --printf="%s" "${p}/${n}_${target_encoder}.${e}")/1024/1024"|bc)
  (( x = 100 - ( a * 100 / b ) ))
  echo "${p}/${n}_${target_encoder}.${e} is ${x}% smaller than the original"
  printf '%40s\n' | tr ' ' -
done

While I am on the subject of videos, recently I went to an event and made a few rather shaky videos with my little Sony camera (lots of people pushing and shoving, and the two beers I had didn’t help steady the hand either).

So here’s a quick-and-dirty way to remove some of the shakiness out of your videos.

ffmpeg -i 00009.MTS -vf vidstabdetect -f null -
ffmpeg -i 00009.MTS -vf vidstabtransform=smoothing=30:input="transforms.trf" -vcodec libx264 00009.mkv

If your video is  extremely shaky, you may need to pass some additional parameters to the first command that generates the transforms.trf file. However, keep in mind that the more smoothing you do, the more weird the outcome might be.

ffmpeg -i 00009.MTS -vf vidstabdetect=shakiness=10:accuracy=15 -f null -

Here’s a useful one: you can make a side-by-side video of the original and the stabilized versions to see the overall effect:

ffmpeg -i 00009.MTS -i 00009.mp4 -vcodec libx264 -filter_complex "[0:v]setpts=PTS-STARTPTS, pad=iw*2:ih[bg]; [1:v]setpts=PTS-STARTPTS[fg]; [bg][fg]overlay=w" 00009_sbs.mp4

Thanks for watching.