[vlc-commits] [Git][videolan/vlc][master] 6 commits: codec: ttml: share nodes to text conversions

François Cartegnie (@fcartegnie) gitlab at videolan.org
Tue Mar 5 20:07:02 UTC 2024



François Cartegnie pushed to branch master at VideoLAN / VLC


Commits:
e92474da by François Cartegnie at 2024-03-05T19:30:25+00:00
codec: ttml: share nodes to text conversions

- - - - -
4d863b1e by François Cartegnie at 2024-03-05T19:30:25+00:00
codec: ttml: refactor ttml_node_new

- - - - -
e27aa372 by François Cartegnie at 2024-03-05T19:30:25+00:00
codec: ttml: expose textnode_New

- - - - -
92e2b88c by François Cartegnie at 2024-03-05T19:30:25+00:00
codec: ttml: add size restricted writes to text nodes

- - - - -
f795d7a4 by François Cartegnie at 2024-03-05T19:30:25+00:00
codec: ttml: add attribute helpers

- - - - -
be7e3e1b by François Cartegnie at 2024-03-05T19:30:25+00:00
codec: ttml: add encoder

- - - - -


8 changed files:

- modules/codec/Makefile.am
- modules/codec/meson.build
- + modules/codec/ttml/encttml.c
- + modules/codec/ttml/genttml.c
- modules/codec/ttml/substtml.c
- modules/codec/ttml/ttml.c
- modules/codec/ttml/ttml.h
- modules/demux/ttml.c


Changes:

=====================================
modules/codec/Makefile.am
=====================================
@@ -223,8 +223,12 @@ codec_LTLIBRARIES += libsubsusf_plugin.la
 libttml_plugin_la_SOURCES = codec/ttml/substtml.c \
                             demux/ttml.c \
                             codec/ttml/ttml.h codec/ttml/ttml.c \
+                            codec/ttml/genttml.c \
                             codec/ttml/imageupdater.h \
                             codec/ttml/ttmlpes.h
+if ENABLE_SOUT
+libttml_plugin_la_SOURCES += codec/ttml/encttml.c
+endif
 codec_LTLIBRARIES += libttml_plugin.la
 
 libwebvtt_plugin_la_SOURCES = codec/webvtt/subsvtt.c \


=====================================
modules/codec/meson.build
=====================================
@@ -342,10 +342,16 @@ vlc_modules += {
     'sources' : files('subsusf.c')
 }
 
-# TTML decoder
+# TTML
+ttml_sources = files('ttml/substtml.c', 'ttml/ttml.c', 'ttml/genttml.c', '../demux/ttml.c')
+
+if get_option('stream_outputs')
+    ttml_sources += files('ttml/encttml.c')
+endif
+
 vlc_modules += {
     'name' : 'ttml',
-    'sources' : files('ttml/substtml.c', 'ttml/ttml.c', '../demux/ttml.c'),
+    'sources' : ttml_sources,
     'include_directories' : include_directories('.')
 }
 


