Back

ffmpeg

2020-11-23

Table of Contents
  1. AVI to MP4
  2. MKV to MP4
  3. MKV HEVC to MP4
  4. Conclusion

Hello! In this chapter we're going to take a stab at using ffmpeg. I've always heard of ffmpeg but never used. I knew going in that it was a video conversion tool and so far I've had a pretty good experience with it. It is quite stupid proof and there is enough help online that you could muddle your way through.

Let's start muddling!

First, the problem. Currently I have my rust streaming site playing mp4 videos in a browser. I need to convert mkvs and avis to mp4s. All those formats are really containers. Inside them are video and audio streams, each encoded in their own specific way.

There are a few different formats but the major one that we'll focus on is using h264 for video and aac for audio. There's no real reason besides they work.

So we need to look at 3 things when we get a video. We need to make sure the file's container is mp4, we need the file's video codec to be h264 and we need it's audio codec to be aac.

We can check all these things by using ffprobe which comes with ffmpeg.

AVI to MP4

We first use ffprobe to take a look at our video file.

bash
> ffprobe In\ July.avi
...
Input #0, avi, from 'Im Juli.(In July).2000.DVDRiP.DE.ENsubs.DivX.MP3/In July.avi':
  Metadata:
    encoder         : Nandub v1.0rc2
  Duration: 01:36:06.77, start: 0.000000, bitrate: 1017 kb/s
    Stream #0:0: Video: mpeg4 (DIVX / 0x58564944), yuv420p, 608x336 [SAR 1:1 DAR 38:21], 898 kb/s, 23.98 fps, 23.98 tbr, 23.98 tbn, 23.98 tbc
    Stream #0:1: Audio: mp3 (U[0][0][0] / 0x0055), 48000 Hz, stereo, s16p, 106 kb/s
...

Here we are going to anaylze In\ July.avi, which by the way is great little movie. Quite cute.

We can see by the file type it is an avi before we run ffprobe but we can see it where it says Input #0. Next we can see the Stream #0 and Stream #1. Stream #0 is the video codec, in this case it is divx and Stream #1 we can see is mp3.

So for this file we need to convert all three parts.

bash
> ffmpeg -i In\ July.avi -c:video h264 -c:audio aac In\ July.mp4

This command takes in the input file and uses the -c option to say which codec we want to change. -c:video means we want to encode the video codec using h264. -c:audio means we cant to conver the audio codec to aac. Finally we have the output file at the very end where we want it in the container mp4.

Voila! We have now converted our avi file to an mp4.

Let's probe our new file.

bash
> ffprobe In\ July.mp4
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'In July.mp4':
  Metadata:
    major_brand     : isom
    minor_version   : 512
    compatible_brands: isomiso2avc1mp41
    encoder         : Lavf57.83.100
  Duration: 01:36:06.77, start: 0.000000, bitrate: 679 kb/s
    Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 608x336 [SAR 1:1 DAR 38:21], 545 kb/s, 23.98 fps, 23.98 tbr, 11988 tbn, 47.95 tbc (default)
    Metadata:
      handler_name    : VideoHandler
    Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 129 kb/s (default)
    Metadata:
      handler_name    : SoundHandler

We can see that our Input #0 is now not avi and contains mp4 among other container types.

We also see our Stream #0 now shows h264 as the video codec and Stream #1 shows that our audio codec is aac.

At this point our mp4 file should work perfectly in our browser!

MKV to MP4

Now let's take a look at an mkv file.

bash
ffprobe Rick.and.Morty.S03E01.The.Rickshank.Rickdemption.1080p.Amazon.WEB-DL.x264-Rapta.mkv


Input #0, matroska,webm, from 'Rick.and.Morty.S03E01.The.Rickshank.Rickdemption.1080p.Amazon.WEB-DL.x264-Rapta.mkv':
  Metadata:
    COMPATIBLE_BRANDS: isomiso2avc1mp41
    MAJOR_BRAND     : isom
    MINOR_VERSION   : 512
    ENCODER         : Lavf57.81.100
  Duration: 00:23:22.46, start: 0.000000, bitrate: 1877 kb/s
    Stream #0:0: Video: h264 (High), yuv420p(progressive), 1920x1080 [SAR 1:1 DAR 16:9], 23.98 fps, 23.98 tbr, 1k tbn, 47.95 tbc (default)
    Metadata:
      HANDLER_NAME    : VideoHandler
      DURATION        : 00:23:22.464000000
    Stream #0:1(eng): Audio: aac (LC), 48000 Hz, stereo, fltp (default)
    Metadata:
      HANDLER_NAME    : SoundHandler
      DURATION        : 00:23:22.421000000

Here we can see that our container is matroska. Our Stream #0 is h264 and Stream#1 is aac. This is pretty close to what we want! If only we could change the container. ! Well we could do just that with ffmpeg.

bash
ffmpeg -i Rick.and.Morty.S03E01.The.Rickshank.Rickdemption.1080p.Amazon.WEB-DL.x264-Rapta.mkv -c:v copy -c:a copy test.mp4

