Skip to content

Build your own tiny FFMPEG

Albert Huang edited this page Apr 7, 2017 · 5 revisions

Introduction

FFMPEG is known to be a swiss knife for all things video related. Decoding, encoding, demuxing, remuxing, resizing, and anything else you can think of regarding video - FFMPEG does it! That said, there are times when you want FFMPEG to do something really simple instead of doing everything. This is the guide that will help you out with that goal! (With this guide, I was able to get the file size down to ~3 MBs!)

Laziness

If you don't feel like following this tutorial, simply grab the repo and run the my_tiny_ffmpeg.sh script inside the FFMPEG_Minimum_Build_Helper_Script directory to build everything from this tutorial!

Definition

For this tutorial, our goal is to achieve the following:

  • FFMPEG should be optimized for final binary size, not speed.
  • FFMPEG should only be able to decode H.264 video.
  • FFMPEG should only be able to encode H.264 video. Optionally, it can just copy the incoming video codec to the destination file.
  • FFMPEG should be able to just copy the incoming audio codec to the destination file.
  • FFMPEG should be able to resize the incoming video.
  • FFMPEG should be able to demux and (re)mux MP4 files. (MP4s are containers that can hold audio and video, in this case H.264 video and arbitrary audio.)

Our target command will look like this:

# Resize "The Simpsons Movie - Trailer.mp4" to half its original width and height,
# then re-encode the result to H.264, copy the audio from the original file, and
# store the result in Simpsons.mp4.
build/ffmpeg -i The\ Simpsons\ Movie\ -\ Trailer.mp4 -vf scale=iw*.5:ih*.5 -vcodec h264 -acodec copy Simpsons.mp4

The source MP4 file can be downloaded from here.

Ready, set, go!

Requirements

For this build, you really only need the following:

  • Linux machine - but may work on other OSes.
  • GCC Compiler - with C/C++ support, should be relatively recent.
  • YASM x86/amd64 assembler
  • Make
  • BASH
  • Python 2 or 3 (for custom build option script)
  • Autoconf (maybe)

No external libraries are needed - if we need any libraries, we will be building them ourselves and explicitly avoiding any system libraries.

Background

The modern day FFMPEG distribution includes the following libraries:

  • libavcodec - codec handling
  • libavdevice - audio/video drivers for recording, playback, capturing, etc. (webcam is a particular example)
  • libavfilter - filters for your video and audio once it has been decoded
  • libavformat - mux and demux handler (e.g. MP4 container file format)
  • libavresample - audio resampling
  • libavutil - utilities library for FFMPEG. Always built and required.
  • libpostproc - ???
  • libswresample - (better?) audio resampling
  • libswscale - image/video scaling

When enabling/disabling features in FFMPEG, configuration for each of these libraries are updated in order for the correct features and tools to be built.

That said, there are some capabilities that are NOT included in FFMPEG. For our example, H.264 encoding is not handled at all in FFMPEG - instead, it is handled with another library called libx264, which provides optimized H.264 encoding for FFMPEG.

More likely than not, your encoding/decoding of a certain codec may very well require external libraries in order for them to work. For a rough idea of what's out there, run:

./configure -h | grep coding

Guide

