[vlc-devel] [PATCH] demux: asf: Add support for TimeCode

Ahmed Hamed ahmedhamed3699 at gmail.com
Mon Apr 21 00:55:07 UTC 2025


Support is added via payload extension and index object, timecode data is parsed and stored.

---
 include/vlc_ancillary.h         |   6 ++
 modules/demux/asf/asfpacket.c   |  24 ++++++-
 modules/demux/asf/asfpacket.h   |   1 +
 modules/demux/asf/libasf.c      | 112 +++++++++++++++++++++++++++++++-
 modules/demux/asf/libasf.h      |  42 ++++++++++++
 modules/demux/asf/libasf_guid.h |   2 +-
 6 files changed, 183 insertions(+), 4 deletions(-)

diff --git a/include/vlc_ancillary.h b/include/vlc_ancillary.h
index 7892d0229c..ca11952e32 100644
--- a/include/vlc_ancillary.h
+++ b/include/vlc_ancillary.h
@@ -229,5 +229,11 @@ typedef struct vlc_vpx_alpha_t
     uint8_t *data;
 } vlc_vpx_alpha_t;
 
+/**
+ * SMPTE timecode data
+ */
+
+#define VLC_ANCILLARY_ID_SMPTE VLC_FOURCC('S','M','P','T')
+
 /** @} */
 #endif /* VLC_ANCILLARY_H */
diff --git a/modules/demux/asf/asfpacket.c b/modules/demux/asf/asfpacket.c
index 2e7790c08e..25f635a353 100644
--- a/modules/demux/asf/asfpacket.c
+++ b/modules/demux/asf/asfpacket.c
@@ -24,6 +24,7 @@
 #endif
 
 #include "asfpacket.h"
+#include <vlc_ancillary.h>
 #include <limits.h>
 
 #ifndef NDEBUG
