[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