Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
 help / color / mirror / Atom feed
* [FFmpeg-devel] [PATCH] libavutil/timestamp.h: Fix loss of precision in timestamps for silencedetect on long files
       [not found] <20240221073915.26055-1-allancady.ref@yahoo.com>
@ 2024-02-21  7:39 ` Allan Cady via ffmpeg-devel
  2024-02-21 20:25   ` Marton Balint
  0 siblings, 1 reply; 37+ messages in thread
From: Allan Cady via ffmpeg-devel @ 2024-02-21  7:39 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Allan Cady

When the silencedetect audio filter is run against long files, the
output timestamps gradually lose precision as the scan proceeds further
into the file. This is because the output format specifier ("%.6g" in
libavutil/timestamp.h) limits the total field width to six significant
digits. As the offset into the file increases, digits drop off the end,
until eventually, for offsets greater than 100000 seconds (about 28
hours), fractions of a second disappear altogether, and the timestamps
are logged as whole seconds.

This patch changes the format to "%.6f" for silencedetect, which will
give microsecond precision for all timestamps regardless of offset.

libavutil/timestamp.h exposes a macro, av_ts2timestr, as the public
interface. This macro was used by silencedetect.c, as well as other
source files. In order to fix the issue for silencedetect without
affecting other files and tests, I have added a new macro,
av_ts2timestr_fixed_precision, which uses the new format specifier.
The original av_ts_make_time_string remains, with the original
behavior.

timestamp.h also exposes a function, av_ts_make_time_string, which
was called only from av_ts2timestr. After this patch, both of the
macros now use a new function, av_ts_make_time_string_format, which
takes a format specifier as an argument, so the function
av_ts_make_time_string is no longer used. I've left it in place, but
flagged it for deprecation with FF_API_AV_MAKETIMESTRING.

The test reference file filter-metadata-silencedetect has been updated
to match the new functionality.

Signed-off-by: Allan Cady <allancady@yahoo.com>
---
 libavfilter/af_silencedetect.c               | 12 +++----
 libavutil/timestamp.h                        | 34 +++++++++++++++++---
 libavutil/version.h                          |  1 +
 tests/ref/fate/filter-metadata-silencedetect |  2 +-
 4 files changed, 38 insertions(+), 11 deletions(-)

diff --git a/libavfilter/af_silencedetect.c b/libavfilter/af_silencedetect.c
index 845c65bfed..f1a8096540 100644
--- a/libavfilter/af_silencedetect.c
+++ b/libavfilter/af_silencedetect.c
@@ -86,11 +86,11 @@ static av_always_inline void update(SilenceDetectContext *s, AVFrame *insamples,
                 s->start[channel] = insamples->pts + av_rescale_q(current_sample / s->channels + 1 - nb_samples_notify * s->independent_channels / s->channels,
                         (AVRational){ 1, s->last_sample_rate }, time_base);
                 set_meta(insamples, s->mono ? channel + 1 : 0, "silence_start",
-                        av_ts2timestr(s->start[channel], &time_base));
+                        av_ts2timestr_fixed_precision(s->start[channel], &time_base));
                 if (s->mono)
                     av_log(s, AV_LOG_INFO, "channel: %d | ", channel);
                 av_log(s, AV_LOG_INFO, "silence_start: %s\n",
-                        av_ts2timestr(s->start[channel], &time_base));
+                        av_ts2timestr_fixed_precision(s->start[channel], &time_base));
             }
         }
     } else {
@@ -101,15 +101,15 @@ static av_always_inline void update(SilenceDetectContext *s, AVFrame *insamples,
             int64_t duration_ts = end_pts - s->start[channel];
             if (insamples) {
                 set_meta(insamples, s->mono ? channel + 1 : 0, "silence_end",
-                        av_ts2timestr(end_pts, &time_base));
+                        av_ts2timestr_fixed_precision(end_pts, &time_base));
                 set_meta(insamples, s->mono ? channel + 1 : 0, "silence_duration",
-                        av_ts2timestr(duration_ts, &time_base));
+                        av_ts2timestr_fixed_precision(duration_ts, &time_base));
             }
             if (s->mono)
                 av_log(s, AV_LOG_INFO, "channel: %d | ", channel);
             av_log(s, AV_LOG_INFO, "silence_end: %s | silence_duration: %s\n",
-                    av_ts2timestr(end_pts, &time_base),
-                    av_ts2timestr(duration_ts, &time_base));
+                    av_ts2timestr_fixed_precision(end_pts, &time_base),
+                    av_ts2timestr_fixed_precision(duration_ts, &time_base));
         }
         s->nb_null_samples[channel] = 0;
         s->start[channel] = INT64_MIN;
diff --git a/libavutil/timestamp.h b/libavutil/timestamp.h
index 9ae64da8a1..b483b5e12d 100644
--- a/libavutil/timestamp.h
+++ b/libavutil/timestamp.h
@@ -31,6 +31,8 @@
 #endif
 
 #define AV_TS_MAX_STRING_SIZE 32
+#define AV_TS_FMT_DEFAULT "%.6g"
+#define AV_TS_FMT_FIXED_PRECISION_6 "%.6f"
 
 /**
  * Fill the provided buffer with a string containing a timestamp
@@ -53,9 +55,14 @@ static inline char *av_ts_make_string(char *buf, int64_t ts)
  */
 #define av_ts2str(ts) av_ts_make_string((char[AV_TS_MAX_STRING_SIZE]){0}, ts)
 
+#if FF_API_AV_MAKETIMESTRING
 /**
+ * This function is probably deprecated. It was originally called by
+ * av_ts_make_time_string defined below, which now uses av_ts_make_time_string_format.
+ * instead. Nothing external references this function directly.
+ *
  * Fill the provided buffer with a string containing a timestamp time
- * representation.
+ * representation, using the default format AV_TS_FMT_DEFAULT.
  *
  * @param buf a buffer with size in bytes of at least AV_TS_MAX_STRING_SIZE
  * @param ts the timestamp to represent
@@ -65,14 +72,33 @@ static inline char *av_ts_make_string(char *buf, int64_t ts)
 static inline char *av_ts_make_time_string(char *buf, int64_t ts, AVRational *tb)
 {
     if (ts == AV_NOPTS_VALUE) snprintf(buf, AV_TS_MAX_STRING_SIZE, "NOPTS");
-    else                      snprintf(buf, AV_TS_MAX_STRING_SIZE, "%.6g", av_q2d(*tb) * ts);
+    else                      snprintf(buf, AV_TS_MAX_STRING_SIZE, AV_TS_FMT_DEFAULT, av_q2d(*tb) * ts);
     return buf;
 }
+#endif
 
 /**
- * Convenience macro, the return value should be used only directly in
+ * Fill the provided buffer with a string containing a timestamp time
+ * representation.
+ *
+ * @param buf a buffer with size in bytes of at least AV_TS_MAX_STRING_SIZE
+ * @param ts the timestamp to represent
+ * @param tb the timebase of the timestamp
+ * @param format format string for timestamp output, e.g. AV_TS_FMT_DEFAULT.
+ * @return the buffer in input
+ */
+static inline char *av_ts_make_time_string_format(char *buf, int64_t ts, AVRational *tb, const char *format)
+{
+    if (ts == AV_NOPTS_VALUE) snprintf(buf, AV_TS_MAX_STRING_SIZE, "NOPTS");
+    else                      snprintf(buf, AV_TS_MAX_STRING_SIZE, format, av_q2d(*tb) * ts);
+    return buf;
+}
+
+/**
+ * Convenience macros, the return values should be used only directly in
  * function arguments but never stand-alone.
  */
-#define av_ts2timestr(ts, tb) av_ts_make_time_string((char[AV_TS_MAX_STRING_SIZE]){0}, ts, tb)
+#define av_ts2timestr(ts, tb) av_ts_make_time_string_format((char[AV_TS_MAX_STRING_SIZE]){0}, ts, tb, AV_TS_FMT_DEFAULT)
+#define av_ts2timestr_fixed_precision(ts, tb) av_ts_make_time_string_format((char[AV_TS_MAX_STRING_SIZE]){0}, ts, tb, AV_TS_FMT_FIXED_PRECISION_6)
 
 #endif /* AVUTIL_TIMESTAMP_H */
diff --git a/libavutil/version.h b/libavutil/version.h
index 9f45af93df..a2daeda772 100644
--- a/libavutil/version.h
+++ b/libavutil/version.h
@@ -119,6 +119,7 @@
 #define FF_API_FRAME_KEY                (LIBAVUTIL_VERSION_MAJOR < 59)
 #define FF_API_PALETTE_HAS_CHANGED      (LIBAVUTIL_VERSION_MAJOR < 59)
 #define FF_API_VULKAN_CONTIGUOUS_MEMORY (LIBAVUTIL_VERSION_MAJOR < 59)
+#define FF_API_AV_MAKETIMESTRING        (LIBAVUTIL_VERSION_MAJOR < 59)
 
 /**
  * @}
diff --git a/tests/ref/fate/filter-metadata-silencedetect b/tests/ref/fate/filter-metadata-silencedetect
index bc53fea047..e66ffe5fdd 100644
--- a/tests/ref/fate/filter-metadata-silencedetect
+++ b/tests/ref/fate/filter-metadata-silencedetect
@@ -1,5 +1,5 @@
 pts=0|tag:lavfi.silence_duration=0.523107|tag:lavfi.silence_end=0.690023|tag:lavfi.silence_start=0.736417
-pts=46080|tag:lavfi.silence_start=1.27626|tag:lavfi.silence_end=1.80751|tag:lavfi.silence_duration=0.531247
+pts=46080|tag:lavfi.silence_start=1.276259|tag:lavfi.silence_end=1.807506|tag:lavfi.silence_duration=0.531247
 pts=92160
 pts=138240
 pts=184320
-- 
2.25.1

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

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

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [FFmpeg-devel] [PATCH] libavutil/timestamp.h: Fix loss of precision in timestamps for silencedetect on long files
  2024-02-21  7:39 ` [FFmpeg-devel] [PATCH] libavutil/timestamp.h: Fix loss of precision in timestamps for silencedetect on long files Allan Cady via ffmpeg-devel
@ 2024-02-21 20:25   ` Marton Balint
  2024-02-22  1:21     ` Allan Cady via ffmpeg-devel
  0 siblings, 1 reply; 37+ messages in thread
From: Marton Balint @ 2024-02-21 20:25 UTC (permalink / raw)
  To: Allan Cady via ffmpeg-devel



On Tue, 20 Feb 2024, Allan Cady via ffmpeg-devel wrote:

> When the silencedetect audio filter is run against long files, the
> output timestamps gradually lose precision as the scan proceeds further
> into the file. This is because the output format specifier ("%.6g" in
> libavutil/timestamp.h) limits the total field width to six significant
> digits. As the offset into the file increases, digits drop off the end,
> until eventually, for offsets greater than 100000 seconds (about 28
> hours), fractions of a second disappear altogether, and the timestamps
> are logged as whole seconds.
>
> This patch changes the format to "%.6f" for silencedetect, which will
> give microsecond precision for all timestamps regardless of offset.
>
> libavutil/timestamp.h exposes a macro, av_ts2timestr, as the public
> interface. This macro was used by silencedetect.c, as well as other
> source files. In order to fix the issue for silencedetect without
> affecting other files and tests, I have added a new macro,
> av_ts2timestr_fixed_precision, which uses the new format specifier.
> The original av_ts_make_time_string remains, with the original
> behavior.

I'd rather just to fix av_ts_make_string to not limit the number of 
significant digits. Something like:

1) Print the number in decimal notation with at most 6 fractional digits. 
2) Use less fractional digits if the first format would not fit into 
AV_TS_MAX_STRING_SIZE.
3) Use scientific notation if the second format would not fit into 
AV_TS_MAX_STRINT_SIZE.

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

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

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [FFmpeg-devel] [PATCH] libavutil/timestamp.h: Fix loss of precision in timestamps for silencedetect on long files
  2024-02-21 20:25   ` Marton Balint
@ 2024-02-22  1:21     ` Allan Cady via ffmpeg-devel
  2024-02-22  9:16       ` Marton Balint
  0 siblings, 1 reply; 37+ messages in thread
From: Allan Cady via ffmpeg-devel @ 2024-02-22  1:21 UTC (permalink / raw)
  To: FFmpeg development discussions and patches; +Cc: Allan Cady, Marton Balint

I had a similar thought, as all timestamps would have the same issue.

This is my first contribution here, and I don't know the code very well, so I was being cautious. I'm open to expanding the scope, but I'm sure I would need some help doing it right and not breaking things.

For starters, I'm curious why there are two functions & macros:

av_ts2str/av_ts_make_string (which used "%" format specifier)
av_ts2timestr/av_ts_make_time_string (which used "%6g")

Do you know the rationale for that? I see that only av_ts2timestr is used in silencedetect.c.

And are you suggesting we should fold those two functions into one?

I did notice something in the output from silencedetect. After I made my change, I see the output is now:


frame:92404 pts:53224175 pts_time:2413.79
lavfi.silence_start=2411.120272
frame:92411 pts:53228207 pts_time:2413.98
lavfi.silence_end=2413.992744
lavfi.silence_duration=2.872472


I see that the pts_time values still have the original formatting. I don't know what pts_time is, or where those lines are coming from. Seems like maybe those should have fixed precision as well.

Guidance for a noob please? Thanks.

(P.S. Can you tell me, when I reply to the list (as opposed to patch submission using git send-email), how should I address the email? Obviously it should go to ffmpeg-devel@ffmpeg.org, but should I include you as a recipient, or as a cc:, or only to the list? or is there some other way it gets directed to you? Any other guidance on how to format email? Thanks.)




On Wednesday, February 21, 2024 at 12:25:23 PM PST, Marton Balint <cus@passwd.hu> wrote: 

On Tue, 20 Feb 2024, Allan Cady via ffmpeg-devel wrote:

> When the silencedetect audio filter is run against long files, the
> output timestamps gradually lose precision as the scan proceeds further
> into the file. This is because the output format specifier ("%.6g" in
> libavutil/timestamp.h) limits the total field width to six significant
> digits. As the offset into the file increases, digits drop off the end,
> until eventually, for offsets greater than 100000 seconds (about 28
> hours), fractions of a second disappear altogether, and the timestamps
> are logged as whole seconds.
>
> This patch changes the format to "%.6f" for silencedetect, which will
> give microsecond precision for all timestamps regardless of offset.
>
> libavutil/timestamp.h exposes a macro, av_ts2timestr, as the public
> interface. This macro was used by silencedetect.c, as well as other
> source files. In order to fix the issue for silencedetect without
> affecting other files and tests, I have added a new macro,
> av_ts2timestr_fixed_precision, which uses the new format specifier.
> The original av_ts_make_time_string remains, with the original
> behavior.

I'd rather just to fix av_ts_make_string to not limit the number of 
significant digits. Something like:

1) Print the number in decimal notation with at most 6 fractional digits. 
2) Use less fractional digits if the first format would not fit into 
AV_TS_MAX_STRING_SIZE.
3) Use scientific notation if the second format would not fit into 
AV_TS_MAX_STRINT_SIZE.

Regards,
Marton

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

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

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

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [FFmpeg-devel] [PATCH] libavutil/timestamp.h: Fix loss of precision in timestamps for silencedetect on long files
  2024-02-22  1:21     ` Allan Cady via ffmpeg-devel
@ 2024-02-22  9:16       ` Marton Balint
  2024-02-23 21:44         ` Allan Cady via ffmpeg-devel
  2024-02-23 21:48         ` Allan Cady via ffmpeg-devel
  0 siblings, 2 replies; 37+ messages in thread
From: Marton Balint @ 2024-02-22  9:16 UTC (permalink / raw)
  To: Allan Cady via ffmpeg-devel



On Thu, 22 Feb 2024, Allan Cady via ffmpeg-devel wrote:

> I had a similar thought, as all timestamps would have the same issue.
>
> This is my first contribution here, and I don't know the code very well, 
> so I was being cautious. I'm open to expanding the scope, but I'm sure I 
> would need some help doing it right and not breaking things.
>
> For starters, I'm curious why there are two functions & macros:
>
> av_ts2str/av_ts_make_string (which used "%" format specifier)

That takes a 64-bit integer timestamp and is actually using "%"PRId64 
because that is the correct (portable) format string for an int64_t 
variable.

> av_ts2timestr/av_ts_make_time_string (which used "%6g")

That takes an integer timestamp and a rational time base. Float timestamps 
(in seconds) is calculated by multiplying the two, that is what is 
printed.

>
> Do you know the rationale for that? I see that only av_ts2timestr is used in silencedetect.c.
>
> And are you suggesting we should fold those two functions into one?

No, they have different purpose. The first prints out a timestamps which 
can be in any time base. The second prints out a timestamp in seconds.

>
> I did notice something in the output from silencedetect. After I made my 
> change, I see the output is now:
>
>
> frame:92404 pts:53224175 pts_time:2413.79
> lavfi.silence_start=2411.120272
> frame:92411 pts:53228207 pts_time:2413.98
> lavfi.silence_end=2413.992744
> lavfi.silence_duration=2.872472
>
>
> I see that the pts_time values still have the original formatting. I 
> don't know what pts_time is, or where those lines are coming from. Seems 
> like maybe those should have fixed precision as well.

Well, that is likely using the same function, but you only fixed 
silencedetect, right?

>
> Guidance for a noob please? Thanks.
>
> (P.S. Can you tell me, when I reply to the list (as opposed to patch 
> submission using git send-email), how should I address the email? 
> Obviously it should go to ffmpeg-devel@ffmpeg.org, but should I include 
> you as a recipient, or as a cc:, or only to the list? or is there some 
> other way it gets directed to you? Any other guidance on how to format 
> email? Thanks.)

I don't think there is a rule, I have seen it happen both with or without 
CC. You don't need to CC me though, as I am a regular on the list, but 
others may have other preference.

Regards,
Marton

PS: Please avoid top positing in you replies.
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

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

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [FFmpeg-devel] [PATCH] libavutil/timestamp.h: Fix loss of precision in timestamps for silencedetect on long files
  2024-02-22  9:16       ` Marton Balint
@ 2024-02-23 21:44         ` Allan Cady via ffmpeg-devel
  2024-02-23 21:48         ` Allan Cady via ffmpeg-devel
  1 sibling, 0 replies; 37+ messages in thread
From: Allan Cady via ffmpeg-devel @ 2024-02-23 21:44 UTC (permalink / raw)
  To: FFmpeg development discussions and patches; +Cc: Allan Cady

  On Thursday, February 22, 2024 at 01:16:19 AM PST, Marton Balint <cus@passwd.hu> wrote:
>> For starters, I'm curious why there are two functions & macros:>>>> av_ts2str/av_ts_make_string (which used "%" format specifier)> > That takes a 64-bit integer timestamp and is actually using "%"PRId64 > because that is the correct (portable) format string for an int64_t > variable.> >> av_ts2timestr/av_ts_make_time_string (which used "%6g")> > That takes an integer timestamp and a rational time base. Float timestamps > (in seconds) is calculated by multiplying the two, that is what is > printed.> >>>> Do you know the rationale for that? I see that only av_ts2timestr is used in silencedetect.c.>>>> And are you suggesting we should fold those two functions into one?> > No, they have different purpose. The first prints out a timestamps which > can be in any time base. The second prints out a timestamp in seconds.
Understood, I think I'm up to speed now. av_ts2str prints a 64-bit integer timestamp;av_ts2timestr prints a floating point timestamp in seconds with the timebase applied.
In your previous email, you said:
> I'd rather just to fix av_ts_make_string to not limit the number of > significant digits. Something like:>> 1) Print the number in decimal notation with at most 6 fractional digits. > 2) Use less fractional digits if the first format would not fit into > AV_TS_MAX_STRING_SIZE.> 3) Use scientific notation if the second format would not fit into > AV_TS_MAX_STRINT_SIZE.
I think you probably meant to say "I'd rather just to fixav_ts_make_time_string" (not av_ts_make_string)?Since it's av_ts_make_time_string() that's formatting floating point.
So it makes sense to me to make the change to av_ts_make_time_string()for all timestamps, as you suggest. As for how specifically to format them,
I'm fine with whatever you think is best, and I'm happy to work on this, but theimplementation has me a bit stumped for the moment, and I may need somehelp with it. My C language skills are rusty to say the least.
It also occurs to me to wonder, would this warrant a formal problem ticket?
I haven't looked into how that works for ffmpeg.

Thanks for your help.


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

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

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [FFmpeg-devel] [PATCH] libavutil/timestamp.h: Fix loss of precision in timestamps for silencedetect on long files
  2024-02-22  9:16       ` Marton Balint
  2024-02-23 21:44         ` Allan Cady via ffmpeg-devel
@ 2024-02-23 21:48         ` Allan Cady via ffmpeg-devel
  2024-02-23 22:47           ` Marton Balint
  1 sibling, 1 reply; 37+ messages in thread
From: Allan Cady via ffmpeg-devel @ 2024-02-23 21:48 UTC (permalink / raw)
  To: FFmpeg development discussions and patches; +Cc: Allan Cady

[Apologies for the awful mess in the previous email. Trying again with straight text.]

On Thursday, February 22, 2024 at 01:16:19 AM PST, Marton Balint <cus@passwd.hu> wrote:

>> For starters, I'm curious why there are two functions & macros:
>>
>> av_ts2str/av_ts_make_string (which used "%" format specifier)
> 
> That takes a 64-bit integer timestamp and is actually using "%"PRId64 
> because that is the correct (portable) format string for an int64_t 
> variable.
> 
>> av_ts2timestr/av_ts_make_time_string (which used "%6g")
> 
> That takes an integer timestamp and a rational time base. Float timestamps 
> (in seconds) is calculated by multiplying the two, that is what is 
> printed.
> 
>>
>> Do you know the rationale for that? I see that only av_ts2timestr is used in silencedetect.c.
>>
>> And are you suggesting we should fold those two functions into one?
> 
> No, they have different purpose. The first prints out a timestamps which 
> can be in any time base. The second prints out a timestamp in seconds.


Understood, I think I'm up to speed now. av_ts2str prints a 64-bit integer timestamp;
av_ts2timestr prints a floating point timestamp in seconds with the timebase applied.


In your previous email, you said:


> I'd rather just to fix av_ts_make_string to not limit the number of 
> significant digits. Something like:
>
> 1) Print the number in decimal notation with at most 6 fractional digits. 
> 2) Use less fractional digits if the first format would not fit into 
> AV_TS_MAX_STRING_SIZE.
> 3) Use scientific notation if the second format would not fit into 
> AV_TS_MAX_STRINT_SIZE.


I think you probably meant to say "I'd rather just to fix
av_ts_make_time_string" (not av_ts_make_string)?
Since it's av_ts_make_time_string() that's formatting floating point.


So it makes sense to me to make the change to av_ts_make_time_string()
for all timestamps, as you suggest. As for how specifically to format them,
I'm fine with whatever you think is best, and I'm happy to work on this, but the
implementation has me a bit stumped for the moment, and I may need some
help with it. My C language skills are rusty to say the least.

It also occurs to me to wonder, would this warrant a formal problem ticket?
I haven't looked into how that works for ffmpeg.

Thanks for your help.




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

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

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [FFmpeg-devel] [PATCH] libavutil/timestamp.h: Fix loss of precision in timestamps for silencedetect on long files
  2024-02-23 21:48         ` Allan Cady via ffmpeg-devel
@ 2024-02-23 22:47           ` Marton Balint
  2024-03-11  2:56             ` [FFmpeg-devel] [PATCH] When the silencedetect filter is run against long files, the output timestamps gradually lose precision as the scan proceeds further into the file. This is because the output is formatted (in libavutil/timestamp.h) as "%.6g", which limits the total field length. Eventually, for offsets greater than 100000 seconds (about 28 hours), fractions of a second disappear altogether, and the timestamps are logged as whole seconds Allan Cady via ffmpeg-devel
                               ` (2 more replies)
  0 siblings, 3 replies; 37+ messages in thread
From: Marton Balint @ 2024-02-23 22:47 UTC (permalink / raw)
  To: Allan Cady via ffmpeg-devel



On Fri, 23 Feb 2024, Allan Cady via ffmpeg-devel wrote:

> [Apologies for the awful mess in the previous email. Trying again with straight text.]
>
> On Thursday, February 22, 2024 at 01:16:19 AM PST, Marton Balint <cus@passwd.hu> wrote:
>
>>> For starters, I'm curious why there are two functions & macros:
>>>
>>> av_ts2str/av_ts_make_string (which used "%" format specifier)
>>  
>> That takes a 64-bit integer timestamp and is actually using "%"PRId64 
>> because that is the correct (portable) format string for an int64_t 
>> variable.
>>  
>>> av_ts2timestr/av_ts_make_time_string (which used "%6g")
>>  
>> That takes an integer timestamp and a rational time base. Float timestamps 
>> (in seconds) is calculated by multiplying the two, that is what is 
>> printed.
>>  
>>>
>>> Do you know the rationale for that? I see that only av_ts2timestr is used in silencedetect.c.
>>>
>>> And are you suggesting we should fold those two functions into one?
>>  
>> No, they have different purpose. The first prints out a timestamps which 
>> can be in any time base. The second prints out a timestamp in seconds.
>
>
> Understood, I think I'm up to speed now. av_ts2str prints a 64-bit integer timestamp;
> av_ts2timestr prints a floating point timestamp in seconds with the timebase applied.
>
>
> In your previous email, you said:
>
>
>> I'd rather just to fix av_ts_make_string to not limit the number of 
>> significant digits. Something like:
>>
>> 1) Print the number in decimal notation with at most 6 fractional digits. 
>> 2) Use less fractional digits if the first format would not fit into 
>> AV_TS_MAX_STRING_SIZE.
>> 3) Use scientific notation if the second format would not fit into 
>> AV_TS_MAX_STRINT_SIZE.
>
>
> I think you probably meant to say "I'd rather just to fix
> av_ts_make_time_string" (not av_ts_make_string)?
> Since it's av_ts_make_time_string() that's formatting floating point.

Yes, indeed.

>
> So it makes sense to me to make the change to av_ts_make_time_string()
> for all timestamps, as you suggest. As for how specifically to format them,
> I'm fine with whatever you think is best, and I'm happy to work on this, but the
> implementation has me a bit stumped for the moment, and I may need some
> help with it. My C language skills are rusty to say the least.

The simplest way is to try snprintf() with 6 fractional digits, and check 
the return value to see how long the string would become.
Based on this you can either accept snprintf result and truncate 
trailing zeros and dots, or try snprintf() with less fractional digits and 
truncate, or fall back to scientific format.

>
> It also occurs to me to wonder, would this warrant a formal problem ticket?
> I haven't looked into how that works for ffmpeg.

Opening a ticket for the problem is not required, but if your patch 
fixes an existing ticket, then you should mention the ticket number
in the commit message.

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

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

^ permalink raw reply	[flat|nested] 37+ messages in thread

* [FFmpeg-devel] [PATCH] When the silencedetect filter is run against long files, the output timestamps gradually lose precision as the scan proceeds further into the file. This is because the output is formatted (in libavutil/timestamp.h) as "%.6g", which limits the total field length. Eventually, for offsets greater than 100000 seconds (about 28 hours), fractions of a second disappear altogether, and the timestamps are logged as whole seconds.
  2024-02-23 22:47           ` Marton Balint
@ 2024-03-11  2:56             ` Allan Cady via ffmpeg-devel
  2024-03-11 13:43               ` Andreas Rheinhardt
  2024-03-11  3:57             ` [FFmpeg-devel] [PATCH] libavutil/timestamp.h: Fix loss of precision in timestamps for silencedetect on long files Allan Cady via ffmpeg-devel
  2024-03-11  4:13             ` Allan Cady via ffmpeg-devel
  2 siblings, 1 reply; 37+ messages in thread
From: Allan Cady via ffmpeg-devel @ 2024-03-11  2:56 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Allan Cady

From: "Allan Cady" <allancady@yahoo.com>

I propose changing the format to "%.6f", which will
give microsecond precision for all timestamps, regardless of
offset. Trailing zeros can be trimmed from the fraction, without
losing precision. If the length of the fixed-precision formatted
timestamp exceeds the length of the allocated buffer
(AV_TS_MAX_STRING_SIZE, currently 32, less one for the
terminating null), then we can fall back to scientific notation, though
this seems almost certain to never occur, because 32 characters would
allow a maximum timestamp value of (32 - 1 - 6 - 1) = 24 characters.
By my calculation, 10^24 seconds is about six orders of magnitude
greater than the age of the universe.

The fix proposed here follows the following logic:

1) Try formatting the number of seconds using "%.6f". This will
always result in a string with six decimal digits in the fraction,
possibly including trailing zeros. (e.g. "897234.73200").

2) Check if that string would overflow the buffer. If it would, then
format it using scientific notation ("%.8g").

3) If the original fixed-point format fits, then trim any trailing
zeros and decimal point, and return that result.

Making this change broke two fate tests, filter-metadata-scdet,
and filter-metadata-silencedetect. To correct this, I've modified
tests/ref/fate/filter-metadata-scdet and
tests/ref/fate/filter-metadata-silencedetect to match the
new output.

Signed-off-by: Allan Cady <allancady@yahoo.com>
---
 libavutil/timestamp.h                        | 53 +++++++++++++++++++-
 tests/ref/fate/filter-metadata-scdet         | 12 ++---
 tests/ref/fate/filter-metadata-silencedetect |  2 +-
 3 files changed, 58 insertions(+), 9 deletions(-)

diff --git a/libavutil/timestamp.h b/libavutil/timestamp.h
index 2b37781eba..2f04f9bb2b 100644
--- a/libavutil/timestamp.h
+++ b/libavutil/timestamp.h
@@ -25,6 +25,7 @@
 #define AVUTIL_TIMESTAMP_H
 
 #include "avutil.h"
+#include <stdbool.h>
 
 #if defined(__cplusplus) && !defined(__STDC_FORMAT_MACROS) && !defined(PRId64)
 #error missing -D__STDC_FORMAT_MACROS / #define __STDC_FORMAT_MACROS
@@ -53,6 +54,32 @@ static inline char *av_ts_make_string(char *buf, int64_t ts)
  */
 #define av_ts2str(ts) av_ts_make_string((char[AV_TS_MAX_STRING_SIZE]){0}, ts)
 
