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 <h...@beauzee.fr>
+ * Sushma Reddy <sushma.re...@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
_______________________________________________
vlc-commits mailing list
vlc-commits@videolan.org
https://mailman.videolan.org/listinfo/vlc-commits