Can ffmpeg burn in time code?
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.
Comments
-
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 almost 14 yearsThat's an interesting approach. Any leads on libraries that might do this? If not I can look around.
-
spinon almost 14 yearsOne 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 over 10 yearsGood idea, unfortunately it does not work with fractions such as
12.34 fps
. -
Lekensteyn over 10 yearsThe documentation contains an error, there is no
t
option. It is namedbasetime
. Anyway, I could not get this to work properly. -
rogerdpack over 10 yearsyes, 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 over 10 yearsSee 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 bebasetime
(which applies toexpansion=strftime
only). -
rogerdpack almost 10 yearsdoes the "localtime" option help here? ffmpeg.org/pipermail/ffmpeg-user/2014-July/022355.html
-
Lekensteyn almost 10 yearslocaltime 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 over 9 yearsIn 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 over 9 yearsthere was a typo in the batch script that would force it to use 25fps, that's fixed now. See how it goes.
-
stib over 9 yearsAlso @ntg, you're going to have to transcode. You can't change the contents of the video raster without transcoding.
-
Neil Slater over 7 yearsEvidently it is now possible with video filters. See gist.github.com/reidransom/2630650
-
Rick2047 about 7 yearsthis 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 about 4 yearsthis works, amazing, btw, what is the meaning of TCR here
-
bk138 over 3 yearsActually
t
is in FFMPEG 4.1, you can useffmpeg -i input.mp4 -vf drawtext="fontsize=60:fontcolor=yellow:text='%{e\:t*1000}':x=(w-text_w):y=(h-text_h)" output.mp4
. -
iluvcapra about 3 yearsI'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.