vlc | branch: master | Francois Cartegnie <[email protected]> | 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 */ _______________________________________________ vlc-commits mailing list [email protected] https://mailman.videolan.org/listinfo/vlc-commits
