[FFmpeg-devel] [PATCH v2] lavfi/drawtext: Add localtime_ms for millisecond precision

Thilo Borgmann thilo.borgmann at mail.de
Tue Feb 8 11:17:11 EET 2022


Am 01.02.22 um 00:40 schrieb Andreas Rheinhardt:
> Thilo Borgmann:
>> Am 31.01.22 um 14:08 schrieb Nicolas George:
>>> Thilo Borgman (12022-01-31):
>>>>> v10 attached.
>>>>
>>>> Also going to apply soon if there are no more comments.
>>>
>>> I think you neglected to attach the file.
>>
>> omg stupid me. Here it is...
>>
>> -Thilo
>>
> 
> Seems like I misunderstood your code and ignored the outer while. Your
> code can leak if there are multiple 'N', because (as I said)
> 
>>
>> +
>> +            if (fmt_new && fmt_new != argv[0] && fmt_new != fmt_default)
>> +                av_freep(&fmt_new);
>> +
> 
> does not free fmt if it is already allocated. It is possible to fix this
> by adding an char *fmt_allocated = NULL; at outer scope and a fmt_new in
> the block that is executed if a 'N' is executed. (You have to free
> fmt_allocated immediately after av_asprintf().)

> But maybe it would be best to use an AVBPrint to write the new string
> instead of allocating a new string for every %N encountered.

v11 does it that way.

Thanks,
Thilo
-------------- next part --------------
From 1831153a5309502414c8639d1364930b305dd5dd Mon Sep 17 00:00:00 2001
From: Thilo Borgmann <thilo.borgmann at mail.de>
Date: Tue, 8 Feb 2022 10:15:13 +0100
Subject: [PATCH v11] lavfi/drawtext: Add %N for drawing fractions of a second

Suggested-By: ffmpeg at fb.com
---
 doc/filters.texi          |  4 +++
 libavfilter/vf_drawtext.c | 67 +++++++++++++++++++++++++++++++++++++--
 2 files changed, 69 insertions(+), 2 deletions(-)

diff --git a/doc/filters.texi b/doc/filters.texi
index 05d4b1a56e..c3895138e0 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -11378,10 +11378,14 @@ It can be used to add padding with zeros from the left.
 @item gmtime
 The time at which the filter is running, expressed in UTC.
 It can accept an argument: a strftime() format string.
+The format string is extended to support the variable @var{%[1-6]N}
+which prints fractions of the second with optionally specified number of digits.
 
 @item localtime
 The time at which the filter is running, expressed in the local time zone.
 It can accept an argument: a strftime() format string.
+The format string is extended to support the variable @var{%[1-6]N}
+which prints fractions of the second with optionally specified number of digits.
 
 @item metadata
 Frame metadata. Takes one or two arguments.
diff --git a/libavfilter/vf_drawtext.c b/libavfilter/vf_drawtext.c
index 2a88692cbd..75ce97a2a6 100644
--- a/libavfilter/vf_drawtext.c
+++ b/libavfilter/vf_drawtext.c
@@ -51,6 +51,7 @@
 #include "libavutil/opt.h"
 #include "libavutil/random_seed.h"
 #include "libavutil/parseutils.h"
+#include "libavutil/time.h"
 #include "libavutil/timecode.h"
 #include "libavutil/time_internal.h"
 #include "libavutil/tree.h"
@@ -1045,15 +1046,77 @@ static int func_strftime(AVFilterContext *ctx, AVBPrint *bp,
                          char *fct, unsigned argc, char **argv, int tag)
 {
     const char *fmt = argc ? argv[0] : "%Y-%m-%d %H:%M:%S";
+    const char *fmt_begin = fmt;
+    int64_t unow;
     time_t now;
     struct tm tm;
+    const char *begin;
+    const char *tmp;
+    int len;
+    int div;
+    AVBPrint fmt_bp;
 
-    time(&now);
-    if (tag == 'L')
+    av_bprint_init(&fmt_bp, 0, AV_BPRINT_SIZE_UNLIMITED);
+
+    unow = av_gettime();
+    now  = unow / 1000000;
+    if (tag == 'L' || tag == 'm')
         localtime_r(&now, &tm);
     else
         tm = *gmtime_r(&now, &tm);
+
+    // manually parse format for %N (fractional seconds)
+    begin = fmt;
+    while ((begin = strchr(begin, '%'))) {
+        tmp = begin + 1;
+        len = 0;
+
+        // skip escaped "%%"
+        if (*tmp == '%') {
+            begin = tmp + 1;
+            continue;
+        }
+
+        // count digits between % and possible N
+        while (*tmp != '\0' && av_isdigit((int)*tmp)) {
+            len++;
+            tmp++;
+        }
+
+        // N encountered, insert time
+        if (*tmp == 'N') {
+            int num_digits = 3; // default show millisecond [1,6]
+
+            // if digit given, expect [1,6], warn & clamp otherwise
+            if (len == 1) {
+                num_digits = av_clip(*(begin + 1) - '0', 1, 6);
+            } else if (len > 1) {
+                av_log(ctx, AV_LOG_WARNING, "Invalid number of decimals for %%N, using default of %i\n", num_digits);
+            }
+
+            len += 2; // add % and N to get length of string part
+
+            div = pow(10, 6 - num_digits);
+
+            av_bprintf(&fmt_bp, "%.*s%0*d", (int)(begin - fmt_begin), fmt_begin, num_digits, (int)(unow % 1000000) / div);
+
+            begin += len;
+            fmt_begin = begin;
+
+            continue;
+        }
+
+        begin = tmp;
+    }
+
+    av_bprintf(&fmt_bp, "%s", fmt_begin);
+    av_bprint_finalize(&fmt_bp, (char**)&fmt);
+    if (!fmt)
+        return AVERROR(ENOMEM);
+
     av_bprint_strftime(bp, fmt, &tm);
+    av_freep(&fmt);
+
     return 0;
 }
 
-- 
2.20.1 (Apple Git-117)



More information about the ffmpeg-devel mailing list