Am 20.01.22 um 13:04 schrieb Thilo Borgmann:
Am 19.01.22 um 04:16 schrieb "zhilizhao(赵志立)":


On Jan 18, 2022, at 8:52 PM, Thilo Borgmann <thilo.borgm...@mail.de> wrote:

Am 16.01.22 um 12:06 schrieb Nicolas George:
Thilo Borgman (12022-01-14):
v6 does:

$> ffmpeg ... drawtext="fontfile=...:text='%{localtime   \:%a %b %d %Y %S}'"    
       (seconds)
$> ffmpeg ... drawtext="fontfile=...:text='%{localtime_ms\:%a %b %d %Y %S}'"    
       (milliseconds)

I suggest v7 should according to your remark:

$> ffmpeg ... drawtext="fontfile=...:text='%{localtime   \:%a %b %d %Y %S}'"    
       (seconds)
$> ffmpeg ... drawtext="fontfile=...:text='%{localtime   \:%a %b %d %Y 
%S}':show_ms=1" (milliseconds)

Good?

I dislike both versions, from a user interface point of view: if there
is a format string, then it stands to reason, for the user, that the
resulting text is governed by the format string, not by an extra option
somewhere else.

There is no "use_four_digit_year=1" option, there is %Y instead of %y.

There is no "use_slashes=1" option, you write %Y/%m/%d instead of
%Y-%m-%d.

There are no "omit_date=1" and "omit_hour=1" options, you just write
what you want in the format string.

My proposal goes the same way:

$> ffmpeg ... drawtext="fontfile=...:text='%{localtime   \:%a %b %d %Y %S.%3N}'"

It has several merits over your proposal:

- It can be extended later to support printing the milliseconds at
  another place than the end (for example to put the time in brackets).

- It can be extended to support microseconds or centiseconds (%6N, %2N).

- It is somewhat compatible with GNU date and possibly a few others.

And I do not think it is harder to implement.

Ok, did introduce a variable: %[1-6]N
Parsing and clipping value to valid range of 1-6.
Default 3.

That way it is position independent and can show any number of decimals from 1 
to 6.


diff --git a/libavfilter/vf_drawtext.c b/libavfilter/vf_drawtext.c
index 2a88692cbd..448b174dbb 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,14 +1046,82 @@ 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";
+    int64_t unow;
      time_t now;
      struct tm tm;
-
-    time(&now);
-    if (tag == 'L')
+    char *begin;
+    char *tmp;
+    int len;
+    char *fmt_new;
+    const char *fmt_tmp;
+    int div;
+
+    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 = (char*)fmt;

Make begin and tmp const char *, so the cast can be removed.

+    while ((begin = av_stristr(begin, "%"))) {

How about strstr() since ‘%’ is caseless?

+        tmp = begin + 1;
+        len = 0;
+        // 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 digits given, parse as number in [1,6]
+            if (len > 0) {
+                av_sscanf(begin + 1, "%i", &num_digits);
+                num_digits = av_clip(num_digits, 1, 6); // ensure valid value

We can ignore len > 1, then the code can be simplified as

if (len == 1)
     num_digits = av_clip(*(begin + 1) - ‘\0’, 1, 6)


+            }
+
+            len += 2; // add % and N to get length of string part
+
+            switch(num_digits) {
+            case 1:
+                fmt_tmp = "%.*s%01d%s";
+                div     = 100000;
+                break;
+            case 2:
+                fmt_tmp = "%.*s%02d%s";
+                div     = 10000;
+                break;
+            case 3:
+                fmt_tmp = "%.*s%03d%s";
+                div     = 1000;
+                break;
+            case 4:
+                fmt_tmp = "%.*s%04d%s";
+                div     = 100;
+                break;
+            case 5:
+                fmt_tmp = "%.*s%05d%s";
+                div     = 10;
+                break;
+            case 6:
+                fmt_tmp = "%.*s%06d%s";
+                div     = 1;
+                break;
+            }

The switch-case can be replaced by “%0*d” and pow(10, 6 - num_digits).

Indeed, simplified.


+
+            fmt_new = av_asprintf(fmt_tmp, begin - fmt, fmt, (int)(unow % 
1000000) / div, begin + len);
+            if (!fmt_new)
+                return AVERROR(ENOMEM);
+            av_bprint_strftime(bp, fmt_new, &tm);
+            av_freep(&fmt_new);
+            return 0;
+        }
+        begin++;

Progress faster by taking account of len.

As well, also added to skip "%%".


+    }
+
      av_bprint_strftime(bp, fmt, &tm);
      return 0;
  }
--

v8 attached.

Fixed off-by-one bug.
Allows for several occurrences of %N parameter now.

v9 attached.

Thanks,
Thilo
From 066ca3d644daea88803b0b7ab1d3c3c66480ddfe Mon Sep 17 00:00:00 2001
From: Thilo Borgmann <thilo.borgm...@mail.de>
Date: Thu, 20 Jan 2022 15:57:14 +0100
Subject: [PATCH v9] lavfi/drawtext: Add %N for drawing fractions of a second

Suggested-By: ffm...@fb.com
---
 doc/filters.texi          |  4 +++
 libavfilter/vf_drawtext.c | 66 +++++++++++++++++++++++++++++++++++++--
 2 files changed, 67 insertions(+), 3 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..49414a3c0d 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,74 @@ 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";
+    int64_t unow;
     time_t now;
     struct tm tm;
-
-    time(&now);
-    if (tag == 'L')
+    const char *begin;
+    const char *tmp;
+    int len;
+    int div;
+
+    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 = av_stristr(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]
+            char *fmt_new = NULL;
+
+            // 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);
+
+            if (fmt_new && fmt_new != argv[0])
+                av_freep(&fmt_new);
+
+            fmt_new = av_asprintf("%.*s%0*d%s", (int)(begin - fmt), fmt, 
num_digits, (int)(unow % 1000000) / div, begin + len);
+
+            if (!fmt_new)
+                return AVERROR(ENOMEM);
+
+            begin = fmt_new + (begin - fmt);
+            fmt = fmt_new;
+            continue;
+        }
+
+        begin = tmp;
+    }
+
     av_bprint_strftime(bp, fmt, &tm);
+    if (fmt && fmt != argv[0])
+        av_freep(&fmt);
+
     return 0;
 }
 
-- 
2.20.1 (Apple Git-117)

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-requ...@ffmpeg.org with subject "unsubscribe".

Reply via email to