Steps are long, but depending on your computer build times may not be too bad. Again, we are trying to build a very minimalist FFMPEG here!

  1. Download and Install libx264

    First things first, we need a library to handle encoding H.264. Note that if you are not doing anything special (e.g. you are just reading in H.264 data, and not doing any conversions or filtering/resizing), you do NOT need this library. Instead, -vcodec copy should work just fine for you!

    Download libx264 here. You'll want the latest git snapshot for the latest fixes.

    Extract libx264 to a separate working directory, say ~/x264. You should now have a folder resembling x264-snapshot-YYYYMMDD-XXXX. Enter that directory, and then type the following commands:

    mkdir build
    cd build
    ../configure --enable-static --disable-cli --disable-gpl --disable-opencl --disable-avs --disable-swscale --disable-lavf --disable-ffms --disable-gpac --disable-lsmash --disable-thread
    make -j4
    

    NOTE: If you want multithreading support, you'll need to remove --disable-thread from the options, and then add in the following FFMPEG --extra-ldflags: -lpthread.

    If everything works well, you should now have a libx264.a in your folder. This will be your libx264 static library to link with from FFMPEG. For convenience, copy this out of your build folder into your main x264 folder, as well as the necessary headers. Assuming ~/x264 is your main x264 folder:

    cp libx264.a x264_config.h ../*.h ~/x264
    
  2. Download and Install FFMPEG

    Now you can go and install FFMPEG! To make things a bit easier, there's a script I wrote that will help you disable unnecessary options that may accidentally cause the build to become larger (and more dependent on your system's libraries)!

    Download FFMPEG from here, and then extract it to a separate working directory, e.g. ~/ffmpeg.

    Then, download my script from my repo (here) and place it within your FFMPEG source directory, e.g. ~/ffmpeg/ffmpeg-3.2.4/. Name the filename get_autodetect_options.py.

    This script simply groks the ../configure --help output, and searches for options marked with "autodetect". It then outputs --disable-FEATURE options to allow for easier disabling of all autodetected 3rd party library features, since they are not disabled with the given --disable-all. (FFMPEG's configure script will still autodetect and add 3rd party libraries if they are not manually disabled by hand, even after a --disable-all.)

    Once the script is saved in the source directory, run the following commands within said directory:

    mkdir build
    cd build
    ../configure --disable-all `python ../get_autodetect_options.py --opts` --enable-ffmpeg --enable-small --enable-avcodec --enable-avformat --enable-avfilter --enable-swresample --enable-swscale --enable-decoder=h264 --enable-encoder=rawvideo,libx264 --enable-parser=h264 --enable-protocol=file --enable-demuxer=mov --enable-muxer=rawvideo,mp4 --enable-filter=scale --enable-gpl --enable-libx264 --extra-cflags="-I../x264" --extra-cxxflags="-I../x264" --extra-ldflags="-L../x264"
    make -j4
    

    What do these options do? Breaking them down:

    • --disable-all: disable building everything - libraries and programs included.
    • python ../get_autodetect_options.py --opts - script that automatically extracts the 3rd party autodetected library dependencies and creates --disable-FEATURE options to disable them. More details above.
    • --enable-ffmpeg - enable the FFMPEG program.
    • --enable-small - optimize for final binary size (not speed)
    • --enable-avcodec - enable building libavcodec (codec support)
    • --enable-avformat - enable building libavformat (mux/demux support)
    • --enable-avfilter - enable building libavfilter (filter/resize support)
    • --enable-swresample - enable building libswresample (audio resampling support)
    • --enable-swscale - enable building libswscale (video/image resizing support)
    • --enable-decoder=h264 - enable building libavcodec with H.264 decoding support
    • --enable-encoder=rawvideo,libx264 - enable building libavcodec with raw video and H.264 (via libx264) encoding support
    • --enable-parser=h264 - enable H.264 packet parsing - required for H.264 decoding
    • --enable-protocol=file - enable file read/write support
    • --enable-demuxer=mov - enable demuxing of MOV-type files (includes MP4)
    • --enable-muxer=rawvideo,mp4 - enable muxing to raw video and MP4
    • --enable-filter=scale - enable scaling (resizing) filter for videos in libavfilter
    • --enable-gpl - enable use of GPL licensed code in FFMPEG/libav*
      • Required for libx264 usage - otherwise configure will silently remove it from the available encoders.
    • --enable-libx264 - enable linking to libx264 library
      • Required for libx264 encoder - otherwise configure will silently remove it from the available encoders.
    • --extra-cflags="-I~/x264" --extra-cxxflags="-I~/x264" --extra-ldflags="-L~/x264" - add CFLAGS, CXXFLAGS, and LDFLAGS compiler options to FFMPEG's build to ensure that both configure and make will find our previously build libx264 static library.
      • From above note: if you are using pthreads, make sure to append -lpthreads to your LDFLAGS.

    At this point, if you set everything up correctly, you should now have a tiny little build of FFMPEG!

  3. Testing FFMPEG

    Let's see if our FFMPEG has our requested features! Put the sample H.264/MP4 file in your build directory, and then run:

    # Resize "The Simpsons Movie - Trailer.mp4" to half its original width and height,
    # then re-encode the result to H.264, copy the audio from the original file, and
    # store the result in Simpsons.mp4.
    ./ffmpeg -i The\ Simpsons\ Movie\ -\ Trailer.mp4 -vf scale=iw*.5:ih*.5 -vcodec h264 -acodec copy Simpsons.mp4
    

    If all goes well, you should now have a smaller Simpsons.mp4 to watch on your favorite media player, with sound and everything!

Results

The resulting build is nice and tiny, and has minimal dependencies:

albert@debian:~/ffmpeg/ffmpeg-3.2.4/build$ ls -lh ffmpeg
-rwxrwxrwx 1 albert root 3.1M Apr  5 00:57 ffmpeg
albert@debian:~/ffmpeg/ffmpeg-3.2.4/build$ ldd ffmpeg
	linux-vdso.so.1 (0x00007ffd5df70000)
	libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f4464ae2000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f4464737000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f4464de3000)
albert@debian:~/ffmpeg/ffmpeg-3.2.4/build$ ./ffmpeg -i The\ Simpsons\ Movie\ -\ Trailer.mp4 -vf scale=iw*.5:ih*.5 -vcodec h264 -acodec copy Simpsons.mp4
ffmpeg version 3.2.4 Copyright (c) 2000-2017 the FFmpeg developers
  built with gcc 4.9.2 (Debian 4.9.2-10)
  configuration: --disable-all --disable-pthreads --disable-w32threads --disable-os2threads --disable-bzlib --disable-iconv --disable-libxcb --disable-libxcb-shm --disable-libxcb-xfixes --disable-libxcb-shape --disable-lzma --disable-schannel --disable-sdl2 --disable-securetransport --disable-xlib --disable-zlib --disable-audiotoolbox --disable-cuvid --disable-d3d11va --disable-dxva2 --disable-nvenc --disable-vaapi --disable-vda --disable-vdpau --disable-videotoolbox --enable-ffmpeg --enable-small --enable-avcodec --enable-avformat --enable-avfilter --enable-swresample --enable-swscale --enable-decoder=h264 --enable-encoder='rawvideo,libx264' --enable-parser=h264 --enable-protocol=file --enable-demuxer=mov --enable-muxer='rawvideo,mp4' --enable-filter=scale --enable-gpl --enable-libx264 --extra-cflags=-I../x264 --extra-cxxflags=-I../x264 --extra-ldflags=-L../x264
  libavutil      55. 34.101 / 55. 34.101
  libavcodec     57. 64.101 / 57. 64.101
  libavformat    57. 56.101 / 57. 56.101
  libavfilter     6. 65.100 /  6. 65.100
  libswscale      4.  2.100 /  4.  2.100
  libswresample   2.  3.100 /  2.  3.100
[h264 @ 0x2627840] Warning: not compiled with thread support, using thread emulation
Guessed Channel Layout for Input Stream #0.1 : stereo
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'The Simpsons Movie - Trailer.mp4':
  Metadata:
    major_brand     : isom
    minor_version   : 1
    compatible_brands: isomavc1
    creation_time   : 2007-02-19T05:03:04.000000Z
  Duration: 00:02:17.30, start: 0.000000, bitrate: 4283 kb/s
    Stream #0:0(und): Video: h264 (avc1 / 0x31637661), yuv420p, 1280x544, 4221 kb/s, 23.98 fps, 23.98 tbr, 24k tbn, 47.95 tbc (default)
    Metadata:
      creation_time   : 2007-02-19T05:03:04.000000Z
      handler_name    : GPAC ISO Video Handler
    Stream #0:1(und): Audio: aac (mp4a / 0x6134706D), 48000 Hz, stereo, 64 kb/s (default)
    Metadata:
      creation_time   : 2007-02-19T05:03:08.000000Z
      handler_name    : GPAC ISO Audio Handler
[h264 @ 0x2638200] Warning: not compiled with thread support, using thread emulation
[libx264 @ 0x26524a0] Warning: not compiled with thread support, using thread emulation
[libx264 @ 0x26524a0] using cpu capabilities: MMX2 SSE2Fast SSSE3 SSE4.2 AVX
[libx264 @ 0x26524a0] profile High, level 2.1
[libx264 @ 0x26524a0] 264 - core 148 - H.264/MPEG-4 AVC codec - Copyright 2003-2017 - http://www.videolan.org/x264.html - options: cabac=1 ref=3 deblock=1:0:0 analyse=0x3:0x113 me=hex subme=7 psy=1 psy_rd=1.00:0.00 mixed_ref=1 me_range=16 chroma_me=1 trellis=1 8x8dct=1 cqm=0 deadzone=21,11 fast_pskip=1 chroma_qp_offset=-2 threads=1 lookahead_threads=1 sliced_threads=0 nr=0 decimate=1 interlaced=0 bluray_compat=0 constrained_intra=0 bframes=3 b_pyramid=2 b_adapt=1 b_bias=0 direct=1 weightb=1 open_gop=0 weightp=2 keyint=250 keyint_min=23 scenecut=40 intra_refresh=0 rc_lookahead=40 rc=crf mbtree=1 crf=23.0 qcomp=0.60 qpmin=0 qpmax=69 qpstep=4 ip_ratio=1.40 aq=1:1.00
[mp4 @ 0x2638d40] track 1: codec frame size is not set
Output #0, mp4, to 'Simpsons.mp4':
  Metadata:
    major_brand     : isom
    minor_version   : 1
    compatible_brands: isomavc1
    encoder         : Lavf57.56.101
    Stream #0:0(und): Video: h264 (libx264) ([33][0][0][0] / 0x0021), yuv420p, 640x272, q=-1--1, 23.98 fps, 24k tbn, 23.98 tbc (default)
    Metadata:
      creation_time   : 2007-02-19T05:03:04.000000Z
      handler_name    : GPAC ISO Video Handler
      encoder         : Lavc57.64.101 libx264
    Side data:
      cpb: bitrate max/min/avg: 0/0/0 buffer size: 0 vbv_delay: -1
    Stream #0:1(und): Audio: aac ([64][0][0][0] / 0x0040), 48000 Hz, stereo, 64 kb/s (default)
    Metadata:
      creation_time   : 2007-02-19T05:03:08.000000Z
      handler_name    : GPAC ISO Audio Handler
Stream mapping:
  Stream #0:0 -> #0:0 (h264 (native) -> h264 (libx264))
  Stream #0:1 -> #0:1 (copy)
Press [q] to stop, [?] for help
frame= 3289 fps= 64 q=28.0 Lsize=   11109kB time=00:02:17.25 bitrate= 663.0kbits/s dup=1 drop=0 speed=2.66x    
video:9957kB audio:1075kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 0.702286%
[libx264 @ 0x26524a0] frame I:108   Avg QP:19.11  size: 24063
[libx264 @ 0x26524a0] frame P:1060  Avg QP:23.44  size:  4280
[libx264 @ 0x26524a0] frame B:2121  Avg QP:26.72  size:  1443
[libx264 @ 0x26524a0] consecutive B-frames: 10.9%  7.8%  5.1% 76.3%
[libx264 @ 0x26524a0] mb I  I16..4: 16.9% 28.0% 55.0%
[libx264 @ 0x26524a0] mb P  I16..4:  2.8%  7.6%  5.5%  P16..4: 18.2%  9.2%  6.0%  0.0%  0.0%    skip:50.8%
[libx264 @ 0x26524a0] mb B  I16..4:  0.3%  0.7%  0.9%  B16..8: 15.7%  4.9%  2.4%  direct: 1.6%  skip:73.5%  L0:44.9% L1:45.7% BI: 9.4%
[libx264 @ 0x26524a0] 8x8 transform intra:39.4% inter:51.3%
[libx264 @ 0x26524a0] coded y,uvDC,uvAC intra: 54.8% 72.0% 53.1% inter: 8.5% 8.9% 3.3%
[libx264 @ 0x26524a0] i16 v,h,dc,p: 37% 37% 12% 15%
[libx264 @ 0x26524a0] i8 v,h,dc,ddl,ddr,vr,hd,vl,hu: 22% 22% 29%  4%  4%  4%  5%  4%  6%
[libx264 @ 0x26524a0] i4 v,h,dc,ddl,ddr,vr,hd,vl,hu: 26% 23% 17%  5%  5%  6%  6%  5%  6%
[libx264 @ 0x26524a0] i8c dc,h,v,p: 48% 26% 19%  6%
[libx264 @ 0x26524a0] Weighted P-Frames: Y:5.5% UV:4.2%
[libx264 @ 0x26524a0] ref P L0: 65.1% 11.6% 14.0%  9.0%  0.3%
[libx264 @ 0x26524a0] ref B L0: 85.2% 10.9%  3.9%
[libx264 @ 0x26524a0] ref B L1: 95.9%  4.1%
[libx264 @ 0x26524a0] kb/s:594.57

If you want to completely eliminate dependencies, add -static to your configure's LDFLAGS. Note that your executable will grow by a few MBs, so beware!