=====================================
modules/codec/ttml/encttml.c
=====================================
@@ -0,0 +1,231 @@
+/*****************************************************************************
+ * encttml.c : TTML encoder
+ *****************************************************************************
+ * Copyright (C) 2018-2024 VideoLabs, VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
+#include <vlc_plugin.h>
+#include <vlc_codec.h>
+#include <vlc_subpicture.h>
+
+#include "../codec/ttml/ttml.h"
+
+#define HEX_COLOR_MAX 10
+static void FillHexColor( uint32_t argb, bool withalpha, char text[HEX_COLOR_MAX] )
+{
+    if( withalpha )
+        snprintf( text, HEX_COLOR_MAX, "#%08x", (argb << 8) | (argb >> 24) );
+    else
+        snprintf( text, HEX_COLOR_MAX, "#%06x", argb & 0x00FFFFFF );
+}
+
+static void AddTextNode( tt_node_t *p_parent, const char *psz_text )
+{
+    const char *psz = psz_text;
+    const char *nl;
+    do
+    {
+        nl = strchr( psz, '\n' );
+        if( nl )
+        {
+            tt_subtextnode_New( p_parent, psz, nl - psz );
+            tt_node_New( p_parent, "br" );
+            psz += nl - psz + 1;
+            if( *psz == '\0' )
+                break;
+        }
+        else
+        {
+            tt_textnode_New( p_parent, psz );
+        }
+    } while ( nl );
+}
+
+static block_t *Encode( encoder_t *p_enc, subpicture_t *p_spu )
+{
+    VLC_UNUSED( p_enc );
+
+    if( p_spu == NULL )
+        return NULL;
+
+    tt_node_t *p_root = tt_node_New( NULL, "tt" );
+    if( !p_root )
+        return NULL;
+
+    tt_node_AddAttribute( p_root, "xmlns", TT_NS );
+    tt_node_AddAttribute( p_root, "xmlns:tts", TT_NS_STYLING );
+
+    tt_node_t *p_body = tt_node_New( p_root, "body" );
+    if( !p_body )
+    {
+        tt_node_RecursiveDelete( p_root );
+        return NULL;
+    }
+
+    tt_node_t *p_div = tt_node_New( p_body, "div" );
+    if( !p_div )
+    {
+        tt_node_RecursiveDelete( p_root );
+        return NULL;
+    }
+
+    subpicture_region_t *p_region;
+    vlc_spu_regions_foreach(p_region, &p_spu->regions)
+    {
+        if( !subpicture_region_IsText( p_region ) ||
+            p_region->p_text == NULL ||
+            p_region->p_text->psz_text == NULL )
+            continue;
+
+        tt_node_t *p_par = tt_node_New( p_div, "p" );
+        if( !p_par )
+            continue;
+
+        if( p_spu->i_start != VLC_TICK_INVALID )
+        {
+            p_par->timings.begin = tt_time_Create( p_spu->i_start - VLC_TICK_0 );
+            if( p_spu->i_stop != VLC_TICK_INVALID &&  p_spu->i_stop > p_spu->i_start )
+                p_par->timings.end = tt_time_Create( p_spu->i_stop - VLC_TICK_0 );
+            tt_node_AddAttribute( p_par, "begin", "" );
+        }
+
+        for( const text_segment_t *p_segment = p_region->p_text;
+             p_segment; p_segment = p_segment->p_next )
+        {
+            if( p_segment->psz_text == NULL )
+                continue;
+
+            const text_style_t *style = p_segment->style;
+            if( style && style->i_features )
+            {
+                tt_node_t *p_span = tt_node_New( p_par, "span" );
+                if( !p_span )
+                    continue;
+
+                if( style->f_font_relsize && p_spu->i_original_picture_height )
+                {
+                    char fontsize[10];
+                    unsigned relem = p_spu->i_original_picture_height * style->f_font_relsize / 16;
+                    snprintf( fontsize, 10, "%u%%", relem );
+                    tt_node_AddAttribute( p_span, "tts:fontSize", fontsize );
+                }
+                else if ( style->i_font_size )
+                {
+                    char fontsize[10];
+                    snprintf( fontsize, 10, "%upx", style->i_font_size );
+                    tt_node_AddAttribute( p_span, "tts:fontSize", fontsize );
+                }
+
+                if( style->psz_fontname )
+                    tt_node_AddAttribute( p_span, "tts:fontFamily", style->psz_fontname );
+
+                if( style->i_features & STYLE_HAS_FLAGS )
+                {
+                    if( style->i_style_flags & STYLE_BOLD )
+                        tt_node_AddAttribute( p_span, "tts:fontWeight", "bold" );
+                    if( style->i_style_flags & STYLE_ITALIC )
+                        tt_node_AddAttribute( p_span, "tts:fontStyle", "italic" );
+                    if( style->i_style_flags & STYLE_UNDERLINE )
+                        tt_node_AddAttribute( p_span, "tts:textDecoration", "underline" );
+                    if( style->i_style_flags & STYLE_STRIKEOUT )
+                        tt_node_AddAttribute( p_span, "tts:textDecoration", "lineThrough" );
+                    if( style->i_style_flags & STYLE_OUTLINE )
+                    {
+                        char color[HEX_COLOR_MAX];
+                        uint32_t argb = style->i_outline_color;
+                        if( style->i_features & STYLE_HAS_OUTLINE_ALPHA )
+                            argb |= style->i_outline_alpha << 24;
+                        FillHexColor( argb, style->i_features & STYLE_HAS_OUTLINE_ALPHA, color );
+                        tt_node_AddAttribute( p_span, "tts:textOutline", color );
+                    }
+                }
+
+                if( style->i_features & STYLE_HAS_FONT_COLOR )
+                {
+                    char color[HEX_COLOR_MAX];
+                    uint32_t argb = style->i_font_color;
+                    if( style->i_features & STYLE_HAS_FONT_ALPHA )
+                        argb |= style->i_font_alpha << 24;
+                    FillHexColor( argb, style->i_features & STYLE_HAS_FONT_ALPHA, color );
+                    tt_node_AddAttribute( p_span, "tts:color", color );
+                }
+
+                if( style->i_features & STYLE_HAS_BACKGROUND_COLOR )
+                {
+                    char color[HEX_COLOR_MAX];
+                    uint32_t argb = style->i_background_color;
+                    if( style->i_features & STYLE_HAS_BACKGROUND_ALPHA )
+                        argb |= style->i_background_alpha << 24;
+                    FillHexColor( argb, style->i_features & STYLE_HAS_BACKGROUND_ALPHA, color );
+                    tt_node_AddAttribute( p_span, "tts:backgroundColor", color );
+                }
+
+                AddTextNode( p_span, p_segment->psz_text );
+            }
+            else
+            {
+                AddTextNode( p_par, p_segment->psz_text );
+            }
+        }
+    }
+
+    block_t* p_block = NULL;
+    struct vlc_memstream stream;
+
+    if( !vlc_memstream_open( &stream ) )
+    {
+        tt_time_t playbacktime = tt_time_Create( p_spu->i_start );
+
+        tt_node_ToText( &stream, (tt_basenode_t *)p_root, &playbacktime );
+        if( !vlc_memstream_close( &stream ) )
+        {
+            p_block = block_heap_Alloc( stream.ptr, stream.length );
+            if( p_block )
+            {
+                p_block->i_dts = p_block->i_pts = VLC_TICK_0 + p_spu->i_start;
+                if( p_spu->i_stop > p_spu->i_start )
+                    p_block->i_length = p_spu->i_stop - p_spu->i_start;
+            }
+        }
+    }
+
+    tt_node_RecursiveDelete( p_root );
+
+    return p_block;
+}
+
+int tt_OpenEncoder( vlc_object_t *p_this )
+{
+    encoder_t *p_enc = (encoder_t *)p_this;
+
+    if( p_enc->fmt_out.i_codec != VLC_CODEC_TTML )
+        return VLC_EGENERIC;
+
+    p_enc->p_sys = NULL;
+
+    static const struct vlc_encoder_operations ops =
+        { .encode_sub = Encode };
+    p_enc->ops = &ops;
+
+    p_enc->fmt_out.i_cat = SPU_ES;
+    return VLC_SUCCESS;
+}


=====================================
modules/codec/ttml/genttml.c
=====================================
@@ -0,0 +1,188 @@
+/*****************************************************************************
+ * genttml.c : TTML formatter
+ *****************************************************************************
+ * Copyright (C) 2015-2018 VLC authors and VideoLAN
+ * Copyright (C) 2017-2018 VideoLabs
+ *
+ * Authors: Hugo Beauzée-Luyssen <hugo at beauzee.fr>
+ *          Sushma Reddy <sushma.reddy at research.iiit.ac.in>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+//#define TTML_GEN_DEBUG
+
+#include <vlc_common.h>
+#include <vlc_strings.h>
+
+#include <assert.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include "../codec/ttml/ttml.h"
+
+char *tt_genTiming( tt_time_t t )
+{
+    if( !tt_time_Valid( &t ) )
+        t.base = 0;
+    unsigned f = t.base % CLOCK_FREQ;
+    t.base /= CLOCK_FREQ;
+    unsigned h = t.base / 3600;
+    unsigned m = t.base % 3600 / 60;
+    unsigned s = t.base % 60;
+
+    int i_ret;
+    char *psz;
+    if( f )
+    {
+        const char *lz = "000000";
+        const char *psz_lz = &lz[6];
+        /* add leading zeroes */
+        for( unsigned i=10*f; i<CLOCK_FREQ; i *= 10 )
+            psz_lz--;
+        /* strip trailing zeroes */
+        for( ; f > 0 && (f % 10) == 0; f /= 10 );
+        i_ret = asprintf( &psz, "%02u:%02u:%02u.%s%u",
+                         h, m, s, psz_lz, f );
+    }
+    else if( t.frames )
+    {
+        i_ret = asprintf( &psz, "%02u:%02u:%02u:%s%u",
+                         h, m, s, t.frames < 10 ? "0" : "", t.frames );
+    }
+    else
+    {
+        i_ret = asprintf( &psz, "%02u:%02u:%02u",
+                         h, m, s );
+    }
+
+    return i_ret < 0 ? NULL : psz;
+}
+
+static void tt_MemstreamPutEntities( struct vlc_memstream *p_stream, const char *psz )
+{
+    char *psz_entities = vlc_xml_encode( psz );
+    if( psz_entities )
+    {
+        vlc_memstream_puts( p_stream, psz_entities );
+        free( psz_entities );
+    }
+}
+
+void tt_node_AttributesToText( struct vlc_memstream *p_stream, const tt_node_t* p_node )
+{
+    bool b_timed_node = false;
+    const vlc_dictionary_t* p_attr_dict = &p_node->attr_dict;
+    for( int i = 0; i < p_attr_dict->i_size; ++i )
+    {
+        for ( vlc_dictionary_entry_t* p_entry = p_attr_dict->p_entries[i];
+             p_entry != NULL; p_entry = p_entry->p_next )
+        {
+            const char *psz_value = NULL;
+
+            if( !strcmp(p_entry->psz_key, "begin") ||
+                !strcmp(p_entry->psz_key, "end") ||
+                !strcmp(p_entry->psz_key, "dur") )
+            {
+                b_timed_node = true;
+                /* will remove duration */
+                continue;
+            }
+            else if( !strcmp(p_entry->psz_key, "timeContainer") )
+            {
+                /* also remove sequential timings info (all abs now) */
+                continue;
+            }
+            else
+            {
+                psz_value = p_entry->p_value;
+            }
+
+            if( psz_value == NULL )
+                continue;
+
+            vlc_memstream_printf( p_stream, " %s=\"", p_entry->psz_key );
+            tt_MemstreamPutEntities( p_stream, psz_value );
+            vlc_memstream_putc( p_stream, '"' );
+        }
+    }
+
+    if( b_timed_node )
+    {
+        if( tt_time_Valid( &p_node->timings.begin ) )
+        {
+            char *psz = tt_genTiming( p_node->timings.begin );
+            vlc_memstream_printf( p_stream, " begin=\"%s\"", psz );
+            free( psz );
+        }
+
+        if( tt_time_Valid( &p_node->timings.end ) )
+        {
+            char *psz = tt_genTiming( p_node->timings.end );
+            vlc_memstream_printf( p_stream, " end=\"%s\"", psz );
+            free( psz );
+        }
+    }
+}
+
+void tt_node_ToText( struct vlc_memstream *p_stream, const tt_basenode_t *p_basenode,
+                     const tt_time_t *playbacktime )
+{
+    if( p_basenode->i_type == TT_NODE_TYPE_ELEMENT )
+    {
+        const tt_node_t *p_node = (const tt_node_t *) p_basenode;
+
+        if( tt_time_Valid( playbacktime ) &&
+            !tt_timings_Contains( &p_node->timings, playbacktime ) )
+            return;
+
+        vlc_memstream_putc( p_stream, '<' );
+        tt_MemstreamPutEntities( p_stream, p_node->psz_node_name );
+
+        tt_node_AttributesToText( p_stream, p_node );
+
+        if( tt_node_HasChild( p_node ) )
+        {
+            vlc_memstream_putc( p_stream, '>' );
+
+#ifdef TTML_DEMUX_DEBUG
+            vlc_memstream_printf( p_stream, "<!-- starts %ld ends %ld -->",
+                                 tt_time_Convert( &p_node->timings.begin ),
+                                 tt_time_Convert( &p_node->timings.end ) );
+#endif
+
+            for( const tt_basenode_t *p_child = p_node->p_child;
+                 p_child; p_child = p_child->p_next )
+            {
+                tt_node_ToText( p_stream, p_child, playbacktime );
+            }
+
+            vlc_memstream_puts( p_stream, "</" );
+            tt_MemstreamPutEntities( p_stream, p_node->psz_node_name );
+            vlc_memstream_putc( p_stream, '>' );
+        }
+        else
+            vlc_memstream_puts( p_stream, "/>" );
+    }
+    else
+    {
+        const tt_textnode_t *p_textnode = (const tt_textnode_t *) p_basenode;
+        tt_MemstreamPutEntities( p_stream, p_textnode->psz_text );
+    }
+}


