drawtext¶
- see also
drawtext with signalstats, drawtext with pts, etc, drawtext, drawgraph, blackframe, freezedetect
Minimal example¶
- see also
If the fontconfig support is disabled, or if you don’t want to care of the fontconfig (or if you aren’t familiar with it),
you must specify fontfile
, so the most minimalistic example for you is:
[me@host: ~]$ ffplay -f lavfi "color=white:s=160x90,loop=-1:size=2" \
> -vf "drawtext='fontfile=c\:/Windows/Fonts/courbd.ttf:text=aaa'"
[me@host: ~]$
[me@host: ~]$ # specify fontsize
[me@host: ~]$ ffplay -f lavfi "color=white:s=160x90,loop=-1:size=2" \
> -vf "drawtext='fontfile=c\:/Windows/Fonts/courbd.ttf:fontsize=60:text=aaa'"
The fontfile parameter needs to be passed the full path to the font file, and if your environment is Windows it contains a colon, so the colon needs to be escaped.
Colons that affect the parsing of the entire filter must be escaped anyway:
[me@host: ~]$ ffplay -f lavfi "color=white:s=800x450,loop=-1:size=2" -vf "drawtext=\
> 'fontfile=c\:/Windows/Fonts/courbd.ttf:\
> text=the special character \: is required to be escaped'"
Besides, because of the text expansion specification, the special character “%” associated with it is also required to be escaped (unless the parameter expansion
is none
):
[me@host: ~]$ ffplay -f lavfi "color=white:s=800x450,loop=-1:size=2" \
> -vf "drawtext=\
> 'fontfile=c\:/Windows/Fonts/courbd.ttf:text=the special characters \\\% \
> is also required to be escaped'"
[me@host: ~]$ ffplay -f lavfi "color=white:s=800x450,loop=-1:size=2" \
> -vf "drawtext=\
> 'fontfile=c\:/Windows/Fonts/courbd.ttf:text=the special characters % \
> is also required to be escaped\
> :expansion=none'"
Note
Note that in the current case, you need three backslashes, as you also need an escape related to the shell (Bourne-shell).
If you have trouble understanding shell expansion rules, consider using set -x
for enabling trace (and set +x
for disabling trace).
If the escaping text is complex, consider using textfile
instead of text
parameter:
[me@host: ~]$ cat > txt.txt
the special character :, and \% are required to be escaped
[me@host: ~]$ ffplay -f lavfi "color=white:s=800x450,loop=-1:size=2" -vf "drawtext=\
> 'fontfile=c\:/Windows/Fonts/courbd.ttf:textfile=txt.txt'"
#! /bin/sh
ffplay "Pexels Videos 1457810.mp4" -vf "\
drawtext='
fontfile=c\:/Windows/Fonts/comic.ttf:
fontsize=120:
fontcolor=white:
x=100:y=100:
text=%{localtime \: \%Y / \%m / \%d \%H \\\\\\: \%M \\\\\\: \%S}
'"
#! /bin/sh
trap 'rm -f dt.txt' 0 1 2 3 15
cat << __END__ > dt.txt
%{localtime : \%Y / \%m / \%d \%H \: \%M \: \%S}
__END__
#
ffplay "Pexels Videos 1457810.mp4" -vf "\
drawtext='
fontfile=c\:/Windows/Fonts/comic.ttf:
fontsize=120:
fontcolor=white:
x=100:y=100:
textfile=dt.txt
'"
This will reduce the need for escape a bit.
To enable default font fallback and the font option¶
:
To enable default font fallback and the font option you need to configure FFmpeg with
--enable-libfontconfig
.
Even if ffmpeg is “Windows builds”, --enable-libfontconfig
is true, but its distribution doesn’t contain fonts.conf
.
The most minimal fonts.conf
looks like this:
<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig>
<dir>./</dir>
<dir>../fonts</dir>
<dir>./fonts</dir>
<dir>~/.fonts</dir>
<dir>WINDOWSFONTDIR</dir> <!-- for Windows -->
<cachedir>WINDOWSTEMPDIR_FONTCONFIG_CACHE</cachedir> <!-- for Windows -->
<cachedir>~/.fontconfig</cachedir>
</fontconfig>
For the official distribution Windows version build,
for example if you put ffmpeg on c:/Program Files/ffmpeg-3.3.2-win64-shared
,
this file seems to work if put in c:/Program Files/ffmpeg-3.3.2-win64-shared/bin/fonts/
.
For the Unix family, especially if you intend to rely on a package manager, it will probably install the right one in the right place. Otherwise, you can probably know by reading the fontconfig manual.
If you have set up fonts.conf like this (with –enable-libfontconfig enabled),
you can use font
parameter like this:
#! /bin/sh
pref="`basename $0 .sh`"
ffmpeg -y -i "Pexels Videos 1457810.mp4" -filter_complex "
[0:v]
drawtext='font=sans-serif:fontsize=90:x=20:y=20:
text=The quick brown fox jumps over a lazy dog.
Sphinx of black quartz, judge my vow.'
" "${pref}.mp4"
Perhaps only Windows users need to write system-wide fonts.conf themselves. If you really want to write, it is better to copy from official example little by little and add to the above minimal stuff:
<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<!-- /etc/fonts/fonts.conf file to configure system font access -->
<fontconfig>
<!-- Font directory list -->
<dir>./</dir>
<dir>../fonts</dir>
<dir>./fonts</dir>
<dir>~/.fonts</dir>
<dir>WINDOWSFONTDIR</dir>
<!-- Font cache directory list -->
<cachedir>WINDOWSTEMPDIR_FONTCONFIG_CACHE</cachedir>
<cachedir>~/.fontconfig</cachedir>
<!--
Alias well known font names to available TrueType fonts.
These substitute TrueType faces for similar Type1
faces to improve screen appearance.
-->
<alias>
<family>Times</family>
<prefer><family>Times New Roman</family></prefer>
<default><family>serif</family></default>
</alias>
<alias>
<family>Helvetica</family>
<prefer><family>Arial</family></prefer>
<default><family>sans</family></default>
</alias>
<alias>
<family>Courier</family>
<prefer><family>Courier New</family></prefer>
<default><family>monospace</family></default>
</alias>
</fontconfig>
This will allow you to write for example:
#! /bin/sh
pref="`basename $0 .sh`"
ffmpeg -y -i "Pexels Videos 1457810.mp4" -filter_complex "
[0:v]
drawtext='
font=Times:
fontsize=90:x=20:y=20:
text=The quick brown fox jumps over a lazy dog.
Sphinx of black quartz, judge my vow.'
" "${pref}.mp4"
Or you can use the family name of font (neither an alias nor a filename):
#! /bin/sh
pref="`basename $0 .sh`"
ffmpeg -y -i "Pexels Videos 1457810.mp4" -filter_complex "
[0:v]
drawtext='
font=MS Gothic:
fontsize=90:x=20:y=20:
text=The quick brown fox jumps over a lazy dog.
Sphinx of black quartz, judge my vow.'
" "${pref}.mp4"
For the relationship between font family names and file names, for example see ImageFont Module (Python Pillow examples). (In Windows, you may be able to see it by opening “Properties” in Explorer.)
Note
Note that giving a base name as shown below will not be what you expect:
#! /bin/sh
pref="`basename $0 .sh`"
ffmpeg -y -i "Pexels Videos 1457810.mp4" -filter_complex "
[0:v]
drawtext='
fontfile=cour.ttf:
fontsize=90:x=20:y=20:
text=The quick brown fox jumps over a lazy dog.
Sphinx of black quartz, judge my vow.'
" "${pref}.mp4"
Using third party fonts¶
You can use any font as long as it is a true type font. Consider using the one included with your TexLive installation. Not limited to this, non-Windows fonts are often stored in structured directories:
[me@host: ~]$ find /c/texlive/2015/texmf-dist/fonts/truetype -type f
/c/texlive/2015/texmf-dist/fonts/truetype/google/roboto/Roboto-Black.ttf
/c/texlive/2015/texmf-dist/fonts/truetype/google/roboto/Roboto-BlackItalic.ttf
/c/texlive/2015/texmf-dist/fonts/truetype/google/roboto/Roboto-Bold.ttf
/c/texlive/2015/texmf-dist/fonts/truetype/google/roboto/Roboto-BoldItalic.ttf
/c/texlive/2015/texmf-dist/fonts/truetype/google/roboto/Roboto-Light.ttf
/c/texlive/2015/texmf-dist/fonts/truetype/google/roboto/Roboto-LightItalic.ttf
/c/texlive/2015/texmf-dist/fonts/truetype/google/roboto/Roboto-Medium.ttf
/c/texlive/2015/texmf-dist/fonts/truetype/google/roboto/Roboto-MediumItalic.ttf
/c/texlive/2015/texmf-dist/fonts/truetype/google/roboto/Roboto-Regular.ttf
/c/texlive/2015/texmf-dist/fonts/truetype/google/roboto/Roboto-RegularItalic.ttf
/c/texlive/2015/texmf-dist/fonts/truetype/google/roboto/Roboto-Thin.ttf
/c/texlive/2015/texmf-dist/fonts/truetype/google/roboto/Roboto-ThinItalic.ttf
/c/texlive/2015/texmf-dist/fonts/truetype/google/roboto/RobotoCondensed-Bold.ttf
/c/texlive/2015/texmf-dist/fonts/truetype/google/roboto/RobotoCondensed-BoldItalic.ttf
/c/texlive/2015/texmf-dist/fonts/truetype/google/roboto/RobotoCondensed-Light.ttf
/c/texlive/2015/texmf-dist/fonts/truetype/google/roboto/RobotoCondensed-LightItalic.ttf
/c/texlive/2015/texmf-dist/fonts/truetype/google/roboto/RobotoCondensed-Regular.ttf
/c/texlive/2015/texmf-dist/fonts/truetype/google/roboto/RobotoCondensed-RegularItalic.ttf
/c/texlive/2015/texmf-dist/fonts/truetype/google/roboto/RobotoSlab-Bold.ttf
/c/texlive/2015/texmf-dist/fonts/truetype/google/roboto/RobotoSlab-Light.ttf
/c/texlive/2015/texmf-dist/fonts/truetype/google/roboto/RobotoSlab-Regular.ttf
/c/texlive/2015/texmf-dist/fonts/truetype/google/roboto/RobotoSlab-Thin.ttf
... (snip) ...
/c/texlive/2015/texmf-dist/fonts/truetype/public/gnu-freefont/FreeMono.ttf
/c/texlive/2015/texmf-dist/fonts/truetype/public/gnu-freefont/FreeMonoBold.ttf
/c/texlive/2015/texmf-dist/fonts/truetype/public/gnu-freefont/FreeMonoBoldOblique.ttf
/c/texlive/2015/texmf-dist/fonts/truetype/public/gnu-freefont/FreeMonoOblique.ttf
/c/texlive/2015/texmf-dist/fonts/truetype/public/gnu-freefont/FreeSans.ttf
/c/texlive/2015/texmf-dist/fonts/truetype/public/gnu-freefont/FreeSansBold.ttf
/c/texlive/2015/texmf-dist/fonts/truetype/public/gnu-freefont/FreeSansBoldOblique.ttf
/c/texlive/2015/texmf-dist/fonts/truetype/public/gnu-freefont/FreeSansOblique.ttf
/c/texlive/2015/texmf-dist/fonts/truetype/public/gnu-freefont/FreeSerif.ttf
/c/texlive/2015/texmf-dist/fonts/truetype/public/gnu-freefont/FreeSerifBold.ttf
/c/texlive/2015/texmf-dist/fonts/truetype/public/gnu-freefont/FreeSerifBoldItalic.ttf
/c/texlive/2015/texmf-dist/fonts/truetype/public/gnu-freefont/FreeSerifItalic.ttf
/c/texlive/2015/texmf-dist/fonts/truetype/public/ipaex/ipaexg.ttf
/c/texlive/2015/texmf-dist/fonts/truetype/public/ipaex/ipaexm.ttf
/c/texlive/2015/texmf-dist/fonts/truetype/public/ipaex/ipag.ttf
/c/texlive/2015/texmf-dist/fonts/truetype/public/ipaex/ipagp.ttf
/c/texlive/2015/texmf-dist/fonts/truetype/public/ipaex/ipam.ttf
/c/texlive/2015/texmf-dist/fonts/truetype/public/ipaex/ipamp.ttf
... (snip) ...
This is very painful if you have to write their full path each time you use them, so you should use fontconfig. You can write it like this:
<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<!-- /etc/fonts/fonts.conf file to configure system font access -->
<fontconfig>
<!-- Font directory list -->
<dir>./</dir>
<dir>../fonts</dir>
<dir>./fonts</dir>
<dir>~/.fonts</dir>
<dir>WINDOWSFONTDIR</dir>
<dir>c:/texlive/2015/texmf-dist/fonts/truetype</dir>
<!-- Font cache directory list -->
<cachedir>WINDOWSTEMPDIR_FONTCONFIG_CACHE</cachedir>
<cachedir>~/.fontconfig</cachedir>
<!--
Alias well known font names to available TrueType fonts.
These substitute TrueType faces for similar Type1
faces to improve screen appearance.
-->
<alias>
<family>Times</family>
<prefer><family>Times New Roman</family></prefer>
<default><family>serif</family></default>
</alias>
<alias>
<family>Helvetica</family>
<prefer><family>Arial</family></prefer>
<default><family>sans</family></default>
</alias>
<alias>
<family>Courier</family>
<prefer><family>Courier New</family></prefer>
<default><family>monospace</family></default>
</alias>
</fontconfig>
Then, if you want to use ipaexg.ttf
, you can:
#! /bin/sh
pref="`basename $0 .sh`"
ffmpeg -y -i "Pexels Videos 1457810.mp4" -filter_complex "
[0:v]
drawtext='
font=IPAexGothic:
fontsize=90:x=20:y=20:
text=The quick brown fox jumps over a lazy dog.
Sphinx of black quartz, judge my vow.'
" "${pref}.mp4"
BTW, on Windows,
it is better to know that you can select truetype fonts in Explorer, right-click, and `Install’ to deploy
to c:/Windows/Fonts
(in terms of fontconfig, WINDOWSFONTDIR
),
and then you can use them just like any other system font. (However,
there seems to be no ways of batch operation, so it would be troublesome if the number of font files is large.)
Newline, and tab¶
A line break as a value for the “text” parameter or as a character in a file specified in “textfile” behaves as you expect:
[me@host: ~]$ ffmpeg -y -filter_complex \
> "color=white:s=160x90,loop=-1:size=2,drawtext='fontfile=c\:/Windows/Fonts/courbd.ttf:fontsize=60:text=aaa
> bbb'" -r 1/1 -t 1 newline_in_drawtext.png
[me@host: ~]$ cat > txt.txt
A line break as a value for the "text" parameter or
as a character in a file specified in "textfile"
behaves as you expect:
[me@host: ~]$ ffmpeg -y -filter_complex \
> "color=white:s=960x135,loop=-1:size=2,drawtext='fontfile=c\:/Windows/Fonts/courbd.ttf:fontsize=30:\
> textfile=txt.txt'" -r 1/1 -t 1 newline_in_drawtext2.png
The same is true for tab codes, but in some cases you may need to control how many spaces to replace with the “tabsize” parameter:
[me@host: ~]$ cat > html_sample.txt
<html>
<body>
<div>
Indented text by tab.
</div>
</body>
</html>
[me@host: ~]$ ffmpeg -filter_complex \
> "color=white:s=480x270,loop=-1:size=2,\
> drawtext='fontfile=c\:/Windows/Fonts/courbd.ttf:fontsize=30:\
> textfile=html_sample.txt'" -r 1/1 -t 1 newline_in_drawtext3.png
[me@host: ~]$ ffmpeg -filter_complex \
> "color=white:s=480x270,loop=-1:size=2,\
> drawtext='fontfile=c\:/Windows/Fonts/courbd.ttf:fontsize=30:\
> textfile=html_sample.txt:tabsize=2'" -r 1/1 -t 1 newline_in_drawtext4.png
Text position¶
- see also
The position of the text can be specified by “x” and “y”:
#! /bin/sh
ffmpeg -y -filter_complex "
color=white:s=1280x720,loop=-1:size=2,
drawtext='fontfile=c\:/Windows/Fonts/courbd.ttf:fontsize=50:text=hello:
x=100:y=100'
" -t 30 drawtext_xy.mp4
As with “overlay”, you can use expressions including timestamps for x
and y
of drawtext
:
#! /bin/sh
ffmpeg -y -filter_complex "
color=white:s=1280x720,loop=-1:size=2,
drawtext='fontfile=c\:/Windows/Fonts/courbd.ttf:fontsize=50:text=hello:
x=(sin(t)+1)/2*(W-tw):y=(cos(t/0.625)+1)/2*(H-th)'
" -t 30 drawtext_use_expr_in_xy.mp4
color, box, border, and shadow¶
fontcolor, fontcolor_expr¶
fontcolor:
The color to be used for drawing fonts. For the syntax of this option, check the (ffmpeg-utils)”Color” section in the ffmpeg-utils manual.
The default value of fontcolor is “black”.
#! /bin/sh
pref="`basename $0 .sh`"
#
ffmpeg -y -i "Pexels Videos 1457810.mp4" -filter_complex "
[0:v]
drawtext='fontfile=c\:/Windows/Fonts/comic.ttf:fontsize=90:x=20:y=20:
fontcolor=white@0.2:
text=The quick brown fox jumps over a lazy dog.
Sphinx of black quartz, judge my vow.'
" "${pref}.mp4"
fontcolor_expr:
String which is expanded the same way as text to obtain dynamic fontcolor value. By default this option has empty value and is not processed. When this option is set, it overrides fontcolor option.
IMO, the design of fontcolor_expr
is a bit ridiculous.
You might think that what should we do is color calculations as 24-bit or 32-bit values (I thought so).
However, for fontcolor_expr
, you must pass “a legal string as the fontcolor
argument string”.
In other words, “we must calculate a hexadecimal string by calculation”.
Therefore, not only calculations but also “hexadecimal digitization of numerical values” is almost indispensable for this purpose:
#! /bin/sh
pref="`basename $0 .sh`"
#
ffmpeg -y -i "Pexels Videos 1457810.mp4" -filter_complex "
[0:v]
drawtext='fontfile=c\:/Windows/Fonts/comic.ttf:fontsize=90:x=20:y=20:
fontcolor_expr=
0x5090FF%{eif\: mod(t * 100, 255) \: x \: 2}:
text=The quick brown fox jumps over a lazy dog.
Sphinx of black quartz, judge my vow.'
" "${pref}.mp4"
Although the knowledge of “we can do it in this way” is not worthless, but it is not easy to use. If you know the other ways of doing the same thing and you adopt it, it would become “easy to maintain” code, I think.
box, boxborderw, boxcolor, line_spacing¶
box:
Used to draw a box around text using the background color. The value must be either 1 (enable) or 0 (disable). The default value of box is 0.
boxborderw:
Set the width of the border to be drawn around the box using boxcolor. The default value of boxborderw is 0.
boxcolor:
The color to be used for drawing box around text. For the syntax of this option, check the (ffmpeg-utils)”Color” section in the ffmpeg-utils manual.
The default value of boxcolor is “white”.
line_spacing:
Set the line spacing in pixels of the border to be drawn around the box using box. The default value of line_spacing is 0.
#! /bin/sh
pref="`basename $0 .sh`"
#
ffmpeg -y -filter_complex "
color=0xAFAFAF:s=1280x720,loop=-1:size=2,
trim=0:6,setpts=PTS-STARTPTS,
drawtext='fontfile=c\:/Windows/Fonts/courbd.ttf:
fontcolor=yellow:
fontsize=45:
x=50:y=620:
box=1:
boxcolor=blue:
boxborderw=5:
line_spacing=32:
text=The quick brown fox jumps over a lazy dog.
Sphinx of black quartz, judge my vow.'
" "${pref}.mp4"
borderw, bordercolor¶
bordercolor:
Set the color to be used for drawing border around text. For the syntax of this option, check the (ffmpeg-utils)”Color” section in the ffmpeg-utils manual.
The default value of bordercolor is “black”.
borderw:
Set the width of the border to be drawn around the text using bordercolor. The default value of borderw is 0.
#! /bin/sh
pref="`basename $0 .sh`"
#
ffmpeg -y -filter_complex "
color=0xAFAFAF:s=1280x720,loop=-1:size=2,
trim=0:6,setpts=PTS-STARTPTS,
drawtext='fontfile=c\:/Windows/Fonts/courbd.ttf:
fontcolor=yellow:
fontsize=45:
x=50:y=620:
bordercolor=blue:
borderw=5:
text=The quick brown fox jumps over a lazy dog.'
" "${pref}.mp4"
shadowcolor, shadowx, shadowy¶
shadowcolor:
The color to be used for drawing a shadow behind the drawn text. For the syntax of this option, check the (ffmpeg-utils)”Color” section in the ffmpeg-utils manual.
The default value of shadowcolor is “black”.
shadowx, shadowy:
The x and y offsets for the text shadow position with respect to the position of the text. They can be either positive or negative values. The default value for both is “0”.
#! /bin/sh
pref="`basename $0 .sh`"
#
ffmpeg -y -filter_complex "
color=0xAFAFAF:s=1280x720,loop=-1:size=2,
trim=0:6,setpts=PTS-STARTPTS,
drawtext='fontfile=c\:/Windows/Fonts/courbd.ttf:
fontcolor=yellow:
fontsize=45:
x=50:y=620:
shadowcolor=blue:
shadowx=5:shadowy=-5:
text=The quick brown fox jumps over a lazy dog.'
" "${pref}.mp4"
alpha¶
alpha:
Draw the text applying alpha blending. The value can be a number between 0.0 and 1.0. The expression accepts the same variables x, y as well. The default value is 1. Please see fontcolor_expr.
#! /bin/sh
pref="`basename $0 .sh`"
#
ffmpeg -y -i "Pexels Videos 1457810.mp4" -filter_complex "
[0:v]
drawtext='fontfile=c\:/Windows/Fonts/courbd.ttf:
fontsize=70:x=50:y=50:
fontcolor=white:
shadowcolor=blue:shadowx=20:shadowy=20:
box=1:boxcolor=0xAAAAAA:boxborderw=32:
bordercolor=black:borderw=5:
alpha=mod(t / 15, 1):
text=The quick brown fox jumps over a lazy dog.
Sphinx of black quartz, judge my vow.'
" "${pref}.mp4"
Note
Specifying alpha affects the whole text drawing (eg the color of the border). If this is not what you want, give an alpha for each color:
#! /bin/sh
pref="`basename $0 .sh`"
#
ffmpeg -y -i "Pexels Videos 1457810.mp4" -filter_complex "
[0:v]
drawtext='fontfile=c\:/Windows/Fonts/courbd.ttf:
fontsize=70:x=50:y=50:
fontcolor=white@0.5:
shadowcolor=blue:shadowx=20:shadowy=20:
text=The quick brown fox jumps over a lazy dog.
Sphinx of black quartz, judge my vow.'
" "${pref}.mp4"
Text expansion¶
- doc
Bad news. The “text expansion” provided by drawtext is extremely limited, and probably can not do most of what you want to do.
The most noticeable limitation is that ‘%{metadata : …}’ only shows frame metadata. So, even if the target video has “title” metadata at the global level, the following will not work:
[me@host: ~]$ ffprobe -hide_banner yourvideo.mp4
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'yourvideo.mp4':
Metadata:
major_brand : isom
minor_version : 512
compatible_brands: isomiso2avc1mp41
title : whats going on
date : 2011
... (snip) ...
[me@host: ~]$ ffplay yourvideo.mp4 -vf "
> drawtext='font=monospace:fontcolor=white:fontsize=60:text=%{metadata\: title}'"
[me@host: ~]$ ffplay yourvideo.mp4 -vf "
> metadata=mode=print,drawtext='font=monospace:fontcolor=white:fontsize=60:text=%{metadata\: title}'"
For a few examples where “metadata” can be used, see drawtext with signalstats, drawtext, drawgraph, blackframe, freezedetect.
So, “metadata” is maybe disappointing for you, but instead, only the display method of time code related (which is definitely the biggest demand) is quite satisfactory:
[me@host: ~]$ ffplay yourvideo.mp4 -vf "
> drawtext='font=monospace:fontcolor=white:fontsize=60:text=%{pts \: hms}'"
[me@host: ~]$ ffplay yourvideo.mp4 -vf "
> drawtext='font=monospace:fontcolor=white:fontsize=40:
> text=%{pts \: gmtime}'"
[me@host: ~]$ ffplay yourvideo.mp4 -vf "
> drawtext='font=monospace:fontcolor=white:fontsize=40:
> text=%{pts \: gmtime \: 0}'"
[me@host: ~]$ ffplay yourvideo.mp4 -vf "
> drawtext='font=monospace:fontcolor=white:fontsize=40:
> text=%{pts \: gmtime \: 0 \: \%Y-\%m-\%d \%H\\\\\\:\%M\\\\\\:\%S}'"
[me@host: ~]$ ffplay -f lavfi "color=s=1920x1080,loop=-1:size=2" -vf "
> drawtext='font=monospace:fontcolor=white:fontsize=200:text=%{localtime}'"
[me@host: ~]$ ffplay -f lavfi "color=s=1920x1080,loop=-1:size=2" -vf "
> drawtext='font=monospace:fontcolor=white:fontsize=200:
> text=%{localtime \: \%Y-\%m-\%d \%H\\\\\\:\%M\\\\\\:\%S}'"
The function “expr” may be used for debugging purposes:
[me@host: ~]$ ffplay -f lavfi "
> color=white:s=ntsc:d=10,
> drawtext='
> fontsize=40:
> text=%{expr\:(st(0, 4 - mod(t * PI, 8));(sin(ld(0)) / ld(0)) * sin(220 * 2 * PI * t))}'"
Note that constants what you can use depends on when the evaluation of the expression is performed as the official documentation mentions, furthermore, the constants that can be used here are only those that can be used with the “x” and “y” parameters of “drawtext”:
[me@host: ~]$ # If you want to display the evaluated value of the following:
[me@host: ~]$ ffplay video.mkv -vf "geq='lum=min(lum(X,Y) + T*7, 255):cr=cr(X,Y):cb=cb(X,Y)'"
[me@host: ~]$ # But you can't do:
[me@host: ~]$ ffplay -f lavfi "
> color=white:s=ntsc:d=10,drawtext='fontsize=40:text=%{expr\:(min(lum(X,Y) + T*7, 255))}'"
It’s not surprising that you can’t use “lum”, but in this example, “T” would disappoint you. If your only goal is to display the evaluation results of an expression, consider using print:
[me@host: ~]$ ffplay video.mkv -vf "geq='lum=print(min(lum(X,Y) + T*7, 255)):cr=cr(X,Y):cb=cb(X,Y)'"
- see also
`textfile’ and `reload=1’¶
Only if you use textfile
, you can use the parameter reload
:
If set to 1, the textfile will be reloaded before each frame. Be sure to update it atomically, or it may be read partially, or even fail.
Warning
For ffmpeg which is not the latest, reload=1 seems to hang up easily. At least version 3.3.2 of Windows hangs up frequently. Use the latest (4.1 at the moment).
You can use this to create something like a video that follows an ever-changing text file, such as a log of something:
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import io
import time
import random
with io.open('words', encoding='latin-1') as fi:
words = fi.read().split('\n')
while True:
r1 = random.randint(0, len(words) - 1)
r2 = random.randint(0, len(words) - 1)
txt = words[r1] + '\n x\n' + words[r2] + '\n'
with io.open('choice.txt', 'w') as fo:
fo.write(txt)
time.sleep(5)
#! /bin/sh
exec 2> /dev/null
ffplay -f lavfi "color=white:s=960x540,loop=-1:size=2" \
-vf "drawtext='fontfile=c\:/Windows/Fonts/courbd.ttf:
fontsize=60:
x=30:y=30:
textfile=choice.txt:
reload=1"
Let me show you a slightly more complicated example.
The following Python script periodically updates random words into the file `excursion.txt’ based on Google search. (However, it is for Japanese people.):
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
try:
# python 3
from urllib.request import build_opener
from http.client import ResponseNotReady
from html.parser import HTMLParser
from urllib.parse import urlencode
except ImportError:
# python 2
from urllib2 import build_opener
from httplib import ResponseNotReady
from HTMLParser import HTMLParser
from urllib import urlencode
import sys
import io
from io import StringIO
import ssl
import time
import re
import random
from subprocess import Popen, PIPE
import unicodedata
import httplib2 # https://pypi.python.org/pypi/httplib2
from bs4 import BeautifulSoup # https://pypi.python.org/bs4/
#
class _GoogleHtmlResExtractor(HTMLParser):
def __init__(self):
HTMLParser.__init__(self)
self._states = 0
self.data = []
def handle_starttag(self, tag, attrs):
d = dict(attrs)
if tag == 'p':
self._states = 1
elif tag == 'a' and self._states == 1:
if d['href'] != '/':
self.data.append({
"href": "https://www.google.com" + d['href']
})
self._states = 2
def handle_endtag(self, tag):
if tag == 'a':
self._states = 0
def handle_data(self, data):
if self._states == 2:
self.data[-1]["data"] = data.strip()
#
def query(*query_keywords):
user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
url = "https://www.google.com/search"
data = urlencode({
"q": " ".join(query_keywords).encode("utf-8"),
})
opener = build_opener()
opener.addheaders = [('User-Agent', user_agent)]
res = opener.open(url + "?" + data)
html = res.read().decode("utf-8")
parser = _GoogleHtmlResExtractor()
parser.feed(html)
return parser.data
# httplib2 wrapper
class httplib2inst(object):
def __init__(self, cache_dir=".cache"):
self._inst = httplib2.Http(cache_dir)
def get(self, url):
retry = 20
while retry >= 0:
try:
return self._inst.request(url, "GET")
except (ResponseNotReady, ssl.SSLEOFError):
retry -= 1
time.sleep(8)
except (httplib2.ServerNotFoundError, ssl.CertificateError):
break
return None, None
# --- main ---
if __name__ == '__main__':
opnr = httplib2inst()
qs = sys.stdin.readline().strip()
if hasattr(qs, "decode"):
qs = qs.decode("mbcs")
srcs = set()
while True:
for lst in query(qs):
headers, contents = opnr.get(lst["href"])
if headers:
try:
soup = BeautifulSoup(StringIO(contents.decode("utf-8", "replace")), "lxml")
except:
continue
[x.extract() for x in soup.findAll('script')]
[x.extract() for x in soup.findAll('iframe')]
body = soup.find("body")
if not body:
continue
p = Popen(
["c:/Program Files (x86)/MeCab/bin/mecab", "-b", "%d" % (2**24)],
shell=False, stdin=PIPE, stdout=PIPE)
stdoutdata, stderrdata = p.communicate(
input=body.text.encode("utf-8"))
for line in re.split(r"\r?\n", stdoutdata.decode("utf-8", "replace")):
if "\t" not in line:
continue
word, _, inf = line.partition("\t")
if re.match(r"[ -/:-@[-`{-~]+", word):
continue
if re.match(r"[a-zA-Z]+", word):
continue
word = unicodedata.normalize('NFKC', word)
if re.match(r"[a-zA-Z]+", word):
continue
typ = inf.split(",")[:2]
if typ[0] in ("記号", "接続詞", "連体詞") or \
"助" in typ[0] or "動" in typ[0] or \
typ[1] in ("非自立", "代名詞", "数"):
continue
srcs.add(word)
sels = list(srcs)
random.shuffle(sels)
for i in range(0, len(sels) // 20 * 20, 20):
s = "\n".join(["{}{}{}{}"] * 5).format(
*sels[i:i+20+1]).encode("utf-8")
with io.open("excursion.txt", "wb") as fo:
fo.write(s)
time.sleep(10)
time.sleep(1)
qs = sels[-1]
This time, let’s draw it by changing the color little by little at random positions:
#! /bin/sh
exec 2> /dev/null
"c:/Program Files/ffmpeg-4.1.1-win64-shared/bin/ffplay" \
-f lavfi "color=white:s=1120x630,loop=-1:size=2" \
-vf "drawtext='fontfile=c\:/Windows/Fonts/HGRSMP.TTF:
fontsize=40:
fontcolor_expr=0x\
%{eif \: mod(t*10, 128) \: x \: 2}\
%{eif \: mod(t*30, 255) \: x \: 2}\
%{eif \: mod(t*50, 255) \: x \: 2}:
x=if(eq(mod(t\,5)\,0)\,rand(50\,(w-text_w-150))\,x):
y=if(eq(mod(t\,5)\,0)\,rand(50\,(h-text_h-150))\,y):
textfile=excursion.txt:reload=1"
Here’s a video showing how this works:
Watch on youtube.comwith sendcmd¶
For example, you may want to change the font over time. In such cases, you can use sendcmd (rather than drawtext itself).
#! /bin/bash
#
# This example is for Windows.
#
(
exec > dtcmd.cmd
t=0
for fon in `(cd /c/Windows/Fonts/ ; ls *.tt[fc])` ; do
echo "\
${t}-$((${t} + 5)) [enter] drawtext reinit 'fontfile=c\\:/Windows/Fonts/${fon}';"
t=$((${t} + 5))
done
)
#
"ffplay" \
-f lavfi "color=white:s=960x540,loop=-1:size=2" \
-vf "sendcmd=f=dtcmd.cmd,drawtext='fontsize=40:x=50:y=50:fontcolor=blue:\
text=The quick brown fox jumps over a lazy dog.'"