23

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?

1
  • So I assume you are wanting to burn in the "current time of the video" into the video itself? Or are you wanting to use libav* to add text to a video with your own timecode here?
    – rogerdpack
    Commented Dec 2, 2013 at 20:09

7 Answers 7

36

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.

5
  • 1
    Good idea, unfortunately it does not work with fractions such as 12.34 fps.
    – Lekensteyn
    Commented Dec 1, 2013 at 13:22
  • 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.
    – ntg
    Commented Dec 5, 2014 at 17:17
  • there was a typo in the batch script that would force it to use 25fps, that's fixed now. See how it goes.
    – stib
    Commented Feb 17, 2015 at 3:04
  • 1
    Also @ntg, you're going to have to transcode. You can't change the contents of the video raster without transcoding.
    – stib
    Commented Feb 17, 2015 at 3:47
  • 1
    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.
    – iluvcapra
    Commented Jun 15, 2021 at 6:57
21

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.
4
  • does the "localtime" option help here? ffmpeg.org/pipermail/ffmpeg-user/2014-July/022355.html
    – rogerdpack
    Commented Jul 10, 2014 at 13:49
  • 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}'
    – Lekensteyn
    Commented Jul 10, 2014 at 16:43
  • 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
    – Rick2047
    Commented May 30, 2017 at 15:22
  • Using drawtext=text='%{prt}' in ffmpeg 41dd50ad0d-20230429 outputs an error: %{prt} is not known.
    – virtualdj
    Commented Mar 16 at 13:19
8
+100

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.

3
  • That's an interesting approach. Any leads on libraries that might do this? If not I can look around.
    – spinon
    Commented Jul 3, 2010 at 1:28
  • 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.
    – spinon
    Commented Jul 6, 2010 at 17:34
  • 2
    Evidently it is now possible with video filters. See gist.github.com/reidransom/2630650 Commented Oct 12, 2016 at 10:44
3

Here's my solution, and I believe it is the correct one, because it both avoids having to manually set the rate, and allows you to format the output.

ffmpeg -i test.mp4 -vf \
    "drawtext=fontfile=arialbd.ttf:text='%{pts\:gmtime\:0\:%H\\\:%M\\\:%S}'" test.avi

This produces a stamp in the HH:MM:SS format; you can change it to whatever you'd like, using strftime.

It may look like it's going to put in a timestamp with gmtime, but that's not what happens. It actually feeds the video's current time, in seconds, to gmtime, producing a date which is 1/1/1970, and a time which is however many seconds after midnight. So you just discard the date portion and use the time portion.

Take note of the triple escaped colons within the pts function, which you'll have to do if you enter it like I did above. Also you can see that I copied my font file for Arial Bold and dropped it right into the working directory for simplicity.

1

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.

4
  • The documentation contains an error, there is no t option. It is named basetime. Anyway, I could not get this to work properly.
    – Lekensteyn
    Commented Dec 1, 2013 at 13:34
  • 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...
    – rogerdpack
    Commented Dec 2, 2013 at 20:39
  • 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).
    – Lekensteyn
    Commented Dec 2, 2013 at 21:41
  • 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-text_h)" output.mp4.
    – bk138
    Commented Dec 30, 2020 at 10:35
1

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

1
  • this works, amazing, btw, what is the meaning of TCR here
    – http8086
    Commented Jun 13, 2020 at 16:59
-1

FFMPEG will be able to do much of the work, but it won't be all packaged up. Using FFMPEG, you can have it decode all of the frames in sequence, and give you the "Presentation Time Stamp" (additional time related metadata may be available on some formats, but PTS is what you'll want to look for to get started.) Then, you are on your own to actually draw the text onto the decoded frame yourself. I use Qt for similar things, by using QPainter on a QImage with the frame data, but there may be some other API to draw on an image that you find more obvious. Then, use the FFMPEG API to make a compressed video that has your newly drawn-on frames in it. It'll be a little more complicated if you also want audio. My own work doesn't really care about audio, so I haven't bothered to learn the audio aspects of the API. Basically, as you do your read loop getting packets out of the file, some of them will be audio. Instead of discarding them like I do, you'll need to keep them and write them into the output file as you got them.

I've only used the C API, rather than C#, so I don't know if there are any special gotcha's there to be worried about.

Not the answer you're looking for? Browse other questions tagged or ask your own question.