+/**
+ * Strip trailing zeros and decimal point from a string. Performed
+ * in-place on input buffer. For local use only by av_ts_make_time_string.
+ * 
+ * e.g.:
+ * "752.378000" -> "752.378"
+ *        "4.0" -> "4"
+ *      "97300" -> "97300"
+ */
+static inline void av_ts_strip_trailing_zeros_and_decimal_point(char *str) {
+    if (strchr(str, '.'))
+    {
+        int i;
+        for (i = strlen(str) - 1; i >= 0 && str[i] == '0'; i--) {
+            str[i] = '\0';
+        }
+        
+        // Remove decimal point if it's the last character
+        if (i >= 0 && str[i] == '.') {
+            str[i] = '\0';
+        }
+
+        // String was modified in place; no need for return value.
+    }
+}
+
 /**
  * Fill the provided buffer with a string containing a timestamp time
  * representation.
@@ -65,8 +92,30 @@ static inline char *av_ts_make_string(char *buf, int64_t ts)
 static inline char *av_ts_make_time_string(char *buf, int64_t ts,
                                            const AVRational *tb)
 {
-    if (ts == AV_NOPTS_VALUE) snprintf(buf, AV_TS_MAX_STRING_SIZE, "NOPTS");
-    else                      snprintf(buf, AV_TS_MAX_STRING_SIZE, "%.6g", av_q2d(*tb) * ts);
+    if (ts == AV_NOPTS_VALUE)
+    {
+        snprintf(buf, AV_TS_MAX_STRING_SIZE, "NOPTS");
+    }
+    else
+    {
+        const int max_fraction_digits = 6;
+
+        // Convert 64-bit timestamp to double, using rational timebase
+        double seconds = av_q2d(*tb) * ts;
+
+        int length = snprintf(NULL, 0, "%.*f", max_fraction_digits, seconds);
+        if (length <= AV_TS_MAX_STRING_SIZE - 1)
+        {
+            snprintf(buf, AV_TS_MAX_STRING_SIZE, "%.*f", max_fraction_digits, seconds);
+            av_ts_strip_trailing_zeros_and_decimal_point(buf);
+        }
+        else
+        {
+            snprintf(buf, AV_TS_MAX_STRING_SIZE, "%.8g", seconds);
+        }
+
+    }
+
     return buf;
 }
 
diff --git a/tests/ref/fate/filter-metadata-scdet b/tests/ref/fate/filter-metadata-scdet
index ca5dbaaefc..d385920fcd 100644
--- a/tests/ref/fate/filter-metadata-scdet
+++ b/tests/ref/fate/filter-metadata-scdet
@@ -1,11 +1,11 @@
 pts=1620|tag:lavfi.scd.score=59.252|tag:lavfi.scd.mafd=60.175|tag:lavfi.scd.time=2.7
 pts=4140|tag:lavfi.scd.score=36.070|tag:lavfi.scd.mafd=44.209|tag:lavfi.scd.time=6.9
-pts=5800|tag:lavfi.scd.score=55.819|tag:lavfi.scd.mafd=55.819|tag:lavfi.scd.time=9.66667
+pts=5800|tag:lavfi.scd.score=55.819|tag:lavfi.scd.mafd=55.819|tag:lavfi.scd.time=9.666667
 pts=6720|tag:lavfi.scd.score=18.580|tag:lavfi.scd.mafd=22.505|tag:lavfi.scd.time=11.2
 pts=8160|tag:lavfi.scd.score=49.240|tag:lavfi.scd.mafd=49.444|tag:lavfi.scd.time=13.6
-pts=9760|tag:lavfi.scd.score=51.497|tag:lavfi.scd.mafd=51.801|tag:lavfi.scd.time=16.2667
-pts=14080|tag:lavfi.scd.score=34.165|tag:lavfi.scd.mafd=34.337|tag:lavfi.scd.time=23.4667
-pts=15700|tag:lavfi.scd.score=58.310|tag:lavfi.scd.mafd=58.315|tag:lavfi.scd.time=26.1667
-pts=18500|tag:lavfi.scd.score=16.504|tag:lavfi.scd.mafd=19.603|tag:lavfi.scd.time=30.8333
+pts=9760|tag:lavfi.scd.score=51.497|tag:lavfi.scd.mafd=51.801|tag:lavfi.scd.time=16.266667
+pts=14080|tag:lavfi.scd.score=34.165|tag:lavfi.scd.mafd=34.337|tag:lavfi.scd.time=23.466667
+pts=15700|tag:lavfi.scd.score=58.310|tag:lavfi.scd.mafd=58.315|tag:lavfi.scd.time=26.166667
+pts=18500|tag:lavfi.scd.score=16.504|tag:lavfi.scd.mafd=19.603|tag:lavfi.scd.time=30.833333
 pts=20040|tag:lavfi.scd.score=13.764|tag:lavfi.scd.mafd=19.060|tag:lavfi.scd.time=33.4
-pts=21760|tag:lavfi.scd.score=64.451|tag:lavfi.scd.mafd=64.551|tag:lavfi.scd.time=36.2667
+pts=21760|tag:lavfi.scd.score=64.451|tag:lavfi.scd.mafd=64.551|tag:lavfi.scd.time=36.266667
diff --git a/tests/ref/fate/filter-metadata-silencedetect b/tests/ref/fate/filter-metadata-silencedetect
index bc53fea047..e66ffe5fdd 100644
--- a/tests/ref/fate/filter-metadata-silencedetect
+++ b/tests/ref/fate/filter-metadata-silencedetect
@@ -1,5 +1,5 @@
 pts=0|tag:lavfi.silence_duration=0.523107|tag:lavfi.silence_end=0.690023|tag:lavfi.silence_start=0.736417
-pts=46080|tag:lavfi.silence_start=1.27626|tag:lavfi.silence_end=1.80751|tag:lavfi.silence_duration=0.531247
+pts=46080|tag:lavfi.silence_start=1.276259|tag:lavfi.silence_end=1.807506|tag:lavfi.silence_duration=0.531247
 pts=92160
 pts=138240
 pts=184320
-- 
2.34.1

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

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

^ permalink raw reply	[flat|nested] 37+ messages in thread

* [FFmpeg-devel] [PATCH] libavutil/timestamp.h: Fix loss of precision in timestamps for silencedetect on long files
  2024-02-23 22:47           ` Marton Balint
  2024-03-11  2:56             ` [FFmpeg-devel] [PATCH] When the silencedetect filter is run against long files, the output timestamps gradually lose precision as the scan proceeds further into the file. This is because the output is formatted (in libavutil/timestamp.h) as "%.6g", which limits the total field length. Eventually, for offsets greater than 100000 seconds (about 28 hours), fractions of a second disappear altogether, and the timestamps are logged as whole seconds Allan Cady via ffmpeg-devel
@ 2024-03-11  3:57             ` Allan Cady via ffmpeg-devel
  2024-03-11  4:13             ` Allan Cady via ffmpeg-devel
  2 siblings, 0 replies; 37+ messages in thread
From: Allan Cady via ffmpeg-devel @ 2024-03-11  3:57 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Allan Cady

When the silencedetect filter is run against long files, the output
timestamps gradually lose precision as the scan proceeds further into
the file. This is because the output is formatted (in
libavutil/timestamp.h) as "%.6g", which limits the total field
length. Eventually, for offsets greater than 100000 seconds (about 28
hours), fractions of a second disappear altogether, and the
timestamps are logged as whole seconds.

I propose changing the format to "%.6f", which will
give microsecond precision for all timestamps, regardless of
offset. Trailing zeros can be trimmed from the fraction, without
losing precision. If the length of the fixed-precision formatted
timestamp exceeds the length of the allocated buffer
(AV_TS_MAX_STRING_SIZE, currently 32, less one for the
terminating null), then we can fall back to scientific notation, though
this seems almost certain to never occur, because 32 characters would
allow a maximum timestamp value of (32 - 1 - 6 - 1) = 24 characters.
By my calculation, 10^24 seconds is about six orders of magnitude
greater than the age of the universe.

The fix proposed here follows the following logic:

1) Try formatting the number of seconds using "%.6f". This will
always result in a string with six decimal digits in the fraction,
possibly including trailing zeros. (e.g. "897234.73200").

2) Check if that string would overflow the buffer. If it would, then
format it using scientific notation ("%.8g").

3) If the original fixed-point format fits, then trim any trailing
zeros and decimal point, and return that result.

Making this change broke two fate tests, filter-metadata-scdet,
and filter-metadata-silencedetect. To correct this, I've modified
tests/ref/fate/filter-metadata-scdet and
tests/ref/fate/filter-metadata-silencedetect to match the
new output.

Signed-off-by: Allan Cady <allancady@yahoo.com>
---
 libavutil/timestamp.h                        | 53 +++++++++++++++++++-
 tests/ref/fate/filter-metadata-scdet         | 12 ++---
 tests/ref/fate/filter-metadata-silencedetect |  2 +-
 3 files changed, 58 insertions(+), 9 deletions(-)

diff --git a/libavutil/timestamp.h b/libavutil/timestamp.h
index 2b37781eba..2f04f9bb2b 100644
--- a/libavutil/timestamp.h
+++ b/libavutil/timestamp.h
@@ -25,6 +25,7 @@
 #define AVUTIL_TIMESTAMP_H
 
 #include "avutil.h"
+#include <stdbool.h>
 
 #if defined(__cplusplus) && !defined(__STDC_FORMAT_MACROS) && !defined(PRId64)
 #error missing -D__STDC_FORMAT_MACROS / #define __STDC_FORMAT_MACROS
@@ -53,6 +54,32 @@ static inline char *av_ts_make_string(char *buf, int64_t ts)
  */
 #define av_ts2str(ts) av_ts_make_string((char[AV_TS_MAX_STRING_SIZE]){0}, ts)
 
