PR #23481 opened by Marton Balint (cus)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23481
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23481.patch

- Minor cleanups
- Fix a regression in `asf_o` demxer which made it report an error when opening 
most files
- Factorize FILETIME and AVTIME conversion functions
- Use the factorized functions all over wtv and asf muxers/demuxers
- Enhance precision of timestamps, and use proper ISO 8601 formatting
- Add support for presenting creation_time metadata for the asf demuxer.


>From 23603abcf17704ae60bf93f1eaf908d4214a13f6 Mon Sep 17 00:00:00 2001
From: Marton Balint <[email protected]>
Date: Sat, 13 Jun 2026 22:29:18 +0200
Subject: [PATCH 1/7] avformat/asfdec_o: remove constants already defined in
 asf.h

Signed-off-by: Marton Balint <[email protected]>
---
 libavformat/asfdec_o.c | 7 -------
 1 file changed, 7 deletions(-)

diff --git a/libavformat/asfdec_o.c b/libavformat/asfdec_o.c
index cc9c5ec396..9338d4ee63 100644
--- a/libavformat/asfdec_o.c
+++ b/libavformat/asfdec_o.c
@@ -37,14 +37,7 @@
 #include "asf.h"
 #include "asfcrypt.h"
 
-#define ASF_BOOL                              0x2
-#define ASF_WORD                              0x5
-#define ASF_GUID                              0x6
-#define ASF_DWORD                             0x3
-#define ASF_QWORD                             0x4
-#define ASF_UNICODE                           0x0
 #define ASF_FLAG_BROADCAST                    0x1
-#define ASF_BYTE_ARRAY                        0x1
 #define ASF_TYPE_AUDIO                        0x2
 #define ASF_TYPE_VIDEO                        0x1
 #define ASF_STREAM_NUM                        0x7F
-- 
2.52.0


>From 2d6f060dd0f81c922384b41500366ff1012b368e Mon Sep 17 00:00:00 2001
From: Marton Balint <[email protected]>
Date: Sun, 14 Jun 2026 00:05:44 +0200
Subject: [PATCH 2/7] avformat/asfdec_o: fix EOF check in read_header

Since d1ac6456369fecdc99044e69bb22130bbedc0558 ff_read_guid() never returns
EOF. This change completely broke the asfdec_o demuxer returning failure on
every file.

Signed-off-by: Marton Balint <[email protected]>
---
 libavformat/asfdec_o.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libavformat/asfdec_o.c b/libavformat/asfdec_o.c
