[vlc-commits] codec: ttml: add support for SMPTE-TT image profile
Francois Cartegnie
git at videolan.org
Tue May 15 21:49:24 CEST 2018
vlc | branch: master | Francois Cartegnie <fcvlcdev at free.fr> | Mon May 14 22:15:09 2018 +0200| [08364771dcb35360203a2f4ae1a0a3daab60ee3a] | committer: Francois Cartegnie
codec: ttml: add support for SMPTE-TT image profile
> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=08364771dcb35360203a2f4ae1a0a3daab60ee3a
---
NEWS | 1 +
modules/codec/Makefile.am | 3 +-
modules/codec/ttml/substtml.c | 146 +++++++++++++++++++++++++++++++++++++++++-
3 files changed, 147 insertions(+), 3 deletions(-)
diff --git a/NEWS b/NEWS
index 342282581c..87ccff42f4 100644
--- a/NEWS
+++ b/NEWS
@@ -22,6 +22,7 @@ Codecs:
* WebVTT encoder
* Remove iomx support for Android
* WebP image decoding
+ * Support for SMPTE-TT image profile
Video output:
* Remove aa plugin
diff --git a/modules/codec/Makefile.am b/modules/codec/Makefile.am
index 1615fd58e1..cbf221cd4e 100644
--- a/modules/codec/Makefile.am
+++ b/modules/codec/Makefile.am
@@ -225,7 +225,8 @@ 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/ttml.h codec/ttml/ttml.c \
+ codec/ttml/imageupdater.h
codec_LTLIBRARIES += libttml_plugin.la
libwebvtt_plugin_la_SOURCES = codec/webvtt/subsvtt.c \
diff --git a/modules/codec/ttml/substtml.c b/modules/codec/ttml/substtml.c
index 599fd30a78..fc6cec2641 100644
--- a/modules/codec/ttml/substtml.c
+++ b/modules/codec/ttml/substtml.c
@@ -30,12 +30,14 @@
#include <vlc_stream.h>
#include <vlc_text_style.h>
#include <vlc_charset.h>
+#include <vlc_image.h>
#include <ctype.h>
#include <assert.h>
#include "substext.h"
#include "ttml.h"
+#include "imageupdater.h"
//#define TTML_DEBUG
@@ -91,6 +93,11 @@ typedef struct
{
substext_updater_region_t updt;
text_segment_t **pp_last_segment;
+ struct
+ {
+ uint8_t *p_bytes;
+ size_t i_bytes;
+ } bgbitmap; /* SMPTE-TT */
} ttml_region_t;
typedef struct
@@ -145,6 +152,7 @@ static ttml_style_t * ttml_style_New( )
static void ttml_region_Delete( ttml_region_t *p_region )
{
SubpictureUpdaterSysRegionClean( &p_region->updt );
+ free( p_region->bgbitmap.p_bytes );
free( p_region );
}
@@ -887,6 +895,42 @@ static void AppendTextToRegion( ttml_context_t *p_ctx, const tt_textnode_t *p_tt
p_region->pp_last_segment = &p_segment->p_next;
}
+static const char * GetSMPTEImage( ttml_context_t *p_ctx, const char *psz_id )
+{
+ if( !p_ctx->p_rootnode )
+ return NULL;
+
+ tt_node_t *p_head = FindNode( p_ctx->p_rootnode, "head", 1, NULL );
+ if( !p_head )
+ return NULL;
+
+ for( tt_basenode_t *p_child = p_head->p_child;
+ p_child; p_child = p_child->p_next )
+ {
+ if( p_child->i_type == TT_NODE_TYPE_TEXT )
+ continue;
+
+ tt_node_t *p_node = (tt_node_t *) p_child;
+ if( tt_node_NameCompare( p_node->psz_node_name, "metadata" ) )
+ continue;
+
+ tt_node_t *p_imagenode = FindNode( p_node, "smpte:image", 1, psz_id );
+ if( !p_imagenode )
+ continue;
+
+ if( !p_imagenode->p_child || p_imagenode->p_child->i_type != TT_NODE_TYPE_TEXT )
+ return NULL; /* was found but empty or not text node */
+
+ tt_textnode_t *p_textnode = (tt_textnode_t *) p_imagenode->p_child;
+ const char *psz_text = p_textnode->psz_text;
+ while( isspace( *psz_text ) )
+ psz_text++;
+ return psz_text;
+ }
+
+ return NULL;
+}
+
static void ConvertNodesToRegionContent( ttml_context_t *p_ctx, const tt_node_t *p_node,
ttml_region_t *p_region,
const ttml_style_t *p_upper_set_styles,
@@ -903,6 +947,25 @@ static void ConvertNodesToRegionContent( ttml_context_t *p_ctx, const tt_node_t
if( psz_regionid || p_region == NULL )
p_region = GetTTMLRegion( p_ctx, psz_regionid );
+ /* Check for bitmap profile defined by ST2052 / SMPTE-TT */
+ if( !tt_node_NameCompare( p_node->psz_node_name, "div" ) &&
+ vlc_dictionary_has_key( &p_node->attr_dict, "smpte:backgroundImage" ) )
+ {
+ if( !p_region->bgbitmap.p_bytes )
+ {
+ const char *psz_id = vlc_dictionary_value_for_key( &p_node->attr_dict,
+ "smpte:backgroundImage" );
+ /* Seems SMPTE can't make diff between html and xml.. */
+ if( psz_id && *psz_id == '#' )
+ {
+ const char *psz_base64 = GetSMPTEImage( p_ctx, &psz_id[1] );
+ if( psz_base64 )
+ p_region->bgbitmap.i_bytes =
+ vlc_b64_decode_binary( &p_region->bgbitmap.p_bytes, psz_base64 );
+ }
+ }
+ }
+
/* awkward paragraph handling */
if( !tt_node_NameCompare( p_node->psz_node_name, "p" ) &&
p_region->updt.p_segments )
@@ -1088,6 +1151,68 @@ static void TTMLRegionsToSpuTextRegions( decoder_t *p_dec, subpicture_t *p_spu,
}
}
+static picture_t * picture_CreateFromPNG( decoder_t *p_dec,
+ const uint8_t *p_data, size_t i_data )
+{
+ if( i_data < 16 )
+ return NULL;
+ video_format_t fmt_out;
+ video_format_Init( &fmt_out, VLC_CODEC_YUVA );
+ video_format_t fmt_in;
+ video_format_Init( &fmt_in, VLC_CODEC_PNG );
+
+ block_t *p_block = block_Alloc( i_data );
+ if( !p_block )
+ return NULL;
+ memcpy( p_block->p_buffer, p_data, i_data );
+
+ picture_t *p_pic = NULL;
+ int i_flags = p_dec->obj.flags;
+ p_dec->obj.flags |= OBJECT_FLAGS_NOINTERACT|OBJECT_FLAGS_QUIET;
+ image_handler_t *p_image = image_HandlerCreate( p_dec );
+ if( p_image )
+ {
+ p_pic = image_Read( p_image, p_block, &fmt_in, &fmt_out );
+ image_HandlerDelete( p_image );
+ }
+ else block_Release( p_block );
+ p_dec->obj.flags = i_flags;
+
+ return p_pic;
+}
+
+static void TTMLRegionsToSpuBitmapRegions( decoder_t *p_dec, subpicture_t *p_spu,
+ ttml_region_t *p_regions )
+{
+ /* Create region update info from each ttml region */
+ for( ttml_region_t *p_region = p_regions;
+ p_region; p_region = (ttml_region_t *) p_region->updt.p_next )
+ {
+ picture_t *p_pic = picture_CreateFromPNG( p_dec, p_region->bgbitmap.p_bytes,
+ p_region->bgbitmap.i_bytes );
+ if( p_pic )
+ {
+ ttml_image_updater_region_t *r = TTML_ImageUpdaterRegionNew( p_pic );
+ if( !r )
+ {
+ picture_Release( p_pic );
+ continue;
+ }
+ /* use text updt values/flags for ease */
+ static_assert((int)UPDT_REGION_ORIGIN_X_IS_RATIO == (int)ORIGIN_X_IS_RATIO,
+ "flag enums values differs");
+ static_assert((int)UPDT_REGION_EXTENT_Y_IS_RATIO == (int)EXTENT_Y_IS_RATIO,
+ "flag enums values differs");
+ r->i_flags = p_region->updt.flags;
+ r->origin.x = p_region->updt.origin.x;
+ r->origin.y = p_region->updt.origin.y;
+ r->extent.x = p_region->updt.extent.x;
+ r->extent.y = p_region->updt.extent.y;
+ TTML_ImageSpuAppendRegion( p_spu->updater.p_sys, r );
+ }
+ }
+}
+
static int ParseBlock( decoder_t *p_dec, const block_t *p_block )
{
tt_time_t *p_timings_array = NULL;
@@ -1133,16 +1258,33 @@ static int ParseBlock( decoder_t *p_dec, const block_t *p_block )
if( tt_time_Convert( &p_timings_array[i] ) + VLC_TS_0 > p_block->i_dts + p_block->i_length )
break;
+ bool b_bitmap_regions = false;
subpicture_t *p_spu = NULL;
ttml_region_t *p_regions = GenerateRegions( p_rootnode, p_timings_array[i] );
- if( p_regions && (p_spu = decoder_NewSubpictureText( p_dec )) )
+ if( p_regions )
+ {
+ if( p_regions->bgbitmap.i_bytes > 0 && p_regions->updt.p_segments == NULL )
+ {
+ b_bitmap_regions = true;
+ p_spu = decoder_NewTTML_ImageSpu( p_dec );
+ }
+ else
+ {
+ p_spu = decoder_NewSubpictureText( p_dec );
+ }
+ }
+
+ if( p_regions && p_spu )
{
p_spu->i_start = VLC_TS_0 + tt_time_Convert( &p_timings_array[i] );
p_spu->i_stop = VLC_TS_0 + tt_time_Convert( &p_timings_array[i+1] ) - 1;
p_spu->b_ephemer = true;
p_spu->b_absolute = true;
- TTMLRegionsToSpuTextRegions( p_dec, p_spu, p_regions );
+ if( !b_bitmap_regions ) /* TEXT regions */
+ TTMLRegionsToSpuTextRegions( p_dec, p_spu, p_regions );
+ else /* BITMAP regions */
+ TTMLRegionsToSpuBitmapRegions( p_dec, p_spu, p_regions );
}
/* cleanup */
More information about the vlc-commits
mailing list