+/**
+ * Strip trailing zeros and decimal point from a string. Performed
+ * in-place on input buffer. For local use only by av_ts_make_time_string.
+ * 
+ * e.g.:
+ * "752.378000" -> "752.378"
+ *        "4.0" -> "4"
+ *      "97300" -> "97300"
+ */
+static inline void av_ts_strip_trailing_zeros_and_decimal_point(char *str) {
+    if (strchr(str, '.'))
+    {
+        int i;
+        for (i = strlen(str) - 1; i >= 0 && str[i] == '0'; i--) {
+            str[i] = '\0';
+        }
+        
+        // Remove decimal point if it's the last character
+        if (i >= 0 && str[i] == '.') {
+            str[i] = '\0';
+        }
+
+        // String was modified in place; no need for return value.
+    }
+}
+
 /**
  * Fill the provided buffer with a string containing a timestamp time
  * representation.
@@ -65,8 +92,30 @@ static inline char *av_ts_make_string(char *buf, int64_t ts)
 static inline char *av_ts_make_time_string(char *buf, int64_t ts,
                                            const AVRational *tb)
 {
-    if (ts == AV_NOPTS_VALUE) snprintf(buf, AV_TS_MAX_STRING_SIZE, "NOPTS");
-    else                      snprintf(buf, AV_TS_MAX_STRING_SIZE, "%.6g", av_q2d(*tb) * ts);
+    if (ts == AV_NOPTS_VALUE)
+    {
+        snprintf(buf, AV_TS_MAX_STRING_SIZE, "NOPTS");
+    }
+    else
+    {
+        const int max_fraction_digits = 6;
+
+        // Convert 64-bit timestamp to double, using rational timebase
+        double seconds = av_q2d(*tb) * ts;
+
+        int length = snprintf(NULL, 0, "%.*f", max_fraction_digits, seconds);
+        if (length <= AV_TS_MAX_STRING_SIZE - 1)
+        {
+            snprintf(buf, AV_TS_MAX_STRING_SIZE, "%.*f", max_fraction_digits, seconds);
+            av_ts_strip_trailing_zeros_and_decimal_point(buf);
+        }
+        else
+        {
+            snprintf(buf, AV_TS_MAX_STRING_SIZE, "%.8g", seconds);
+        }
+
+    }
+
     return buf;
 }
 
diff --git a/tests/ref/fate/filter-metadata-scdet b/tests/ref/fate/filter-metadata-scdet
index ca5dbaaefc..d385920fcd 100644
--- a/tests/ref/fate/filter-metadata-scdet
+++ b/tests/ref/fate/filter-metadata-scdet
@@ -1,11 +1,11 @@
 pts=1620|tag:lavfi.scd.score=59.252|tag:lavfi.scd.mafd=60.175|tag:lavfi.scd.time=2.7
 pts=4140|tag:lavfi.scd.score=36.070|tag:lavfi.scd.mafd=44.209|tag:lavfi.scd.time=6.9
-pts=5800|tag:lavfi.scd.score=55.819|tag:lavfi.scd.mafd=55.819|tag:lavfi.scd.time=9.66667
+pts=5800|tag:lavfi.scd.score=55.819|tag:lavfi.scd.mafd=55.819|tag:lavfi.scd.time=9.666667
 pts=6720|tag:lavfi.scd.score=18.580|tag:lavfi.scd.mafd=22.505|tag:lavfi.scd.time=11.2
 pts=8160|tag:lavfi.scd.score=49.240|tag:lavfi.scd.mafd=49.444|tag:lavfi.scd.time=13.6
-pts=9760|tag:lavfi.scd.score=51.497|tag:lavfi.scd.mafd=51.801|tag:lavfi.scd.time=16.2667
-pts=14080|tag:lavfi.scd.score=34.165|tag:lavfi.scd.mafd=34.337|tag:lavfi.scd.time=23.4667
-pts=15700|tag:lavfi.scd.score=58.310|tag:lavfi.scd.mafd=58.315|tag:lavfi.scd.time=26.1667
-pts=18500|tag:lavfi.scd.score=16.504|tag:lavfi.scd.mafd=19.603|tag:lavfi.scd.time=30.8333
+pts=9760|tag:lavfi.scd.score=51.497|tag:lavfi.scd.mafd=51.801|tag:lavfi.scd.time=16.266667
+pts=14080|tag:lavfi.scd.score=34.165|tag:lavfi.scd.mafd=34.337|tag:lavfi.scd.time=23.466667
+pts=15700|tag:lavfi.scd.score=58.310|tag:lavfi.scd.mafd=58.315|tag:lavfi.scd.time=26.166667
+pts=18500|tag:lavfi.scd.score=16.504|tag:lavfi.scd.mafd=19.603|tag:lavfi.scd.time=30.833333
 pts=20040|tag:lavfi.scd.score=13.764|tag:lavfi.scd.mafd=19.060|tag:lavfi.scd.time=33.4
-pts=21760|tag:lavfi.scd.score=64.451|tag:lavfi.scd.mafd=64.551|tag:lavfi.scd.time=36.2667
+pts=21760|tag:lavfi.scd.score=64.451|tag:lavfi.scd.mafd=64.551|tag:lavfi.scd.time=36.266667
diff --git a/tests/ref/fate/filter-metadata-silencedetect b/tests/ref/fate/filter-metadata-silencedetect
index bc53fea047..e66ffe5fdd 100644
--- a/tests/ref/fate/filter-metadata-silencedetect
+++ b/tests/ref/fate/filter-metadata-silencedetect
@@ -1,5 +1,5 @@
 pts=0|tag:lavfi.silence_duration=0.523107|tag:lavfi.silence_end=0.690023|tag:lavfi.silence_start=0.736417
-pts=46080|tag:lavfi.silence_start=1.27626|tag:lavfi.silence_end=1.80751|tag:lavfi.silence_duration=0.531247
+pts=46080|tag:lavfi.silence_start=1.276259|tag:lavfi.silence_end=1.807506|tag:lavfi.silence_duration=0.531247
 pts=92160
 pts=138240
 pts=184320
-- 
2.34.1

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

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

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [FFmpeg-devel] [PATCH] libavutil/timestamp.h: Fix loss of precision in timestamps for silencedetect on long files
  2024-02-23 22:47           ` Marton Balint
  2024-03-11  2:56             ` [FFmpeg-devel] [PATCH] When the silencedetect filter is run against long files, the output timestamps gradually lose precision as the scan proceeds further into the file. This is because the output is formatted (in libavutil/timestamp.h) as "%.6g", which limits the total field length. Eventually, for offsets greater than 100000 seconds (about 28 hours), fractions of a second disappear altogether, and the timestamps are logged as whole seconds Allan Cady via ffmpeg-devel
  2024-03-11  3:57             ` [FFmpeg-devel] [PATCH] libavutil/timestamp.h: Fix loss of precision in timestamps for silencedetect on long files Allan Cady via ffmpeg-devel
@ 2024-03-11  4:13             ` Allan Cady via ffmpeg-devel
  2 siblings, 0 replies; 37+ messages in thread
From: Allan Cady via ffmpeg-devel @ 2024-03-11  4:13 UTC (permalink / raw)
  To: FFmpeg Development Discussions and Patches; +Cc: Allan Cady, Martin Balint

Greetings Martin et al,

I've been trying to resubmit this patch based on your earlier suggestions. Most of the tools in the toolchain for this are new to me, so it's been awkward going. 

I did finally get the email sent with the new patch, but for some reason I haven't been able to figure out yet, it's going to the wrong thread. Here's the link to the message with the patch.

https://ffmpeg.org/pipermail/ffmpeg-devel/2024-March/323170.html

Looking forward to comments and to hopefully getting this approved and into the codebase.

Allan







On Friday, February 23, 2024 at 02:47:27 PM PST, Marton Balint <cus@passwd.hu> wrote: 







On Fri, 23 Feb 2024, Allan Cady via ffmpeg-devel wrote:

> [Apologies for the awful mess in the previous email. Trying again with straight text.]
>
> On Thursday, February 22, 2024 at 01:16:19 AM PST, Marton Balint <cus@passwd.hu> wrote:
>
>>> For starters, I'm curious why there are two functions & macros:
>>>
>>> av_ts2str/av_ts_make_string (which used "%" format specifier)
>>  
>> That takes a 64-bit integer timestamp and is actually using "%"PRId64 
>> because that is the correct (portable) format string for an int64_t 
>> variable.
>>  
>>> av_ts2timestr/av_ts_make_time_string (which used "%6g")
>>  
>> That takes an integer timestamp and a rational time base. Float timestamps 
>> (in seconds) is calculated by multiplying the two, that is what is 
>> printed.
>>  
>>>
>>> Do you know the rationale for that? I see that only av_ts2timestr is used in silencedetect.c.
>>>
>>> And are you suggesting we should fold those two functions into one?
>>  
>> No, they have different purpose. The first prints out a timestamps which 
>> can be in any time base. The second prints out a timestamp in seconds.
>
>
> Understood, I think I'm up to speed now. av_ts2str prints a 64-bit integer timestamp;
> av_ts2timestr prints a floating point timestamp in seconds with the timebase applied.
>
>
> In your previous email, you said:
>
>
>> I'd rather just to fix av_ts_make_string to not limit the number of 
>> significant digits. Something like:
>>
>> 1) Print the number in decimal notation with at most 6 fractional digits. 
>> 2) Use less fractional digits if the first format would not fit into 
>> AV_TS_MAX_STRING_SIZE.
>> 3) Use scientific notation if the second format would not fit into 
>> AV_TS_MAX_STRINT_SIZE.
>
>
> I think you probably meant to say "I'd rather just to fix
> av_ts_make_time_string" (not av_ts_make_string)?
> Since it's av_ts_make_time_string() that's formatting floating point.

Yes, indeed.

>
> So it makes sense to me to make the change to av_ts_make_time_string()
> for all timestamps, as you suggest. As for how specifically to format them,
> I'm fine with whatever you think is best, and I'm happy to work on this, but the
> implementation has me a bit stumped for the moment, and I may need some
> help with it. My C language skills are rusty to say the least.

The simplest way is to try snprintf() with 6 fractional digits, and check 
the return value to see how long the string would become.
Based on this you can either accept snprintf result and truncate 
trailing zeros and dots, or try snprintf() with less fractional digits and 
truncate, or fall back to scientific format.

>
> It also occurs to me to wonder, would this warrant a formal problem ticket?
> I haven't looked into how that works for ffmpeg.

Opening a ticket for the problem is not required, but if your patch 
fixes an existing ticket, then you should mention the ticket number
in the commit message.

Regards,

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

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

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

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [FFmpeg-devel] [PATCH] When the silencedetect filter is run against long files, the output timestamps gradually lose precision as the scan proceeds further into the file. This is because the output is formatted (in libavutil/timestamp.h) as "%.6g", which limits the total field length. Eventually, for offsets greater than 100000 seconds (about 28 hours), fractions of a second disappear altogether, and the timestamps are logged as whole seconds.
  2024-03-11  2:56             ` [FFmpeg-devel] [PATCH] When the silencedetect filter is run against long files, the output timestamps gradually lose precision as the scan proceeds further into the file. This is because the output is formatted (in libavutil/timestamp.h) as "%.6g", which limits the total field length. Eventually, for offsets greater than 100000 seconds (about 28 hours), fractions of a second disappear altogether, and the timestamps are logged as whole seconds Allan Cady via ffmpeg-devel
@ 2024-03-11 13:43               ` Andreas Rheinhardt
  2024-03-11 14:26                 ` Andreas Rheinhardt
  2024-03-11 19:11                 ` [FFmpeg-devel] [PATCH] change av_ts_make_time_string precision Marton Balint
  0 siblings, 2 replies; 37+ messages in thread
From: Andreas Rheinhardt @ 2024-03-11 13:43 UTC (permalink / raw)
  To: ffmpeg-devel

Allan Cady via ffmpeg-devel:
> From: "Allan Cady" <allancady@yahoo.com>
> 
> I propose changing the format to "%.6f", which will
> give microsecond precision for all timestamps, regardless of
> offset. Trailing zeros can be trimmed from the fraction, without
> losing precision. If the length of the fixed-precision formatted
> timestamp exceeds the length of the allocated buffer
> (AV_TS_MAX_STRING_SIZE, currently 32, less one for the
> terminating null), then we can fall back to scientific notation, though
> this seems almost certain to never occur, because 32 characters would
> allow a maximum timestamp value of (32 - 1 - 6 - 1) = 24 characters.
> By my calculation, 10^24 seconds is about six orders of magnitude
> greater than the age of the universe.
> 
> The fix proposed here follows the following logic:
> 
> 1) Try formatting the number of seconds using "%.6f". This will
> always result in a string with six decimal digits in the fraction,
> possibly including trailing zeros. (e.g. "897234.73200").
> 
> 2) Check if that string would overflow the buffer. If it would, then
> format it using scientific notation ("%.8g").
> 
> 3) If the original fixed-point format fits, then trim any trailing
> zeros and decimal point, and return that result.
> 
> Making this change broke two fate tests, filter-metadata-scdet,
> and filter-metadata-silencedetect. To correct this, I've modified
> tests/ref/fate/filter-metadata-scdet and
> tests/ref/fate/filter-metadata-silencedetect to match the
> new output.
> 
> Signed-off-by: Allan Cady <allancady@yahoo.com>
> ---
>  libavutil/timestamp.h                        | 53 +++++++++++++++++++-
>  tests/ref/fate/filter-metadata-scdet         | 12 ++---
>  tests/ref/fate/filter-metadata-silencedetect |  2 +-
>  3 files changed, 58 insertions(+), 9 deletions(-)
> 
> diff --git a/libavutil/timestamp.h b/libavutil/timestamp.h
> index 2b37781eba..2f04f9bb2b 100644
> --- a/libavutil/timestamp.h
> +++ b/libavutil/timestamp.h
> @@ -25,6 +25,7 @@
>  #define AVUTIL_TIMESTAMP_H
>  
>  #include "avutil.h"
> +#include <stdbool.h>
>  
>  #if defined(__cplusplus) && !defined(__STDC_FORMAT_MACROS) && !defined(PRId64)
>  #error missing -D__STDC_FORMAT_MACROS / #define __STDC_FORMAT_MACROS
> @@ -53,6 +54,32 @@ static inline char *av_ts_make_string(char *buf, int64_t ts)
>   */
>  #define av_ts2str(ts) av_ts_make_string((char[AV_TS_MAX_STRING_SIZE]){0}, ts)
>  
> +/**
> + * Strip trailing zeros and decimal point from a string. Performed
> + * in-place on input buffer. For local use only by av_ts_make_time_string.
> + * 
> + * e.g.:
> + * "752.378000" -> "752.378"
> + *        "4.0" -> "4"
> + *      "97300" -> "97300"
> + */
> +static inline void av_ts_strip_trailing_zeros_and_decimal_point(char *str) {
> +    if (strchr(str, '.'))
> +    {
> +        int i;
> +        for (i = strlen(str) - 1; i >= 0 && str[i] == '0'; i--) {
> +            str[i] = '\0';
> +        }
> +        
> +        // Remove decimal point if it's the last character
> +        if (i >= 0 && str[i] == '.') {
> +            str[i] = '\0';
> +        }
> +
> +        // String was modified in place; no need for return value.
> +    }
> +}
> +
>  /**
>   * Fill the provided buffer with a string containing a timestamp time
>   * representation.
> @@ -65,8 +92,30 @@ static inline char *av_ts_make_string(char *buf, int64_t ts)
>  static inline char *av_ts_make_time_string(char *buf, int64_t ts,
>                                             const AVRational *tb)
>  {
> -    if (ts == AV_NOPTS_VALUE) snprintf(buf, AV_TS_MAX_STRING_SIZE, "NOPTS");
> -    else                      snprintf(buf, AV_TS_MAX_STRING_SIZE, "%.6g", av_q2d(*tb) * ts);
> +    if (ts == AV_NOPTS_VALUE)
> +    {
> +        snprintf(buf, AV_TS_MAX_STRING_SIZE, "NOPTS");
> +    }
> +    else
> +    {
> +        const int max_fraction_digits = 6;
> +
> +        // Convert 64-bit timestamp to double, using rational timebase
> +        double seconds = av_q2d(*tb) * ts;
> +
> +        int length = snprintf(NULL, 0, "%.*f", max_fraction_digits, seconds);
> +        if (length <= AV_TS_MAX_STRING_SIZE - 1)
> +        {
> +            snprintf(buf, AV_TS_MAX_STRING_SIZE, "%.*f", max_fraction_digits, seconds);
> +            av_ts_strip_trailing_zeros_and_decimal_point(buf);
> +        }
> +        else
> +        {
> +            snprintf(buf, AV_TS_MAX_STRING_SIZE, "%.8g", seconds);
> +        }
> +
> +    }
> +
>      return buf;
>  }
>  

1. What makes you believe that all users want the new format and that it
does not cause undesired behaviour for some (maybe a lot) of them? The
number of characters written by the earlier code stayed pretty constant
even when the times became big (in this case, it just printed 8 chars if
ts>=0), yet your code will really make use of the whole buffer.
Granted, we could tell our users that they have no right to complain
about this, given that we always had a "right" to use the full buffer,
but I consider this a violation of the principle of least surprise. Why
don't you just change silencedetect or add another function?
2. For very small timestamps (< 10^-4), the new code will print a lot of
useless leading zeros (after the decimal point). In fact, it can be so
many that the new code has less precision than the old code, despite
using the fill buffer.
2. This is way too much code for an inline function.
3. Anyway, your placement of {} on their own lines does not match the
project coding style.

- Andreas

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

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

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [FFmpeg-devel] [PATCH] When the silencedetect filter is run against long files, the output timestamps gradually lose precision as the scan proceeds further into the file. This is because the output is formatted (in libavutil/timestamp.h) as "%.6g", which limits the total field length. Eventually, for offsets greater than 100000 seconds (about 28 hours), fractions of a second disappear altogether, and the timestamps are logged as whole seconds.
  2024-03-11 13:43               ` Andreas Rheinhardt
@ 2024-03-11 14:26                 ` Andreas Rheinhardt
  2024-03-11 19:49                   ` epirat07
  2024-03-11 19:11                 ` [FFmpeg-devel] [PATCH] change av_ts_make_time_string precision Marton Balint
  1 sibling, 1 reply; 37+ messages in thread
From: Andreas Rheinhardt @ 2024-03-11 14:26 UTC (permalink / raw)
  To: ffmpeg-devel

Andreas Rheinhardt:
> Allan Cady via ffmpeg-devel:
>> From: "Allan Cady" <allancady@yahoo.com>
>>
>> I propose changing the format to "%.6f", which will
>> give microsecond precision for all timestamps, regardless of
>> offset. Trailing zeros can be trimmed from the fraction, without
>> losing precision. If the length of the fixed-precision formatted
>> timestamp exceeds the length of the allocated buffer
>> (AV_TS_MAX_STRING_SIZE, currently 32, less one for the
>> terminating null), then we can fall back to scientific notation, though
>> this seems almost certain to never occur, because 32 characters would
>> allow a maximum timestamp value of (32 - 1 - 6 - 1) = 24 characters.
>> By my calculation, 10^24 seconds is about six orders of magnitude
>> greater than the age of the universe.
>>
>> The fix proposed here follows the following logic:
>>
>> 1) Try formatting the number of seconds using "%.6f". This will
>> always result in a string with six decimal digits in the fraction,
>> possibly including trailing zeros. (e.g. "897234.73200").
>>
>> 2) Check if that string would overflow the buffer. If it would, then
>> format it using scientific notation ("%.8g").
>>
>> 3) If the original fixed-point format fits, then trim any trailing
>> zeros and decimal point, and return that result.
>>
>> Making this change broke two fate tests, filter-metadata-scdet,
>> and filter-metadata-silencedetect. To correct this, I've modified
>> tests/ref/fate/filter-metadata-scdet and
>> tests/ref/fate/filter-metadata-silencedetect to match the
>> new output.
>>
>> Signed-off-by: Allan Cady <allancady@yahoo.com>
>> ---
>>  libavutil/timestamp.h                        | 53 +++++++++++++++++++-
>>  tests/ref/fate/filter-metadata-scdet         | 12 ++---
>>  tests/ref/fate/filter-metadata-silencedetect |  2 +-
>>  3 files changed, 58 insertions(+), 9 deletions(-)
>>
>> diff --git a/libavutil/timestamp.h b/libavutil/timestamp.h
>> index 2b37781eba..2f04f9bb2b 100644
>> --- a/libavutil/timestamp.h
>> +++ b/libavutil/timestamp.h
>> @@ -25,6 +25,7 @@
>>  #define AVUTIL_TIMESTAMP_H
>>  
>>  #include "avutil.h"
>> +#include <stdbool.h>
>>  
>>  #if defined(__cplusplus) && !defined(__STDC_FORMAT_MACROS) && !defined(PRId64)
>>  #error missing -D__STDC_FORMAT_MACROS / #define __STDC_FORMAT_MACROS
>> @@ -53,6 +54,32 @@ static inline char *av_ts_make_string(char *buf, int64_t ts)
>>   */
>>  #define av_ts2str(ts) av_ts_make_string((char[AV_TS_MAX_STRING_SIZE]){0}, ts)
>>  
>> +/**
>> + * Strip trailing zeros and decimal point from a string. Performed
>> + * in-place on input buffer. For local use only by av_ts_make_time_string.
>> + * 
>> + * e.g.:
>> + * "752.378000" -> "752.378"
>> + *        "4.0" -> "4"
>> + *      "97300" -> "97300"
>> + */
>> +static inline void av_ts_strip_trailing_zeros_and_decimal_point(char *str) {
>> +    if (strchr(str, '.'))
>> +    {
>> +        int i;
>> +        for (i = strlen(str) - 1; i >= 0 && str[i] == '0'; i--) {
>> +            str[i] = '\0';
>> +        }
>> +        
>> +        // Remove decimal point if it's the last character
>> +        if (i >= 0 && str[i] == '.') {
>> +            str[i] = '\0';
>> +        }
>> +
>> +        // String was modified in place; no need for return value.
>> +    }
>> +}
>> +
>>  /**
>>   * Fill the provided buffer with a string containing a timestamp time
>>   * representation.
>> @@ -65,8 +92,30 @@ static inline char *av_ts_make_string(char *buf, int64_t ts)
>>  static inline char *av_ts_make_time_string(char *buf, int64_t ts,
>>                                             const AVRational *tb)
>>  {
>> -    if (ts == AV_NOPTS_VALUE) snprintf(buf, AV_TS_MAX_STRING_SIZE, "NOPTS");
>> -    else                      snprintf(buf, AV_TS_MAX_STRING_SIZE, "%.6g", av_q2d(*tb) * ts);
>> +    if (ts == AV_NOPTS_VALUE)
>> +    {
>> +        snprintf(buf, AV_TS_MAX_STRING_SIZE, "NOPTS");
>> +    }
>> +    else
>> +    {
>> +        const int max_fraction_digits = 6;
>> +
>> +        // Convert 64-bit timestamp to double, using rational timebase
>> +        double seconds = av_q2d(*tb) * ts;
>> +
>> +        int length = snprintf(NULL, 0, "%.*f", max_fraction_digits, seconds);
>> +        if (length <= AV_TS_MAX_STRING_SIZE - 1)
>> +        {
>> +            snprintf(buf, AV_TS_MAX_STRING_SIZE, "%.*f", max_fraction_digits, seconds);
>> +            av_ts_strip_trailing_zeros_and_decimal_point(buf);
>> +        }
>> +        else
>> +        {
>> +            snprintf(buf, AV_TS_MAX_STRING_SIZE, "%.8g", seconds);
>> +        }
>> +
>> +    }
>> +
>>      return buf;
>>  }
>>  
> 
> 1. What makes you believe that all users want the new format and that it
> does not cause undesired behaviour for some (maybe a lot) of them? The
> number of characters written by the earlier code stayed pretty constant
> even when the times became big (in this case, it just printed 8 chars if
> ts>=0), yet your code will really make use of the whole buffer.
> Granted, we could tell our users that they have no right to complain
> about this, given that we always had a "right" to use the full buffer,
> but I consider this a violation of the principle of least surprise. Why
> don't you just change silencedetect or add another function?
> 2. For very small timestamps (< 10^-4), the new code will print a lot of
> useless leading zeros (after the decimal point). In fact, it can be so
> many that the new code has less precision than the old code, despite
> using the fill buffer.
> 2. This is way too much code for an inline function.
> 3. Anyway, your placement of {} on their own lines does not match the
> project coding style.
> 

In addition to this, there is another issue here: Your
av_ts_strip_trailing_zeros_and_decimal_point() presumes that the
"decimal-point character" is always '.', but this can be changed via
setlocale().

- Andreas

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

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

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [FFmpeg-devel] [PATCH] change av_ts_make_time_string precision
  2024-03-11 13:43               ` Andreas Rheinhardt
  2024-03-11 14:26                 ` Andreas Rheinhardt
@ 2024-03-11 19:11                 ` Marton Balint
  2024-03-12  2:57                   ` Allan Cady via ffmpeg-devel
  1 sibling, 1 reply; 37+ messages in thread
From: Marton Balint @ 2024-03-11 19:11 UTC (permalink / raw)
  To: FFmpeg development discussions and patches



On Mon, 11 Mar 2024, Andreas Rheinhardt wrote:

> Allan Cady via ffmpeg-devel:
>> From: "Allan Cady" <allancady@yahoo.com>
>>
>> I propose changing the format to "%.6f", which will
>> give microsecond precision for all timestamps, regardless of
>> offset. Trailing zeros can be trimmed from the fraction, without
>> losing precision. If the length of the fixed-precision formatted
>> timestamp exceeds the length of the allocated buffer
>> (AV_TS_MAX_STRING_SIZE, currently 32, less one for the
>> terminating null), then we can fall back to scientific notation, though
>> this seems almost certain to never occur, because 32 characters would
>> allow a maximum timestamp value of (32 - 1 - 6 - 1) = 24 characters.
>> By my calculation, 10^24 seconds is about six orders of magnitude
>> greater than the age of the universe.
>>
>> The fix proposed here follows the following logic:
>>
>> 1) Try formatting the number of seconds using "%.6f". This will
>> always result in a string with six decimal digits in the fraction,
>> possibly including trailing zeros. (e.g. "897234.73200").
>>
>> 2) Check if that string would overflow the buffer. If it would, then
>> format it using scientific notation ("%.8g").
>>
>> 3) If the original fixed-point format fits, then trim any trailing
>> zeros and decimal point, and return that result.
>>
>> Making this change broke two fate tests, filter-metadata-scdet,
>> and filter-metadata-silencedetect. To correct this, I've modified
>> tests/ref/fate/filter-metadata-scdet and
>> tests/ref/fate/filter-metadata-silencedetect to match the
>> new output.
>>
>> Signed-off-by: Allan Cady <allancady@yahoo.com>
>> ---
>>  libavutil/timestamp.h                        | 53 +++++++++++++++++++-
>>  tests/ref/fate/filter-metadata-scdet         | 12 ++---
>>  tests/ref/fate/filter-metadata-silencedetect |  2 +-
>>  3 files changed, 58 insertions(+), 9 deletions(-)
>>
>> diff --git a/libavutil/timestamp.h b/libavutil/timestamp.h
>> index 2b37781eba..2f04f9bb2b 100644
>> --- a/libavutil/timestamp.h
>> +++ b/libavutil/timestamp.h
>> @@ -25,6 +25,7 @@
>>  #define AVUTIL_TIMESTAMP_H
>>
>>  #include "avutil.h"
>> +#include <stdbool.h>
>>
>>  #if defined(__cplusplus) && !defined(__STDC_FORMAT_MACROS) && !defined(PRId64)
>>  #error missing -D__STDC_FORMAT_MACROS / #define __STDC_FORMAT_MACROS
>> @@ -53,6 +54,32 @@ static inline char *av_ts_make_string(char *buf, int64_t ts)
>>   */
>>  #define av_ts2str(ts) av_ts_make_string((char[AV_TS_MAX_STRING_SIZE]){0}, ts)
>>
>> +/**
>> + * Strip trailing zeros and decimal point from a string. Performed
>> + * in-place on input buffer. For local use only by av_ts_make_time_string.
>> + *
>> + * e.g.:
>> + * "752.378000" -> "752.378"
>> + *        "4.0" -> "4"
>> + *      "97300" -> "97300"
>> + */
>> +static inline void av_ts_strip_trailing_zeros_and_decimal_point(char *str) {
>> +    if (strchr(str, '.'))
>> +    {
>> +        int i;
>> +        for (i = strlen(str) - 1; i >= 0 && str[i] == '0'; i--) {
>> +            str[i] = '\0';
>> +        }
>> +
>> +        // Remove decimal point if it's the last character
>> +        if (i >= 0 && str[i] == '.') {
>> +            str[i] = '\0';
>> +        }
>> +
>> +        // String was modified in place; no need for return value.
>> +    }
>> +}
>> +
>>  /**
>>   * Fill the provided buffer with a string containing a timestamp time
>>   * representation.
>> @@ -65,8 +92,30 @@ static inline char *av_ts_make_string(char *buf, int64_t ts)
>>  static inline char *av_ts_make_time_string(char *buf, int64_t ts,
>>                                             const AVRational *tb)
>>  {
>> -    if (ts == AV_NOPTS_VALUE) snprintf(buf, AV_TS_MAX_STRING_SIZE, "NOPTS");
>> -    else                      snprintf(buf, AV_TS_MAX_STRING_SIZE, "%.6g", av_q2d(*tb) * ts);
>> +    if (ts == AV_NOPTS_VALUE)
>> +    {
>> +        snprintf(buf, AV_TS_MAX_STRING_SIZE, "NOPTS");
>> +    }
>> +    else
>> +    {
>> +        const int max_fraction_digits = 6;
>> +
>> +        // Convert 64-bit timestamp to double, using rational timebase
>> +        double seconds = av_q2d(*tb) * ts;
>> +
>> +        int length = snprintf(NULL, 0, "%.*f", max_fraction_digits, seconds);
>> +        if (length <= AV_TS_MAX_STRING_SIZE - 1)
>> +        {
>> +            snprintf(buf, AV_TS_MAX_STRING_SIZE, "%.*f", max_fraction_digits, seconds);
>> +            av_ts_strip_trailing_zeros_and_decimal_point(buf);
>> +        }
>> +        else
>> +        {
>> +            snprintf(buf, AV_TS_MAX_STRING_SIZE, "%.8g", seconds);
>> +        }
>> +
>> +    }
>> +
>>      return buf;
>>  }
>>
>
> 1. What makes you believe that all users want the new format and that it
> does not cause undesired behaviour for some (maybe a lot) of them? The
> number of characters written by the earlier code stayed pretty constant
> even when the times became big (in this case, it just printed 8 chars if
> ts>=0), yet your code will really make use of the whole buffer.
> Granted, we could tell our users that they have no right to complain
> about this, given that we always had a "right" to use the full buffer,
> but I consider this a violation of the principle of least surprise. Why
> don't you just change silencedetect or add another function?

I suggested to change av_ts_make_time_string() because this 
problem affect all detection filters, not only silencedetect. So fixing 
it in silencedetect only is wrong.

The API did not make any promises about the format, and as long as it 
provides less chars than AV_TS_MAX_STRING_SIZE, and the string is 
parseable string representation of a floating point number, it is not a 
break.

Sure, it changes behaviour, but that is not unheard of if there is a good 
reason, and in this case, I believe there is. And I think it is more 
likely that some code is broken right now because the function 
loses precision or returns scientific notation for relatively small 
timestamps. Actually, it _was_ a suprise for me, so IMHO the element of 
least suprise is that precision will not fade away for reasonably small 
timestamps.

> 2. For very small timestamps (< 10^-4), the new code will print a lot of
> useless leading zeros (after the decimal point). In fact, it can be so
> many that the new code has less precision than the old code, despite
> using the fill buffer.
> 2. This is way too much code for an inline function.
> 3. Anyway, your placement of {} on their own lines does not match the
> project coding style.

I am OK with any implementation which keeps at least millisecond 
precision for timestamps < 10^10. You are right, it should be as simple as 
possible.

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

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

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [FFmpeg-devel] [PATCH] When the silencedetect filter is run against long files, the output timestamps gradually lose precision as the scan proceeds further into the file. This is because the output is formatted (in libavutil/timestamp.h) as "%.6g", which limits the total field length. Eventually, for offsets greater than 100000 seconds (about 28 hours), fractions of a second disappear altogether, and the timestamps are logged as whole seconds.
  2024-03-11 14:26                 ` Andreas Rheinhardt
@ 2024-03-11 19:49                   ` epirat07
  2024-03-11 23:46                     ` [FFmpeg-devel] [libavutil/timestamp.h: Fix loss of precision in timestamps for silencedetect on long files] Allan Cady via ffmpeg-devel
  0 siblings, 1 reply; 37+ messages in thread
From: epirat07 @ 2024-03-11 19:49 UTC (permalink / raw)
  To: FFmpeg development discussions and patches



On 11 Mar 2024, at 15:26, Andreas Rheinhardt wrote:

> Andreas Rheinhardt:
>> Allan Cady via ffmpeg-devel:
>>> From: "Allan Cady" <allancady@yahoo.com>
>>>
>>> I propose changing the format to "%.6f", which will
>>> give microsecond precision for all timestamps, regardless of
>>> offset. Trailing zeros can be trimmed from the fraction, without
>>> losing precision. If the length of the fixed-precision formatted
>>> timestamp exceeds the length of the allocated buffer
>>> (AV_TS_MAX_STRING_SIZE, currently 32, less one for the
>>> terminating null), then we can fall back to scientific notation, though
>>> this seems almost certain to never occur, because 32 characters would
>>> allow a maximum timestamp value of (32 - 1 - 6 - 1) = 24 characters.
>>> By my calculation, 10^24 seconds is about six orders of magnitude
>>> greater than the age of the universe.
>>>
>>> The fix proposed here follows the following logic:
>>>
>>> 1) Try formatting the number of seconds using "%.6f". This will
>>> always result in a string with six decimal digits in the fraction,
>>> possibly including trailing zeros. (e.g. "897234.73200").
>>>
>>> 2) Check if that string would overflow the buffer. If it would, then
>>> format it using scientific notation ("%.8g").
>>>
>>> 3) If the original fixed-point format fits, then trim any trailing
>>> zeros and decimal point, and return that result.
>>>
>>> Making this change broke two fate tests, filter-metadata-scdet,
>>> and filter-metadata-silencedetect. To correct this, I've modified
>>> tests/ref/fate/filter-metadata-scdet and
>>> tests/ref/fate/filter-metadata-silencedetect to match the
>>> new output.
>>>
>>> Signed-off-by: Allan Cady <allancady@yahoo.com>
>>> ---
>>>  libavutil/timestamp.h                        | 53 +++++++++++++++++++-
>>>  tests/ref/fate/filter-metadata-scdet         | 12 ++---
>>>  tests/ref/fate/filter-metadata-silencedetect |  2 +-
>>>  3 files changed, 58 insertions(+), 9 deletions(-)
>>>
>>> diff --git a/libavutil/timestamp.h b/libavutil/timestamp.h
>>> index 2b37781eba..2f04f9bb2b 100644
>>> --- a/libavutil/timestamp.h
>>> +++ b/libavutil/timestamp.h
>>> @@ -25,6 +25,7 @@
>>>  #define AVUTIL_TIMESTAMP_H
>>>
>>>  #include "avutil.h"
>>> +#include <stdbool.h>
>>>
>>>  #if defined(__cplusplus) && !defined(__STDC_FORMAT_MACROS) && !defined(PRId64)
>>>  #error missing -D__STDC_FORMAT_MACROS / #define __STDC_FORMAT_MACROS
>>> @@ -53,6 +54,32 @@ static inline char *av_ts_make_string(char *buf, int64_t ts)
>>>   */
>>>  #define av_ts2str(ts) av_ts_make_string((char[AV_TS_MAX_STRING_SIZE]){0}, ts)
>>>
>>> +/**
>>> + * Strip trailing zeros and decimal point from a string. Performed
>>> + * in-place on input buffer. For local use only by av_ts_make_time_string.
>>> + *
>>> + * e.g.:
>>> + * "752.378000" -> "752.378"
>>> + *        "4.0" -> "4"
>>> + *      "97300" -> "97300"
>>> + */
>>> +static inline void av_ts_strip_trailing_zeros_and_decimal_point(char *str) {
>>> +    if (strchr(str, '.'))
>>> +    {
>>> +        int i;
>>> +        for (i = strlen(str) - 1; i >= 0 && str[i] == '0'; i--) {
>>> +            str[i] = '\0';
>>> +        }
>>> +
>>> +        // Remove decimal point if it's the last character
>>> +        if (i >= 0 && str[i] == '.') {
>>> +            str[i] = '\0';
>>> +        }
>>> +
>>> +        // String was modified in place; no need for return value.
>>> +    }
>>> +}
>>> +
>>>  /**
>>>   * Fill the provided buffer with a string containing a timestamp time
>>>   * representation.
>>> @@ -65,8 +92,30 @@ static inline char *av_ts_make_string(char *buf, int64_t ts)
>>>  static inline char *av_ts_make_time_string(char *buf, int64_t ts,
>>>                                             const AVRational *tb)
>>>  {
>>> -    if (ts == AV_NOPTS_VALUE) snprintf(buf, AV_TS_MAX_STRING_SIZE, "NOPTS");
>>> -    else                      snprintf(buf, AV_TS_MAX_STRING_SIZE, "%.6g", av_q2d(*tb) * ts);
>>> +    if (ts == AV_NOPTS_VALUE)
>>> +    {
>>> +        snprintf(buf, AV_TS_MAX_STRING_SIZE, "NOPTS");
>>> +    }
>>> +    else
>>> +    {
>>> +        const int max_fraction_digits = 6;
>>> +
>>> +        // Convert 64-bit timestamp to double, using rational timebase
>>> +        double seconds = av_q2d(*tb) * ts;
>>> +
>>> +        int length = snprintf(NULL, 0, "%.*f", max_fraction_digits, seconds);
>>> +        if (length <= AV_TS_MAX_STRING_SIZE - 1)
>>> +        {
>>> +            snprintf(buf, AV_TS_MAX_STRING_SIZE, "%.*f", max_fraction_digits, seconds);
>>> +            av_ts_strip_trailing_zeros_and_decimal_point(buf);
>>> +        }
>>> +        else
>>> +        {
>>> +            snprintf(buf, AV_TS_MAX_STRING_SIZE, "%.8g", seconds);
>>> +        }
>>> +
>>> +    }
>>> +
>>>      return buf;
>>>  }
>>>
>>
>> 1. What makes you believe that all users want the new format and that it
>> does not cause undesired behaviour for some (maybe a lot) of them? The
>> number of characters written by the earlier code stayed pretty constant
>> even when the times became big (in this case, it just printed 8 chars if
>> ts>=0), yet your code will really make use of the whole buffer.
>> Granted, we could tell our users that they have no right to complain
>> about this, given that we always had a "right" to use the full buffer,
>> but I consider this a violation of the principle of least surprise. Why
>> don't you just change silencedetect or add another function?
>> 2. For very small timestamps (< 10^-4), the new code will print a lot of
>> useless leading zeros (after the decimal point). In fact, it can be so
>> many that the new code has less precision than the old code, despite
>> using the fill buffer.
>> 2. This is way too much code for an inline function.
>> 3. Anyway, your placement of {} on their own lines does not match the
>> project coding style.
>>
>
> In addition to this, there is another issue here: Your
> av_ts_strip_trailing_zeros_and_decimal_point() presumes that the
> "decimal-point character" is always '.', but this can be changed via
> setlocale().

True, though I would consider this a more general bug. We should be
consistent and not generate files that are locale-dependent and then
not parseable anymore with a different one… That’s just a huge mess.

Also in general FFmpeg is completely broken if you use any locale that
does not use . as decimal separator. (This never shows for most users
currently as most people use FFmpeg CLI which does not respect the
users locale)

>
> - Andreas
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

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

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [FFmpeg-devel] [libavutil/timestamp.h: Fix loss of precision in timestamps for silencedetect on long files]
  2024-03-11 19:49                   ` epirat07
@ 2024-03-11 23:46                     ` Allan Cady via ffmpeg-devel
  0 siblings, 0 replies; 37+ messages in thread
From: Allan Cady via ffmpeg-devel @ 2024-03-11 23:46 UTC (permalink / raw)
  To: FFmpeg development discussions and patches; +Cc: Allan Cady

> On Monday, March 11, 2024 at 12:50:11 PM PDT, <epirat07@gmail.com> wrote:
> On 11 Mar 2024, at 15:26, Andreas Rheinhardt wrote:
>> Andreas Rheinhardt:
>>> Allan Cady via ffmpeg-devel:
>>>> From: "Allan Cady" <allancady@yahoo.com>
>>>>
>>>> I propose changing the format to "%.6f", which will
>>>> give microsecond precision for all timestamps, regardless of
>>>> offset. Trailing zeros can be trimmed from the fraction, without
>>>> losing precision. If the length of the fixed-precision formatted
>>>> timestamp exceeds the length of the allocated buffer
>>>> (AV_TS_MAX_STRING_SIZE, currently 32, less one for the
>>>> terminating null), then we can fall back to scientific notation, though
>>>> this seems almost certain to never occur, because 32 characters would
>>>> allow a maximum timestamp value of (32 - 1 - 6 - 1) = 24 characters.
>>>> By my calculation, 10^24 seconds is about six orders of magnitude
>>>> greater than the age of the universe.
>>>>
>>>> The fix proposed here follows the following logic:
>>>>
>>>> 1) Try formatting the number of seconds using "%.6f". This will
>>>> always result in a string with six decimal digits in the fraction,
>>>> possibly including trailing zeros. (e.g. "897234.73200").
>>>>
>>>> 2) Check if that string would overflow the buffer. If it would, then
>>>> format it using scientific notation ("%.8g").
>>>>
>>>> 3) If the original fixed-point format fits, then trim any trailing
>>>> zeros and decimal point, and return that result.
>>>>
>>>> Making this change broke two fate tests, filter-metadata-scdet,
>>>> and filter-metadata-silencedetect. To correct this, I've modified
>>>> tests/ref/fate/filter-metadata-scdet and
>>>> tests/ref/fate/filter-metadata-silencedetect to match the
>>>> new output.
>>>>
>>>> Signed-off-by: Allan Cady <allancady@yahoo.com>
>>>> ---
>>>>  libavutil/timestamp.h                        | 53 +++++++++++++++++++-
>>>>  tests/ref/fate/filter-metadata-scdet        | 12 ++---
>>>>  tests/ref/fate/filter-metadata-silencedetect |  2 +-
>>>>  3 files changed, 58 insertions(+), 9 deletions(-)
>>>>
>>>> diff --git a/libavutil/timestamp.h b/libavutil/timestamp.h
>>>> index 2b37781eba..2f04f9bb2b 100644
>>>> --- a/libavutil/timestamp.h
>>>> +++ b/libavutil/timestamp.h
>>>> @@ -25,6 +25,7 @@
>>>>  #define AVUTIL_TIMESTAMP_H
>>>>
>>>>  #include "avutil.h"
>>>> +#include <stdbool.h>
>>>>
>>>>  #if defined(__cplusplus) && !defined(__STDC_FORMAT_MACROS) && !defined(PRId64)
>>>>  #error missing -D__STDC_FORMAT_MACROS / #define __STDC_FORMAT_MACROS
>>>> @@ -53,6 +54,32 @@ static inline char *av_ts_make_string(char *buf, int64_t ts)
>>>>  */
>>>>  #define av_ts2str(ts) av_ts_make_string((char[AV_TS_MAX_STRING_SIZE]){0}, ts)
>>>>
>>>> +/**
>>>> + * Strip trailing zeros and decimal point from a string. Performed
>>>> + * in-place on input buffer. For local use only by av_ts_make_time_string.
>>>> + *
>>>> + * e.g.:
>>>> + * "752.378000" -> "752.378"
>>>> + *        "4.0" -> "4"
>>>> + *      "97300" -> "97300"
>>>> + */
>>>> +static inline void av_ts_strip_trailing_zeros_and_decimal_point(char *str) {
>>>> +    if (strchr(str, '.'))
>>>> +    {
>>>> +        int i;
>>>> +        for (i = strlen(str) - 1; i >= 0 && str[i] == '0'; i--) {
>>>> +            str[i] = '\0';
>>>> +        }
>>>> +
>>>> +        // Remove decimal point if it's the last character
>>>> +        if (i >= 0 && str[i] == '.') {
>>>> +            str[i] = '\0';
>>>> +        }
>>>> +
>>>> +        // String was modified in place; no need for return value.
>>>> +    }
>>>> +}
>>>> +
>>>>  /**
>>>>  * Fill the provided buffer with a string containing a timestamp time
>>>>  * representation.
>>>> @@ -65,8 +92,30 @@ static inline char *av_ts_make_string(char *buf, int64_t ts)
>>>>  static inline char *av_ts_make_time_string(char *buf, int64_t ts,
>>>>                                            const AVRational *tb)
>>>>  {
>>>> -    if (ts == AV_NOPTS_VALUE) snprintf(buf, AV_TS_MAX_STRING_SIZE, "NOPTS");
>>>> -    else                      snprintf(buf, AV_TS_MAX_STRING_SIZE, "%.6g", av_q2d(*tb) * ts);
>>>> +    if (ts == AV_NOPTS_VALUE)
>>>> +    {
>>>> +        snprintf(buf, AV_TS_MAX_STRING_SIZE, "NOPTS");
>>>> +    }
>>>> +    else
>>>> +    {
>>>> +        const int max_fraction_digits = 6;
>>>> +
>>>> +        // Convert 64-bit timestamp to double, using rational timebase
>>>> +        double seconds = av_q2d(*tb) * ts;
>>>> +
>>>> +        int length = snprintf(NULL, 0, "%.*f", max_fraction_digits, seconds);
>>>> +        if (length <= AV_TS_MAX_STRING_SIZE - 1)
>>>> +        {
>>>> +            snprintf(buf, AV_TS_MAX_STRING_SIZE, "%.*f", max_fraction_digits, seconds);
>>>> +            av_ts_strip_trailing_zeros_and_decimal_point(buf);
>>>> +        }
>>>> +        else
>>>> +        {
>>>> +            snprintf(buf, AV_TS_MAX_STRING_SIZE, "%.8g", seconds);
>>>> +        }
>>>> +
>>>> +    }
>>>> +
>>>>      return buf;
>>>>  }
>>>>
>>>




>>> 1. What makes you believe that all users want the new format and that it
>>> does not cause undesired behaviour for some (maybe a lot) of them?


I definitely do not know what other users would want. I would think
maybe some would like the change, others wouldn't, and most would
never know.


>>> The
>>> number of characters written by the earlier code stayed pretty constant
>>> even when the times became big (in this case, it just printed 8 chars if
>>> ts>=0), yet your code will really make use of the whole buffer.


It's true that my change will increase the potential length of
the output beyond 8 significant digits.


The issue I was having that brought this up was, I have some very long
audio files (up to 50 hours long), which I was wanting to split
into smaller pieces. I wrote some scripts that use silencedetect to get
the locations of breaks and then split the files at the breaks, but I
discovered that for segments near the end of the file, silencedetect was
returning whole-number timestamps, which was causing undesirable 
results for me. Thinking functionally, it seems like timestamps further
out in a file ought to have the same precision as those near the
beginning. So this seems to me like a minor oversight in the original
design, that might warrant fixing.


>>> Granted, we could tell our users that they have no right to complain
>>> about this, given that we always had a "right" to use the full buffer,
>>> but I consider this a violation of the principle of least surprise. 


I definitely agree with you there.


>>> Why don't you just change silencedetect or add another function?


I actually started out taking that approach in my submission a few weeks
ago. Marton Balint suggested (in a message on 20 Feb) that we make the
change in av_ts_make_time_string, so I did that for this submission.


I'm open to whatever approach you all consider is best.


>>> 2. For very small timestamps (< 10^-4), the new code will print a lot of
>>> useless leading zeros (after the decimal point). In fact, it can be so
>>> many that the new code has less precision than the old code, despite
>>> using the fill buffer.


I don't understand. Leading zeros after the decimal point are far from
useless -- they are part of the value. Maybe what you're saying is that
six digits is more precision than necessary? That may be so. I could
personally do fine with just two digits (hundredths), as long as it's
consistent through the length of the file. 


>>> 2. This is way too much code for an inline function.


No disagreement there.


>>> 3. Anyway, your placement of {} on their own lines does not match the
>>> project coding style.


I'm happy to conform with project coding style.


>> In addition to this, there is another issue here: Your
>> av_ts_strip_trailing_zeros_and_decimal_point() presumes that the
>> "decimal-point character" is always '.', but this can be changed via
>> setlocale().


Excellent point, which I hadn't considered. I have no experience with
how locale is handled in C. I would welcome advice on the best way to
handle this.


> True, though I would consider this a more general bug. We should be
> consistent and not generate files that are locale-dependent and then
> not parseable anymore with a different one… That’s just a huge mess.
> 
> Also in general FFmpeg is completely broken if you use any locale that
> does not use . as decimal separator. (This never shows for most users
> currently as most people use FFmpeg CLI which does not respect the
> users locale)


I'll leave that conversation to the experts here.


Thanks for giving my code a look. 
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

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

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [FFmpeg-devel] [PATCH] change av_ts_make_time_string precision
  2024-03-11 19:11                 ` [FFmpeg-devel] [PATCH] change av_ts_make_time_string precision Marton Balint
@ 2024-03-12  2:57                   ` Allan Cady via ffmpeg-devel
  2024-03-12 21:24                     ` Marton Balint
  0 siblings, 1 reply; 37+ messages in thread
From: Allan Cady via ffmpeg-devel @ 2024-03-12  2:57 UTC (permalink / raw)
  To: FFmpeg development discussions and patches; +Cc: Allan Cady

On Monday, March 11, 2024 at 12:11:45 PM PDT, Marton Balint <cus@passwd.hu> wrote: 
> On Mon, 11 Mar 2024, Andreas Rheinhardt wrote:
> Allan Cady via ffmpeg-devel:
>> From: "Allan Cady" <allancady@yahoo.com>
>>
>> I propose changing the format to "%.6f", which will
>> give microsecond precision for all timestamps, regardless of
>> offset. Trailing zeros can be trimmed from the fraction, without
>> losing precision. If the length of the fixed-precision formatted
>> timestamp exceeds the length of the allocated buffer
>> (AV_TS_MAX_STRING_SIZE, currently 32, less one for the
>> terminating null), then we can fall back to scientific notation, though
>> this seems almost certain to never occur, because 32 characters would
>> allow a maximum timestamp value of (32 - 1 - 6 - 1) = 24 characters.
>> By my calculation, 10^24 seconds is about six orders of magnitude
>> greater than the age of the universe.
>>
>> The fix proposed here follows the following logic:
>>
>> 1) Try formatting the number of seconds using "%.6f". This will
>> always result in a string with six decimal digits in the fraction,
>> possibly including trailing zeros. (e.g. "897234.73200").
>>
>> 2) Check if that string would overflow the buffer. If it would, then
>> format it using scientific notation ("%.8g").
>>
>> 3) If the original fixed-point format fits, then trim any trailing
>> zeros and decimal point, and return that result.
>>
>> Making this change broke two fate tests, filter-metadata-scdet,
>> and filter-metadata-silencedetect. To correct this, I've modified
>> tests/ref/fate/filter-metadata-scdet and
>> tests/ref/fate/filter-metadata-silencedetect to match the
>> new output.
>>
>> Signed-off-by: Allan Cady <allancady@yahoo.com>
>> ---
>>  libavutil/timestamp.h                        | 53 +++++++++++++++++++-
>>  tests/ref/fate/filter-metadata-scdet        | 12 ++---
>>  tests/ref/fate/filter-metadata-silencedetect |  2 +-
>>  3 files changed, 58 insertions(+), 9 deletions(-)
>>
>> diff --git a/libavutil/timestamp.h b/libavutil/timestamp.h
>> index 2b37781eba..2f04f9bb2b 100644
>> --- a/libavutil/timestamp.h
>> +++ b/libavutil/timestamp.h
>> @@ -25,6 +25,7 @@
>>  #define AVUTIL_TIMESTAMP_H
>>
>>  #include "avutil.h"
>> +#include <stdbool.h>
>>
>>  #if defined(__cplusplus) && !defined(__STDC_FORMAT_MACROS) && !defined(PRId64)
>>  #error missing -D__STDC_FORMAT_MACROS / #define __STDC_FORMAT_MACROS
>> @@ -53,6 +54,32 @@ static inline char *av_ts_make_string(char *buf, int64_t ts)
>>  */
>>  #define av_ts2str(ts) av_ts_make_string((char[AV_TS_MAX_STRING_SIZE]){0}, ts)
>>
>> +/**
>> + * Strip trailing zeros and decimal point from a string. Performed
>> + * in-place on input buffer. For local use only by av_ts_make_time_string.
>> + *
>> + * e.g.:
>> + * "752.378000" -> "752.378"
>> + *        "4.0" -> "4"
>> + *      "97300" -> "97300"
>> + */
>> +static inline void av_ts_strip_trailing_zeros_and_decimal_point(char *str) {
>> +    if (strchr(str, '.'))
>> +    {
>> +        int i;
>> +        for (i = strlen(str) - 1; i >= 0 && str[i] == '0'; i--) {
>> +            str[i] = '\0';
>> +        }
>> +
>> +        // Remove decimal point if it's the last character
>> +        if (i >= 0 && str[i] == '.') {
>> +            str[i] = '\0';
>> +        }
>> +
>> +        // String was modified in place; no need for return value.
>> +    }
>> +}
>> +
>>  /**
>>  * Fill the provided buffer with a string containing a timestamp time
>>  * representation.
>> @@ -65,8 +92,30 @@ static inline char *av_ts_make_string(char *buf, int64_t ts)
>>  static inline char *av_ts_make_time_string(char *buf, int64_t ts,
>>                                            const AVRational *tb)
>>  {
>> -    if (ts == AV_NOPTS_VALUE) snprintf(buf, AV_TS_MAX_STRING_SIZE, "NOPTS");
>> -    else                      snprintf(buf, AV_TS_MAX_STRING_SIZE, "%.6g", av_q2d(*tb) * ts);
>> +    if (ts == AV_NOPTS_VALUE)
>> +    {
>> +        snprintf(buf, AV_TS_MAX_STRING_SIZE, "NOPTS");
>> +    }
>> +    else
>> +    {
>> +        const int max_fraction_digits = 6;
>> +
>> +        // Convert 64-bit timestamp to double, using rational timebase
>> +        double seconds = av_q2d(*tb) * ts;
>> +
>> +        int length = snprintf(NULL, 0, "%.*f", max_fraction_digits, seconds);
>> +        if (length <= AV_TS_MAX_STRING_SIZE - 1)
>> +        {
>> +            snprintf(buf, AV_TS_MAX_STRING_SIZE, "%.*f", max_fraction_digits, seconds);
>> +            av_ts_strip_trailing_zeros_and_decimal_point(buf);
>> +        }
>> +        else
>> +        {
>> +            snprintf(buf, AV_TS_MAX_STRING_SIZE, "%.8g", seconds);
>> +        }
>> +
>> +    }
>> +
>>      return buf;
>>  }
>>
>
> 1. What makes you believe that all users want the new format and that it
> does not cause undesired behaviour for some (maybe a lot) of them? The
> number of characters written by the earlier code stayed pretty constant
> even when the times became big (in this case, it just printed 8 chars if
> ts>=0), yet your code will really make use of the whole buffer.
> Granted, we could tell our users that they have no right to complain
> about this, given that we always had a "right" to use the full buffer,
> but I consider this a violation of the principle of least surprise. Why
> don't you just change silencedetect or add another function?
>
> I suggested to change av_ts_make_time_string() because this
> problem affect all detection filters, not only silencedetect. So fixing
> it in silencedetect only is wrong.
>
> The API did not make any promises about the format, and as long as it
> provides less chars than AV_TS_MAX_STRING_SIZE, and the string is
> parseable string representation of a floating point number, it is not a
> break.
>
> Sure, it changes behaviour, but that is not unheard of if there is a good
> reason, and in this case, I believe there is. And I think it is more
> likely that some code is broken right now because the function
> loses precision or returns scientific notation for relatively small
> timestamps. Actually, it _was_ a suprise for me, so IMHO the element of
> least suprise is that precision will not fade away for reasonably small
> timestamps.
>
> 2. For very small timestamps (< 10^-4), the new code will print a lot of
> useless leading zeros (after the decimal point). In fact, it can be so
> many that the new code has less precision than the old code, despite
> using the fill buffer.
> 2. This is way too much code for an inline function.
> 3. Anyway, your placement of {} on their own lines does not match the
> project coding style.
>
> I am OK with any implementation which keeps at least millisecond
> precision for timestamps < 10^10. You are right, it should be as simple as
> possible.
>

Milliseconds would be fine with me.

Marton, do you have any other comments on my implementation? I have
from Andreas that I should remove the inlines, and move the curly
braces to match coding style. I also plan on removing the
#include <stdbool.h>, which I added at some point but is no longer
needed. And I would be happy to change from %.6f to %.3f.

If that sounds good, I'll submit another patch sometime tomorrow.

The only other thing I had thought of is to wonder if I should add some
additional testing for the new formatting. I did a fair amount of testing
on my own, but it would probably be good to have at least some of that
in FATE. I had thought about maybe generating an audio file with just
a tone and several silence intervals.
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

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

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [FFmpeg-devel] [PATCH] change av_ts_make_time_string precision
  2024-03-12  2:57                   ` Allan Cady via ffmpeg-devel
@ 2024-03-12 21:24                     ` Marton Balint
  2024-03-13  6:03                       ` Allan Cady via ffmpeg-devel
  2024-03-13  6:09                       ` Allan Cady via ffmpeg-devel
  0 siblings, 2 replies; 37+ messages in thread
From: Marton Balint @ 2024-03-12 21:24 UTC (permalink / raw)
  To: Allan Cady via ffmpeg-devel



On Tue, 12 Mar 2024, Allan Cady via ffmpeg-devel wrote:

> On Monday, March 11, 2024 at 12:11:45 PM PDT, Marton Balint <cus@passwd.hu> wrote: 
>> On Mon, 11 Mar 2024, Andreas Rheinhardt wrote:
>> Allan Cady via ffmpeg-devel:
>>> From: "Allan Cady" <allancady@yahoo.com>
>>>
>>> I propose changing the format to "%.6f", which will
>>> give microsecond precision for all timestamps, regardless of
>>> offset. Trailing zeros can be trimmed from the fraction, without
>>> losing precision. If the length of the fixed-precision formatted
>>> timestamp exceeds the length of the allocated buffer
>>> (AV_TS_MAX_STRING_SIZE, currently 32, less one for the
>>> terminating null), then we can fall back to scientific notation, though
>>> this seems almost certain to never occur, because 32 characters would
>>> allow a maximum timestamp value of (32 - 1 - 6 - 1) = 24 characters.
>>> By my calculation, 10^24 seconds is about six orders of magnitude
>>> greater than the age of the universe.
>>>
>>> The fix proposed here follows the following logic:
>>>
>>> 1) Try formatting the number of seconds using "%.6f". This will
>>> always result in a string with six decimal digits in the fraction,
>>> possibly including trailing zeros. (e.g. "897234.73200").
>>>
>>> 2) Check if that string would overflow the buffer. If it would, then
>>> format it using scientific notation ("%.8g").
>>>
>>> 3) If the original fixed-point format fits, then trim any trailing
>>> zeros and decimal point, and return that result.
>>>
>>> Making this change broke two fate tests, filter-metadata-scdet,
>>> and filter-metadata-silencedetect. To correct this, I've modified
>>> tests/ref/fate/filter-metadata-scdet and
>>> tests/ref/fate/filter-metadata-silencedetect to match the
>>> new output.
>>>
>>> Signed-off-by: Allan Cady <allancady@yahoo.com>
>>> ---
>>>   libavutil/timestamp.h                        | 53 +++++++++++++++++++-
>>>   tests/ref/fate/filter-metadata-scdet        | 12 ++---
>>>   tests/ref/fate/filter-metadata-silencedetect |  2 +-
>>>   3 files changed, 58 insertions(+), 9 deletions(-)
>>>
>>> diff --git a/libavutil/timestamp.h b/libavutil/timestamp.h
>>> index 2b37781eba..2f04f9bb2b 100644
>>> --- a/libavutil/timestamp.h
>>> +++ b/libavutil/timestamp.h
>>> @@ -25,6 +25,7 @@
>>>   #define AVUTIL_TIMESTAMP_H
>>>
>>>   #include "avutil.h"
>>> +#include <stdbool.h>
>>>
>>>   #if defined(__cplusplus) && !defined(__STDC_FORMAT_MACROS) && !defined(PRId64)
>>>   #error missing -D__STDC_FORMAT_MACROS / #define __STDC_FORMAT_MACROS
>>> @@ -53,6 +54,32 @@ static inline char *av_ts_make_string(char *buf, int64_t ts)
>>>   */
>>>   #define av_ts2str(ts) av_ts_make_string((char[AV_TS_MAX_STRING_SIZE]){0}, ts)
>>>
>>> +/**
>>> + * Strip trailing zeros and decimal point from a string. Performed
>>> + * in-place on input buffer. For local use only by av_ts_make_time_string.
>>> + *
>>> + * e.g.:
>>> + * "752.378000" -> "752.378"
>>> + *        "4.0" -> "4"
>>> + *      "97300" -> "97300"
>>> + */
>>> +static inline void av_ts_strip_trailing_zeros_and_decimal_point(char *str) {
>>> +    if (strchr(str, '.'))
>>> +    {
>>> +        int i;
>>> +        for (i = strlen(str) - 1; i >= 0 && str[i] == '0'; i--) {
>>> +            str[i] = '\0';
>>> +        }
>>> +
>>> +        // Remove decimal point if it's the last character
>>> +        if (i >= 0 && str[i] == '.') {
>>> +            str[i] = '\0';
>>> +        }
>>> +
>>> +        // String was modified in place; no need for return value.
>>> +    }
>>> +}
>>> +
>>>   /**
>>>   * Fill the provided buffer with a string containing a timestamp time
>>>   * representation.
>>> @@ -65,8 +92,30 @@ static inline char *av_ts_make_string(char *buf, int64_t ts)
>>>   static inline char *av_ts_make_time_string(char *buf, int64_t ts,
>>>                                             const AVRational *tb)
>>>   {
>>> -    if (ts == AV_NOPTS_VALUE) snprintf(buf, AV_TS_MAX_STRING_SIZE, "NOPTS");
>>> -    else                      snprintf(buf, AV_TS_MAX_STRING_SIZE, "%.6g", av_q2d(*tb) * ts);
>>> +    if (ts == AV_NOPTS_VALUE)
>>> +    {
>>> +        snprintf(buf, AV_TS_MAX_STRING_SIZE, "NOPTS");
>>> +    }
>>> +    else
>>> +    {
>>> +        const int max_fraction_digits = 6;
>>> +
>>> +        // Convert 64-bit timestamp to double, using rational timebase
>>> +        double seconds = av_q2d(*tb) * ts;
>>> +
>>> +        int length = snprintf(NULL, 0, "%.*f", max_fraction_digits, seconds);
>>> +        if (length <= AV_TS_MAX_STRING_SIZE - 1)
>>> +        {
>>> +            snprintf(buf, AV_TS_MAX_STRING_SIZE, "%.*f", max_fraction_digits, seconds);
>>> +            av_ts_strip_trailing_zeros_and_decimal_point(buf);
>>> +        }
>>> +        else
>>> +        {
>>> +            snprintf(buf, AV_TS_MAX_STRING_SIZE, "%.8g", seconds);
>>> +        }
>>> +
>>> +    }
>>> +
>>>       return buf;
>>>   }
>>>
>>
>> 1. What makes you believe that all users want the new format and that it
>> does not cause undesired behaviour for some (maybe a lot) of them? The
>> number of characters written by the earlier code stayed pretty constant
>> even when the times became big (in this case, it just printed 8 chars if
>> ts>=0), yet your code will really make use of the whole buffer.
>> Granted, we could tell our users that they have no right to complain
>> about this, given that we always had a "right" to use the full buffer,
>> but I consider this a violation of the principle of least surprise. Why
>> don't you just change silencedetect or add another function?
>>
>> I suggested to change av_ts_make_time_string() because this
>> problem affect all detection filters, not only silencedetect. So fixing
>> it in silencedetect only is wrong.
>>
>> The API did not make any promises about the format, and as long as it
>> provides less chars than AV_TS_MAX_STRING_SIZE, and the string is
>> parseable string representation of a floating point number, it is not a
>> break.
>>
>> Sure, it changes behaviour, but that is not unheard of if there is a good
>> reason, and in this case, I believe there is. And I think it is more
>> likely that some code is broken right now because the function
>> loses precision or returns scientific notation for relatively small
>> timestamps. Actually, it _was_ a suprise for me, so IMHO the element of
>> least suprise is that precision will not fade away for reasonably small
>> timestamps.
>>
>> 2. For very small timestamps (< 10^-4), the new code will print a lot of
>> useless leading zeros (after the decimal point). In fact, it can be so
>> many that the new code has less precision than the old code, despite
>> using the fill buffer.
>> 2. This is way too much code for an inline function.
>> 3. Anyway, your placement of {} on their own lines does not match the
>> project coding style.
>>
>> I am OK with any implementation which keeps at least millisecond
>> precision for timestamps < 10^10. You are right, it should be as simple as
>> possible.
>>
>
> Milliseconds would be fine with me.
>
> Marton, do you have any other comments on my implementation? I have
> from Andreas that I should remove the inlines, and move the curly
> braces to match coding style. I also plan on removing the
> #include <stdbool.h>, which I added at some point but is no longer
> needed. And I would be happy to change from %.6f to %.3f.

TBH I'd rather keep the precision as is. If you want to convert the 
function to non-inlined, then you will have to move the implementation 
from the header to an existing or new C file and unconditionally compile 
it to avutil... Maybe we should give it another go keeping it inlineable 
by simplifying it a little.

>
> If that sounds good, I'll submit another patch sometime tomorrow.
>
> The only other thing I had thought of is to wonder if I should add some
> additional testing for the new formatting. I did a fair amount of testing
> on my own, but it would probably be good to have at least some of that
> in FATE. I had thought about maybe generating an audio file with just
> a tone and several silence intervals.

One thing to notice is that you will not need to use the scientific 
representation at all, because maximum value this function prints is the 
product of an INT32 and an INT64, and that is 96 bit, which is at most 29 
chars. Adding the optional sign and the decimal point, that is still only 
31. So we can be sure that by using %.6f, the first character of 
the decimal point is going to be present in the output. Which is great, 
because that means we only have to
- do a single snprintf("%.6f")
- calculate last char position by subtracting 1 from the minimum of 
AV_TS_MAX_STRING_SIZE-1 and the result of the snprintf() call.
- decrement string length while last char is '0' to remove trailing 0s
- decrement string length while last char is non-digit to remove decimal 
point (which can be a multiple chars for some locales).
- update last+1 char to \0.

Ot is it still too complex to keep it inline?

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

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

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [FFmpeg-devel] [PATCH] change av_ts_make_time_string precision
  2024-03-12 21:24                     ` Marton Balint
@ 2024-03-13  6:03                       ` Allan Cady via ffmpeg-devel
  2024-03-17 23:40                         ` [FFmpeg-devel] [PATCH] avutil/timestamp: keep microsecond precision in av_ts_make_time_string Marton Balint
  2024-03-17 23:43                         ` [FFmpeg-devel] [PATCH] change av_ts_make_time_string precision Marton Balint
  2024-03-13  6:09                       ` Allan Cady via ffmpeg-devel
  1 sibling, 2 replies; 37+ messages in thread
From: Allan Cady via ffmpeg-devel @ 2024-03-13  6:03 UTC (permalink / raw)
  To: FFmpeg development discussions and patches; +Cc: Allan Cady

On Tuesday, March 12, 2024 at 02:24:47 PM PDT, Marton Balint <cus@passwd.hu> wrote: 
> On Tue, 12 Mar 2024, Allan Cady via ffmpeg-devel wrote:
>> On Monday, March 11, 2024 at 12:11:45 PM PDT, Marton Balint <cus@passwd.hu> wrote:
>>> On Mon, 11 Mar 2024, Andreas Rheinhardt wrote:
>>> Allan Cady via ffmpeg-devel:
>>>> From: "Allan Cady" <allancady@yahoo.com>
>>>>
>>>> I propose changing the format to "%.6f", which will
>>>> give microsecond precision for all timestamps, regardless of
>>>> offset. Trailing zeros can be trimmed from the fraction, without
>>>> losing precision. If the length of the fixed-precision formatted
>>>> timestamp exceeds the length of the allocated buffer
>>>> (AV_TS_MAX_STRING_SIZE, currently 32, less one for the
>>>> terminating null), then we can fall back to scientific notation, though
>>>> this seems almost certain to never occur, because 32 characters would
>>>> allow a maximum timestamp value of (32 - 1 - 6 - 1) = 24 characters.
>>>> By my calculation, 10^24 seconds is about six orders of magnitude
>>>> greater than the age of the universe.
>>>>
>>>> The fix proposed here follows the following logic:
>>>>
>>>> 1) Try formatting the number of seconds using "%.6f". This will
>>>> always result in a string with six decimal digits in the fraction,
>>>> possibly including trailing zeros. (e.g. "897234.73200").
>>>>
>>>> 2) Check if that string would overflow the buffer. If it would, then
>>>> format it using scientific notation ("%.8g").
>>>>
>>>> 3) If the original fixed-point format fits, then trim any trailing
>>>> zeros and decimal point, and return that result.
>>>>
>>>> Making this change broke two fate tests, filter-metadata-scdet,
>>>> and filter-metadata-silencedetect. To correct this, I've modified
>>>> tests/ref/fate/filter-metadata-scdet and
>>>> tests/ref/fate/filter-metadata-silencedetect to match the
>>>> new output.
>>>>
>>>> Signed-off-by: Allan Cady <allancady@yahoo.com>
>>>> ---
>>>>   libavutil/timestamp.h                        | 53 +++++++++++++++++++-
>>>>   tests/ref/fate/filter-metadata-scdet        | 12 ++---
>>>>   tests/ref/fate/filter-metadata-silencedetect |  2 +-
>>>>   3 files changed, 58 insertions(+), 9 deletions(-)
>>>>
>>>> diff --git a/libavutil/timestamp.h b/libavutil/timestamp.h
>>>> index 2b37781eba..2f04f9bb2b 100644
>>>> --- a/libavutil/timestamp.h
>>>> +++ b/libavutil/timestamp.h
>>>> @@ -25,6 +25,7 @@
>>>>   #define AVUTIL_TIMESTAMP_H
>>>>
>>>>   #include "avutil.h"
>>>> +#include <stdbool.h>
>>>>
>>>>   #if defined(__cplusplus) && !defined(__STDC_FORMAT_MACROS) && !defined(PRId64)
>>>>   #error missing -D__STDC_FORMAT_MACROS / #define __STDC_FORMAT_MACROS
>>>> @@ -53,6 +54,32 @@ static inline char *av_ts_make_string(char *buf, int64_t ts)
>>>>   */
>>>>   #define av_ts2str(ts) av_ts_make_string((char[AV_TS_MAX_STRING_SIZE]){0}, ts)
>>>>
>>>> +/**
>>>> + * Strip trailing zeros and decimal point from a string. Performed
>>>> + * in-place on input buffer. For local use only by av_ts_make_time_string.
>>>> + *
>>>> + * e.g.:
>>>> + * "752.378000" -> "752.378"
>>>> + *        "4.0" -> "4"
>>>> + *      "97300" -> "97300"
>>>> + */
>>>> +static inline void av_ts_strip_trailing_zeros_and_decimal_point(char *str) {
>>>> +    if (strchr(str, '.'))
>>>> +    {
>>>> +        int i;
>>>> +        for (i = strlen(str) - 1; i >= 0 && str[i] == '0'; i--) {
>>>> +            str[i] = '\0';
>>>> +        }
>>>> +
>>>> +        // Remove decimal point if it's the last character
>>>> +        if (i >= 0 && str[i] == '.') {
>>>> +            str[i] = '\0';
>>>> +        }
>>>> +
>>>> +        // String was modified in place; no need for return value.
>>>> +    }
>>>> +}
>>>> +
>>>>   /**
>>>>   * Fill the provided buffer with a string containing a timestamp time
>>>>   * representation.
>>>> @@ -65,8 +92,30 @@ static inline char *av_ts_make_string(char *buf, int64_t ts)
>>>>   static inline char *av_ts_make_time_string(char *buf, int64_t ts,
>>>>                                             const AVRational *tb)
>>>>   {
>>>> -    if (ts == AV_NOPTS_VALUE) snprintf(buf, AV_TS_MAX_STRING_SIZE, "NOPTS");
>>>> -    else                      snprintf(buf, AV_TS_MAX_STRING_SIZE, "%.6g", av_q2d(*tb) * ts);
>>>> +    if (ts == AV_NOPTS_VALUE)
>>>> +    {
>>>> +        snprintf(buf, AV_TS_MAX_STRING_SIZE, "NOPTS");
>>>> +    }
>>>> +    else
>>>> +    {
>>>> +        const int max_fraction_digits = 6;
>>>> +
>>>> +        // Convert 64-bit timestamp to double, using rational timebase
>>>> +        double seconds = av_q2d(*tb) * ts;
>>>> +
>>>> +        int length = snprintf(NULL, 0, "%.*f", max_fraction_digits, seconds);
>>>> +        if (length <= AV_TS_MAX_STRING_SIZE - 1)
>>>> +        {
>>>> +            snprintf(buf, AV_TS_MAX_STRING_SIZE, "%.*f", max_fraction_digits, seconds);
>>>> +            av_ts_strip_trailing_zeros_and_decimal_point(buf);
>>>> +        }
>>>> +        else
>>>> +        {
>>>> +            snprintf(buf, AV_TS_MAX_STRING_SIZE, "%.8g", seconds);
>>>> +        }
>>>> +
>>>> +    }
>>>> +
>>>>       return buf;
>>>>   }
>>>>
>>>
>>> 1. What makes you believe that all users want the new format and that it
>>> does not cause undesired behaviour for some (maybe a lot) of them? The
>>> number of characters written by the earlier code stayed pretty constant
>>> even when the times became big (in this case, it just printed 8 chars if
>>> ts>=0), yet your code will really make use of the whole buffer.
>>> Granted, we could tell our users that they have no right to complain
>>> about this, given that we always had a "right" to use the full buffer,
>>> but I consider this a violation of the principle of least surprise. Why
>>> don't you just change silencedetect or add another function?
>>>
>>> I suggested to change av_ts_make_time_string() because this
>>> problem affect all detection filters, not only silencedetect. So fixing
>>> it in silencedetect only is wrong.
>>>
>>> The API did not make any promises about the format, and as long as it
>>> provides less chars than AV_TS_MAX_STRING_SIZE, and the string is
>>> parseable string representation of a floating point number, it is not a
>>> break.
>>>
>>> Sure, it changes behaviour, but that is not unheard of if there is a good
>>> reason, and in this case, I believe there is. And I think it is more
>>> likely that some code is broken right now because the function
>>> loses precision or returns scientific notation for relatively small
>>> timestamps. Actually, it _was_ a suprise for me, so IMHO the element of
>>> least suprise is that precision will not fade away for reasonably small
>>> timestamps.
>>>
>>> 2. For very small timestamps (< 10^-4), the new code will print a lot of
>>> useless leading zeros (after the decimal point). In fact, it can be so
>>> many that the new code has less precision than the old code, despite
>>> using the fill buffer.
>>> 2. This is way too much code for an inline function.
>>> 3. Anyway, your placement of {} on their own lines does not match the
>>> project coding style.
>>>
>>> I am OK with any implementation which keeps at least millisecond
>>> precision for timestamps < 10^10. You are right, it should be as simple as
>>> possible.
>>>
>>
>> Milliseconds would be fine with me.
>>
>> Marton, do you have any other comments on my implementation? I have
>> from Andreas that I should remove the inlines, and move the curly
>> braces to match coding style. I also plan on removing the
>> #include <stdbool.h>, which I added at some point but is no longer
>> needed. And I would be happy to change from %.6f to %.3f.


> TBH I'd rather keep the precision as is. If you want to convert the
> function to non-inlined, then you will have to move the implementation
> from the header to an existing or new C file and unconditionally compile
> it to avutil... Maybe we should give it another go keeping it inlineable
> by simplifying it a little.


It's been years (decades) since I had to think about inlines -- and I 
don't remember there being any fixed criteria for them. Is it a
language rule that subroutines in header files have to be inlined? Or
just a policy? I really don't know the language. 


Anyway, I'm all for simple. I was fairly uncomfortable with that much 
complexity, especially in a header file. I don't really like having
that extra sub to strip trailing zeros, but I couldn't come up with a
better way to do it.


>>
>> If that sounds good, I'll submit another patch sometime tomorrow.
>>
>> The only other thing I had thought of is to wonder if I should add some
>> additional testing for the new formatting. I did a fair amount of testing
>> on my own, but it would probably be good to have at least some of that
>> in FATE. I had thought about maybe generating an audio file with just
>> a tone and several silence intervals.
> 


> One thing to notice is that you will not need to use the scientific
> representation at all, because maximum value this function prints is the
> product of an INT32 and an INT64, and that is 96 bit, which is at most 29
> chars. Adding the optional sign and the decimal point, that is still only
> 31. So we can be sure that by using %.6f, the first character of
> the decimal point is going to be present in the output.


I had done some similar calculation and came to a similar conclusion. 


Which is great,
> because that means we only have to
> - do a single snprintf("%.6f")
> - calculate last char position by subtracting 1 from the minimum of
> AV_TS_MAX_STRING_SIZE-1 and the result of the snprintf() call.
> - decrement string length while last char is '0' to remove trailing 0s
> - decrement string length while last char is non-digit to remove decimal
> point (which can be a multiple chars for some locales).
> - update last+1 char to \0.
> Ot is it still too complex to keep it inline?


I'll give your suggestion a spin tomorrow. Thanks.

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

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

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [FFmpeg-devel] [PATCH] change av_ts_make_time_string precision
  2024-03-12 21:24                     ` Marton Balint
  2024-03-13  6:03                       ` Allan Cady via ffmpeg-devel
@ 2024-03-13  6:09                       ` Allan Cady via ffmpeg-devel
  1 sibling, 0 replies; 37+ messages in thread
From: Allan Cady via ffmpeg-devel @ 2024-03-13  6:09 UTC (permalink / raw)
  To: FFmpeg development discussions and patches; +Cc: Allan Cady

I've been meaning to ask -- what version of C are we using? I know it's at least 99,
because of the compound literal (had to look that up).
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

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

^ permalink raw reply	[flat|nested] 37+ messages in thread

* [FFmpeg-devel] [PATCH] avutil/timestamp: keep microsecond precision in av_ts_make_time_string
  2024-03-13  6:03                       ` Allan Cady via ffmpeg-devel
@ 2024-03-17 23:40                         ` Marton Balint
  2024-03-19 21:14                           ` Marton Balint
  2024-03-19 23:38                           ` Andreas Rheinhardt
  2024-03-17 23:43                         ` [FFmpeg-devel] [PATCH] change av_ts_make_time_string precision Marton Balint
  1 sibling, 2 replies; 37+ messages in thread
From: Marton Balint @ 2024-03-17 23:40 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Marton Balint

av_ts_make_time_string() used "%.6g" format in the past, but this format was
losing precision even when the timestamp to be printed was not that large. For
example for 3 hours (10800) seconds, only 1 decimal digit was printed, which
made this format inaccurate when it was used in e.g. the silencedetect filter.
Other detection filters printing timestamps had similar issues.

So let's change the used format to "%.6f" instead, we have plenty of space in
the string buffer, we can somewhat imitate existing behaviour of %g by trimming
ending zeroes and the potential decimal point, which can be any non-numeric
character. In order not to trim "inf" as well, we assume that the decimal
point does not contain the letter "f".

We also no longer use scientific representation to make parsing and printing
easier, because the theoretical maximum of INT64_MAX*INT32_MAX still fits into
the string buffer in normal form.

Since the additional processing yields more code, inlineing this function no
longer make sense, so this commit also changes the API to actually export the
function instead of having it inlinable in the header.

Thanks for Allan Cady for bringing up this issue.

Signed-off-by: Marton Balint <cus@passwd.hu>
---
 doc/APIchanges                               |  3 ++
 libavutil/Makefile                           |  1 +
 libavutil/timestamp.c                        | 33 ++++++++++++++++++++
 libavutil/timestamp.h                        |  8 +----
 libavutil/version.h                          |  2 +-
 tests/ref/fate/filter-metadata-scdet         | 12 +++----
 tests/ref/fate/filter-metadata-silencedetect |  2 +-
 7 files changed, 46 insertions(+), 15 deletions(-)
 create mode 100644 libavutil/timestamp.c

diff --git a/doc/APIchanges b/doc/APIchanges
index a44c8e4f10..1afde062a5 100644
--- a/doc/APIchanges
+++ b/doc/APIchanges
@@ -2,6 +2,9 @@ The last version increases of all libraries were on 2024-03-07
 
 API changes, most recent first:
 
+2024-03-xx - xxxxxxxxxx - lavu 59.3.100 - timestamp.h
+  av_ts_make_time_string() is no longer an inline function. It is now exported.
+
 2024-03-xx - xxxxxxxxxx - lavu 59.2.100 - channel_layout.h
   Add AV_CHANNEL_LAYOUT_RETYPE_FLAG_CANONICAL.
 
diff --git a/libavutil/Makefile b/libavutil/Makefile
index e7709b97d0..5e75aa1855 100644
--- a/libavutil/Makefile
+++ b/libavutil/Makefile
@@ -174,6 +174,7 @@ OBJS = adler32.o                                                        \
        threadmessage.o                                                  \
        time.o                                                           \
        timecode.o                                                       \
+       timestamp.o                                                      \
        tree.o                                                           \
        twofish.o                                                        \
        utils.o                                                          \
diff --git a/libavutil/timestamp.c b/libavutil/timestamp.c
new file mode 100644
index 0000000000..06fb47e94c
--- /dev/null
+++ b/libavutil/timestamp.c
@@ -0,0 +1,33 @@
+/*
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "timestamp.h"
+
+char *av_ts_make_time_string(char *buf, int64_t ts, const AVRational *tb)
+{
+    if (ts == AV_NOPTS_VALUE) {
+        snprintf(buf, AV_TS_MAX_STRING_SIZE, "NOPTS");
+    } else {
+        int last = snprintf(buf, AV_TS_MAX_STRING_SIZE, "%.6f", av_q2d(*tb) * ts);
+        last = FFMIN(last, AV_TS_MAX_STRING_SIZE - 1) - 1;
+        for (; last && buf[last] == '0'; last--);
+        for (; last && buf[last] != 'f' && (buf[last] < '0' || buf[0] > '9'); last--);
+        buf[last + 1] = '\0';
+    }
+    return buf;
+}
diff --git a/libavutil/timestamp.h b/libavutil/timestamp.h
index 2b37781eba..a02d873060 100644
--- a/libavutil/timestamp.h
+++ b/libavutil/timestamp.h
@@ -62,13 +62,7 @@ static inline char *av_ts_make_string(char *buf, int64_t ts)
  * @param tb the timebase of the timestamp
  * @return the buffer in input
  */