index 9338d4ee63..dd022cb404 100644
--- a/libavformat/asfdec_o.c
+++ b/libavformat/asfdec_o.c
@@ -1616,7 +1616,7 @@ static int asf_read_header(AVFormatContext *s)
             break;
         asf->offset = avio_tell(pb);
         if ((ret = ff_get_guid(pb, &guid)) < 0) {
-            if (ret == AVERROR_EOF && asf->data_reached)
+            if (avio_feof(pb) && asf->data_reached)
                 break;
             else
                 goto failed;
-- 
2.52.0


>From 97138ba0e49351c56c060d3e75d1c3f1e7acf403 Mon Sep 17 00:00:00 2001
From: Marton Balint <[email protected]>
Date: Sun, 14 Jun 2026 01:16:18 +0200
Subject: [PATCH 3/7] avformat/asf: add helper functions to convert between
 filetime and avtime

Signed-off-by: Marton Balint <[email protected]>
---
 libavformat/asf.c | 16 ++++++++++++++++
 libavformat/asf.h |  5 +++++
 2 files changed, 21 insertions(+)

diff --git a/libavformat/asf.c b/libavformat/asf.c
index 5224a5d88d..117c9fff4c 100644
--- a/libavformat/asf.c
+++ b/libavformat/asf.c
@@ -152,3 +152,19 @@ int ff_asf_handle_byte_array(AVFormatContext *s, const 
char *name,
 
     return 1;
 }
+
+int64_t ff_asf_avtime_to_filetime(int64_t avtime)
+{
+    int64_t t;
+    t  = avtime * INT64_C(10);
+    t += INT64_C(116444736000000000);
+    return t;
+}
+
+int64_t ff_asf_filetime_to_avtime(int64_t filetime)
+{
+    int64_t t;
+    t  = filetime / INT64_C(10);
+    t -= INT64_C(11644473600000000);
+    return t;
+}
diff --git a/libavformat/asf.h b/libavformat/asf.h
index b77dabe1ff..6379908700 100644
--- a/libavformat/asf.h
+++ b/libavformat/asf.h
@@ -113,6 +113,11 @@ extern const AVMetadataConv ff_asf_metadata_conv[];
 int ff_asf_handle_byte_array(AVFormatContext *s, const char *name,
                              int val_len);
 
+/* convert from av time to windows filetime */
+int64_t ff_asf_avtime_to_filetime(int64_t avtime);
+
+/* convert from windows filetime to av time */
+int64_t ff_asf_filetime_to_avtime(int64_t filetime);
 
 #define ASF_PACKET_FLAG_ERROR_CORRECTION_PRESENT 0x80 //1000 0000
 
-- 
2.52.0


>From ce666990bfb5cb3ecb9e7bf86d74b38e070e5b59 Mon Sep 17 00:00:00 2001
From: Marton Balint <[email protected]>
Date: Sun, 14 Jun 2026 01:20:08 +0200
Subject: [PATCH 4/7] avformat/wtvdec: use ISO timestamps with timezone and
 higher precision

Signed-off-by: Marton Balint <[email protected]>
---
 libavformat/wtvdec.c | 58 +++++++++-----------------------------------
 1 file changed, 12 insertions(+), 46 deletions(-)

diff --git a/libavformat/wtvdec.c b/libavformat/wtvdec.c
index 088f15f87e..873ba72243 100644
--- a/libavformat/wtvdec.c
+++ b/libavformat/wtvdec.c
@@ -26,13 +26,11 @@
  */
 
 #include <inttypes.h>
-#include <time.h>
 
 #include "libavutil/channel_layout.h"
 #include "libavutil/intreadwrite.h"
 #include "libavutil/intfloat.h"
 #include "libavutil/mem.h"
-#include "libavutil/time_internal.h"
 #include "avformat.h"
 #include "avio_internal.h"
 #include "demux.h"
@@ -385,51 +383,19 @@ static int read_probe(const AVProbeData *p)
 }
 
 /**
- * Convert win32 FILETIME to ISO-8601 string
- * @return <0 on error
+ * Convert crazy time (100ns since 1 Jan 0001) to av time (unix timestamp in 
microseconds)
  */
-static int filetime_to_iso8601(char *buf, int buf_size, int64_t value)
+static int64_t crazytime_to_avtime(int64_t value)
 {
-    time_t t = (value / 10000000LL) - 11644473600LL;
-    struct tm tmbuf;
-    struct tm *tm = gmtime_r(&t, &tmbuf);
-    if (!tm)
-        return -1;
-    if (!strftime(buf, buf_size, "%Y-%m-%d %H:%M:%S", tm))
-        return -1;
-    return 0;
+    return (value / 10LL) - 719162LL*86400000000LL;
 }
 
 /**
- * Convert crazy time (100ns since 1 Jan 0001) to ISO-8601 string
- * @return <0 on error
+ * Convert OLE DATE to av time (unix timestamp in microseconds)
  */
-static int crazytime_to_iso8601(char *buf, int buf_size, int64_t value)
+static int64_t oledate_to_avtime(int64_t value)
 {
-    time_t t = (value / 10000000LL) - 719162LL*86400LL;
-    struct tm tmbuf;
-    struct tm *tm = gmtime_r(&t, &tmbuf);
-    if (!tm)
-        return -1;
-    if (!strftime(buf, buf_size, "%Y-%m-%d %H:%M:%S", tm))
-        return -1;
-    return 0;
-}
-
-/**
- * Convert OLE DATE to ISO-8601 string
- * @return <0 on error
- */
-static int oledate_to_iso8601(char *buf, int buf_size, int64_t value)
-{
-    time_t t = (av_int2double(value) - 25569.0) * 86400;
-    struct tm tmbuf;
-    struct tm *tm= gmtime_r(&t, &tmbuf);
-    if (!tm)
-        return -1;
-    if (!strftime(buf, buf_size, "%Y-%m-%d %H:%M:%S", tm))
-        return -1;
-    return 0;
+    return (av_int2double(value) - 25569.0) * 86400000000;
 }
 
 static void get_attachment(AVFormatContext *s, AVIOContext *pb, int length)
