[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