[vlc-devel] [PATCH] demux: asf: Add support for TimeCode
Felix Paul Kühne
fkuehne at videolan.org
Mon Apr 21 05:22:19 UTC 2025
Hello Ahmed,
Thanks for your submission! We no longer accept contributions by patch through the mailing list. Please open a merge request on code.videolan.org <http://code.videolan.org/>.
Best regards,
Felix
> On 21. Apr 2025, at 02:55, Ahmed Hamed <ahmedhamed3699 at gmail.com> wrote:
>
> 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
>
> _______________________________________________
> vlc-devel mailing list
> To unsubscribe or modify your subscription options:
> https://mailman.videolan.org/listinfo/vlc-devel
More information about the vlc-devel
mailing list