Can ffmpeg burn in time code?

35,392

Solution 1

Short answer, no.

Long answer, yes, but not without using a separate library to create the frames with the rendered time code on them, with transparency filling the rest of the frame, then using FFmpeg to overlay the frames on the existing video. Off the top of my head I don't know how to do this, but I'm sure if you're creative you can figure it out.

Edit: I've been working on this problem because it is an interesting question/project for me. I have come a little further in the solution by writing a Perl script that will generate a .srt file with the time code embedded in it for any given video file from which FFmpeg is configured to be able to read the metadata. It uses the Video::FFmpeg library to read the duration and saves a subtitle file as ${video}.srt. This will make it so it will render automatically in Mplayer if you insert the following lines in your ~/.mplayer/config:

# select subtitle files automatically in the current directory, all files
# matching the basename of the current playing file
sub-fuzziness=1

Still working on how to position and overlay the rendered subtitles on a video and re-encode in the same format. I'll update this post as I know more.

Solution 2

FFMPEG's drawtext filter works for me, you specify the starting timecode and its format thusly:

-vf drawtext="fontsize=15:fontfile=/Library/Fonts/DroidSansMono.ttf:\
timecode='00\:00\:00\:00':rate=25:text='TCR\:':fontsize=72:fontcolor='white':\
boxcolor=0x000000AA:box=1:x=860-text_w/2:y=960"

you have to specify timecode format in the form hh:mm:ss[:;,]ff. Note that you have to escape the colons in the timecode format string, and you have to specify a timecode rate (here 25fps). You can also specify additional text - here it's "TCR:"

You can get the frame rate with ffprobe and a bit of shell fu:

frame_rate=$(ffprobe -i "movie.mov" -show_streams 2>&1|grep fps|sed "s/.*, \([0-9.]*\) fps,.*/\1/")

So you could easily plug it all together in a batch - processing script, eg

for i in *.mov
frame_rate=$(ffprobe -i "$i" -show_streams 2>&1|grep fps|sed "s/.*, \([0-9.]*\) fps,.*/\1/")
clipname=${(basename "$i")/\.*/}
ffmpeg -i "$i" -vcodec whatever -acodec whatever \
-vf drawtext="fontsize=15:fontfile=/Library/Fonts/DroidSansMono.ttf:\
timecode='00\:00\:00\:00':rate=$frame_rate:text='$clipname' TCR:':\
fontsize=72:fontcolor='white':boxcolor=0x000000AA:\
box=1:x=860-text_w/2:y=960" "${i/.mov/_tc.mov}"
done

That would add the clip's name and rolling timecode in a semi-opaque box at the bottom center of a 1920x1080 frame

Edit Since I've come to the dark side I now do this in a Windows Powershell environment, and this is what I use:

ls -R -File -filter *.M*|%{
ffmpeg -n -i $_.fullname -vf drawtext="fontsize=72:x=12:y=12:`
timecode='00\:00\:00\:00':rate=25:fontcolor='white':`
boxcolor=0x000000AA:box=1" `
("c:\path\to\destination\{0}" -F ($_.name -replace 'M[OPT][V4S]', 'mp4'))}

This creates mp4s given a folder containing .MOV, .MP4 and .MTS files (using the -filter command it looks for files with *.M* in the name, which you would have to change if you were doing .AVI files), and it's a bit more minimal, it just uses libx264 with default settings as output codec and doesn't specify font etc. The timecode in this case is burnt in at the top left of the frame.

Solution 3

The drawtext filter mentioned in @stib's answer is the key to insert the time. Using the timecode option, however, does not match the wall clock time. If you get the r (timecode_rate) parameter wrong, then your the time will not match your playback time.

Other options exist, for instance the text='%{prt}' option allows you to display the elapsed time with microsecond precision. Command:

ffmpeg -i video.mp4 -vf "drawtext=text='%{prt}'" output.mp4

To get a clock instead, I had to use the deprecated strftime option. This has an undocumented basetime option that can be used to set the start time in microseconds. An example where I set the start time to 12:00 PM on 1 December 2013 (the $(...) part is shell-expansion done by the shell) and have only the time displayed (see the strftime manual for possible formats):

ffmpeg -i video.mp4 -vf "drawtext=expansion=strftime: \
    basetime=$(date +%s -d'2013-12-01 12:00:00')000000: \
    text='%H\\:%S\\:%S'" output.mp4

\\: is used to escape the : which would otherwise get the meaning of an option separator.

Another example: a command to insert the date + time within a black box, some pixels away from the top-left corner and "some padding" (actually, two spaces and newlines on the edges):

newline=$'\r'
ffmpeg -i video.mp4 -vf "drawtext=x=8:y=8:box=1:fontcolor=white:boxcolor=black: \
    expansion=strftime:basetime=$(date +%s -d'2013-12-01 12:00:00')000000: \
    text='$newline %Y-%m-%d %H\\:%M\\:%S $newline'" output.mp4

Another example to get the microseconds below the clock:

