On 11 Mar 2024, at 15:26, Andreas Rheinhardt wrote:
> Andreas Rheinhardt: >> Allan Cady via ffmpeg-devel: >>> From: "Allan Cady" <allanc...@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 <allanc...@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-requ...@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-requ...@ffmpeg.org with subject "unsubscribe".