5

I am trying to write text on a video in such a way that the text "writes itself" - letter by letter.

To do this I was able to do something like this:

-vf "[in]drawtext=text='a':fontfile='/Windows/Fonts/arial.ttf'\
:enable='between(t\,1\,1.5)':x=300:y=(h)-100-th, \
drawtext=text='ab':fontfile='/Windows/Fonts/arial.ttf':enable='between(t\,1.5\,2)':\
x=300:y=(h)-100-th"

This replaces the text on screen with a new one every half a second adding a new character. ( a changes to ab ). Obviously if the texts are long than this line would be really long.

I was able to do something similar and shorter with :

-vf "[in]drawtext=fontsize=80:shadowcolor=Black:shadowx=2:shadowy=2:fontcolor=White:\
fontfile='/Windows/Fonts/arial.ttf':text='%{expr\:if(gt(t\,1.5)\,12\,1)}':\
x=300:y=(h)-100-th"

Where the text itself is an expression dependent on time. However this only works for numbers: 1 changes to 12 (ignoring the numbers after the decimal point for now).

Now the issue is that if I try to do the same expression for texts (a->ab) this fails. and I get:

Expression 'if(gt(t,1.5),ab,a)' for the expr text expansion function is not valid.

The question is: Is it possible to have the expression return the actual text after expansion and not just numbers?.

Thanks in advance for anyone helping!

  • Try with 'if(gt(t,1.5),'ab','a')' – Gyan Feb 3 '16 at 14:59
  • tried that (and with all conbinations of \, \' \" etc...) didn't help – eof Feb 3 '16 at 17:07
  • Other than the ultralong multiple drawtexts approach, your choice is to draw the full text to a blank canvas, animate a crop and overlay the result on the video. – Gyan Feb 3 '16 at 17:49
  • Thats an interesting idea.... But can I do it with a transparent background so to overlay just the text? – eof Feb 3 '16 at 19:11
  • Yeah, use color=black@0.0:WxH – Gyan Feb 3 '16 at 19:18
4

Ok, it can be done, but it's inelegant and has limitations, which make it in the end, not worthwhile. The limitation is that this only properly works with fixed-width fonts. And due to a still-unfixed-bug in ffmpeg, it will look somewhat ugly.

But here it is,

ffmpeg -i input.mp4 -lavfi "color=white:1280x120,fps=30[c]; \
[c]format=rgba, \
drawtext=fontfile=cour.ttf:fontcolor=red:fontsize=60:text='    A   AB  ABC ABCD':x=0:y=0, \
crop=144:120:min(144*trunc(2*t)\,432):0, \
colorkey=FFFFFF:0.01:1,setpts=PTS+(delay/TB)[tx]; \
[0:v][tx]overlay=576:600[out]" \
-map [out] -map 0:a -c:a copy output.mp4

The basic flow is

  1. create an opaque canvas - I used full width as my video input and enough height to contain the text, and I used the same framerate as my video
  2. On this canvas, draw the text once. The basic method here is to animate the crop window. So if your text is 5 characters long, then your print string is text=' 1 12 123 1234 12345'. The idea is that the crop width is equal to the full string width and the crop source shifts one string width periodically, thus simulating typing, since each new cropped area contains one additional character. So, first 5 blank spaces for the 5 characters, then after the first 1 there are four spaces to pad out till a full string width is reached, then the 2nd stage text follows with 2 characters and 3 spaces, and so on. I've specified the color space of RGBA since it seemed to provide better results in the keying step further on.
  3. Now the canvas with text on it is cropped. Width is the full string width, and I've kept the same height, but as long it doesn't cut the text, doesn't matter. The crop offset X is animated as follows: StringWidth * integer part of (time divided by period). This shifts the crop window right by one string width after every period i.e. 0.5 seconds in this case. Now, this is enclosed in a minimum function because we want the crop to stay still after all characters have been printed. So, the 2nd value is stringWidth * (no. of characters - 1). Y isn't animated since this is one line only.
  4. The next part makes it ugly. Because of the bug, I kept the background opaque. So, now it has to be chroma keyed out. The filter looks for the BG color of white, and selects only that color (0.01) and fully blends it. I was hoping to smooth out the edges. But doesn't work very well.
  5. The setpts allows to set a delay in seconds. The first character will appear at delay + period seconds.
  6. Finally, we simply overlay the result on the video.
  • ok this is really complicated.. I will check this and see if I can handle it. Could it have been done using several iterations and not trying to do it all in command pass. and (sorry if this is wrong etiquette) but are you available to do some freelance work on this project... it is clear that I am out of my league here. – eof Feb 4 '16 at 15:10
  • Well looks like ffmpeg is crashing after consuming too much memory every time I run this. And since I cannot use fixed width fonts I need to explore different routes. Can maybe I render a stand alone video that has the name, and a simple croping from left to right. Than overlay that video onto the other video. Maybe the first video has transperancy/alpha, or maybe just use chromakey (green screen) when overlaying with no need for alpha channel? Anyway, let me know if you are available for freelance or know anyone that is . Thanks for all the help... – eof Feb 4 '16 at 16:15
  • leave your email and I'll get in touch. – Gyan Feb 4 '16 at 18:21
  • eran_stackoverflow_nzxy at mailnicks dot org – eof Feb 4 '16 at 20:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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