Here we use the shorthands for video and audio and instead of saying the codec we want, we simply say copy. This means that we just want the codecs copied. Then our final statement, test.mp4, changes the container type.

bash
>  ffprobe test.mp4
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'test.mp4':
  Metadata:
    major_brand     : isom
    minor_version   : 512
    compatible_brands: isomiso2avc1mp41
    encoder         : Lavf57.83.100
  Duration: 00:23:22.46, start: 0.000000, bitrate: 1880 kb/s
    Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 1920x1080 [SAR 1:1 DAR 16:9], 1775 kb/s, 23.98 fps, 47.95 tbr, 16k tbn, 47.95 tbc (default)
    Metadata:
      handler_name    : VideoHandler
    Stream #0:1(eng): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 98 kb/s (default)
    Metadata:
      handler_name    : SoundHandler

We can see here that our conversion made no changes to the codecs and only changed the Input #0 which is the container. This has the added benefit that this conversion is much faster than doing a re-encode to a different format. The avi to mp4 above would have been much longer than this one.

Voila! We have now converted a mkv to mp4.

MKV HEVC to MP4

Now let's look at another mp4 but this time with a different codec. It will be quite similar to the way we worked no the avi codec.

But as always, let's first probe the file.

bash
> ffprobe [pseudo\]\ Rick\ and\ Morty\ S01E01\ -\ Pilot\ \[1080p\]\ \[h.265\].mkv
Input #0, matroska,webm, from '[pseudo] Rick and Morty S01E01 - Pilot [1080p] [h.265].mkv':
  Metadata:
    encoder         : libebml v1.3.1 + libmatroska v1.4.2
    creation_time   : 2015-07-26T11:38:26.000000Z
  Duration: 00:21:58.19, start: 0.000000, bitrate: 1746 kb/s
    Stream #0:0(eng): Video: hevc (Main), yuv420p(tv), 1920x1080 [SAR 1:1 DAR 16:9], 23.98 fps, 23.98 tbr, 1k tbn, 23.98 tbc (default)
    ...
    Stream #0:1(eng): Audio: aac (LC), 48000 Hz, 5.1, fltp (default)

Here we can see the Input#0 is matroska. The Stream#0 is hevc and the Stream#1 is aac. In this case the audio codec works with the browser however hecv doesn't.

bash
ffmpeg -i [pseudo\]\ Rick\ and\ Morty\ S01E01\ -\ Pilot\ \[1080p\]\ \[h.265\].mkv -c:v h264 -c:a copy test.mp4

Here we want to convert the video codec to h264 while copying the audio codec that is already aac. We set the container to mp4 by way of the file type.

bash
> ffprobe test.mp4

Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'test1.mp4':
  Metadata:
    major_brand     : isom
    minor_version   : 512
    compatible_brands: isomiso2avc1mp41
    encoder         : Lavf57.83.100
  Duration: 00:21:58.19, start: 0.000000, bitrate: 1901 kb/s
    Stream #0:0(eng): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 1920x1080 [SAR 1:1 DAR 16:9], 1641 kb/s, 23.98 fps, 23.98 tbr, 24k tbn, 47.95 tbc (default)
    Metadata:
      handler_name    : VideoHandler
    Stream #0:1(eng): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, 5.1, fltp, 252 kb/s (default)
    Metadata:
      handler_name    : SoundHandler

Voila! Our file has been converted to mp4 with a video codec of h264 and an audio codec of aac which was the original.

We now have a hevc mkv file in a format we can play in the browser!

With that! we have a good idea of how to use ffmpeg and ffprobe to convert our video and we know how to interrogate a file and see what codecs it uses.

Conclusion

Now that we have an understanding of the simplest cases, we can look at the ffmpeg site and play around with the various options that ffmpeg is full of.

Below are some options you can play with for h264, for example the preset option let's you speed up the encoding and the constant rate filter let's you play around with the quality.

=> https://trac.ffmpeg.org/wiki/Encode/H.264

bash
ffmpeg -i input.mkv -c:v libx264 -preset slow -crf 22 -c:a copy output.mkv

This says that we want to convert the video codec of input.mkv to h264 and we want to copy the audio codec. We want to keep the container the same so we continued to use mkv for the container type, output.mkv. Finally we have our preset which says to use a slow compression and our constant rate filter is 22 which is just about a medium level of quality.

A crf of 0 is lossless quality, a crf of 18 is visually lossless and 23 is the medium. So in this command our quality is just a bit better than medium.

The slow compression will result in a much better file size so we should choose the slowest speed we are comfortable with.

=> https://trac.ffmpeg.org/wiki/Encode/AAC

This page has some settings you can play with for the audio but I didn't play with these ones as I didn't understand them.

! There we have it, a very simple introduction to ffmpeg and ffprobe. I've only used ffmpeg for converting videos to a format that my browser can play so that all I've learned.

=> https://drewdevault.com/2018/08/26/Self-hosted-livestreaming.html

Here is an example of using ffmpeg to stream video, so you can see that it has quite a bit more to it, we've only seen the simplest use case!