burnt-in timecode with ffmpeg
Burning-in timecode is easy in Avid or Final Cut, but if for any reason you need to do it the hard way with command-line ffmpeg, here is how.
To not make it harder than necessary, there are links to pre-compiled versions of ffmpeg on their download page. For Mac OS X, as of August 2013, there were these 2 versions:
- http://ffmpegmac.net, which unfortunately didn't have the needed filter. It would give the error
"AVFilterGraph ...] No such filter: 'drawtext'". - the version 2.0.1 built by Helmut Tessarek worked fine. Unfortunately, it is compressed with 7-zip, so you may need to get a decompressor first. I used Keka (not open source, but free).
Below is the command I used to quickly encode Sony mpeg2 MXF files into H264 Quicktimes, preserving the original timecode in the QT TC track (ffmpeg does this automatically), and also burning it into the picture.
Since the command itself is quite awful, it is best to predefine variables, so that the long command itself can be copy/pasted directly, without further editing, or at least not too much...
# set variables for the input and output files:
in=/path/to/input_file out=/path/to/ouput_file.mov
# the timecode rate must be set. Should be identical to the FPS.
tc_rate=25
# select a monospaced font file on your machine. On Linux, try:
font="/usr/share/fonts/truetype/droid/DroidSansMono.ttf"
# or on Mac:
font="/Library/Fonts/Andale Mono.ttf"
# size and position:
fontsize=46 position="x=w-text_w-(text_w/6):y=text_h" # top right
# For bottom right, try this instead: position="x=(w-tw)/2: y=h-(2*lh)"
# get the timecode, and escape the ":" to be able to use it in the burn-in filter
timecode=$( ffmpeg -i "$in" 2>&1 | awk '$1 ~ /^timecode/ {print $NF}' ) tc_escaped=${timecode//:/\\:}
# To test encoding only the first x seconds, use:
test_secs="-t 20"
# or for the whole video, leave this empty:
test_secs=
# quality/size/speed : (try crf between 18 and 25? lower is better quality and bigger file.)
crf=23 preset=ultrafast # (superfast, fast, slow, ...)
# And finally (with de-interlacing and without scaling):
ffmpeg -threads 0 -i "$in" $test_secs -acodec copy -vcodec libx264 -preset $preset -crf $crf -deinterlace -vf "drawtext=fontfile=$font: timecode='$tc_escaped': r=$tc_rate: $position: fontcolor=white: fontsize=$fontsize: box=1: boxcolor=black@0.2" "$out"
or to keep only video with audio channel 1 (throwing away audio channels 2, etc. ):
ffmpeg -threads 0 -i "$in" $test_secs -map 0:0 -map 0:1 -acodec copy -vcodec libx264 -preset $preset -crf $crf -deinterlace -vf "drawtext=fontfile=$font: timecode='$tc_escaped': r=$tc_rate: $position: fontcolor=white: fontsize=$fontsize: box=1: boxcolor=black@0.2" "$out"