[FFmpeg-devel] [PATCH] allow vf_drawtext to draw framenumbers

Jean First jeanfirst at gmail.com
Sat Apr 9 01:49:00 CEST 2011


simplified the patch and added some documentation.
---
 doc/filters.texi          |   14 ++++++++
 libavfilter/vf_drawtext.c |   74 +++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 85 insertions(+), 3 deletions(-)

diff --git a/doc/filters.texi b/doc/filters.texi
index 5d63b5b..208170d 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -378,6 +378,12 @@ This parameter is mandatory.
 @item text
 The text string to be drawn. The text must be a sequence of UTF-8
 encoded characters.
+
+Some special characters are defined:
+#f Unpadded frame number, starting with 1
+#F Four-digit padded frame number, starting with 0001
+The # (hash / number sign) character needs to be escaped with a double ##.
+
 This parameter is mandatory if no file is specified with the parameter
 @var{textfile}.
 
@@ -470,6 +476,14 @@ drawtext="fontfile=/usr/share/fonts/truetype/freefont/FreeSerif.ttf: text='Test
 will draw "Test Text" with font FreeSerif, using the default values
 for the optional parameters.
 
+For example the command:
+ at example
+drawtext="fontfile=/usr/share/fonts/truetype/freefont/FreeSerif.ttf: text='Frame: #F'"
+ at end example
+
+will write four-digit padded continuos frame numbers prefixed with 'Frame: ' with font 
+FreeSerif, using the default values for the optional parameters.
+
 The command:
 @example
 drawtext="fontfile=/usr/share/fonts/truetype/freefont/FreeSerif.ttf: text='Test Text':\
diff --git a/libavfilter/vf_drawtext.c b/libavfilter/vf_drawtext.c
index 99045b7..54da940 100644
--- a/libavfilter/vf_drawtext.c
+++ b/libavfilter/vf_drawtext.c
@@ -83,6 +83,7 @@ typedef struct {
     int pixel_step[4];              ///< distance in bytes between the component of each pixel
     uint8_t rgba_map[4];            ///< map RGBA offsets to the positions in the packed RGBA format
     uint8_t *box_line[4];           ///< line used for filling the box background
+    unsigned int frame_index;       ///< current frame index
 } DrawTextContext;
 
 #define OFFSET(x) offsetof(DrawTextContext, x)
@@ -213,6 +214,7 @@ static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
     dtext->fontcolor_string = av_strdup("black");
     dtext->boxcolor_string = av_strdup("white");
     dtext->shadowcolor_string = av_strdup("black");
+    dtext->frame_index = 0;
 
     if ((err = (av_set_options_string(dtext, args, "=", ":"))) < 0) {
         av_log(ctx, AV_LOG_ERROR, "Error parsing options string: '%s'\n", args);
@@ -498,7 +500,7 @@ static inline void drawbox(AVFilterBufferRef *picref, unsigned int x, unsigned i
 static int draw_glyphs(DrawTextContext *dtext, AVFilterBufferRef *picref,
                        int width, int height, const uint8_t rgbcolor[4], const uint8_t yuvcolor[4], int x, int y)
 {
-    char *text = HAVE_LOCALTIME_R ? dtext->expanded_text : dtext->text;
+    char *text = dtext->expanded_text;
     uint32_t code = 0;
     int i;
     uint8_t *p;
@@ -533,6 +535,61 @@ static int draw_glyphs(DrawTextContext *dtext, AVFilterBufferRef *picref,
     return 0;
 }
 
+static char *stradd(const char *str, char *pt, const char *ptlim)
+{
+    while (pt < ptlim && (*pt = *str++) != '\0')
+        ++pt;
+    return pt;
+}
+
+static char *strconv(const int n, const char *format, char *pt,
+                     const char *ptlim)
+{
+    char buf[MAX_EXPANDED_TEXT_SIZE];
+    (void) snprintf(buf, sizeof(buf), format, n);
+    return stradd(buf, pt, ptlim);
+}
+
+static char *strfmt(const char *format, char *pt, const char *ptlim,
+                    AVFilterContext *ctx)
+{
+    DrawTextContext *dtext = ctx->priv;
+
+    for ( ; *format; ++format) {
+        if (*format == '#') {
+            switch (*++format) {
+            case '\0':
+                --format;
+                break;
+            case 'f':
+                pt = strconv(dtext->frame_index, "%d", pt, ptlim);
+                continue;
+            case 'F':
+                pt = strconv(dtext->frame_index, "%04d", pt, ptlim);
+                continue;
+            case '#':
+            default:
+                break;
+            }
+        }
+        if (pt == ptlim)
+            break;
+        *pt++ = *format;
+    }
+    return pt;
+}
+
+static size_t strfctx(char *s, size_t maxsize, const char *format,
+                       AVFilterContext *ctx)
+{
+    char *p;
+    p = strfmt(((format == NULL) ? "%c" : format), s, s + maxsize, ctx);
+    if (p == s + maxsize)
+        return 0;
+    *p = '\0';
+    return p - s;
+}
+
 static int draw_text(AVFilterContext *ctx, AVFilterBufferRef *picref,
                      int width, int height)
 {
@@ -547,13 +604,24 @@ static int draw_text(AVFilterContext *ctx, AVFilterBufferRef *picref,
     FT_Vector delta;
     Glyph *glyph = NULL, *prev_glyph = NULL;
     Glyph dummy = { 0 };
+    size_t expanded_text_len;
+
+    dtext->frame_index++;
+
+    dtext->expanded_text[0] = '\1';
+    expanded_text_len = strfctx(dtext->expanded_text, MAX_EXPANDED_TEXT_SIZE, text, ctx);
+    text = dtext->expanded_text;
+    if (expanded_text_len == 0 && dtext->expanded_text[0] != '\0') {
+        av_log(ctx, AV_LOG_ERROR,
+               "Impossible to print text, string is too big\n");
+        return AVERROR(EINVAL);
+    }
 
 #if HAVE_LOCALTIME_R
     time_t now = time(0);
     struct tm ltime;
-    size_t expanded_text_len;
 
-    dtext->expanded_text[0] = '\1';
+    expanded_text_len = 0;
     expanded_text_len = strftime(dtext->expanded_text, MAX_EXPANDED_TEXT_SIZE,
                                  text, localtime_r(&now, &ltime));
     text = dtext->expanded_text;
-- 
1.7.4.2



More information about the ffmpeg-devel mailing list