19

I have the code:

import subprocess , os

ffmpeg = "C:\\ffmpeg_10_6_11.exe"
inVid = "C:\\test_in.avi"
outVid = "C:\\test_out.avi"

if os.path.exists( outVid ):
os.remove( outVid )
proc = subprocess.Popen(ffmpeg + " -i " + inVid + ''' -vf drawtext=fontfile=/Windows/Fonts/arial.ttf:text="onLine1 onLine2 onLine3":fontcolor=white:fontsize=20 -y ''' + outVid , shell=True, stderr=subprocess.PIPE)
proc.wait()
print proc.stderr.read()
os.startfile( outVid )

to write text to a video file. But I want to write out many lines of text instead of just having it all on the one line.

How can I do that?

desertnaut
  • 57,590
  • 26
  • 140
  • 166
Jay
  • 3,373
  • 6
  • 38
  • 55

6 Answers6

32

You can specify multiple drawtexts on one file by using the [in] tag and listing each drawtext using commas. This allows you to use multiple lines if you orient each drawtext through their respective positioning methods. In your example, the command line would look something like this (puts the first line in the middle of the screen, and puts each subsequent line 25 pixels down):

ffmpeg -i test_in.avi -vf "[in]drawtext=fontsize=20:fontcolor=White:fontfile='/Windows/Fonts/arial.ttf':text='onLine1':x=(w)/2:y=(h)/2, drawtext=fontsize=20:fontcolor=White:fontfile='/Windows/Fonts/arial.ttf':text='onLine2':x=(w)/2:y=((h)/2)+25, drawtext=fontsize=20:fontcolor=White:fontfile='/Windows/Fonts/arial.ttf':text='onLine3':x=(w)/2:y=((h)/2)+50[out]" -y test_out.avi
desertnaut
  • 57,590
  • 26
  • 140
  • 166
Ben
  • 951
  • 12
  • 19
23

Looking at the source code in ffmpeg (vs_drawtext.c):

static inline int is_newline(uint32_t c)
{
    return c == '\n' || c == '\r' || c == '\f' || c == '\v';
}

so you can try inserting \f or \v in your text line which correspond to ^L or ^K characters. For example:

-filter_complex "[in] drawtext=fontsize=40:fontcolor=white:fontfile=/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf:x=(w-tw)/2:y=(h-th)/2:box=1:boxcolor=black@0.5:text='two^Llines'[out]"

^L being the actual Ctrl-L character and not ^ and L obviously.

Sreenikethan I
  • 319
  • 5
  • 17
  • 8
    +1 for referring to the source code. Welcome to Stack Overflow! – GargantuChet Oct 11 '12 at 12:04
  • I don't know if this is version-specific or not, but there is to my best knowledge no way to control the spacing between the lines with this solution and I ended up using @Ben 's one while controlling the spacing with the `h:{int}` command. Otherwise the lines would end up being drawn on top of each others or at least so close that it was unreadable. – Bertrand Caron Feb 25 '15 at 23:03
  • 2
    To enter `^L` in most terminals, you can do `Ctrl-V` first then press `Ctrl-L`. – neuro_sys Apr 11 '18 at 18:30
  • Here is the source, if anyone wants to take a look at: https://github.com/FFmpeg/FFmpeg/blob/2e2b44baba575a33aa66796bc0a0f93070ab6c53/libavfilter/vf_drawtext.c#L592 – Sreenikethan I Jan 29 '19 at 14:24
  • 1
    i can not make it work with this, can someone send me the characters in other site or something? – Gaspar Dec 20 '19 at 01:13
  • Doesn't work (ffmpeg v4.3.1 on MacOS), I get `two^Llines` written on my video – Vincent Apr 16 '21 at 07:32
  • Does not work neither on windows with ffmpeg version 4.3.2-2021-02-27-full_build-www.gyan.dev – Dorian Grv Oct 27 '21 at 07:35
14

I simple added new lines inside command and ffmpeg handled it properly.

ffmpeg -i input.avi -vf "[in]drawtext=fontsize=20:text='hello
world':x=(w)/2:y=(h)/2:fontcolor=white[out]" -y out.mp4

No Ctrl+L, Ctrl+K hacks are needed!

I.e. I just pressed Enter after 'hello'.

You can do it editing script file or even in bash command line.

Atento
  • 786
  • 6
  • 7
6

I have managed to get this to work from the command line by specifying the 'textfile' parameter and putting my text into this file.

See http://ffmpeg.org/libavfilter.html#drawtext for more help. Using ffmpeg build N-35057-g2c44aed on windows, but the important thing is that you have recent version of the libavfilter.

  • Yeah I already have that in place as a work around but I don't like the fact that my script is constantly making and deleting files. BTW you can also call the drawtext flag more than once and just offset the text on the Y axis to create a new line. I have tested and gotten that working too but as my question states I would like to add many lines with one drawtext flag and without having to create an external file. Thanks for taking the time to answer tho. Much appreciated :) – Jay Nov 28 '11 at 12:39
0
TEXT=$(printf "$1") 

In a shell script

With your text as your shell script argument including new line characters

desertnaut
  • 57,590
  • 26
  • 140
  • 166
mightcouldb1
  • 649
  • 2
  • 8
  • 14
0

Just split the text into lines of a certain length (20 characters, you can set according to your need) before passing to ffmpeg.

import subprocess , os

ffmpeg = "C:\\ffmpeg_10_6_11.exe"
inVid = "C:\\test_in.avi"
outVid = "C:\\test_out.avi"
length = 20
txt = "onLine1 onLine2 onLine3"
inpTxt = split_txt_into_multi_lines(txt,length)

if os.path.exists( outVid ):
  os.remove( outVid )
  proc = subprocess.Popen(ffmpeg + " -i " + inVid + f''' -vf drawtext=fontfile=/Windows/Fonts/arial.ttf:text={inpTxt}:fontcolor=white:fontsize=20 -y ''' + outVid , shell=True, 
  stderr=subprocess.PIPE)
  proc.wait()
  print proc.stderr.read()
  os.startfile( outVid )


#################################################################
def split_txt_into_multi_lines(input_str: str, line_length: int):
    words = input_str.split(" ")
    line_count = 0
    split_input = ""
    for word in words:
        line_count += 1
        line_count += len(word)
        if line_count > line_length:
            split_input += "\n"
            line_count = len(word) + 1
            split_input += word
            split_input += " "
        else:
            split_input += word
            split_input += " "
    
    return split_input
        


Yogesh Yadav
  • 723
  • 6
  • 21