newline=$'\r'
ffmpeg -i video.mp4 -vf "drawtext=expansion=strftime: \
    basetime=$(date +%s -d'2013-12-01 12:00:00')000000: \
    text='$newline %H\\:%M\\:%S $newline':fontcolor=white:box=1:boxcolor=black, \
    drawtext=text='$newline %{pts} $newline': \
    y=2*th/3:box=1:fontcolor=white:boxcolor=black:" output.mp4

This uses the fact that the text is actually three lines long and that both text have a newline (carriage return, ^M) prepended and appended. (Without this newline, the space gets stripped)

Other hints:

  • -vf and -filter:v are equal.
  • You cannot specify filters multiple times, e.g. -vf drawtext=text=1 -vf drawtext=text=2 will only draw the second text. You can combine filters with a comma as I showed above.

Solution 4

The simplest solution i found to show the clock when the file has been captured, not its duration and it working /based on this post, thanks!/ is

D:\Temp\2>ffmpeg.exe -i test.avi -vf "drawtext=fontfile=C\\:/Windows/Fonts/arial.ttf:timecode='00\:20\:10\:00':rate=25:text='TCR\:':fontsize=46:fontcolor=white:x=500:y=50: box=1: boxcolor=0x00000000@1"  -f mp4 textts.mp4

So simple in timecode - put your starting time, then the counting is going fine! The example is for Windows

Solution 5

in more recent builds, you can use the drawtext filter (the "t" in its examples is, I believe, the timestamp of the frame) to insert text. It also works for srtftime on the "current" system time as well.

Share:
35,392
spinon
Author by

spinon

Developer by day!

Updated on August 12, 2020

Comments

  • spinon
    spinon almost 4 years

    I have a need to burn in a time code to a video and am wondering if this is something that ffmpeg is capable of?

  • spinon
    spinon almost 14 years
    That's an interesting approach. Any leads on libraries that might do this? If not I can look around.
  • spinon
    spinon almost 14 years
    One thing I should add since you are obviously doing some great work here. Is that what I am ultimately looking to do is create a new video with the timecodes present on it. So not just play them in a player but create a new video so it can be played in any player. I will add a bounty to the question since you have given this so much effort.
  • Lekensteyn
    Lekensteyn over 10 years
    Good idea, unfortunately it does not work with fractions such as 12.34 fps.
  • Lekensteyn
    Lekensteyn over 10 years
    The documentation contains an error, there is no t option. It is named basetime. Anyway, I could not get this to work properly.
  • rogerdpack
    rogerdpack over 10 years
    yes, apparently it was deprecated and removed. A few other things that might be helpful: you can use anything "eval" supports, and have "frame_num" and "localtime" available for the strftime stuff...
  • Lekensteyn
    Lekensteyn over 10 years
    See my answer to this question, I yesterday discovered some other methods to get the time. The t appears to be a documentation error, it should be basetime (which applies to expansion=strftime only).
  • rogerdpack
    rogerdpack almost 10 years
    does the "localtime" option help here? ffmpeg.org/pipermail/ffmpeg-user/2014-July/022355.html
  • Lekensteyn
    Lekensteyn almost 10 years
    localtime does not help, it bases off the local time instead of a fixed moment. The command I tried: ffplay -i video.mp4 -vf drawtext='expansion=normal:text=%{localtime\\:%a %b %d %Y}'
  • ntg
    ntg over 9 years
    In fact it seems that it only works for frame_rate standard, such as 25, had to transcode, and even then, the times were wrong for some reason for me.
  • stib
    stib over 9 years
    there was a typo in the batch script that would force it to use 25fps, that's fixed now. See how it goes.
  • stib
    stib over 9 years
    Also @ntg, you're going to have to transcode. You can't change the contents of the video raster without transcoding.
  • Neil Slater
    Neil Slater over 7 years
    Evidently it is now possible with video filters. See gist.github.com/reidransom/2630650
  • Rick2047
    Rick2047 about 7 years
    this one worked newline=$'\r' ffmpeg -i video.mp4 -vf "drawtext=expansion=strftime: \ basetime=$(date +%s -d'2013-12-01 12:00:00')000000: \ text='$newline %H\\:%M\\:%S $newline':fontcolor=white:box=1:boxcolor=black, \ drawtext=text='$newline %{pts} $newline': \ y=2*th/3:box=1:fontcolor=white:boxcolor=black:" output.mp4
  • http8086
    http8086 about 4 years
    this works, amazing, btw, what is the meaning of TCR here
  • bk138
    bk138 over 3 years
    Actually t is in FFMPEG 4.1, you can use ffmpeg -i input.mp4 -vf drawtext="fontsize=60:fontcolor=yellow:text='%{e\:t*1000}':x‌​=(w-text_w):y=(h-tex‌​t_h)" output.mp4.
  • iluvcapra
    iluvcapra about 3 years
    I'm not sure how you'd do this with ffprobe but the rate parameter works if you specify it as a rational, so if you say "24000/1001" it works for me.