-static inline char *av_ts_make_time_string(char *buf, int64_t ts,
-                                           const AVRational *tb)
-{
-    if (ts == AV_NOPTS_VALUE) snprintf(buf, AV_TS_MAX_STRING_SIZE, "NOPTS");
-    else                      snprintf(buf, AV_TS_MAX_STRING_SIZE, "%.6g", av_q2d(*tb) * ts);
-    return buf;
-}
+char *av_ts_make_time_string(char *buf, int64_t ts, const AVRational *tb);
 
 /**
  * Convenience macro, the return value should be used only directly in
diff --git a/libavutil/version.h b/libavutil/version.h
index 57cad02ec0..5027b025be 100644
--- a/libavutil/version.h
+++ b/libavutil/version.h
@@ -79,7 +79,7 @@
  */
 
 #define LIBAVUTIL_VERSION_MAJOR  59
-#define LIBAVUTIL_VERSION_MINOR   2
+#define LIBAVUTIL_VERSION_MINOR   3
 #define LIBAVUTIL_VERSION_MICRO 100
 
 #define LIBAVUTIL_VERSION_INT   AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \
diff --git a/tests/ref/fate/filter-metadata-scdet b/tests/ref/fate/filter-metadata-scdet
index ca5dbaaefc..d385920fcd 100644
--- a/tests/ref/fate/filter-metadata-scdet
+++ b/tests/ref/fate/filter-metadata-scdet
@@ -1,11 +1,11 @@
 pts=1620|tag:lavfi.scd.score=59.252|tag:lavfi.scd.mafd=60.175|tag:lavfi.scd.time=2.7
 pts=4140|tag:lavfi.scd.score=36.070|tag:lavfi.scd.mafd=44.209|tag:lavfi.scd.time=6.9