=====================================
modules/codec/ttml/substtml.c
=====================================
@@ -31,6 +31,7 @@
 #include <vlc_text_style.h>
 #include <vlc_charset.h>
 #include <vlc_image.h>
+#include <vlc_memstream.h>
 
 #include <ctype.h>
 #include <assert.h>
@@ -728,7 +729,7 @@ static int ParseTTMLChunk( xml_reader_t *p_reader, tt_node_t **pp_rootnode )
                     *pp_rootnode != NULL )
                     return VLC_EGENERIC;
 
-                *pp_rootnode = tt_node_New( p_reader, NULL, psz_node_name );
+                *pp_rootnode = tt_node_NewRead( p_reader, NULL, psz_node_name );
                 if( !*pp_rootnode ||
                     tt_nodes_Read( p_reader, *pp_rootnode ) != VLC_SUCCESS )
                     return VLC_EGENERIC;


=====================================
modules/codec/ttml/ttml.c
=====================================
@@ -55,7 +55,14 @@ vlc_module_begin ()
         set_subcategory( SUBCAT_INPUT_DEMUX )
         set_callbacks( tt_OpenDemux, tt_CloseDemux )
         add_shortcut( "ttml" )
-
+#ifdef ENABLE_SOUT
+    add_submodule()
+        set_shortname( N_("TTML") )
+        set_description( N_("TTML encoder") )
+        set_capability( "spu encoder", 101 )
+        set_subcategory( SUBCAT_INPUT_SCODEC )
+        set_callbacks( tt_OpenEncoder, NULL )
+#endif
 vlc_module_end ()
 
 