@@ -489,15 +455,15 @@ static void get_tag(AVFormatContext *s, AVIOContext *pb, 
const char *key, int ty
         int64_t num = avio_rl64(pb);
         if (!strcmp(key, "WM/EncodingTime") ||
             !strcmp(key, "WM/MediaOriginalBroadcastDateTime")) {
-            if (filetime_to_iso8601(buf, sizeof(buf), num) < 0)
-                return;
+            ff_dict_set_timestamp(&s->metadata, key, 
ff_asf_filetime_to_avtime(num));
+            return;
         } else if (!strcmp(key, "WM/WMRVEncodeTime") ||
                    !strcmp(key, "WM/WMRVEndTime")) {
-            if (crazytime_to_iso8601(buf, sizeof(buf), num) < 0)
-                return;
+            ff_dict_set_timestamp(&s->metadata, key, crazytime_to_avtime(num));
+            return;
         } else if (!strcmp(key, "WM/WMRVExpirationDate")) {
-            if (oledate_to_iso8601(buf, sizeof(buf), num) < 0)
-                return;
+            ff_dict_set_timestamp(&s->metadata, key, oledate_to_avtime(num));
+            return;
         } else if (!strcmp(key, "WM/WMRVBitrate"))
             snprintf(buf, sizeof(buf), "%f", av_int2double(num));
         else
-- 
2.52.0


>From 98b082517d8b524ac2892ba8eb51e0decbaedaac Mon Sep 17 00:00:00 2001
From: Marton Balint <[email protected]>
Date: Sun, 14 Jun 2026 01:21:22 +0200
Subject: [PATCH 5/7] avformat/asfenc: use common function for timestamp
 conversion

Signed-off-by: Marton Balint <[email protected]>
---
 libavformat/asfenc.c | 12 +-----------
 1 file changed, 1 insertion(+), 11 deletions(-)

diff --git a/libavformat/asfenc.c b/libavformat/asfenc.c
index 75285b065c..59638e7b22 100644
--- a/libavformat/asfenc.c
+++ b/libavformat/asfenc.c
@@ -294,16 +294,6 @@ static void put_chunk(AVFormatContext *s, int type,
     asf->seqno++;
 }
 
-/* convert from av time to windows time */
-static int64_t unix_to_file_time(int64_t ti)
-{
-    int64_t t;
-
-    t  = ti * INT64_C(10);
-    t += INT64_C(116444736000000000);
-    return t;
-}
-
 static int32_t get_send_time(ASFContext *asf, int64_t pres_time, uint64_t 
*offset)
 {
     int32_t send_time = 0;
@@ -442,7 +432,7 @@ static int asf_write_header1(AVFormatContext *s, int64_t 
file_size,
     hpos          = put_header(pb, &ff_asf_file_header);
     ff_put_guid(pb, &ff_asf_my_guid);
     avio_wl64(pb, file_size);
-    avio_wl64(pb, unix_to_file_time(asf->creation_time));
+    avio_wl64(pb, ff_asf_avtime_to_filetime(asf->creation_time));
     avio_wl64(pb, asf->nb_packets); /* number of packets */
     avio_wl64(pb, duration); /* end time stamp (in 100ns units) */
     avio_wl64(pb, asf->duration); /* duration (in 100ns units) */
-- 
2.52.0


>From bc28c3b0d80caef4f277ee6608563b55645f8439 Mon Sep 17 00:00:00 2001
From: Marton Balint <[email protected]>
Date: Sun, 14 Jun 2026 01:34:09 +0200
Subject: [PATCH 6/7] avformat/asfdec_o: use common function for setting
 creation time

This also increases precision.

Signed-off-by: Marton Balint <[email protected]>
---
 libavformat/asfdec_o.c | 25 +++----------------------
 1 file changed, 3 insertions(+), 22 deletions(-)

diff --git a/libavformat/asfdec_o.c b/libavformat/asfdec_o.c
index dd022cb404..f3d3d06891 100644
--- a/libavformat/asfdec_o.c
+++ b/libavformat/asfdec_o.c
@@ -19,15 +19,12 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
-#include <time.h>
-
 #include "libavutil/attributes.h"
 #include "libavutil/common.h"
 #include "libavutil/dict.h"
 #include "libavutil/internal.h"
 #include "libavutil/mathematics.h"
 #include "libavutil/mem.h"
-#include "libavutil/time_internal.h"
 
 #include "avformat.h"
 #include "avlanguage.h"
@@ -534,31 +531,15 @@ static int asf_read_properties(AVFormatContext *s, const 
GUIDParseTable *g)
 {
     ASFContext *asf = s->priv_data;
     AVIOContext *pb = s->pb;
-    time_t creation_time;
+    int64_t creation_time;
 
     avio_rl64(pb); // read object size
     avio_skip(pb, 16); // skip File ID
     avio_skip(pb, 8);  // skip File size
     creation_time = avio_rl64(pb);
     if (!(asf->b_flags & ASF_FLAG_BROADCAST)) {
-        struct tm tmbuf;
-        struct tm *tm;
-        char buf[64];
-
-        // creation date is in 100 ns units from 1 Jan 1601, conversion to s
-        creation_time /= 10000000;
-        // there are 11644473600 seconds between 1 Jan 1601 and 1 Jan 1970
-        creation_time -= 11644473600;
-        tm = gmtime_r(&creation_time, &tmbuf);
-        if (tm) {
-            if (!strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", tm))
-                buf[0] = '\0';
-        } else
-            buf[0] = '\0';
-        if (buf[0]) {
-            if (av_dict_set(&s->metadata, "creation_time", buf, 0) < 0)
-                av_log(s, AV_LOG_WARNING, "av_dict_set failed.\n");
-        }
+        if (ff_dict_set_timestamp(&s->metadata, "creation_time", 
ff_asf_filetime_to_avtime(creation_time)) < 0)
+            av_log(s, AV_LOG_WARNING, "av_dict_set failed.\n");
     }
     asf->nb_packets  = avio_rl64(pb);
     asf->duration    = avio_rl64(pb) / 10000; // stream duration
-- 
2.52.0


>From b9bbb262e59f99f61f484b5ff48773dbc29d4252 Mon Sep 17 00:00:00 2001
From: Marton Balint <[email protected]>
Date: Sun, 14 Jun 2026 01:34:54 +0200
Subject: [PATCH 7/7] avformat/asfdec_f: set creation time as metadata

Signed-off-by: Marton Balint <[email protected]>
---
 libavformat/asfdec_f.c                | 1 +
 tests/ref/fate/generic-tags-remux-asf | 1 +
 tests/ref/fate/id3v2-wma-comm         | 1 +
 3 files changed, 3 insertions(+)

diff --git a/libavformat/asfdec_f.c b/libavformat/asfdec_f.c
index 1e9ecfe91f..2948461ab1 100644
--- a/libavformat/asfdec_f.c
+++ b/libavformat/asfdec_f.c
@@ -295,6 +295,7 @@ static int asf_read_file_properties(AVFormatContext *s)
     asf->hdr.max_bitrate = avio_rl32(pb);
     s->packet_size       = asf->hdr.max_pktsize;
 
+    ff_dict_set_timestamp(&s->metadata, "creation_time", 
ff_asf_filetime_to_avtime(asf->hdr.create_time));
     return 0;
 }
 
diff --git a/tests/ref/fate/generic-tags-remux-asf 
b/tests/ref/fate/generic-tags-remux-asf
index 982ac7cf1c..3f8f0ee0cd 100644
--- a/tests/ref/fate/generic-tags-remux-asf
+++ b/tests/ref/fate/generic-tags-remux-asf
@@ -29,6 +29,7 @@
 0,        928,        928,       46,      743, 0xdf740eee
 0,        975,        975,       46,      743, 0x41c84afe
 [FORMAT]
+TAG:creation_time=1970-01-01T00:00:00.000000Z
 TAG:publisher=M83 Recording Inc
 TAG:album_artist=M83
 TAG:composer=Anthony Gonzalez
diff --git a/tests/ref/fate/id3v2-wma-comm b/tests/ref/fate/id3v2-wma-comm
index 5a615d6d93..58130ed733 100644
--- a/tests/ref/fate/id3v2-wma-comm
+++ b/tests/ref/fate/id3v2-wma-comm
@@ -21,4 +21,5 @@ TAG:composer=Jacques Higelin
 TAG:WM/EncodingTime=1120324736
 TAG:album_artist=Jacques Higelin
 TAG:WM/Provider=User Feedback
+TAG:creation_time=2003-03-23T11:52:22.967000Z
 [/FORMAT]
-- 
2.52.0

_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to