-pts=5800|tag:lavfi.scd.score=55.819|tag:lavfi.scd.mafd=55.819|tag:lavfi.scd.time=9.66667
+pts=5800|tag:lavfi.scd.score=55.819|tag:lavfi.scd.mafd=55.819|tag:lavfi.scd.time=9.666667
 pts=6720|tag:lavfi.scd.score=18.580|tag:lavfi.scd.mafd=22.505|tag:lavfi.scd.time=11.2
 pts=8160|tag:lavfi.scd.score=49.240|tag:lavfi.scd.mafd=49.444|tag:lavfi.scd.time=13.6
-pts=9760|tag:lavfi.scd.score=51.497|tag:lavfi.scd.mafd=51.801|tag:lavfi.scd.time=16.2667
-pts=14080|tag:lavfi.scd.score=34.165|tag:lavfi.scd.mafd=34.337|tag:lavfi.scd.time=23.4667
-pts=15700|tag:lavfi.scd.score=58.310|tag:lavfi.scd.mafd=58.315|tag:lavfi.scd.time=26.1667
-pts=18500|tag:lavfi.scd.score=16.504|tag:lavfi.scd.mafd=19.603|tag:lavfi.scd.time=30.8333
+pts=9760|tag:lavfi.scd.score=51.497|tag:lavfi.scd.mafd=51.801|tag:lavfi.scd.time=16.266667
+pts=14080|tag:lavfi.scd.score=34.165|tag:lavfi.scd.mafd=34.337|tag:lavfi.scd.time=23.466667
+pts=15700|tag:lavfi.scd.score=58.310|tag:lavfi.scd.mafd=58.315|tag:lavfi.scd.time=26.166667
+pts=18500|tag:lavfi.scd.score=16.504|tag:lavfi.scd.mafd=19.603|tag:lavfi.scd.time=30.833333
 pts=20040|tag:lavfi.scd.score=13.764|tag:lavfi.scd.mafd=19.060|tag:lavfi.scd.time=33.4
-pts=21760|tag:lavfi.scd.score=64.451|tag:lavfi.scd.mafd=64.551|tag:lavfi.scd.time=36.2667
+pts=21760|tag:lavfi.scd.score=64.451|tag:lavfi.scd.mafd=64.551|tag:lavfi.scd.time=36.266667
diff --git a/tests/ref/fate/filter-metadata-silencedetect b/tests/ref/fate/filter-metadata-silencedetect
index bc53fea047..e66ffe5fdd 100644
--- a/tests/ref/fate/filter-metadata-silencedetect
+++ b/tests/ref/fate/filter-metadata-silencedetect
@@ -1,5 +1,5 @@
 pts=0|tag:lavfi.silence_duration=0.523107|tag:lavfi.silence_end=0.690023|tag:lavfi.silence_start=0.736417
-pts=46080|tag:lavfi.silence_start=1.27626|tag:lavfi.silence_end=1.80751|tag:lavfi.silence_duration=0.531247
+pts=46080|tag:lavfi.silence_start=1.276259|tag:lavfi.silence_end=1.807506|tag:lavfi.silence_duration=0.531247
 pts=92160
 pts=138240
 pts=184320
-- 
2.35.3

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

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

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [FFmpeg-devel] [PATCH] change av_ts_make_time_string precision
  2024-03-13  6:03                       ` Allan Cady via ffmpeg-devel
  2024-03-17 23:40                         ` [FFmpeg-devel] [PATCH] avutil/timestamp: keep microsecond precision in av_ts_make_time_string Marton Balint
@ 2024-03-17 23:43                         ` Marton Balint
  2024-03-19 22:46                           ` Allan Cady via ffmpeg-devel
  1 sibling, 1 reply; 37+ messages in thread
From: Marton Balint @ 2024-03-17 23:43 UTC (permalink / raw)
  To: Allan Cady via ffmpeg-devel



On Wed, 13 Mar 2024, Allan Cady via ffmpeg-devel wrote:

> On Tuesday, March 12, 2024 at 02:24:47 PM PDT, Marton Balint <cus@passwd.hu> wrote: 
>> On Tue, 12 Mar 2024, Allan Cady via ffmpeg-devel wrote:
>>> On Monday, March 11, 2024 at 12:11:45 PM PDT, Marton Balint <cus@passwd.hu> wrote:
>>>> On Mon, 11 Mar 2024, Andreas Rheinhardt wrote:
>>>> Allan Cady via ffmpeg-devel:
>>>>> From: "Allan Cady" <allancady@yahoo.com>
>>>>>

[...]

>
>
>> One thing to notice is that you will not need to use the scientific
>> representation at all, because maximum value this function prints is the
>> product of an INT32 and an INT64, and that is 96 bit, which is at most 29
>> chars. Adding the optional sign and the decimal point, that is still only
>> 31. So we can be sure that by using %.6f, the first character of
>> the decimal point is going to be present in the output.
>
>
> I had done some similar calculation and came to a similar conclusion. 
>
>
> Which is great,
>> because that means we only have to
>> - do a single snprintf("%.6f")
>> - calculate last char position by subtracting 1 from the minimum of
>> AV_TS_MAX_STRING_SIZE-1 and the result of the snprintf() call.
>> - decrement string length while last char is '0' to remove trailing 0s
>> - decrement string length while last char is non-digit to remove decimal
>> point (which can be a multiple chars for some locales).
>> - update last+1 char to \0.
>> Ot is it still too complex to keep it inline?
>
>
> I'll give your suggestion a spin tomorrow. Thanks.
>

In the end I posted a patch myself, sorry if you were working on it too, 
it just looked a bit too complex for a new developer, since it touched 
API/build system/etc... I hope you don't mind.

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

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

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [FFmpeg-devel] [PATCH] avutil/timestamp: keep microsecond precision in av_ts_make_time_string
  2024-03-17 23:40                         ` [FFmpeg-devel] [PATCH] avutil/timestamp: keep microsecond precision in av_ts_make_time_string Marton Balint
@ 2024-03-19 21:14                           ` Marton Balint
  2024-03-19 23:38                           ` Andreas Rheinhardt
  1 sibling, 0 replies; 37+ messages in thread
From: Marton Balint @ 2024-03-19 21:14 UTC (permalink / raw)
  To: FFmpeg development discussions and patches



On Mon, 18 Mar 2024, Marton Balint wrote:

> av_ts_make_time_string() used "%.6g" format in the past, but this format was
> losing precision even when the timestamp to be printed was not that large. For
> example for 3 hours (10800) seconds, only 1 decimal digit was printed, which
> made this format inaccurate when it was used in e.g. the silencedetect filter.
> Other detection filters printing timestamps had similar issues.
>
> So let's change the used format to "%.6f" instead, we have plenty of space in
> the string buffer, we can somewhat imitate existing behaviour of %g by trimming
> ending zeroes and the potential decimal point, which can be any non-numeric
> character. In order not to trim "inf" as well, we assume that the decimal
> point does not contain the letter "f".
>
> We also no longer use scientific representation to make parsing and printing
> easier, because the theoretical maximum of INT64_MAX*INT32_MAX still fits into
> the string buffer in normal form.
>
> Since the additional processing yields more code, inlineing this function no
> longer make sense, so this commit also changes the API to actually export the
> function instead of having it inlinable in the header.
>
> Thanks for Allan Cady for bringing up this issue.

Ping for this. Considering the API and behaviour change, I prefer we apply 
this before branching 7.0.

Thanks,
Marton

>
> Signed-off-by: Marton Balint <cus@passwd.hu>
> ---
> doc/APIchanges                               |  3 ++
> libavutil/Makefile                           |  1 +
> libavutil/timestamp.c                        | 33 ++++++++++++++++++++
> libavutil/timestamp.h                        |  8 +----
> libavutil/version.h                          |  2 +-
> tests/ref/fate/filter-metadata-scdet         | 12 +++----
> tests/ref/fate/filter-metadata-silencedetect |  2 +-
> 7 files changed, 46 insertions(+), 15 deletions(-)
> create mode 100644 libavutil/timestamp.c
>
> diff --git a/doc/APIchanges b/doc/APIchanges
> index a44c8e4f10..1afde062a5 100644
> --- a/doc/APIchanges
> +++ b/doc/APIchanges
> @@ -2,6 +2,9 @@ The last version increases of all libraries were on 2024-03-07
>
> API changes, most recent first:
>
> +2024-03-xx - xxxxxxxxxx - lavu 59.3.100 - timestamp.h
> +  av_ts_make_time_string() is no longer an inline function. It is now exported.
> +
> 2024-03-xx - xxxxxxxxxx - lavu 59.2.100 - channel_layout.h
>   Add AV_CHANNEL_LAYOUT_RETYPE_FLAG_CANONICAL.
>
> diff --git a/libavutil/Makefile b/libavutil/Makefile
> index e7709b97d0..5e75aa1855 100644
> --- a/libavutil/Makefile
> +++ b/libavutil/Makefile
> @@ -174,6 +174,7 @@ OBJS = adler32.o                                                        \
>        threadmessage.o                                                  \
>        time.o                                                           \
>        timecode.o                                                       \
> +       timestamp.o                                                      \
>        tree.o                                                           \
>        twofish.o                                                        \
>        utils.o                                                          \
> diff --git a/libavutil/timestamp.c b/libavutil/timestamp.c
> new file mode 100644
> index 0000000000..06fb47e94c
> --- /dev/null
> +++ b/libavutil/timestamp.c
> @@ -0,0 +1,33 @@
> +/*
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * FFmpeg is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with FFmpeg; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
> + */
> +
> +#include "timestamp.h"
> +
> +char *av_ts_make_time_string(char *buf, int64_t ts, const AVRational *tb)
> +{
> +    if (ts == AV_NOPTS_VALUE) {
> +        snprintf(buf, AV_TS_MAX_STRING_SIZE, "NOPTS");
> +    } else {
> +        int last = snprintf(buf, AV_TS_MAX_STRING_SIZE, "%.6f", av_q2d(*tb) * ts);
> +        last = FFMIN(last, AV_TS_MAX_STRING_SIZE - 1) - 1;
> +        for (; last && buf[last] == '0'; last--);
> +        for (; last && buf[last] != 'f' && (buf[last] < '0' || buf[0] > '9'); last--);
> +        buf[last + 1] = '\0';
> +    }
> +    return buf;
> +}
> diff --git a/libavutil/timestamp.h b/libavutil/timestamp.h
> index 2b37781eba..a02d873060 100644
> --- a/libavutil/timestamp.h
> +++ b/libavutil/timestamp.h
> @@ -62,13 +62,7 @@ static inline char *av_ts_make_string(char *buf, int64_t ts)
>  * @param tb the timebase of the timestamp
>  * @return the buffer in input
>  */
> -static inline char *av_ts_make_time_string(char *buf, int64_t ts,
> -                                           const AVRational *tb)
> -{
> -    if (ts == AV_NOPTS_VALUE) snprintf(buf, AV_TS_MAX_STRING_SIZE, "NOPTS");
> -    else                      snprintf(buf, AV_TS_MAX_STRING_SIZE, "%.6g", av_q2d(*tb) * ts);
> -    return buf;
> -}
> +char *av_ts_make_time_string(char *buf, int64_t ts, const AVRational *tb);
>
> /**
>  * Convenience macro, the return value should be used only directly in
> diff --git a/libavutil/version.h b/libavutil/version.h
> index 57cad02ec0..5027b025be 100644
> --- a/libavutil/version.h
> +++ b/libavutil/version.h
> @@ -79,7 +79,7 @@
>  */
>
> #define LIBAVUTIL_VERSION_MAJOR  59
> -#define LIBAVUTIL_VERSION_MINOR   2
> +#define LIBAVUTIL_VERSION_MINOR   3
> #define LIBAVUTIL_VERSION_MICRO 100
>
> #define LIBAVUTIL_VERSION_INT   AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \
> diff --git a/tests/ref/fate/filter-metadata-scdet b/tests/ref/fate/filter-metadata-scdet
> index ca5dbaaefc..d385920fcd 100644
> --- a/tests/ref/fate/filter-metadata-scdet
> +++ b/tests/ref/fate/filter-metadata-scdet
> @@ -1,11 +1,11 @@
> pts=1620|tag:lavfi.scd.score=59.252|tag:lavfi.scd.mafd=60.175|tag:lavfi.scd.time=2.7
> pts=4140|tag:lavfi.scd.score=36.070|tag:lavfi.scd.mafd=44.209|tag:lavfi.scd.time=6.9
> -pts=5800|tag:lavfi.scd.score=55.819|tag:lavfi.scd.mafd=55.819|tag:lavfi.scd.time=9.66667
> +pts=5800|tag:lavfi.scd.score=55.819|tag:lavfi.scd.mafd=55.819|tag:lavfi.scd.time=9.666667
> pts=6720|tag:lavfi.scd.score=18.580|tag:lavfi.scd.mafd=22.505|tag:lavfi.scd.time=11.2
> pts=8160|tag:lavfi.scd.score=49.240|tag:lavfi.scd.mafd=49.444|tag:lavfi.scd.time=13.6
> -pts=9760|tag:lavfi.scd.score=51.497|tag:lavfi.scd.mafd=51.801|tag:lavfi.scd.time=16.2667
> -pts=14080|tag:lavfi.scd.score=34.165|tag:lavfi.scd.mafd=34.337|tag:lavfi.scd.time=23.4667
> -pts=15700|tag:lavfi.scd.score=58.310|tag:lavfi.scd.mafd=58.315|tag:lavfi.scd.time=26.1667
> -pts=18500|tag:lavfi.scd.score=16.504|tag:lavfi.scd.mafd=19.603|tag:lavfi.scd.time=30.8333
> +pts=9760|tag:lavfi.scd.score=51.497|tag:lavfi.scd.mafd=51.801|tag:lavfi.scd.time=16.266667
> +pts=14080|tag:lavfi.scd.score=34.165|tag:lavfi.scd.mafd=34.337|tag:lavfi.scd.time=23.466667
> +pts=15700|tag:lavfi.scd.score=58.310|tag:lavfi.scd.mafd=58.315|tag:lavfi.scd.time=26.166667
> +pts=18500|tag:lavfi.scd.score=16.504|tag:lavfi.scd.mafd=19.603|tag:lavfi.scd.time=30.833333
> pts=20040|tag:lavfi.scd.score=13.764|tag:lavfi.scd.mafd=19.060|tag:lavfi.scd.time=33.4
> -pts=21760|tag:lavfi.scd.score=64.451|tag:lavfi.scd.mafd=64.551|tag:lavfi.scd.time=36.2667
> +pts=21760|tag:lavfi.scd.score=64.451|tag:lavfi.scd.mafd=64.551|tag:lavfi.scd.time=36.266667
> diff --git a/tests/ref/fate/filter-metadata-silencedetect b/tests/ref/fate/filter-metadata-silencedetect
> index bc53fea047..e66ffe5fdd 100644
> --- a/tests/ref/fate/filter-metadata-silencedetect
> +++ b/tests/ref/fate/filter-metadata-silencedetect
> @@ -1,5 +1,5 @@
> pts=0|tag:lavfi.silence_duration=0.523107|tag:lavfi.silence_end=0.690023|tag:lavfi.silence_start=0.736417
> -pts=46080|tag:lavfi.silence_start=1.27626|tag:lavfi.silence_end=1.80751|tag:lavfi.silence_duration=0.531247
> +pts=46080|tag:lavfi.silence_start=1.276259|tag:lavfi.silence_end=1.807506|tag:lavfi.silence_duration=0.531247
> pts=92160
> pts=138240
> pts=184320
> -- 
> 2.35.3
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
>
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

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

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [FFmpeg-devel] [PATCH] change av_ts_make_time_string precision
  2024-03-17 23:43                         ` [FFmpeg-devel] [PATCH] change av_ts_make_time_string precision Marton Balint
