FFmpeg

From Segfault
Jump to navigation Jump to search

Installation

Installation

Usage

Transcoding

We want to convert a video to a smaller one, of constant size. Let's find out what we have:

$ ls -hgo file.mkv
-rw-r--r--. 1 2.5G Dec 29  2012 file.mkv

$ ffmpeg -i file.mkv 2>&1 | grep -E 'Duration|Video|Audio'
 Duration: 01:29:31.28, start: 0.000000, bitrate: 3883 kb/s
   Stream #0:0(eng): Video: h264 (High), yuv420p(tv, bt709), 1280x720, SAR 1:1 DAR 16:9, 25 fps, 25 tbr, 1k tbn, 50 tbc (default)
   Stream #0:1(ger): Audio: ac3, 48000 Hz, stereo, fltp, 448 kb/s (default)

Let's see if can convert it to maybe 800 MB. First, calculate the duration of the video in seconds:

$ echo "01:29:31.28" | awk -F: '{ print ($1 * 3600) + ($2 * 60) + $3 }'
5371.28

Our 800 MB output file should contain a 160 kbps audio track too, so let's do some math here:

           800 MB = 819200   kB
5371.28 * 160 / 8 = 107425.6 kB                              ← audio
                    711774.4 kB / 5371.28 * 8 = 1060.1 kbps  ← video

With that information we can now begin the two-pass encoding[1] process. First we analyze the video track, ignore the audio and discard all output:

ffmpeg -i file.mkv -pass 1 -c:v libx264 -b:v 1060k -an -f rawvideo -y /dev/null 

While the output is discarded, two files are generated, ffmpeg2pass-0.log and ffmpeg2pass-0.log.mbtree:

$ ls -hgo ffmpeg2pass-0.log*
-rw-------. 1  16M Jan 20 09:15 ffmpeg2pass-0.log
-rw-------. 1 603M Jan 20 09:15 ffmpeg2pass-0.log.mbtree

Both will be used in the next step:

ffmpeg -i file.mkv -pass 2 -c:v libx264 -b:v 1060k -c:a libvorbis -b:a 160k -ac 2 file.mp4

The result is slightly below 800 MB:

$ ls -hgo file.mp4
-rw-------. 1 770M Jan 21 10:46 file.mp4
$ ffmpeg -i file.mp4 2>&1 | grep -E 'Duration|Stream'
 Duration: 01:29:31.36, start: 0.002667, bitrate: 1201 kb/s
   Stream #0:0(eng): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 1280x720 [SAR 1:1 DAR 16:9], 1059 kb/s, 25 fps, 25 tbr, 12800 tbn, 50 tbc (default)
   Stream #0:1(ger): Audio: vorbis (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 133 kb/s (default)

Apparently our audio track encoded by libvorbis is using VBR by default[2].

Results (on a very slow machine, throttled to 800 MHz):

One Pass Encoding
$ ffmpeg -i file.mkv -c:v libx264 -b:v 1060k -c:a libvorbis -b:a 160k -ac 2 file.mp4
=> finished after 29h, 768 MB file

Tow Pass Encoding
$ ffmpeg -i file.mkv -pass 1 -c:v libx264 -b:v 1060k -an -f rawvideo -y /dev/null
$ ffmpeg -i file.mkv -pass 2 -c:v libx264 -b:v 1060k -c:a libvorbis -b:a 160k -ac 2 file.mp4
=> finished after 35h (10h for pass-1, 25h for pass-2), 770 MB file

On a more recent machine, converting a similar file should take no longer than 2-3 hours.

The files from both encoding jobs shared the same properties:

Stream #0:0(eng): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 1280x720 [SAR 1:1 DAR 16:9], 1059 kb/s, 25 fps, 25 tbr, 12800 tbn, 50 tbc (default)
Stream #0:1(ger): Audio: vorbis (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 133 kb/s (default)

Combining

Here we wanted to speed up a video (25min15) so that it could be merged together with an MP3 file (4min22):

ffmpeg -i a.mov -i b.mp3 -acodec copy -ab 128k -ar 44100 -map 0:0 -map 1:0 -vf "setpts=(1/6)*PTS" c.mp4

The order of the arguments matter:

-i         video input, audio input
-acdodec   copy audio stream
-ab        audio bitrate (bps)
-ar        audio sampling rate (Hz)
-map 0:0   get video from the video file
-map 1:0   get audio from the audio file
-vf        apply a filter graph to the input video