@@ -183,6 +190,20 @@ void tt_node_RecursiveDelete( tt_node_t *p_node )
     tt_node_Delete( p_node );
 }
 
+void tt_node_RemoveAttribute( tt_node_t *p_node, const char *key )
+{
+    vlc_dictionary_remove_value_for_key( &p_node->attr_dict, key,
+                                        tt_node_FreeDictValue, NULL );
+}
+
+int tt_node_AddAttribute( tt_node_t *p_node, const char *key, const char *value )
+{
+    char *p_dup = strdup( value );
+    if( p_dup )
+        vlc_dictionary_insert( &p_node->attr_dict, key, p_dup );
+    return p_dup ? VLC_SUCCESS : VLC_EGENERIC;
+}
+
 static void tt_node_ParentAddChild( tt_node_t* p_parent, tt_basenode_t *p_child )
 {
     tt_basenode_t **pp_node = &p_parent->p_child;
@@ -191,20 +212,35 @@ static void tt_node_ParentAddChild( tt_node_t* p_parent, tt_basenode_t *p_child
     *pp_node = p_child;
 }
 
-static tt_textnode_t *tt_textnode_New( tt_node_t *p_parent, const char *psz_text )
+static tt_textnode_t *tt_textnode_NewImpl( tt_node_t *p_parent, char *psz )
 {
+    if( !psz )
+        return NULL;
     tt_textnode_t *p_node = calloc( 1, sizeof( *p_node ) );
     if( !p_node )
+    {
+        free( psz );
         return NULL;
+    }
+    p_node->psz_text = psz;
     p_node->i_type = TT_NODE_TYPE_TEXT;
     p_node->p_parent = p_parent;
     if( p_parent )
         tt_node_ParentAddChild( p_parent, (tt_basenode_t *) p_node );
-    p_node->psz_text = strdup( psz_text );
     return p_node;
 }
 
-tt_node_t * tt_node_New( xml_reader_t* reader, tt_node_t* p_parent, const char* psz_node_name )
+tt_textnode_t *tt_textnode_New( tt_node_t *p_parent, const char *psz_text )
+{
+    return tt_textnode_NewImpl( p_parent, strdup( psz_text ) );
+}
+
+tt_textnode_t *tt_subtextnode_New( tt_node_t *p_parent, const char *psz_text, size_t len )
+{
+    return tt_textnode_NewImpl( p_parent, strndup( psz_text, len ) );
+}
+
+tt_node_t * tt_node_New( tt_node_t* p_parent, const char* psz_node_name )
 {
     tt_node_t *p_node = calloc( 1, sizeof( *p_node ) );
     if( !p_node )
@@ -225,6 +261,15 @@ tt_node_t * tt_node_New( xml_reader_t* reader, tt_node_t* p_parent, const char*
     if( p_parent )
         tt_node_ParentAddChild( p_parent, (tt_basenode_t *) p_node );
 
+    return p_node;
+}
+
+tt_node_t * tt_node_NewRead( xml_reader_t* reader, tt_node_t* p_parent, const char* psz_node_name )
+{
+    tt_node_t *p_node = tt_node_New( p_parent, psz_node_name );
+    if( !p_node )
+        return NULL;
+
     const char* psz_value = NULL;
     for( const char* psz_key = xml_ReaderNextAttr( reader, &psz_value );
          psz_key != NULL;
@@ -328,7 +373,7 @@ int tt_nodes_Read( xml_reader_t *p_reader, tt_node_t *p_root_node )
 
             case XML_READER_STARTELEM:
             {
-                tt_node_t *p_newnode = tt_node_New( p_reader, p_node, psz_node_name );
+                tt_node_t *p_newnode = tt_node_NewRead( p_reader, p_node, psz_node_name );
                 if( !p_newnode )
                     return VLC_EGENERIC;
                 if( !b_empty )


=====================================
modules/codec/ttml/ttml.h
=====================================
@@ -20,12 +20,15 @@
 
 #include <vlc_tick.h>
 #include <vlc_arrays.h>
+#include <vlc_memstream.h>
 
 int tt_OpenDemux( vlc_object_t* p_this );
 void tt_CloseDemux( vlc_object_t* p_demux );
 
 int  tt_OpenDecoder   ( vlc_object_t * );
 
+int  tt_OpenEncoder   ( vlc_object_t * );
+
 enum
 {
     TT_TIMINGS_UNSPEC = 0,
@@ -99,10 +102,15 @@ typedef struct
     char *psz_text;
 } tt_textnode_t;
 
-tt_node_t * tt_node_New( xml_reader_t* reader, tt_node_t* p_parent, const char* psz_node_name );
+tt_textnode_t *tt_textnode_New( tt_node_t *p_parent, const char *psz_text );
+tt_textnode_t *tt_subtextnode_New( tt_node_t *p_parent, const char *psz_text, size_t );
+tt_node_t * tt_node_New( tt_node_t* p_parent, const char* psz_node_name );
+tt_node_t * tt_node_NewRead( xml_reader_t* reader, tt_node_t* p_parent, const char* psz_node_name );
 void tt_node_RecursiveDelete( tt_node_t *p_node );
 int  tt_node_NameCompare( const char* psz_tagname, const char* psz_pattern );
 bool tt_node_HasChild( const tt_node_t *p_node );
+int  tt_node_AddAttribute( tt_node_t *p_node, const char *key, const char *value );
+void tt_node_RemoveAttribute( tt_node_t *p_node, const char *key );
 
 int tt_nodes_Read( xml_reader_t *p_reader, tt_node_t *p_root_node );
 
@@ -168,3 +176,10 @@ static inline tt_time_t tt_time_Sub( tt_time_t t1, tt_time_t t2 )
     t1.base -= t2.base;
     return t1;
 }
+
+/* Encoding */
+
+char *tt_genTiming( tt_time_t t );
+void tt_node_AttributesToText( struct vlc_memstream *p_stream, const tt_node_t* p_node );
+void tt_node_ToText( struct vlc_memstream *p_stream, const tt_basenode_t *p_basenode,
+                     const tt_time_t *playbacktime );


=====================================
modules/demux/ttml.c
=====================================
@@ -67,156 +67,6 @@ typedef struct
     } times;
 } demux_sys_t;
 
-static char *tt_genTiming( tt_time_t t )
-{
-    if( !tt_time_Valid( &t ) )
-        t.base = 0;
-    unsigned f = t.base % CLOCK_FREQ;
-    t.base /= CLOCK_FREQ;
-    unsigned h = t.base / 3600;
-    unsigned m = t.base % 3600 / 60;
-    unsigned s = t.base % 60;
-
-    int i_ret;
-    char *psz;
-    if( f )
-    {
-        const char *lz = "000000";
-        const char *psz_lz = &lz[6];
-        /* add leading zeroes */
-        for( unsigned i=10*f; i<CLOCK_FREQ; i *= 10 )
-            psz_lz--;
-        /* strip trailing zeroes */
-        for( ; f > 0 && (f % 10) == 0; f /= 10 );
-        i_ret = asprintf( &psz, "%02u:%02u:%02u.%s%u",
-                                 h, m, s, psz_lz, f );
-    }
-    else if( t.frames )
-    {
-        i_ret = asprintf( &psz, "%02u:%02u:%02u:%s%u",
-                                 h, m, s, t.frames < 10 ? "0" : "", t.frames );
-    }
-    else
-    {
-        i_ret = asprintf( &psz, "%02u:%02u:%02u",
-                                 h, m, s );
-    }
-
-    return i_ret < 0 ? NULL : psz;
-}
-
-static void tt_MemstreamPutEntities( struct vlc_memstream *p_stream, const char *psz )
-{
-    char *psz_entities = vlc_xml_encode( psz );
-    if( psz_entities )
-    {
-        vlc_memstream_puts( p_stream, psz_entities );
-        free( psz_entities );
-    }
-}
-
-static void tt_node_AttributesToText( struct vlc_memstream *p_stream, const tt_node_t* p_node )
-{
-    bool b_timed_node = false;
-    const vlc_dictionary_t* p_attr_dict = &p_node->attr_dict;
-    for( int i = 0; i < p_attr_dict->i_size; ++i )
-    {
-        for ( vlc_dictionary_entry_t* p_entry = p_attr_dict->p_entries[i];
-                                      p_entry != NULL; p_entry = p_entry->p_next )
-        {
-            const char *psz_value = NULL;
-
-            if( !strcmp(p_entry->psz_key, "begin") ||
-                !strcmp(p_entry->psz_key, "end") ||
-                !strcmp(p_entry->psz_key, "dur") )
-            {
-                b_timed_node = true;
-                /* will remove duration */
-                continue;
-            }
-            else if( !strcmp(p_entry->psz_key, "timeContainer") )
-            {
-                /* also remove sequential timings info (all abs now) */
-                continue;
-            }
-            else
-            {
-                psz_value = p_entry->p_value;
-            }
-
-            if( psz_value == NULL )
-                continue;
-
-            vlc_memstream_printf( p_stream, " %s=\"", p_entry->psz_key );
-            tt_MemstreamPutEntities( p_stream, psz_value );
-            vlc_memstream_putc( p_stream, '"' );
-        }
-    }
-
-    if( b_timed_node )
-    {
-        if( tt_time_Valid( &p_node->timings.begin ) )
-        {
-            char *psz = tt_genTiming( p_node->timings.begin );
-            vlc_memstream_printf( p_stream, " begin=\"%s\"", psz );
-            free( psz );
-        }
-
-        if( tt_time_Valid( &p_node->timings.end ) )
-        {
-            char *psz = tt_genTiming( p_node->timings.end );
-            vlc_memstream_printf( p_stream, " end=\"%s\"", psz );
-            free( psz );
-        }
-    }
-}
-
-static void tt_node_ToText( struct vlc_memstream *p_stream, const tt_basenode_t *p_basenode,
-                            const tt_time_t *playbacktime )
-{
-    if( p_basenode->i_type == TT_NODE_TYPE_ELEMENT )
-    {
-        const tt_node_t *p_node = (const tt_node_t *) p_basenode;
-
-        if( tt_time_Valid( playbacktime ) &&
-           !tt_timings_Contains( &p_node->timings, playbacktime ) )
-            return;
-
-        vlc_memstream_putc( p_stream, '<' );
-        tt_MemstreamPutEntities( p_stream, p_node->psz_node_name );
-
-        tt_node_AttributesToText( p_stream, p_node );
-
-        if( tt_node_HasChild( p_node ) )
-        {
-            vlc_memstream_putc( p_stream, '>' );
-
-#ifdef TTML_DEMUX_DEBUG
-            vlc_memstream_printf( p_stream, "<!-- starts %ld ends %ld -->",
-                                  tt_time_Convert( &p_node->timings.begin ),
-                                  tt_time_Convert( &p_node->timings.end ) );
-#endif
-
-            for( const tt_basenode_t *p_child = p_node->p_child;
-                                   p_child; p_child = p_child->p_next )
-            {
-                tt_node_ToText( p_stream, p_child, playbacktime );
-            }
-
-            vlc_memstream_puts( p_stream, "</" );
-            tt_MemstreamPutEntities( p_stream, p_node->psz_node_name );
-            vlc_memstream_putc( p_stream, '>' );
-        }
-        else
-            vlc_memstream_puts( p_stream, "/>" );
-    }
-    else
-    {
-        const tt_textnode_t *p_textnode = (const tt_textnode_t *) p_basenode;
-        tt_MemstreamPutEntities( p_stream, p_textnode->psz_text );
-    }
-}
-
 static int Control( demux_t* p_demux, int i_query, va_list args )
 {
     demux_sys_t *p_sys = p_demux->p_sys;
@@ -327,7 +177,7 @@ static int ReadTTML( demux_t* p_demux )
                     p_sys->p_rootnode != NULL )
                     return VLC_EGENERIC;
 
-                p_sys->p_rootnode = tt_node_New( p_sys->p_reader, NULL, psz_node_name );
+                p_sys->p_rootnode = tt_node_NewRead( p_sys->p_reader, NULL, psz_node_name );
                 if( b_empty )
                     break;
                 if( !p_sys->p_rootnode ||



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/a20f72c674c1c0c9b012790ed8b149c0532881c7...be7e3e1be998ad97b83fb983e804d7051fc64b9f

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/a20f72c674c1c0c9b012790ed8b149c0532881c7...be7e3e1be998ad97b83fb983e804d7051fc64b9f
You're receiving this email because of your account on code.videolan.org.


VideoLAN code repository instance


More information about the vlc-commits mailing list