@ 2024-03-19 22:46                           ` Allan Cady via ffmpeg-devel
  0 siblings, 0 replies; 37+ messages in thread
From: Allan Cady via ffmpeg-devel @ 2024-03-19 22:46 UTC (permalink / raw)
  To: FFmpeg development discussions and patches; +Cc: Allan Cady

 
On Sunday, March 17, 2024 at 04:43:31 PM PDT, Marton Balint <cus@passwd.hu> wrote:
 
> On Wed, 13 Mar 2024, Allan Cady via ffmpeg-devel wrote:>> On Tuesday, March 12, 2024 at 02:24:47 PM PDT, Marton Balint <cus@passwd.hu> wrote:>>> On Tue, 12 Mar 2024, Allan Cady via ffmpeg-devel wrote:>>>> On Monday, March 11, 2024 at 12:11:45 PM PDT, Marton Balint <cus@passwd.hu> wrote:>>>>> On Mon, 11 Mar 2024, Andreas Rheinhardt wrote:>>>>> Allan Cady via ffmpeg-devel:>>>>>> From: "Allan Cady" <allancady@yahoo.com>>>>>>>> > [...]> >>>>>>> One thing to notice is that you will not need to use the scientific>>> representation at all, because maximum value this function prints is the>>> product of an INT32 and an INT64, and that is 96 bit, which is at most 29>>> chars. Adding the optional sign and the decimal point, that is still only>>> 31. So we can be sure that by using %.6f, the first character of>>> the decimal point is going to be present in the output.>>>>>> I had done some similar calculation and came to a similar conclusion. >>>>>> Which is great,>>> because that means we only have to>>> - do a single snprintf("%.6f")>>> - calculate last char position by subtracting 1 from the minimum of>>> AV_TS_MAX_STRING_SIZE-1 and the result of the snprintf() call.>>> - decrement string length while last char is '0' to remove trailing 0s>>> - decrement string length while last char is non-digit to remove decimal>>> point (which can be a multiple chars for some locales).>>> - update last+1 char to \0.>>> Ot is it still too complex to keep it inline?>>>>>> I'll give your suggestion a spin tomorrow. Thanks.>>> > In the end I posted a patch myself, sorry if you were working on it too,> it just looked a bit too complex for a new developer, since it touched> API/build system/etc... I hope you don't mind.> > Regards,> > Marton
No problem at all. I was just getting ready to resubmit, but I'm happyto see you beat me to the punch. And I'm delighted that I won't need tokeep dragging around my own customized copy of ffmpeg going forward.
Any idea how soon this will show up on the master branch?

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

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

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [FFmpeg-devel] [PATCH] avutil/timestamp: keep microsecond precision in av_ts_make_time_string
  2024-03-17 23:40                         ` [FFmpeg-devel] [PATCH] avutil/timestamp: keep microsecond precision in av_ts_make_time_string Marton Balint
  2024-03-19 21:14                           ` Marton Balint
@ 2024-03-19 23:38                           ` Andreas Rheinhardt
  2024-03-20 12:44                             ` Andreas Rheinhardt
  1 sibling, 1 reply; 37+ messages in thread
From: Andreas Rheinhardt @ 2024-03-19 23:38 UTC (permalink / raw)
  To: ffmpeg-devel

Marton Balint:
> av_ts_make_time_string() used "%.6g" format in the past, but this format was
> losing precision even when the timestamp to be printed was not that large. For
> example for 3 hours (10800) seconds, only 1 decimal digit was printed, which
> made this format inaccurate when it was used in e.g. the silencedetect filter.
> Other detection filters printing timestamps had similar issues.
> 
> So let's change the used format to "%.6f" instead, we have plenty of space in
> the string buffer, we can somewhat imitate existing behaviour of %g by trimming
> ending zeroes and the potential decimal point, which can be any non-numeric
> character. In order not to trim "inf" as well, we assume that the decimal
> point does not contain the letter "f".
> 
> We also no longer use scientific representation to make parsing and printing
> easier, because the theoretical maximum of INT64_MAX*INT32_MAX still fits into
> the string buffer in normal form.
> 
> Since the additional processing yields more code, inlineing this function no
> longer make sense, so this commit also changes the API to actually export the
> function instead of having it inlinable in the header.
> 
> Thanks for Allan Cady for bringing up this issue.
> 
> Signed-off-by: Marton Balint <cus@passwd.hu>
> ---
>  doc/APIchanges                               |  3 ++
>  libavutil/Makefile                           |  1 +
>  libavutil/timestamp.c                        | 33 ++++++++++++++++++++
>  libavutil/timestamp.h                        |  8 +----
>  libavutil/version.h                          |  2 +-
>  tests/ref/fate/filter-metadata-scdet         | 12 +++----
>  tests/ref/fate/filter-metadata-silencedetect |  2 +-
>  7 files changed, 46 insertions(+), 15 deletions(-)
>  create mode 100644 libavutil/timestamp.c
> 
> diff --git a/doc/APIchanges b/doc/APIchanges
> index a44c8e4f10..1afde062a5 100644
> --- a/doc/APIchanges
> +++ b/doc/APIchanges
> @@ -2,6 +2,9 @@ The last version increases of all libraries were on 2024-03-07
>  
>  API changes, most recent first:
>  
> +2024-03-xx - xxxxxxxxxx - lavu 59.3.100 - timestamp.h
> +  av_ts_make_time_string() is no longer an inline function. It is now exported.
> +
>  2024-03-xx - xxxxxxxxxx - lavu 59.2.100 - channel_layout.h
>    Add AV_CHANNEL_LAYOUT_RETYPE_FLAG_CANONICAL.
>  
> diff --git a/libavutil/Makefile b/libavutil/Makefile
> index e7709b97d0..5e75aa1855 100644
> --- a/libavutil/Makefile
> +++ b/libavutil/Makefile
> @@ -174,6 +174,7 @@ OBJS = adler32.o                                                        \
>         threadmessage.o                                                  \
>         time.o                                                           \
>         timecode.o                                                       \
> +       timestamp.o                                                      \
>         tree.o                                                           \
>         twofish.o                                                        \
>         utils.o                                                          \
> diff --git a/libavutil/timestamp.c b/libavutil/timestamp.c
> new file mode 100644
> index 0000000000..06fb47e94c
> --- /dev/null
> +++ b/libavutil/timestamp.c
> @@ -0,0 +1,33 @@
> +/*
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * FFmpeg is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with FFmpeg; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
> + */
> +
> +#include "timestamp.h"
> +
> +char *av_ts_make_time_string(char *buf, int64_t ts, const AVRational *tb)
> +{
> +    if (ts == AV_NOPTS_VALUE) {
> +        snprintf(buf, AV_TS_MAX_STRING_SIZE, "NOPTS");
> +    } else {
> +        int last = snprintf(buf, AV_TS_MAX_STRING_SIZE, "%.6f", av_q2d(*tb) * ts);
> +        last = FFMIN(last, AV_TS_MAX_STRING_SIZE - 1) - 1;
> +        for (; last && buf[last] == '0'; last--);
> +        for (; last && buf[last] != 'f' && (buf[last] < '0' || buf[0] > '9'); last--);
> +        buf[last + 1] = '\0';
> +    }
> +    return buf;
> +}

As has already been said before: Simply using %.6f will discard
significant digits for small timestamps. E.g. 10^-7 will simply print
0.000000, which will then be converted to "0" by your code. The old code
printed 1.

Furthermore, there are more problems:
"A double argument representing an infinity is converted in one of the
styles [-]inf or [-]infinity — which style is implementation-defined."
Your code would trim infinity to "inf". It would be even worse with nan:
"A double argument representing a NaN is converted in one of the styles
[-]nan or [-]nan(n-char-sequence) — which style, and the meaning of
any n-char-sequence, is implementation-defined."
Furthermore, there is no guarantee that the "decimal-point character" is
actually a single character (it's a char* in struct lcov after all).

If I am not mistaken, then NANs and infinities can't happen here unless
the timebase is malformed (denominator zero); and in this case one could
use the NOPTS codepath.

- Andreas

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

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

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [FFmpeg-devel] [PATCH] avutil/timestamp: keep microsecond precision in av_ts_make_time_string
  2024-03-19 23:38                           ` Andreas Rheinhardt
@ 2024-03-20 12:44                             ` Andreas Rheinhardt
  2024-03-20 19:51                               ` Marton Balint
  0 siblings, 1 reply; 37+ messages in thread
From: Andreas Rheinhardt @ 2024-03-20 12:44 UTC (permalink / raw)
  To: ffmpeg-devel