The map and vf options are kinda advanced and the setpts parameter was kinda hard to get right. Basically it increases video speed by 6. The audio input is not affected by this, as it is located on a different (internal) stream, see the map option above. Increasing the video speed by 6 meant that the video would be ~4 minutes long, a bit shorter than the audio track. But for now this was good enough.

Merging

Merging video files is a bit more complicated than it is for audio files. Luckily it's all explained in the official documentation. So, let's assume we want to merge 4 MP4 files into one:

mkfifo temp{1..4}
i=1; ls file*.mp4 | while read f; do ffmpeg -y -i "$f" -c copy -bsf:v h264_mp4toannexb -f mpegts temp${i} 2>/dev/null & i=$((i+1)); done

At this point we have 4 ffmpeg processes running, waiting for the FIFOs to be read from:

$ ps -f -p `pgrep ffmpeg`
UID     PID  PPID  C STIME TTY      STAT   TIME CMD
dummy 10852     1  0 00:34 pts/3    S      0:00 ffmpeg -y -i file1.mp4 -c copy -bsf:v h264_mp4toannexb -f mpegts temp1
dummy 10853     1  0 00:34 pts/3    S      0:00 ffmpeg -y -i file2.mp4 -c copy -bsf:v h264_mp4toannexb -f mpegts temp2
dummy 10854     1  0 00:34 pts/3    S      0:00 ffmpeg -y -i file3.mp4 -c copy -bsf:v h264_mp4toannexb -f mpegts temp3
dummy 10855     1  0 00:34 pts/3    S      0:00 ffmpeg -y -i file4.mp4 -c copy -bsf:v h264_mp4toannexb -f mpegts temp4

Start another ffmpeg to read from these FIFO files and produce the output file:

ffmpeg -f mpegts -i "concat:$(ls temp*|xargs|sed 's/ /|/g')" -c copy -bsf:a aac_adtstoasc combined.mp4
rm -f temp{1..4}

Container Formats

To change the container format (instead of re-encoding[3]), we can use:

ffmpeg -i input.mkv -c:v copy -c:a copy output.mp4

Audio

Converting[4] audio files to another format, e.g. Vorbis[5]

ffmpeg -y -i file.wav -c:a libvorbis  -qscale:a 3 file.ogg     # qscale:a 3 is the default for libvorbis[6]
ffmpeg -y -i file.wav -c:a libmp3lame -qscale:a 2 file.mp3     # qscale:a 0 translates to a bitrate of ~220-260 kbps[7]
ffmpeg -y -i file.wav -c:a libopus    -vbr on     file.opus    # ?

Notes:

  • To get a list of all supported encoders, use ffmpeg -encoders.
  • When used in a script, ffmpeg sometimes attempts to read stdin[8][9]:
Press [q] to stop, [?] for help
Enter command: <target>|all 

Use -nostdin or read from /dev/null to prevent that, e.g.:

ls wav/ | while read f; do ffmpeg -y -vn -i wav/"$f" -c:a libvorbis -qscale:a 3 ogg/"${f/%wav/ogg}" < /dev/null; done

Encode to MP3 with ffmpeg[7][10]

ffmpeg -i file.flac -c:a libmp3lame -qscale:a 0 file.mp3

Slideshow

Make a slideshow video from images:

ffmpeg -pattern_type glob -i 'image*.jpg' -c:v libx264 slideshow.mp4

The single quotes are important, as ffmpeg will do the globbing, not the shell.

DVD Rip

Let the DVD play once, then select all the parts containing the movie (i.e. skipping VTS_01_0.VOB):

cat VTS_01_[1-8].VOB | ffmpeg -i - -codec:a libvorbis -b:a 128k -codec:v libx264 -b:v 1024k out.mp4

Trim

Trim a long (audio) file to 45 minutes:

ffmpeg -i  long.mp3 -t $((45*60)) -c copy short.mp3

But, now our short file will end abpruptly. Let's add a 5 seconds fade out, and trim the file afterwards, and leave some room at the end:

ffmpeg -i  long.mp3 -af "afade=t=out:st=$((45*60)):d=5" short.mp3
ffmpeg -i short.mp3 -t $((45*60+10))            -c copy short_fadeout.mp3

Links

References