@@ -118,13 +119,23 @@ static int DemuxSubPayload( asf_packet_sys_t *p_packetsys,
     if ( b_keyframe )
         p_frag->i_flags |= BLOCK_FLAG_TYPE_I;
 
+    char psz_timecode[16];
+    snprintf( psz_timecode, sizeof(psz_timecode), "%02u:%02u:%02u:%02u",
+            ( p_tkinfo->p_timecode->i_timecode >> 24 ) & 0xFF,
+            ( p_tkinfo->p_timecode->i_timecode >> 16 ) & 0xFF,
+            ( p_tkinfo->p_timecode->i_timecode >> 8 ) & 0xFF,
+              p_tkinfo->p_timecode->i_timecode & 0xFF );
+    struct vlc_ancillary *p_anc = vlc_ancillary_Create( psz_timecode, VLC_ANCILLARY_ID_SMPTE );
+    if ( vlc_frame_AttachAncillary( p_frag, p_anc ) != VLC_SUCCESS )
+        vlc_ancillary_Release( p_anc );
+
     block_ChainAppend( pp_frame, p_frag );
 
     return 0;
 }
 
 static void ParsePayloadExtensions( asf_packet_sys_t *p_packetsys,
-                                    const asf_track_info_t *p_tkinfo,
+                                    asf_track_info_t *p_tkinfo,
                                     const uint8_t *p_data, size_t i_data,
                                     bool *b_keyframe )
 {
@@ -185,6 +196,17 @@ static void ParsePayloadExtensions( asf_packet_sys_t *p_packetsys,
             if ( i_payload_extensions_size != 48 ) goto sizeerror;
             /* const int64_t i_pts = GetQWLE(&p_data[8]); */
         }
+        else if ( guidcmp( &p_ext->i_extension_id, &mfasf_sampleextension_smpte_guid ) )
+        {
+            if ( i_payload_extensions_size != 14 ) goto sizeerror;
+            p_tkinfo->p_timecode->i_reserved = GetDWLE( p_data + 10 );
+            if ( p_tkinfo->p_timecode->i_reserved == 0 )
+            {
+                p_tkinfo->p_timecode->i_timecode_range = GetWLE( p_data );
+                p_tkinfo->p_timecode->i_timecode = GetDWLE( p_data + 2 );
+                p_tkinfo->p_timecode->i_user_bits = GetDWLE( p_data + 6 );
+            }
+        }
 #if 0
         else
         {
diff --git a/modules/demux/asf/asfpacket.h b/modules/demux/asf/asfpacket.h
index 6d52a59304..1f74cb2ab3 100644
--- a/modules/demux/asf/asfpacket.h
+++ b/modules/demux/asf/asfpacket.h
@@ -35,6 +35,7 @@ typedef struct
     block_t *p_frame; /* used to gather complete frame */
     asf_object_stream_properties_t *p_sp;
     asf_object_extended_stream_properties_t *p_esp;
+    asf_payload_extension_system_timecode_t *p_timecode;
     int i_cat;
     struct
     {
diff --git a/modules/demux/asf/libasf.c b/modules/demux/asf/libasf.c
index 8128bcb88c..4188279f5b 100644
--- a/modules/demux/asf/libasf.c
+++ b/modules/demux/asf/libasf.c
@@ -310,6 +310,107 @@ static void ASF_FreeObject_Index( asf_object_t *p_obj )
     FREENULL( p_index->index_entry );
 }
 
+static void ASF_FreeObject_TimecodeIndex( asf_object_t *p_obj )
+{
+    asf_object_timecode_index_t *p_index = &p_obj->timecode_index;
+
+    for ( uint32_t i = 0; i < p_index->i_index_blocks_count; i++ )
+    {
+        asf_timecode_index_block_t *p_block = &p_index->p_index_blocks[i];
+        for ( uint32_t k = 0; k < p_block->i_index_entry_count; k++ )
+            FREENULL( p_block->p_index_entries[k].p_offsets );
+        FREENULL( p_block->p_index_entries );
+        FREENULL( p_block->p_block_positions );
+    }
+    FREENULL( p_index->p_index_blocks );
+    FREENULL( p_index->p_index_specifiers );
+    p_index->i_index_specifiers_count = 0;
+    p_index->i_index_blocks_count = 0;
+}
+
+static int ASF_ReadObject_TimecodeIndex( stream_t *s, asf_object_t *p_obj )
+{
+    asf_object_timecode_index_t *p_index = &p_obj->timecode_index;
+    const uint8_t *p_peek, *p_data;
+
+    if ( p_index->i_object_size < 34
+      || p_index->i_object_size > INT32_MAX
+      || vlc_stream_Peek(s, &p_peek, p_index->i_object_size)
+         < (int64_t)p_index->i_object_size )
+      return VLC_SUCCESS;
+
+    p_data = p_peek + ASF_OBJECT_COMMON_SIZE;
+    p_index->i_reserved = GetDWLE( p_data );
+    if ( p_index->i_reserved != 1 )
+        return VLC_SUCCESS;
+
+    p_index->i_index_specifiers_count = GetWLE( p_data + 4 );
+    p_index->i_index_blocks_count = GetDWLE( p_data + 6 );
+    p_index->p_index_specifiers = NULL;
+    p_index->p_index_blocks = NULL;
+    p_data += 10;
+
+    p_index->p_index_specifiers = calloc( p_index->i_index_specifiers_count, sizeof(asf_timecode_index_specifier_t) );
+    if ( !p_index->p_index_specifiers )
+    {
+        p_index->i_index_specifiers_count = 0;
+        return VLC_ENOMEM;
+    }
+
+    for ( uint32_t i = 0; i < p_index->i_index_specifiers_count; i++ )
+    {
+        p_index->p_index_specifiers[i].i_stream_number = GetWLE( p_data );
+        p_index->p_index_specifiers[i].i_index_type = GetWLE( p_data + 2 );
+        p_data += 4;
+    }
+
+    p_index->p_index_blocks = calloc( p_index->i_index_blocks_count, sizeof(asf_timecode_index_block_t) );
+    if (!p_index->p_index_blocks)
+    {
+        FREENULL( p_index->p_index_specifiers );
+        p_index->i_index_specifiers_count = 0;
+        p_index->i_index_blocks_count = 0;
+        return VLC_ENOMEM;
+    }
+
+    for ( uint32_t i = 0; i < p_index->i_index_blocks_count; i++ )
+    {
+        asf_timecode_index_block_t *p_block = &p_index->p_index_blocks[i];
+        p_block->i_index_entry_count = GetDWLE( p_data );
+        p_block->i_timecode_range = GetWLE( p_data + 4 );
+        p_data += 6;
+
+        p_block->p_block_positions = calloc( p_index->i_index_specifiers_count, sizeof(uint64_t) );
+        if ( !p_block->p_block_positions ) goto no_mem_error;
+        for ( uint32_t j = 0; j < p_index->i_index_specifiers_count; j++ )
+        {
+            p_block->p_block_positions[j] = GetQWLE( p_data );
+            p_data += 8;
+        }
+
+        p_block->p_index_entries = calloc( p_block->i_index_entry_count, sizeof(asf_timecode_index_entry_t) );
+        if ( !p_block->p_index_entries ) goto no_mem_error;
+        for ( uint32_t j = 0; j < p_block->i_index_entry_count; j++ )
+        {
+            p_block->p_index_entries[j].i_timecode = GetDWLE( p_data );
+            p_data += 4;
+            p_block->p_index_entries[j].p_offsets = calloc( p_index->i_index_specifiers_count, sizeof(uint32_t) );
+            if ( !p_block->p_index_entries[j].p_offsets ) goto no_mem_error;
+            for ( uint32_t k = 0; k < p_index->i_index_specifiers_count; k++ )
+            {
+                p_block->p_index_entries[j].p_offsets[k] = GetDWLE( p_data );
+                p_data += 4;
+            }
+        }
+    }
+
+    return VLC_SUCCESS;
+
+no_mem_error:
+    ASF_FreeObject_TimecodeIndex( p_obj );
+    return VLC_ENOMEM;
+}
+
 static int ASF_ReadObject_file_properties( stream_t *s, asf_object_t *p_obj )
 {
     asf_object_file_properties_t *p_fp = &p_obj->file_properties;
@@ -1447,6 +1548,8 @@ static const struct ASF_Object_Function_entry
       ASF_ReadObject_Data, ASF_FreeObject_Null },
     { &asf_object_simple_index_guid, ASF_OBJECT_INDEX,
       ASF_ReadObject_Index, ASF_FreeObject_Index },
+    { &asf_object_timecode_index_guid, ASF_OBJECT_INDEX,
+      ASF_ReadObject_TimecodeIndex, ASF_FreeObject_TimecodeIndex },
     { &asf_object_file_properties_guid, ASF_OBJECT_FILE_PROPERTIES,
       ASF_ReadObject_file_properties, ASF_FreeObject_Null },
     { &asf_object_stream_properties_guid, ASF_OBJECT_STREAM_PROPERTIES,
@@ -1615,6 +1718,7 @@ static const struct
     { &asf_object_data_guid, "Data" },
     { &asf_object_index_guid, "Index" },
     { &asf_object_simple_index_guid, "Simple Index" },
+    { &asf_object_timecode_index_guid, "Timecode Index"},
     { &asf_object_file_properties_guid, "File Properties" },
     { &asf_object_stream_properties_guid, "Stream Properties" },
     { &asf_object_content_description_guid, "Content Description" },
@@ -1715,6 +1819,7 @@ asf_object_root_t *ASF_ReadObjectRoot( stream_t *s, int b_seekable )
     p_root->p_data  = NULL;
     p_root->p_fp    = NULL;
     p_root->p_index = NULL;
+    p_root->p_timecode_index = NULL;
     p_root->p_metadata = NULL;
 
     for( ; ; )
@@ -1737,8 +1842,11 @@ asf_object_root_t *ASF_ReadObjectRoot( stream_t *s, int b_seekable )
                 p_root->p_data = (asf_object_data_t*)p_obj;
             break;
             case( ASF_OBJECT_INDEX ):
-                if ( p_root->p_index ) break;
-                p_root->p_index = (asf_object_index_t*)p_obj;
+                if ( p_root->p_index && p_root->p_timecode_index) break;
+                if ( guidcmp(&p_obj->common.i_object_id, &asf_object_timecode_index_guid) )
+                    p_root->p_timecode_index = (asf_object_timecode_index_t *)p_obj;
+                else
+                    p_root->p_index = (asf_object_index_t *)p_obj;
                 break;
             default:
                 msg_Warn( s, "unknown top-level object found: " GUID_FMT,
diff --git a/modules/demux/asf/libasf.h b/modules/demux/asf/libasf.h
index 106c825a77..8981556b36 100644
--- a/modules/demux/asf/libasf.h
+++ b/modules/demux/asf/libasf.h
@@ -54,6 +54,26 @@ typedef struct
 
 } asf_index_entry_t;
 
+typedef struct
+{
+    uint16_t i_stream_number;
+    uint16_t i_index_type;
+} asf_timecode_index_specifier_t;
+
+typedef struct
+{
+    uint32_t i_timecode;
+    uint32_t *p_offsets;
+} asf_timecode_index_entry_t;
+
+typedef struct
+{
+    uint32_t i_index_entry_count;
+    uint16_t i_timecode_range;
+    uint64_t *p_block_positions;
+    asf_timecode_index_entry_t *p_index_entries;
+} asf_timecode_index_block_t;
+
 /****************************************************************************
  * High level asf object
  ****************************************************************************/
@@ -90,6 +110,18 @@ typedef struct
 
 } asf_object_index_t;
 
+typedef struct
+{
+    ASF_OBJECT_COMMON
+    uint32_t i_reserved; /* must be 1 */
+    uint16_t i_index_specifiers_count;
+    uint32_t i_index_blocks_count;
+
+    asf_timecode_index_specifier_t *p_index_specifiers;
+    asf_timecode_index_block_t *p_index_blocks;
+
+} asf_object_timecode_index_t;
+
 /****************************************************************************
  * Sub level asf object
  ****************************************************************************/
@@ -260,6 +292,14 @@ typedef struct
     uint32_t i_info_length;
     char     *pi_info;
 } asf_payload_extension_system_t;
+
+typedef struct
+{
+    uint16_t    i_timecode_range;
+    uint32_t    i_timecode;
+    uint32_t    i_user_bits;
+    uint32_t    i_reserved; /* must be 0 */
+} asf_payload_extension_system_timecode_t;
 #define ASF_EXTENSION_VIDEOFRAME_NEWFRAME  0x08
 #define ASF_EXTENSION_VIDEOFRAME_IFRAME    0x01
 #define ASF_EXTENSION_VIDEOFRAME_TYPE_MASK 0x07
@@ -350,6 +390,7 @@ typedef struct
     asf_object_data_t   *p_data;
     /* could be NULL if !b_seekable or not-present */
     asf_object_index_t  *p_index;
+    asf_object_timecode_index_t *p_timecode_index;
 
     /* from asf_object_header_t */
     asf_object_file_properties_t *p_fp;
@@ -368,6 +409,7 @@ typedef union asf_object_u
     asf_object_header_t header;
     asf_object_data_t   data;
     asf_object_index_t  index;
+    asf_object_timecode_index_t timecode_index;
     asf_object_root_t   root;
     asf_object_file_properties_t    file_properties;
     asf_object_stream_properties_t  stream_properties;
diff --git a/modules/demux/asf/libasf_guid.h b/modules/demux/asf/libasf_guid.h
index 984d50b0f7..3a59546829 100644
--- a/modules/demux/asf/libasf_guid.h
+++ b/modules/demux/asf/libasf_guid.h
@@ -232,7 +232,7 @@ static const vlc_guid_t mfasf_sampleextension_sampleduration_guid =
 static const vlc_guid_t mfasf_sampleextension_outputcleanpoint_guid =
 {0xF72A3C6F, 0x6EB4, 0x4EBC, {0xB1, 0x92, 0x09, 0xAD, 0x97, 0x59, 0xE8, 0x28}};
 
-static const vlc_guid_t mfasf_sampleextension_smtpe_guid =
+static const vlc_guid_t mfasf_sampleextension_smpte_guid =
 {0x399595EC, 0x8667, 0x4E2D, {0x8F, 0xDB, 0x98, 0x81, 0x4C, 0xE7, 0x6C, 0x1E}};
 
 static const vlc_guid_t mfasf_sampleextension_filename_guid =
-- 
2.43.0



More information about the vlc-devel mailing list