Andreas Rheinhardt:
> Marton Balint:
>> av_ts_make_time_string() used "%.6g" format in the past, but this format was
>> losing precision even when the timestamp to be printed was not that large. For
>> example for 3 hours (10800) seconds, only 1 decimal digit was printed, which
>> made this format inaccurate when it was used in e.g. the silencedetect filter.
>> Other detection filters printing timestamps had similar issues.
>>
>> So let's change the used format to "%.6f" instead, we have plenty of space in
>> the string buffer, we can somewhat imitate existing behaviour of %g by trimming
>> ending zeroes and the potential decimal point, which can be any non-numeric
>> character. In order not to trim "inf" as well, we assume that the decimal
>> point does not contain the letter "f".
>>
>> We also no longer use scientific representation to make parsing and printing
>> easier, because the theoretical maximum of INT64_MAX*INT32_MAX still fits into
>> the string buffer in normal form.
>>
>> Since the additional processing yields more code, inlineing this function no
>> longer make sense, so this commit also changes the API to actually export the
>> function instead of having it inlinable in the header.
>>
>> Thanks for Allan Cady for bringing up this issue.
>>
>> Signed-off-by: Marton Balint <cus@passwd.hu>
>> ---
>>  doc/APIchanges                               |  3 ++
>>  libavutil/Makefile                           |  1 +
>>  libavutil/timestamp.c                        | 33 ++++++++++++++++++++
>>  libavutil/timestamp.h                        |  8 +----
>>  libavutil/version.h                          |  2 +-
>>  tests/ref/fate/filter-metadata-scdet         | 12 +++----
>>  tests/ref/fate/filter-metadata-silencedetect |  2 +-
>>  7 files changed, 46 insertions(+), 15 deletions(-)
>>  create mode 100644 libavutil/timestamp.c
>>
>> diff --git a/doc/APIchanges b/doc/APIchanges
>> index a44c8e4f10..1afde062a5 100644
>> --- a/doc/APIchanges
>> +++ b/doc/APIchanges
>> @@ -2,6 +2,9 @@ The last version increases of all libraries were on 2024-03-07
>>  
>>  API changes, most recent first:
>>  
>> +2024-03-xx - xxxxxxxxxx - lavu 59.3.100 - timestamp.h
>> +  av_ts_make_time_string() is no longer an inline function. It is now exported.
>> +
>>  2024-03-xx - xxxxxxxxxx - lavu 59.2.100 - channel_layout.h
>>    Add AV_CHANNEL_LAYOUT_RETYPE_FLAG_CANONICAL.
>>  
>> diff --git a/libavutil/Makefile b/libavutil/Makefile
>> index e7709b97d0..5e75aa1855 100644
>> --- a/libavutil/Makefile
>> +++ b/libavutil/Makefile
>> @@ -174,6 +174,7 @@ OBJS = adler32.o                                                        \
>>         threadmessage.o                                                  \
>>         time.o                                                           \
>>         timecode.o                                                       \
>> +       timestamp.o                                                      \
>>         tree.o                                                           \
>>         twofish.o                                                        \
>>         utils.o                                                          \
>> diff --git a/libavutil/timestamp.c b/libavutil/timestamp.c
>> new file mode 100644
>> index 0000000000..06fb47e94c
>> --- /dev/null
>> +++ b/libavutil/timestamp.c
>> @@ -0,0 +1,33 @@
>> +/*
>> + * This file is part of FFmpeg.
>> + *
>> + * FFmpeg is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU Lesser General Public
>> + * License as published by the Free Software Foundation; either
>> + * version 2.1 of the License, or (at your option) any later version.
>> + *
>> + * FFmpeg is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> + * Lesser General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU Lesser General Public
>> + * License along with FFmpeg; if not, write to the Free Software
>> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
>> + */
>> +
>> +#include "timestamp.h"
>> +
>> +char *av_ts_make_time_string(char *buf, int64_t ts, const AVRational *tb)
>> +{
>> +    if (ts == AV_NOPTS_VALUE) {
>> +        snprintf(buf, AV_TS_MAX_STRING_SIZE, "NOPTS");
>> +    } else {
>> +        int last = snprintf(buf, AV_TS_MAX_STRING_SIZE, "%.6f", av_q2d(*tb) * ts);
>> +        last = FFMIN(last, AV_TS_MAX_STRING_SIZE - 1) - 1;
>> +        for (; last && buf[last] == '0'; last--);
>> +        for (; last && buf[last] != 'f' && (buf[last] < '0' || buf[0] > '9'); last--);
>> +        buf[last + 1] = '\0';
>> +    }
>> +    return buf;
>> +}
> 
> As has already been said before: Simply using %.6f will discard
> significant digits for small timestamps. E.g. 10^-7 will simply print
> 0.000000, which will then be converted to "0" by your code. The old code
> printed 1.
> 
> Furthermore, there are more problems:
> "A double argument representing an infinity is converted in one of the
> styles [-]inf or [-]infinity — which style is implementation-defined."
> Your code would trim infinity to "inf". It would be even worse with nan:
> "A double argument representing a NaN is converted in one of the styles
> [-]nan or [-]nan(n-char-sequence) — which style, and the meaning of
> any n-char-sequence, is implementation-defined."
> Furthermore, there is no guarantee that the "decimal-point character" is
> actually a single character (it's a char* in struct lcov after all).
> 
> If I am not mistaken, then NANs and infinities can't happen here unless
> the timebase is malformed (denominator zero); and in this case one could
> use the NOPTS codepath.
> 

While just at it: As I already mentioned, this function has a design
defect: It passes the timebase by pointer and not just by value. So if
we add a non-inlined version of this, it should be a new function that
receives the timebase by value and av_ts_make_time_string() should
instead call this new function like
static inline char *av_ts_make_time_string(char *buf, int64_t ts, const
AVRational *tb)
{
    return new_func(buf, ts, *tb);
}

- Andreas

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

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

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [FFmpeg-devel] [PATCH] avutil/timestamp: keep microsecond precision in av_ts_make_time_string
  2024-03-20 12:44                             ` Andreas Rheinhardt
@ 2024-03-20 19:51                               ` Marton Balint
  2024-03-22 21:50                                 ` Marton Balint
  2024-03-22 21:58                                 ` Andreas Rheinhardt
  0 siblings, 2 replies; 37+ messages in thread
From: Marton Balint @ 2024-03-20 19:51 UTC (permalink / raw)
  To: FFmpeg development discussions and patches



On Wed, 20 Mar 2024, Andreas Rheinhardt wrote:

> Andreas Rheinhardt:
>> Marton Balint:
>>> av_ts_make_time_string() used "%.6g" format in the past, but this format was
>>> losing precision even when the timestamp to be printed was not that large. For
>>> example for 3 hours (10800) seconds, only 1 decimal digit was printed, which
>>> made this format inaccurate when it was used in e.g. the silencedetect filter.
>>> Other detection filters printing timestamps had similar issues.
>>>
>>> So let's change the used format to "%.6f" instead, we have plenty of space in
>>> the string buffer, we can somewhat imitate existing behaviour of %g by trimming
>>> ending zeroes and the potential decimal point, which can be any non-numeric
>>> character. In order not to trim "inf" as well, we assume that the decimal
>>> point does not contain the letter "f".
>>>
>>> We also no longer use scientific representation to make parsing and printing
>>> easier, because the theoretical maximum of INT64_MAX*INT32_MAX still fits into
>>> the string buffer in normal form.
>>>
>>> Since the additional processing yields more code, inlineing this function no
>>> longer make sense, so this commit also changes the API to actually export the
>>> function instead of having it inlinable in the header.
>>>
>>> Thanks for Allan Cady for bringing up this issue.
>>>
>>> Signed-off-by: Marton Balint <cus@passwd.hu>
>>> ---
>>>  doc/APIchanges                               |  3 ++
>>>  libavutil/Makefile                           |  1 +
>>>  libavutil/timestamp.c                        | 33 ++++++++++++++++++++
>>>  libavutil/timestamp.h                        |  8 +----
>>>  libavutil/version.h                          |  2 +-
>>>  tests/ref/fate/filter-metadata-scdet         | 12 +++----
>>>  tests/ref/fate/filter-metadata-silencedetect |  2 +-
>>>  7 files changed, 46 insertions(+), 15 deletions(-)
>>>  create mode 100644 libavutil/timestamp.c
>>>
>>> diff --git a/doc/APIchanges b/doc/APIchanges
>>> index a44c8e4f10..1afde062a5 100644
>>> --- a/doc/APIchanges
>>> +++ b/doc/APIchanges
>>> @@ -2,6 +2,9 @@ The last version increases of all libraries were on 2024-03-07
>>>
>>>  API changes, most recent first:
>>> 
>>> +2024-03-xx - xxxxxxxxxx - lavu 59.3.100 - timestamp.h
>>> +  av_ts_make_time_string() is no longer an inline function. It is now exported.
>>> +
>>>  2024-03-xx - xxxxxxxxxx - lavu 59.2.100 - channel_layout.h
>>>    Add AV_CHANNEL_LAYOUT_RETYPE_FLAG_CANONICAL.
>>> 
>>> diff --git a/libavutil/Makefile b/libavutil/Makefile
>>> index e7709b97d0..5e75aa1855 100644
>>> --- a/libavutil/Makefile
>>> +++ b/libavutil/Makefile
>>> @@ -174,6 +174,7 @@ OBJS = adler32.o                                                        \
>>>         threadmessage.o                                                  \
>>>         time.o                                                           \
>>>         timecode.o                                                       \
>>> +       timestamp.o                                                      \
>>>         tree.o                                                           \
>>>         twofish.o                                                        \
>>>         utils.o                                                          \
>>> diff --git a/libavutil/timestamp.c b/libavutil/timestamp.c
>>> new file mode 100644
>>> index 0000000000..06fb47e94c
>>> --- /dev/null
>>> +++ b/libavutil/timestamp.c
>>> @@ -0,0 +1,33 @@
>>> +/*
>>> + * This file is part of FFmpeg.
>>> + *
>>> + * FFmpeg is free software; you can redistribute it and/or
>>> + * modify it under the terms of the GNU Lesser General Public
>>> + * License as published by the Free Software Foundation; either
>>> + * version 2.1 of the License, or (at your option) any later version.
>>> + *
>>> + * FFmpeg is distributed in the hope that it will be useful,
>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>>> + * Lesser General Public License for more details.
>>> + *
>>> + * You should have received a copy of the GNU Lesser General Public
>>> + * License along with FFmpeg; if not, write to the Free Software
>>> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
>>> + */
>>> +
>>> +#include "timestamp.h"
>>> +
>>> +char *av_ts_make_time_string(char *buf, int64_t ts, const AVRational *tb)
>>> +{
>>> +    if (ts == AV_NOPTS_VALUE) {
>>> +        snprintf(buf, AV_TS_MAX_STRING_SIZE, "NOPTS");
>>> +    } else {
>>> +        int last = snprintf(buf, AV_TS_MAX_STRING_SIZE, "%.6f", av_q2d(*tb) * ts);
>>> +        last = FFMIN(last, AV_TS_MAX_STRING_SIZE - 1) - 1;
>>> +        for (; last && buf[last] == '0'; last--);
>>> +        for (; last && buf[last] != 'f' && (buf[last] < '0' || buf[0] > '9'); last--);
>>> +        buf[last + 1] = '\0';
>>> +    }
>>> +    return buf;
>>> +}
>> 
>> As has already been said before: Simply using %.6f will discard
>> significant digits for small timestamps. E.g. 10^-7 will simply print
>> 0.000000, which will then be converted to "0" by your code. The old code
>> printed 1.

Admittedly we will lose precision < 10^-6 by using %.6f, but I don't 
think this will cause any real problems, so I'd rather just leave this as 
is. Or you prefer some special handling of small values? E.g. using %.9f 
or something?

>> 
>> Furthermore, there are more problems:
>> "A double argument representing an infinity is converted in one of the
>> styles [-]inf or [-]infinity — which style is implementation-defined."
>> Your code would trim infinity to "inf". It would be even worse with nan:
>> "A double argument representing a NaN is converted in one of the styles
>> [-]nan or [-]nan(n-char-sequence) — which style, and the meaning of
>> any n-char-sequence, is implementation-defined."
>> Furthermore, there is no guarantee that the "decimal-point character" is
>> actually a single character (it's a char* in struct lcov after all).
>> 
>> If I am not mistaken, then NANs and infinities can't happen here unless
>> the timebase is malformed (denominator zero); and in this case one could
>> use the NOPTS codepath.

So the NAN case can't happen. The multiple character decimal separator is 
not a problem because we are trimming multiple chars. The inf instead of 
infinity case should not be a problem either, inf is parseable the 
same way as infinity, strtod() supports both. But if you prefer, I 
can make the code look for 'y' as well to stop trimming, I don't think it 
really matters. I am not sure about returning NOPTS for infinity.

> While just at it: As I already mentioned, this function has a design
> defect: It passes the timebase by pointer and not just by value. So if
> we add a non-inlined version of this, it should be a new function that
> receives the timebase by value and av_ts_make_time_string() should
> instead call this new function like
> static inline char *av_ts_make_time_string(char *buf, int64_t ts, const
> AVRational *tb)
> {
>    return new_func(buf, ts, *tb);
> }

I am uneasy about adding another public function signature for this 
purpose, because that will still have the flaw of using a locale 
dependant decimal point.

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

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

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [FFmpeg-devel] [PATCH] avutil/timestamp: keep microsecond precision in av_ts_make_time_string
  2024-03-20 19:51                               ` Marton Balint
@ 2024-03-22 21:50                                 ` Marton Balint
  2024-03-22 21:58                                 ` Andreas Rheinhardt
  1 sibling, 0 replies; 37+ messages in thread
From: Marton Balint @ 2024-03-22 21:50 UTC (permalink / raw)
  To: FFmpeg development discussions and patches



On Wed, 20 Mar 2024, Marton Balint wrote:

>
>
> On Wed, 20 Mar 2024, Andreas Rheinhardt wrote:
>
>>  Andreas Rheinhardt:
>>>  Marton Balint:
>>>>  av_ts_make_time_string() used "%.6g" format in the past, but this format
>>>>  was
>>>>  losing precision even when the timestamp to be printed was not that
>>>>  large. For
>>>>  example for 3 hours (10800) seconds, only 1 decimal digit was printed,
>>>>  which
>>>>  made this format inaccurate when it was used in e.g. the silencedetect
>>>>  filter.
>>>>  Other detection filters printing timestamps had similar issues.
>>>>
>>>>  So let's change the used format to "%.6f" instead, we have plenty of
>>>>  space in
>>>>  the string buffer, we can somewhat imitate existing behaviour of %g by
>>>>  trimming
>>>>  ending zeroes and the potential decimal point, which can be any
>>>>  non-numeric
>>>>  character. In order not to trim "inf" as well, we assume that the
>>>>  decimal
>>>>  point does not contain the letter "f".
>>>>
>>>>  We also no longer use scientific representation to make parsing and
>>>>  printing
>>>>  easier, because the theoretical maximum of INT64_MAX*INT32_MAX still
>>>>  fits into
>>>>  the string buffer in normal form.
>>>>
>>>>  Since the additional processing yields more code, inlineing this
>>>>  function no
>>>>  longer make sense, so this commit also changes the API to actually
>>>>  export the
>>>>  function instead of having it inlinable in the header.
>>>>
>>>>  Thanks for Allan Cady for bringing up this issue.
>>>>
>>>>  Signed-off-by: Marton Balint <cus@passwd.hu>
>>>>  ---
>>>>   doc/APIchanges                               |  3 ++
>>>>   libavutil/Makefile                           |  1 +
>>>>   libavutil/timestamp.c                        | 33 ++++++++++++++++++++
>>>>   libavutil/timestamp.h                        |  8 +----
>>>>   libavutil/version.h                          |  2 +-
>>>>   tests/ref/fate/filter-metadata-scdet         | 12 +++----
>>>>   tests/ref/fate/filter-metadata-silencedetect |  2 +-
>>>>   7 files changed, 46 insertions(+), 15 deletions(-)
>>>>   create mode 100644 libavutil/timestamp.c
>>>>
>>>>  diff --git a/doc/APIchanges b/doc/APIchanges
>>>>  index a44c8e4f10..1afde062a5 100644
>>>>  --- a/doc/APIchanges
>>>>  +++ b/doc/APIchanges
>>>> @@  -2,6 +2,9 @@ The last version increases of all libraries were on 
>>>> @@  2024-03-07
>>>>
>>>>   API changes, most recent first:
>>>>
>>>>  +2024-03-xx - xxxxxxxxxx - lavu 59.3.100 - timestamp.h
>>>>  +  av_ts_make_time_string() is no longer an inline function. It is now
>>>>  exported.
>>>>  +
>>>>   2024-03-xx - xxxxxxxxxx - lavu 59.2.100 - channel_layout.h
>>>>     Add AV_CHANNEL_LAYOUT_RETYPE_FLAG_CANONICAL.
>>>>
>>>>  diff --git a/libavutil/Makefile b/libavutil/Makefile
>>>>  index e7709b97d0..5e75aa1855 100644
>>>>  --- a/libavutil/Makefile
>>>>  +++ b/libavutil/Makefile
>>>> @@  -174,6 +174,7 @@ OBJS = adler32.o 
>>>> @@  \
>>>>          threadmessage.o
>>>>          \
>>>>          time.o
>>>>          \
>>>>          timecode.o
>>>>          \
>>>>  +       timestamp.o
>>>>  \
>>>>          tree.o
>>>>          \
>>>>          twofish.o
>>>>          \
>>>>          utils.o
>>>>          \
>>>>  diff --git a/libavutil/timestamp.c b/libavutil/timestamp.c
>>>>  new file mode 100644
>>>>  index 0000000000..06fb47e94c
>>>>  --- /dev/null
>>>>  +++ b/libavutil/timestamp.c
>>>> @@  -0,0 +1,33 @@
>>>>  +/*
>>>>  + * This file is part of FFmpeg.
>>>>  + *
>>>>  + * FFmpeg is free software; you can redistribute it and/or
>>>>  + * modify it under the terms of the GNU Lesser General Public
>>>>  + * License as published by the Free Software Foundation; either
>>>>  + * version 2.1 of the License, or (at your option) any later version.
>>>>  + *
>>>>  + * FFmpeg is distributed in the hope that it will be useful,
>>>>  + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>>>>  + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>>>>  + * Lesser General Public License for more details.
>>>>  + *
>>>>  + * You should have received a copy of the GNU Lesser General Public
>>>>  + * License along with FFmpeg; if not, write to the Free Software
>>>>  + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
>>>>  02110-1301 USA
>>>>  + */
>>>>  +
>>>>  +#include "timestamp.h"
>>>>  +
>>>>  +char *av_ts_make_time_string(char *buf, int64_t ts, const AVRational
>>>>  *tb)
>>>>  +{
>>>>  +    if (ts == AV_NOPTS_VALUE) {
>>>>  +        snprintf(buf, AV_TS_MAX_STRING_SIZE, "NOPTS");
>>>>  +    } else {
>>>>  +        int last = snprintf(buf, AV_TS_MAX_STRING_SIZE, "%.6f",
>>>>  av_q2d(*tb) * ts);
>>>>  +        last = FFMIN(last, AV_TS_MAX_STRING_SIZE - 1) - 1;
>>>>  +        for (; last && buf[last] == '0'; last--);
>>>>  +        for (; last && buf[last] != 'f' && (buf[last] < '0' || buf[0] >
>>>>  '9'); last--);
>>>>  +        buf[last + 1] = '\0';
>>>>  +    }
>>>>  +    return buf;
>>>>  +}
>>>
>>>  As has already been said before: Simply using %.6f will discard
>>>  significant digits for small timestamps. E.g. 10^-7 will simply print
>>>  0.000000, which will then be converted to "0" by your code. The old code
>>>  printed 1.
>
> Admittedly we will lose precision < 10^-6 by using %.6f, but I don't think 
> this will cause any real problems, so I'd rather just leave this as is. Or 
> you prefer some special handling of small values? E.g. using %.9f or 
> something?
>
>>>
>>>  Furthermore, there are more problems:
>>>  "A double argument representing an infinity is converted in one of the
>>>  styles [-]inf or [-]infinity — which style is implementation-defined."
>>>  Your code would trim infinity to "inf". It would be even worse with nan:
>>>  "A double argument representing a NaN is converted in one of the styles
>>>  [-]nan or [-]nan(n-char-sequence) — which style, and the meaning of
>>>  any n-char-sequence, is implementation-defined."
>>>  Furthermore, there is no guarantee that the "decimal-point character" is
>>>  actually a single character (it's a char* in struct lcov after all).
>>>
>>>  If I am not mistaken, then NANs and infinities can't happen here unless
>>>  the timebase is malformed (denominator zero); and in this case one could
>>>  use the NOPTS codepath.
>
> So the NAN case can't happen. The multiple character decimal separator is not 
> a problem because we are trimming multiple chars. The inf instead of infinity 
> case should not be a problem either, inf is parseable the same way as 
> infinity, strtod() supports both. But if you prefer, I can make the code look 
> for 'y' as well to stop trimming, I don't think it really matters. I am not 
> sure about returning NOPTS for infinity.
>
>>  While just at it: As I already mentioned, this function has a design
>>  defect: It passes the timebase by pointer and not just by value. So if
>>  we add a non-inlined version of this, it should be a new function that
>>  receives the timebase by value and av_ts_make_time_string() should
>>  instead call this new function like
>>  static inline char *av_ts_make_time_string(char *buf, int64_t ts, const
>>  AVRational *tb)
>>  {
>>     return new_func(buf, ts, *tb);
>>  }
>
> I am uneasy about adding another public function signature for this purpose, 
> because that will still have the flaw of using a locale dependant decimal 
> point.

Andreas, please make it clear what is needed for this patch to become 
acceptable to you, which parts you insist on changing, which parts are you 
OK with as is.

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

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

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [FFmpeg-devel] [PATCH] avutil/timestamp: keep microsecond precision in av_ts_make_time_string
  2024-03-20 19:51                               ` Marton Balint
  2024-03-22 21:50                                 ` Marton Balint
@ 2024-03-22 21:58                                 ` Andreas Rheinhardt
  2024-03-23 11:14                                   ` [FFmpeg-devel] [PATCH v2 1/2] avutil/timestamp: introduce av_ts_make_time_string2 for better precision Marton Balint
  1 sibling, 1 reply; 37+ messages in thread
From: Andreas Rheinhardt @ 2024-03-22 21:58 UTC (permalink / raw)
  To: ffmpeg-devel

Marton Balint:
> 
> 
> On Wed, 20 Mar 2024, Andreas Rheinhardt wrote:
> 
>> Andreas Rheinhardt:
>>> Marton Balint:
>>>> av_ts_make_time_string() used "%.6g" format in the past, but this
>>>> format was
>>>> losing precision even when the timestamp to be printed was not that
>>>> large. For
>>>> example for 3 hours (10800) seconds, only 1 decimal digit was
>>>> printed, which
>>>> made this format inaccurate when it was used in e.g. the
>>>> silencedetect filter.
>>>> Other detection filters printing timestamps had similar issues.
>>>>
>>>> So let's change the used format to "%.6f" instead, we have plenty of
>>>> space in
>>>> the string buffer, we can somewhat imitate existing behaviour of %g
>>>> by trimming
>>>> ending zeroes and the potential decimal point, which can be any
>>>> non-numeric
>>>> character. In order not to trim "inf" as well, we assume that the
>>>> decimal
>>>> point does not contain the letter "f".
>>>>
>>>> We also no longer use scientific representation to make parsing and
>>>> printing
>>>> easier, because the theoretical maximum of INT64_MAX*INT32_MAX still
>>>> fits into
>>>> the string buffer in normal form.
>>>>
>>>> Since the additional processing yields more code, inlineing this
>>>> function no
>>>> longer make sense, so this commit also changes the API to actually
>>>> export the
>>>> function instead of having it inlinable in the header.
>>>>
>>>> Thanks for Allan Cady for bringing up this issue.
>>>>
>>>> Signed-off-by: Marton Balint <cus@passwd.hu>
>>>> ---
>>>>  doc/APIchanges                               |  3 ++
>>>>  libavutil/Makefile                           |  1 +
>>>>  libavutil/timestamp.c                        | 33 ++++++++++++++++++++
>>>>  libavutil/timestamp.h                        |  8 +----
>>>>  libavutil/version.h                          |  2 +-
>>>>  tests/ref/fate/filter-metadata-scdet         | 12 +++----
>>>>  tests/ref/fate/filter-metadata-silencedetect |  2 +-
>>>>  7 files changed, 46 insertions(+), 15 deletions(-)
>>>>  create mode 100644 libavutil/timestamp.c
>>>>
>>>> diff --git a/doc/APIchanges b/doc/APIchanges
>>>> index a44c8e4f10..1afde062a5 100644
>>>> --- a/doc/APIchanges
>>>> +++ b/doc/APIchanges
>>>> @@ -2,6 +2,9 @@ The last version increases of all libraries were on
>>>> 2024-03-07
>>>>
>>>>  API changes, most recent first:
>>>>
>>>> +2024-03-xx - xxxxxxxxxx - lavu 59.3.100 - timestamp.h
>>>> +  av_ts_make_time_string() is no longer an inline function. It is
>>>> now exported.
>>>> +
>>>>  2024-03-xx - xxxxxxxxxx - lavu 59.2.100 - channel_layout.h
>>>>    Add AV_CHANNEL_LAYOUT_RETYPE_FLAG_CANONICAL.
>>>>
>>>> diff --git a/libavutil/Makefile b/libavutil/Makefile
>>>> index e7709b97d0..5e75aa1855 100644
>>>> --- a/libavutil/Makefile
>>>> +++ b/libavutil/Makefile
>>>> @@ -174,6 +174,7 @@ OBJS =
>>>> adler32.o                                                        \
>>>>        
>>>> threadmessage.o                                                  \
>>>>        
>>>> time.o                                                           \
>>>>        
>>>> timecode.o                                                       \
>>>> +      
>>>> timestamp.o                                                      \
>>>>        
>>>> tree.o                                                           \
>>>>        
>>>> twofish.o                                                        \
>>>>        
>>>> utils.o                                                          \
>>>> diff --git a/libavutil/timestamp.c b/libavutil/timestamp.c
>>>> new file mode 100644
>>>> index 0000000000..06fb47e94c
>>>> --- /dev/null
>>>> +++ b/libavutil/timestamp.c
>>>> @@ -0,0 +1,33 @@
>>>> +/*
>>>> + * This file is part of FFmpeg.
>>>> + *
>>>> + * FFmpeg is free software; you can redistribute it and/or
>>>> + * modify it under the terms of the GNU Lesser General Public
>>>> + * License as published by the Free Software Foundation; either
>>>> + * version 2.1 of the License, or (at your option) any later version.
>>>> + *
>>>> + * FFmpeg is distributed in the hope that it will be useful,
>>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>>>> + * Lesser General Public License for more details.
>>>> + *
>>>> + * You should have received a copy of the GNU Lesser General Public
>>>> + * License along with FFmpeg; if not, write to the Free Software
>>>> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
>>>> 02110-1301 USA
>>>> + */
>>>> +
>>>> +#include "timestamp.h"
>>>> +
>>>> +char *av_ts_make_time_string(char *buf, int64_t ts, const
>>>> AVRational *tb)
>>>> +{
>>>> +    if (ts == AV_NOPTS_VALUE) {
>>>> +        snprintf(buf, AV_TS_MAX_STRING_SIZE, "NOPTS");
>>>> +    } else {
>>>> +        int last = snprintf(buf, AV_TS_MAX_STRING_SIZE, "%.6f",
>>>> av_q2d(*tb) * ts);
>>>> +        last = FFMIN(last, AV_TS_MAX_STRING_SIZE - 1) - 1;
>>>> +        for (; last && buf[last] == '0'; last--);
>>>> +        for (; last && buf[last] != 'f' && (buf[last] < '0' ||
>>>> buf[0] > '9'); last--);
>>>> +        buf[last + 1] = '\0';
>>>> +    }
>>>> +    return buf;
>>>> +}
>>>
>>> As has already been said before: Simply using %.6f will discard
>>> significant digits for small timestamps. E.g. 10^-7 will simply print
>>> 0.000000, which will then be converted to "0" by your code. The old code
>>> printed 1.
> 
> Admittedly we will lose precision < 10^-6 by using %.6f, but I don't
> think this will cause any real problems, so I'd rather just leave this
> as is. Or you prefer some special handling of small values? E.g. using
> %.9f or something?
> 

Yeah, special casing small values seems good. Or just keep the original
%.6g for small values.

>>>
>>> Furthermore, there are more problems:
>>> "A double argument representing an infinity is converted in one of the
>>> styles [-]inf or [-]infinity — which style is implementation-defined."
>>> Your code would trim infinity to "inf". It would be even worse with nan:
>>> "A double argument representing a NaN is converted in one of the styles
>>> [-]nan or [-]nan(n-char-sequence) — which style, and the meaning of
>>> any n-char-sequence, is implementation-defined."
>>> Furthermore, there is no guarantee that the "decimal-point character" is
>>> actually a single character (it's a char* in struct lcov after all).
>>>
>>> If I am not mistaken, then NANs and infinities can't happen here unless
>>> the timebase is malformed (denominator zero); and in this case one could
>>> use the NOPTS codepath.
> 
> So the NAN case can't happen. The multiple character decimal separator
> is not a problem because we are trimming multiple chars. The inf instead
> of infinity case should not be a problem either, inf is parseable the
> same way as infinity, strtod() supports both. But if you prefer, I can
> make the code look for 'y' as well to stop trimming, I don't think it
> really matters. I am not sure about returning NOPTS for infinity.
> 
>> While just at it: As I already mentioned, this function has a design
>> defect: It passes the timebase by pointer and not just by value. So if
>> we add a non-inlined version of this, it should be a new function that
>> receives the timebase by value and av_ts_make_time_string() should
>> instead call this new function like
>> static inline char *av_ts_make_time_string(char *buf, int64_t ts, const
>> AVRational *tb)
>> {
>>    return new_func(buf, ts, *tb);
>> }
> 
> I am uneasy about adding another public function signature for this
> purpose, because that will still have the flaw of using a locale
> dependant decimal point.
> 

I do not really get this point here. Just because it has a flaw (I'm not
certain everyone sees the locale dependcy as a flaw) means that continue
another flaw.

- Andreas

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

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

^ permalink raw reply	[flat|nested] 37+ messages in thread

* [FFmpeg-devel] [PATCH v2 1/2] avutil/timestamp: introduce av_ts_make_time_string2 for better precision
  2024-03-22 21:58                                 ` Andreas Rheinhardt
@ 2024-03-23 11:14                                   ` Marton Balint
  2024-03-23 11:14                                     ` [FFmpeg-devel] [PATCH v2 2/2] avutil/timestamp: change precision of av_ts_make_time_string() Marton Balint
                                                       ` (2 more replies)
  0 siblings, 3 replies; 37+ messages in thread
From: Marton Balint @ 2024-03-23 11:14 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Marton Balint

av_ts_make_time_string() used "%.6g" format, but this format was losing
precision even when the timestamp to be printed was not that large. For example
for 3 hours (10800) seconds, only 1 decimal digit was printed, which made this
format inaccurate when it was used in e.g. the silencedetect filter. Other
detection filters printing timestamps had similar issues. Also time base
parameter of the function was *AVRational instead of AVRational.

Resolve these problems by introducing a new function, av_ts_make_time_string2().

We change the used format to "%.*f", use a precision of 6, except when printing
values near 0, in which case we calculate the precision dynamically to aim for
a similar precision in normal form as with %.6g.  No longer using scientific
representation can make parsing the timestamp easier for the users, we can
safely do this because the theoretical maximum of INT64_MAX*INT32_MAX still
fits into the string buffer in normal form.

We somewhat imitate %g by trimming ending zeroes and the potential decimal
point characters. In order not to trim "inf" as well, we assume that the
decimal point string does not contain the letter "f". Note that depending on
printf %f implementation, we might trim "infinity" to "inf".

Thanks for Allan Cady for bringing up this issue.

Signed-off-by: Marton Balint <cus@passwd.hu>
---
 doc/APIchanges        |  5 +++++
 libavutil/Makefile    |  1 +
 libavutil/timestamp.c | 36 ++++++++++++++++++++++++++++++++++++
 libavutil/timestamp.h |  8 ++++++++
 libavutil/version.h   |  2 +-
 5 files changed, 51 insertions(+), 1 deletion(-)
 create mode 100644 libavutil/timestamp.c

diff --git a/doc/APIchanges b/doc/APIchanges
index 2796b4d0c2..9c6fa381e1 100644
--- a/doc/APIchanges
+++ b/doc/APIchanges
@@ -2,6 +2,11 @@ The last version increases of all libraries were on 2024-03-07
 
 API changes, most recent first:
 
+2024-03-xx - xxxxxxxxxx - lavu 59.5.100 - timestamp.h
+  Add av_ts_make_time_string2() for better timestamp precision, the new
+  function accepts AVRational as time base instead of *AVRational, and is not
+  an inline function like its predecessor.
+
 2024-03-xx - xxxxxxxxxx - lavc 61.3.100 - jni.h
   Add av_jni_set_android_app_ctx() and av_jni_get_android_app_ctx().
 
diff --git a/libavutil/Makefile b/libavutil/Makefile
index 008fcfcd9c..6e6fa8d800 100644
--- a/libavutil/Makefile
+++ b/libavutil/Makefile
@@ -174,6 +174,7 @@ OBJS = adler32.o                                                        \
        threadmessage.o                                                  \
        time.o                                                           \
        timecode.o                                                       \
+       timestamp.o                                                      \
        tree.o                                                           \
        twofish.o                                                        \
        utils.o                                                          \
diff --git a/libavutil/timestamp.c b/libavutil/timestamp.c
new file mode 100644
index 0000000000..2a3e3012a4
--- /dev/null
+++ b/libavutil/timestamp.c
@@ -0,0 +1,36 @@
+/*
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "timestamp.h"
+
+char *av_ts_make_time_string2(char *buf, int64_t ts, AVRational tb)
+{
+    if (ts == AV_NOPTS_VALUE) {
+        snprintf(buf, AV_TS_MAX_STRING_SIZE, "NOPTS");
+    } else {
+        double val = av_q2d(tb) * ts;
+        double log = floor(log10(fabs(val)));
+        int precision = (isfinite(log) && log < 0) ? -log + 5 : 6;
+        int last = snprintf(buf, AV_TS_MAX_STRING_SIZE, "%.*f", precision, val);
+        last = FFMIN(last, AV_TS_MAX_STRING_SIZE - 1) - 1;
+        for (; last && buf[last] == '0'; last--);
+        for (; last && buf[last] != 'f' && (buf[last] < '0' || buf[0] > '9'); last--);
+        buf[last + 1] = '\0';
+    }
+    return buf;
+}
diff --git a/libavutil/timestamp.h b/libavutil/timestamp.h
index 2b37781eba..7e6da894df 100644
--- a/libavutil/timestamp.h
+++ b/libavutil/timestamp.h
@@ -62,6 +62,14 @@ static inline char *av_ts_make_string(char *buf, int64_t ts)
  * @param tb the timebase of the timestamp
  * @return the buffer in input
  */
+char *av_ts_make_time_string2(char *buf, int64_t ts, AVRational tb);
+
+/**
+ * Fill the provided buffer with a string containing a timestamp
+ * representation.
+ *
+ * @see av_ts_make_time_string2
+ */
 static inline char *av_ts_make_time_string(char *buf, int64_t ts,
                                            const AVRational *tb)
 {
diff --git a/libavutil/version.h b/libavutil/version.h
index 882003f719..8a1ecd4451 100644
--- a/libavutil/version.h
+++ b/libavutil/version.h
@@ -79,7 +79,7 @@
  */
 
 #define LIBAVUTIL_VERSION_MAJOR  59
-#define LIBAVUTIL_VERSION_MINOR   4
+#define LIBAVUTIL_VERSION_MINOR   5
 #define LIBAVUTIL_VERSION_MICRO 100
 
 #define LIBAVUTIL_VERSION_INT   AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \
-- 
2.35.3

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

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

^ permalink raw reply	[flat|nested] 37+ messages in thread

* [FFmpeg-devel] [PATCH v2 2/2] avutil/timestamp: change precision of av_ts_make_time_string()
  2024-03-23 11:14                                   ` [FFmpeg-devel] [PATCH v2 1/2] avutil/timestamp: introduce av_ts_make_time_string2 for better precision Marton Balint
@ 2024-03-23 11:14                                     ` Marton Balint
  2024-03-25 18:27                                     ` [FFmpeg-devel] [PATCH v2 1/2] avutil/timestamp: introduce av_ts_make_time_string2 for better precision Marton Balint
  2024-06-17 20:08                                     ` Hendrik Leppkes
  2 siblings, 0 replies; 37+ messages in thread
From: Marton Balint @ 2024-03-23 11:14 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Marton Balint

By calling the av_ts_make_time_string2() from the function we can fix the
precision issue.

Signed-off-by: Marton Balint <cus@passwd.hu>
---
 libavutil/timestamp.h                        |  4 +---
 tests/ref/fate/filter-metadata-scdet         | 12 ++++++------
 tests/ref/fate/filter-metadata-silencedetect |  2 +-
 3 files changed, 8 insertions(+), 10 deletions(-)

diff --git a/libavutil/timestamp.h b/libavutil/timestamp.h
index 7e6da894df..fa53a46b98 100644
--- a/libavutil/timestamp.h
+++ b/libavutil/timestamp.h
@@ -73,9 +73,7 @@ char *av_ts_make_time_string2(char *buf, int64_t ts, AVRational tb);
 static inline char *av_ts_make_time_string(char *buf, int64_t ts,
                                            const AVRational *tb)
 {
-    if (ts == AV_NOPTS_VALUE) snprintf(buf, AV_TS_MAX_STRING_SIZE, "NOPTS");
-    else                      snprintf(buf, AV_TS_MAX_STRING_SIZE, "%.6g", av_q2d(*tb) * ts);
-    return buf;
+    return av_ts_make_time_string2(buf, ts, *tb);
 }
 
 /**
diff --git a/tests/ref/fate/filter-metadata-scdet b/tests/ref/fate/filter-metadata-scdet
index ca5dbaaefc..d385920fcd 100644
--- a/tests/ref/fate/filter-metadata-scdet
+++ b/tests/ref/fate/filter-metadata-scdet
@@ -1,11 +1,11 @@
 pts=1620|tag:lavfi.scd.score=59.252|tag:lavfi.scd.mafd=60.175|tag:lavfi.scd.time=2.7
 pts=4140|tag:lavfi.scd.score=36.070|tag:lavfi.scd.mafd=44.209|tag:lavfi.scd.time=6.9
-pts=5800|tag:lavfi.scd.score=55.819|tag:lavfi.scd.mafd=55.819|tag:lavfi.scd.time=9.66667
+pts=5800|tag:lavfi.scd.score=55.819|tag:lavfi.scd.mafd=55.819|tag:lavfi.scd.time=9.666667
 pts=6720|tag:lavfi.scd.score=18.580|tag:lavfi.scd.mafd=22.505|tag:lavfi.scd.time=11.2
 pts=8160|tag:lavfi.scd.score=49.240|tag:lavfi.scd.mafd=49.444|tag:lavfi.scd.time=13.6
-pts=9760|tag:lavfi.scd.score=51.497|tag:lavfi.scd.mafd=51.801|tag:lavfi.scd.time=16.2667
-pts=14080|tag:lavfi.scd.score=34.165|tag:lavfi.scd.mafd=34.337|tag:lavfi.scd.time=23.4667
-pts=15700|tag:lavfi.scd.score=58.310|tag:lavfi.scd.mafd=58.315|tag:lavfi.scd.time=26.1667
-pts=18500|tag:lavfi.scd.score=16.504|tag:lavfi.scd.mafd=19.603|tag:lavfi.scd.time=30.8333
+pts=9760|tag:lavfi.scd.score=51.497|tag:lavfi.scd.mafd=51.801|tag:lavfi.scd.time=16.266667
+pts=14080|tag:lavfi.scd.score=34.165|tag:lavfi.scd.mafd=34.337|tag:lavfi.scd.time=23.466667
+pts=15700|tag:lavfi.scd.score=58.310|tag:lavfi.scd.mafd=58.315|tag:lavfi.scd.time=26.166667
+pts=18500|tag:lavfi.scd.score=16.504|tag:lavfi.scd.mafd=19.603|tag:lavfi.scd.time=30.833333
 pts=20040|tag:lavfi.scd.score=13.764|tag:lavfi.scd.mafd=19.060|tag:lavfi.scd.time=33.4
-pts=21760|tag:lavfi.scd.score=64.451|tag:lavfi.scd.mafd=64.551|tag:lavfi.scd.time=36.2667
+pts=21760|tag:lavfi.scd.score=64.451|tag:lavfi.scd.mafd=64.551|tag:lavfi.scd.time=36.266667
diff --git a/tests/ref/fate/filter-metadata-silencedetect b/tests/ref/fate/filter-metadata-silencedetect
index bc53fea047..e66ffe5fdd 100644
--- a/tests/ref/fate/filter-metadata-silencedetect
+++ b/tests/ref/fate/filter-metadata-silencedetect
@@ -1,5 +1,5 @@
 pts=0|tag:lavfi.silence_duration=0.523107|tag:lavfi.silence_end=0.690023|tag:lavfi.silence_start=0.736417
-pts=46080|tag:lavfi.silence_start=1.27626|tag:lavfi.silence_end=1.80751|tag:lavfi.silence_duration=0.531247
+pts=46080|tag:lavfi.silence_start=1.276259|tag:lavfi.silence_end=1.807506|tag:lavfi.silence_duration=0.531247
 pts=92160
 pts=138240
 pts=184320
-- 
2.35.3

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

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

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [FFmpeg-devel] [PATCH v2 1/2] avutil/timestamp: introduce av_ts_make_time_string2 for better precision
  2024-03-23 11:14                                   ` [FFmpeg-devel] [PATCH v2 1/2] avutil/timestamp: introduce av_ts_make_time_string2 for better precision Marton Balint
  2024-03-23 11:14                                     ` [FFmpeg-devel] [PATCH v2 2/2] avutil/timestamp: change precision of av_ts_make_time_string() Marton Balint
@ 2024-03-25 18:27                                     ` Marton Balint
  2024-03-25 22:13                                       ` Allan Cady via ffmpeg-devel
  2024-06-17 20:08                                     ` Hendrik Leppkes
  2 siblings, 1 reply; 37+ messages in thread
From: Marton Balint @ 2024-03-25 18:27 UTC (permalink / raw)
  To: FFmpeg development discussions and patches



On Sat, 23 Mar 2024, Marton Balint wrote:

> av_ts_make_time_string() used "%.6g" format, but this format was losing
> precision even when the timestamp to be printed was not that large. For example
> for 3 hours (10800) seconds, only 1 decimal digit was printed, which made this
> format inaccurate when it was used in e.g. the silencedetect filter. Other
> detection filters printing timestamps had similar issues. Also time base
> parameter of the function was *AVRational instead of AVRational.
>
> Resolve these problems by introducing a new function, av_ts_make_time_string2().
>
> We change the used format to "%.*f", use a precision of 6, except when printing
> values near 0, in which case we calculate the precision dynamically to aim for
> a similar precision in normal form as with %.6g.  No longer using scientific
> representation can make parsing the timestamp easier for the users, we can
> safely do this because the theoretical maximum of INT64_MAX*INT32_MAX still
> fits into the string buffer in normal form.
>
> We somewhat imitate %g by trimming ending zeroes and the potential decimal
> point characters. In order not to trim "inf" as well, we assume that the
> decimal point string does not contain the letter "f". Note that depending on
> printf %f implementation, we might trim "infinity" to "inf".
>
> Thanks for Allan Cady for bringing up this issue.

Will apply the series, so it can make it into 7.0.

Regards,
Marton

>
> Signed-off-by: Marton Balint <cus@passwd.hu>
> ---
> doc/APIchanges        |  5 +++++
> libavutil/Makefile    |  1 +
> libavutil/timestamp.c | 36 ++++++++++++++++++++++++++++++++++++
> libavutil/timestamp.h |  8 ++++++++
> libavutil/version.h   |  2 +-
> 5 files changed, 51 insertions(+), 1 deletion(-)
> create mode 100644 libavutil/timestamp.c
>
> diff --git a/doc/APIchanges b/doc/APIchanges
> index 2796b4d0c2..9c6fa381e1 100644
> --- a/doc/APIchanges
> +++ b/doc/APIchanges
> @@ -2,6 +2,11 @@ The last version increases of all libraries were on 2024-03-07
>
> API changes, most recent first:
>
> +2024-03-xx - xxxxxxxxxx - lavu 59.5.100 - timestamp.h
> +  Add av_ts_make_time_string2() for better timestamp precision, the new
> +  function accepts AVRational as time base instead of *AVRational, and is not
> +  an inline function like its predecessor.
> +
> 2024-03-xx - xxxxxxxxxx - lavc 61.3.100 - jni.h
>   Add av_jni_set_android_app_ctx() and av_jni_get_android_app_ctx().
>
> diff --git a/libavutil/Makefile b/libavutil/Makefile
> index 008fcfcd9c..6e6fa8d800 100644
> --- a/libavutil/Makefile
> +++ b/libavutil/Makefile
> @@ -174,6 +174,7 @@ OBJS = adler32.o                                                        \
>        threadmessage.o                                                  \
>        time.o                                                           \
>        timecode.o                                                       \
> +       timestamp.o                                                      \
>        tree.o                                                           \
>        twofish.o                                                        \
>        utils.o                                                          \
> diff --git a/libavutil/timestamp.c b/libavutil/timestamp.c
> new file mode 100644
> index 0000000000..2a3e3012a4
> --- /dev/null
> +++ b/libavutil/timestamp.c
> @@ -0,0 +1,36 @@
> +/*
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * FFmpeg is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with FFmpeg; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
> + */
> +
> +#include "timestamp.h"
> +
> +char *av_ts_make_time_string2(char *buf, int64_t ts, AVRational tb)
> +{
> +    if (ts == AV_NOPTS_VALUE) {
> +        snprintf(buf, AV_TS_MAX_STRING_SIZE, "NOPTS");
> +    } else {
> +        double val = av_q2d(tb) * ts;
> +        double log = floor(log10(fabs(val)));
> +        int precision = (isfinite(log) && log < 0) ? -log + 5 : 6;
> +        int last = snprintf(buf, AV_TS_MAX_STRING_SIZE, "%.*f", precision, val);
> +        last = FFMIN(last, AV_TS_MAX_STRING_SIZE - 1) - 1;
> +        for (; last && buf[last] == '0'; last--);
> +        for (; last && buf[last] != 'f' && (buf[last] < '0' || buf[0] > '9'); last--);
> +        buf[last + 1] = '\0';
> +    }
> +    return buf;
> +}
> diff --git a/libavutil/timestamp.h b/libavutil/timestamp.h
> index 2b37781eba..7e6da894df 100644
> --- a/libavutil/timestamp.h
> +++ b/libavutil/timestamp.h
> @@ -62,6 +62,14 @@ static inline char *av_ts_make_string(char *buf, int64_t ts)
>  * @param tb the timebase of the timestamp
>  * @return the buffer in input
>  */
> +char *av_ts_make_time_string2(char *buf, int64_t ts, AVRational tb);
> +
> +/**
> + * Fill the provided buffer with a string containing a timestamp
> + * representation.
> + *
> + * @see av_ts_make_time_string2
> + */
> static inline char *av_ts_make_time_string(char *buf, int64_t ts,
>                                            const AVRational *tb)
> {
> diff --git a/libavutil/version.h b/libavutil/version.h
> index 882003f719..8a1ecd4451 100644
> --- a/libavutil/version.h
> +++ b/libavutil/version.h
> @@ -79,7 +79,7 @@
>  */
>
> #define LIBAVUTIL_VERSION_MAJOR  59
> -#define LIBAVUTIL_VERSION_MINOR   4
> +#define LIBAVUTIL_VERSION_MINOR   5
> #define LIBAVUTIL_VERSION_MICRO 100
>
> #define LIBAVUTIL_VERSION_INT   AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \
> -- 
> 2.35.3
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
>
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

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

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [FFmpeg-devel] [PATCH v2 1/2] avutil/timestamp: introduce av_ts_make_time_string2 for better precision
  2024-03-25 18:27                                     ` [FFmpeg-devel] [PATCH v2 1/2] avutil/timestamp: introduce av_ts_make_time_string2 for better precision Marton Balint
@ 2024-03-25 22:13                                       ` Allan Cady via ffmpeg-devel
  0 siblings, 0 replies; 37+ messages in thread
From: Allan Cady via ffmpeg-devel @ 2024-03-25 22:13 UTC (permalink / raw)
  To: FFmpeg development discussions and patches
  Cc: Allan Cady, Andreas Rheinhardt, Marton Balint



> On Sat, 23 Mar 2024, Marton Balint wrote:


>> av_ts_make_time_string() used "%.6g" format, but this format was losing
>> precision even when the timestamp to be printed was not that large. For example
>> for 3 hours (10800) seconds, only 1 decimal digit was printed, which made this
>> format inaccurate when it was used in e.g. the silencedetect filter. Other
>> detection filters printing timestamps had similar issues. Also time base
>> parameter of the function was *AVRational instead of AVRational.
>>
>> Resolve these problems by introducing a new function, av_ts_make_time_string2().
>>
>> We change the used format to "%.*f", use a precision of 6, except when printing
>> values near 0, in which case we calculate the precision dynamically to aim for
>> a similar precision in normal form as with %.6g.  No longer using scientific
>> representation can make parsing the timestamp easier for the users, we can
>> safely do this because the theoretical maximum of INT64_MAX*INT32_MAX still
>> fits into the string buffer in normal form.
>>
>> We somewhat imitate %g by trimming ending zeroes and the potential decimal
>> point characters. In order not to trim "inf" as well, we assume that the
>> decimal point string does not contain the letter "f". Note that depending on
>> printf %f implementation, we might trim "infinity" to "inf".
>>
>> Thanks for Allan Cady for bringing up this issue.

> Will apply the series, so it can make it into 7.0.


> Regards,
> Marton


This looks great. I ran the patch against my test program with a couple
dozen vectors of inputs, and it looks perfect to me.


Thanks to both of you Marton and Andreas for seeing this through. I
really appreciate it.


Allan


>>
>> Signed-off-by: Marton Balint <cus@passwd.hu>
>> ---
>> doc/APIchanges        |  5 +++++
>> libavutil/Makefile    |  1 +
>> libavutil/timestamp.c | 36 ++++++++++++++++++++++++++++++++++++
>> libavutil/timestamp.h |  8 ++++++++
>> libavutil/version.h  |  2 +-
>> 5 files changed, 51 insertions(+), 1 deletion(-)
>> create mode 100644 libavutil/timestamp.c
>>
>> diff --git a/doc/APIchanges b/doc/APIchanges
>> index 2796b4d0c2..9c6fa381e1 100644
>> --- a/doc/APIchanges
>> +++ b/doc/APIchanges
>> @@ -2,6 +2,11 @@ The last version increases of all libraries were on 2024-03-07
>>
>> API changes, most recent first:
>>
>> +2024-03-xx - xxxxxxxxxx - lavu 59.5.100 - timestamp.h
>> +  Add av_ts_make_time_string2() for better timestamp precision, the new
>> +  function accepts AVRational as time base instead of *AVRational, and is not
>> +  an inline function like its predecessor.
>> +
>> 2024-03-xx - xxxxxxxxxx - lavc 61.3.100 - jni.h
>>  Add av_jni_set_android_app_ctx() and av_jni_get_android_app_ctx().
>>
>> diff --git a/libavutil/Makefile b/libavutil/Makefile
>> index 008fcfcd9c..6e6fa8d800 100644
>> --- a/libavutil/Makefile
>> +++ b/libavutil/Makefile
>> @@ -174,6 +174,7 @@ OBJS = adler32.o                                                        \
>>        threadmessage.o                                                  \
>>        time.o                                                          \
>>        timecode.o                                                      \
>> +      timestamp.o                                                      \
>>        tree.o                                                          \
>>        twofish.o                                                        \
>>        utils.o                                                          \
>> diff --git a/libavutil/timestamp.c b/libavutil/timestamp.c
>> new file mode 100644
>> index 0000000000..2a3e3012a4
>> --- /dev/null
>> +++ b/libavutil/timestamp.c
>> @@ -0,0 +1,36 @@
>> +/*
>> + * This file is part of FFmpeg.
>> + *
>> + * FFmpeg is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU Lesser General Public
>> + * License as published by the Free Software Foundation; either
>> + * version 2.1 of the License, or (at your option) any later version.
>> + *
>> + * FFmpeg is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> + * Lesser General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU Lesser General Public
>> + * License along with FFmpeg; if not, write to the Free Software
>> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
>> + */
>> +
>> +#include "timestamp.h"
>> +
>> +char *av_ts_make_time_string2(char *buf, int64_t ts, AVRational tb)
>> +{
>> +    if (ts == AV_NOPTS_VALUE) {
>> +        snprintf(buf, AV_TS_MAX_STRING_SIZE, "NOPTS");
>> +    } else {
>> +        double val = av_q2d(tb) * ts;
>> +        double log = floor(log10(fabs(val)));
>> +        int precision = (isfinite(log) && log < 0) ? -log + 5 : 6;
>> +        int last = snprintf(buf, AV_TS_MAX_STRING_SIZE, "%.*f", precision, val);
>> +        last = FFMIN(last, AV_TS_MAX_STRING_SIZE - 1) - 1;
>> +        for (; last && buf[last] == '0'; last--);
>> +        for (; last && buf[last] != 'f' && (buf[last] < '0' || buf[0] > '9'); last--);
>> +        buf[last + 1] = '\0';
>> +    }
>> +    return buf;
>> +}
>> diff --git a/libavutil/timestamp.h b/libavutil/timestamp.h
>> index 2b37781eba..7e6da894df 100644
>> --- a/libavutil/timestamp.h
>> +++ b/libavutil/timestamp.h
>> @@ -62,6 +62,14 @@ static inline char *av_ts_make_string(char *buf, int64_t ts)
>>  * @param tb the timebase of the timestamp
>>  * @return the buffer in input
>>  */
>> +char *av_ts_make_time_string2(char *buf, int64_t ts, AVRational tb);
>> +
>> +/**
>> + * Fill the provided buffer with a string containing a timestamp
>> + * representation.
>> + *
>> + * @see av_ts_make_time_string2
>> + */
>> static inline char *av_ts_make_time_string(char *buf, int64_t ts,
>>                                            const AVRational *tb)
>> {
>> diff --git a/libavutil/version.h b/libavutil/version.h
>> index 882003f719..8a1ecd4451 100644
>> --- a/libavutil/version.h
>> +++ b/libavutil/version.h
>> @@ -79,7 +79,7 @@
>>  */
>>
>> #define LIBAVUTIL_VERSION_MAJOR  59
>> -#define LIBAVUTIL_VERSION_MINOR  4
>> +#define LIBAVUTIL_VERSION_MINOR  5
>> #define LIBAVUTIL_VERSION_MICRO 100
>>
>> #define LIBAVUTIL_VERSION_INT  AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \
>> --
>> 2.35.3
>>
>> _______________________________________________
>> ffmpeg-devel mailing list
>> ffmpeg-devel@ffmpeg.org
>> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>>
>> To unsubscribe, visit link above, or email
>> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".


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

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

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [FFmpeg-devel] [PATCH v2 1/2] avutil/timestamp: introduce av_ts_make_time_string2 for better precision
  2024-03-23 11:14                                   ` [FFmpeg-devel] [PATCH v2 1/2] avutil/timestamp: introduce av_ts_make_time_string2 for better precision Marton Balint
  2024-03-23 11:14                                     ` [FFmpeg-devel] [PATCH v2 2/2] avutil/timestamp: change precision of av_ts_make_time_string() Marton Balint
  2024-03-25 18:27                                     ` [FFmpeg-devel] [PATCH v2 1/2] avutil/timestamp: introduce av_ts_make_time_string2 for better precision Marton Balint
@ 2024-06-17 20:08                                     ` Hendrik Leppkes
  2024-06-17 20:33                                       ` [FFmpeg-devel] [PATCH] avutil/timestamp: avoid possible FPE when 0 is passed to av_ts_make_time_string2() Marton Balint
  2 siblings, 1 reply; 37+ messages in thread
From: Hendrik Leppkes @ 2024-06-17 20:08 UTC (permalink / raw)
  To: FFmpeg development discussions and patches

On Sat, Mar 23, 2024 at 12:14 PM Marton Balint <cus@passwd.hu> wrote:
> +char *av_ts_make_time_string2(char *buf, int64_t ts, AVRational tb)
> +{
> +    if (ts == AV_NOPTS_VALUE) {
> +        snprintf(buf, AV_TS_MAX_STRING_SIZE, "NOPTS");
> +    } else {
> +        double val = av_q2d(tb) * ts;
> +        double log = floor(log10(fabs(val)));

This causes a floating point exception on some systems (div by zero)
if val ends up zero. Can we introduce a check to avoid the FPE?
log10(0) seems to be implementation defined, and may raise a FPE,
which we should avoid.

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

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

^ permalink raw reply	[flat|nested] 37+ messages in thread

* [FFmpeg-devel] [PATCH] avutil/timestamp: avoid possible FPE when 0 is passed to av_ts_make_time_string2()
  2024-06-17 20:08                                     ` Hendrik Leppkes
@ 2024-06-17 20:33                                       ` Marton Balint
  2024-06-18  7:55                                         ` Rémi Denis-Courmont
  0 siblings, 1 reply; 37+ messages in thread
From: Marton Balint @ 2024-06-17 20:33 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Marton Balint

Signed-off-by: Marton Balint <cus@passwd.hu>
---
 libavutil/timestamp.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libavutil/timestamp.c b/libavutil/timestamp.c
index 2a3e3012a4..6c231a517d 100644
--- a/libavutil/timestamp.c
+++ b/libavutil/timestamp.c
@@ -24,7 +24,7 @@ char *av_ts_make_time_string2(char *buf, int64_t ts, AVRational tb)
         snprintf(buf, AV_TS_MAX_STRING_SIZE, "NOPTS");
     } else {
         double val = av_q2d(tb) * ts;
-        double log = floor(log10(fabs(val)));
+        double log = (fpclassify(val) == FP_ZERO ? -INFINITY : floor(log10(fabs(val))));
         int precision = (isfinite(log) && log < 0) ? -log + 5 : 6;
         int last = snprintf(buf, AV_TS_MAX_STRING_SIZE, "%.*f", precision, val);
         last = FFMIN(last, AV_TS_MAX_STRING_SIZE - 1) - 1;
-- 
2.35.3

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

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

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [FFmpeg-devel] [PATCH] avutil/timestamp: avoid possible FPE when 0 is passed to av_ts_make_time_string2()
  2024-06-17 20:33                                       ` [FFmpeg-devel] [PATCH] avutil/timestamp: avoid possible FPE when 0 is passed to av_ts_make_time_string2() Marton Balint
@ 2024-06-18  7:55                                         ` Rémi Denis-Courmont
  2024-06-18 19:07                                           ` Marton Balint
  0 siblings, 1 reply; 37+ messages in thread
From: Rémi Denis-Courmont @ 2024-06-18  7:55 UTC (permalink / raw)
  To: FFmpeg development discussions and patches



Le 17 juin 2024 22:33:01 GMT+02:00, Marton Balint <cus@passwd.hu> a écrit :
>Signed-off-by: Marton Balint <cus@passwd.hu>
>---
> libavutil/timestamp.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
>diff --git a/libavutil/timestamp.c b/libavutil/timestamp.c
>index 2a3e3012a4..6c231a517d 100644
>--- a/libavutil/timestamp.c
>+++ b/libavutil/timestamp.c
>@@ -24,7 +24,7 @@ char *av_ts_make_time_string2(char *buf, int64_t ts, AVRational tb)
>         snprintf(buf, AV_TS_MAX_STRING_SIZE, "NOPTS");
>     } else {
>         double val = av_q2d(tb) * ts;
>-        double log = floor(log10(fabs(val)));
>+        double log = (fpclassify(val) == FP_ZERO ? -INFINITY : floor(log10(fabs(val))));
>         int precision = (isfinite(log) && log < 0) ? -log + 5 : 6;

Can the log be infinite anymore?

>         int last = snprintf(buf, AV_TS_MAX_STRING_SIZE, "%.*f", precision, val);
>         last = FFMIN(last, AV_TS_MAX_STRING_SIZE - 1) - 1;
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

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

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [FFmpeg-devel] [PATCH] avutil/timestamp: avoid possible FPE when 0 is passed to av_ts_make_time_string2()
  2024-06-18  7:55                                         ` Rémi Denis-Courmont
@ 2024-06-18 19:07                                           ` Marton Balint
  2024-06-29  8:24                                             ` Marton Balint
  0 siblings, 1 reply; 37+ messages in thread
From: Marton Balint @ 2024-06-18 19:07 UTC (permalink / raw)
  To: FFmpeg development discussions and patches



On Tue, 18 Jun 2024, Rémi Denis-Courmont wrote:

>
>
> Le 17 juin 2024 22:33:01 GMT+02:00, Marton Balint <cus@passwd.hu> a écrit :
>> Signed-off-by: Marton Balint <cus@passwd.hu>
>> ---
>> libavutil/timestamp.c | 2 +-
>> 1 file changed, 1 insertion(+), 1 deletion(-)
>>
>> diff --git a/libavutil/timestamp.c b/libavutil/timestamp.c
>> index 2a3e3012a4..6c231a517d 100644
>> --- a/libavutil/timestamp.c
>> +++ b/libavutil/timestamp.c
>> @@ -24,7 +24,7 @@ char *av_ts_make_time_string2(char *buf, int64_t ts, AVRational tb)
>>         snprintf(buf, AV_TS_MAX_STRING_SIZE, "NOPTS");
>>     } else {
>>         double val = av_q2d(tb) * ts;
>> -        double log = floor(log10(fabs(val)));
>> +        double log = (fpclassify(val) == FP_ZERO ? -INFINITY : floor(log10(fabs(val))));
>>         int precision = (isfinite(log) && log < 0) ? -log + 5 : 6;
>
> Can the log be infinite anymore?

Yes, in case of zero value we explicitly set it to -INFINITY, but implicit 
infinity or NaN is also possible, because av_q2d can return both.

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

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

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [FFmpeg-devel] [PATCH] avutil/timestamp: avoid possible FPE when 0 is passed to av_ts_make_time_string2()
  2024-06-18 19:07                                           ` Marton Balint
@ 2024-06-29  8:24                                             ` Marton Balint
  0 siblings, 0 replies; 37+ messages in thread
From: Marton Balint @ 2024-06-29  8:24 UTC (permalink / raw)
  To: FFmpeg development discussions and patches



On Tue, 18 Jun 2024, Marton Balint wrote:

>
>
> On Tue, 18 Jun 2024, Rémi Denis-Courmont wrote:
>
>> 
>>
>>  Le 17 juin 2024 22:33:01 GMT+02:00, Marton Balint <cus@passwd.hu> a
>>  écrit :
>>>  Signed-off-by: Marton Balint <cus@passwd.hu>
>>>  ---
>>>  libavutil/timestamp.c | 2 +-
>>>  1 file changed, 1 insertion(+), 1 deletion(-)
>>>
>>>  diff --git a/libavutil/timestamp.c b/libavutil/timestamp.c
>>>  index 2a3e3012a4..6c231a517d 100644
>>>  --- a/libavutil/timestamp.c
>>>  +++ b/libavutil/timestamp.c
>>>  @@ -24,7 +24,7 @@ char *av_ts_make_time_string2(char *buf, int64_t ts,
>>>  AVRational tb)
>>>          snprintf(buf, AV_TS_MAX_STRING_SIZE, "NOPTS");
>>>      } else {
>>>          double val = av_q2d(tb) * ts;
>>>  -        double log = floor(log10(fabs(val)));
>>>  +        double log = (fpclassify(val) == FP_ZERO ? -INFINITY :
>>>  floor(log10(fabs(val))));
>>>          int precision = (isfinite(log) && log < 0) ? -log + 5 : 6;
>>
>>  Can the log be infinite anymore?
>
> Yes, in case of zero value we explicitly set it to -INFINITY, but implicit 
> infinity or NaN is also possible, because av_q2d can return both.

Will apply.

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

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

^ permalink raw reply	[flat|nested] 37+ messages in thread

end of thread, other threads:[~2024-06-29  8:24 UTC | newest]

Thread overview: 37+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <20240221073915.26055-1-allancady.ref@yahoo.com>
2024-02-21  7:39 ` [FFmpeg-devel] [PATCH] libavutil/timestamp.h: Fix loss of precision in timestamps for silencedetect on long files Allan Cady via ffmpeg-devel
2024-02-21 20:25   ` Marton Balint
2024-02-22  1:21     ` Allan Cady via ffmpeg-devel
2024-02-22  9:16       ` Marton Balint
2024-02-23 21:44         ` Allan Cady via ffmpeg-devel
2024-02-23 21:48         ` Allan Cady via ffmpeg-devel
2024-02-23 22:47           ` Marton Balint
2024-03-11  2:56             ` [FFmpeg-devel] [PATCH] When the silencedetect filter is run against long files, the output timestamps gradually lose precision as the scan proceeds further into the file. This is because the output is formatted (in libavutil/timestamp.h) as "%.6g", which limits the total field length. Eventually, for offsets greater than 100000 seconds (about 28 hours), fractions of a second disappear altogether, and the timestamps are logged as whole seconds Allan Cady via ffmpeg-devel
2024-03-11 13:43               ` Andreas Rheinhardt
2024-03-11 14:26                 ` Andreas Rheinhardt
2024-03-11 19:49                   ` epirat07
2024-03-11 23:46                     ` [FFmpeg-devel] [libavutil/timestamp.h: Fix loss of precision in timestamps for silencedetect on long files] Allan Cady via ffmpeg-devel
2024-03-11 19:11                 ` [FFmpeg-devel] [PATCH] change av_ts_make_time_string precision Marton Balint
2024-03-12  2:57                   ` Allan Cady via ffmpeg-devel
2024-03-12 21:24                     ` Marton Balint
2024-03-13  6:03                       ` Allan Cady via ffmpeg-devel
2024-03-17 23:40                         ` [FFmpeg-devel] [PATCH] avutil/timestamp: keep microsecond precision in av_ts_make_time_string Marton Balint
2024-03-19 21:14                           ` Marton Balint
2024-03-19 23:38                           ` Andreas Rheinhardt
2024-03-20 12:44                             ` Andreas Rheinhardt
2024-03-20 19:51                               ` Marton Balint
2024-03-22 21:50                                 ` Marton Balint
2024-03-22 21:58                                 ` Andreas Rheinhardt
2024-03-23 11:14                                   ` [FFmpeg-devel] [PATCH v2 1/2] avutil/timestamp: introduce av_ts_make_time_string2 for better precision Marton Balint
2024-03-23 11:14                                     ` [FFmpeg-devel] [PATCH v2 2/2] avutil/timestamp: change precision of av_ts_make_time_string() Marton Balint
2024-03-25 18:27                                     ` [FFmpeg-devel] [PATCH v2 1/2] avutil/timestamp: introduce av_ts_make_time_string2 for better precision Marton Balint
2024-03-25 22:13                                       ` Allan Cady via ffmpeg-devel
2024-06-17 20:08                                     ` Hendrik Leppkes
2024-06-17 20:33                                       ` [FFmpeg-devel] [PATCH] avutil/timestamp: avoid possible FPE when 0 is passed to av_ts_make_time_string2() Marton Balint
2024-06-18  7:55                                         ` Rémi Denis-Courmont
2024-06-18 19:07                                           ` Marton Balint
2024-06-29  8:24                                             ` Marton Balint
2024-03-17 23:43                         ` [FFmpeg-devel] [PATCH] change av_ts_make_time_string precision Marton Balint
2024-03-19 22:46                           ` Allan Cady via ffmpeg-devel
2024-03-13  6:09                       ` Allan Cady via ffmpeg-devel
2024-03-11  3:57             ` [FFmpeg-devel] [PATCH] libavutil/timestamp.h: Fix loss of precision in timestamps for silencedetect on long files Allan Cady via ffmpeg-devel
2024-03-11  4:13             ` Allan Cady via ffmpeg-devel

Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

This inbox may be cloned and mirrored by anyone:

	git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \
		ffmpegdev@gitmailbox.com
	public-inbox-index ffmpegdev

Example config snippet for mirrors.


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git