[vlc-devel] [PATCH 4/5] codec: add tx3g spu
Francois Cartegnie
fcvlcdev at free.fr
Sat Mar 22 07:15:14 CET 2014
---
include/vlc_fourcc.h | 1 +
modules/codec/Makefile.am | 2 +
modules/codec/substext.h | 41 ++++-
modules/codec/substx3g.c | 460 ++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 499 insertions(+), 5 deletions(-)
create mode 100644 modules/codec/substx3g.c
diff --git a/include/vlc_fourcc.h b/include/vlc_fourcc.h
index 8af8d41..46903ea 100644
--- a/include/vlc_fourcc.h
+++ b/include/vlc_fourcc.h
@@ -416,6 +416,7 @@
#define VLC_CODEC_USF VLC_FOURCC('u','s','f',' ')
#define VLC_CODEC_OGT VLC_FOURCC('o','g','t',' ')
#define VLC_CODEC_CVD VLC_FOURCC('c','v','d',' ')
+#define VLC_CODEC_TX3G VLC_FOURCC('t','x','3','g')
/* Blu-ray Presentation Graphics */
#define VLC_CODEC_BD_PG VLC_FOURCC('b','d','p','g')
/* EBU STL (TECH. 3264-E) */
diff --git a/modules/codec/Makefile.am b/modules/codec/Makefile.am
index e013263..649ee5f 100644
--- a/modules/codec/Makefile.am
+++ b/modules/codec/Makefile.am
@@ -183,6 +183,8 @@ endif
EXTRA_LTLIBRARIES += libzvbi_plugin.la
codec_LTLIBRARIES += $(LTLIBzvbi)
+libsubstx3g_plugin_la_SOURCES = codec/substx3g.c codec/substext.h
+codec_LTLIBRARIES += libsubstx3g_plugin.la
### Xiph ###
diff --git a/modules/codec/substext.h b/modules/codec/substext.h
index fe5df95..b88ba7a 100644
--- a/modules/codec/substext.h
+++ b/modules/codec/substext.h
@@ -1,3 +1,9 @@
+typedef struct
+{
+ bool b_set;
+ unsigned int i_value;
+} subpicture_updater_sys_option_t;
+
struct subpicture_updater_sys_t {
char *text;
char *html;
@@ -11,6 +17,14 @@ struct subpicture_updater_sys_t {
int fixed_width;
int fixed_height;
bool renderbg;
+
+ /* styling */
+ subpicture_updater_sys_option_t style_flags;
+ subpicture_updater_sys_option_t font_color;
+ subpicture_updater_sys_option_t background_color;
+ int16_t i_alpha;
+ int16_t i_drop_shadow;
+ int16_t i_drop_shadow_alpha;
};
static int SubpictureTextValidate(subpicture_t *subpic,
@@ -84,16 +98,33 @@ static void SubpictureTextUpdate(subpicture_t *subpic,
r->i_y = sys->y * fmt_dst->i_height / sys->fixed_height;
}
- if (sys->i_font_height_percent != 0)
+ if (sys->i_font_height_percent || sys->i_alpha ||
+ sys->style_flags.b_set ||
+ sys->font_color.b_set ||
+ sys->background_color.b_set )
{
r->p_style = text_style_New();
- if (r->p_style)
+ if (!r->p_style) return;
+
+ if (sys->i_font_height_percent)
{
- r->p_style->i_font_size = sys->i_font_height_percent *
- subpic->i_original_picture_height / 100;
+ r->p_style->i_font_size = sys->i_font_height_percent *
+ subpic->i_original_picture_height / 100;
r->p_style->i_font_color = 0xffffff;
r->p_style->i_font_alpha = 0xff;
- }
+ }
+ if (sys->style_flags.b_set)
+ r->p_style->i_style_flags = sys->style_flags.i_value;
+ if (sys->font_color.b_set)
+ r->p_style->i_font_color = sys->font_color.i_value;
+ if (sys->background_color.b_set)
+ r->p_style->i_background_color = sys->background_color.i_value;
+ if (sys->i_alpha)
+ r->p_style->i_font_alpha = sys->i_alpha;
+ if (sys->i_drop_shadow)
+ r->p_style->i_shadow_width = sys->i_drop_shadow;
+ if (sys->i_drop_shadow_alpha)
+ r->p_style->i_shadow_alpha = sys->i_drop_shadow_alpha;
}
}
static void SubpictureTextDestroy(subpicture_t *subpic)
diff --git a/modules/codec/substx3g.c b/modules/codec/substx3g.c
new file mode 100644
index 0000000..10233a8
--- /dev/null
+++ b/modules/codec/substx3g.c
@@ -0,0 +1,460 @@
+/*****************************************************************************
+ * substx3gsub.c : MP4 tx3g subtitles decoder
+ *****************************************************************************
+ * Copyright (C) 2014 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.,
+ * 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_sout.h>
+
+#include "substext.h"
+
+/*****************************************************************************
+ * Module descriptor.
+ *****************************************************************************/
+static int Open ( vlc_object_t * );
+static void Close( vlc_object_t * );
+static subpicture_t *Decode( decoder_t *, block_t ** );
+
+vlc_module_begin ()
+ set_description( N_("tx3g subtitles decoder") )
+ set_shortname( N_("tx3g subtitles") )
+ set_capability( "decoder", 100 )
+ set_category( CAT_INPUT )
+ set_subcategory( SUBCAT_INPUT_SCODEC )
+ set_callbacks( Open, Close )
+vlc_module_end ()
+
+/****************************************************************************
+ * Local structs
+ ****************************************************************************/
+
+/*****************************************************************************
+ * Open: probe the decoder and return score
+ *****************************************************************************/
+static int Open( vlc_object_t *p_this )
+{
+ decoder_t *p_dec = (decoder_t *) p_this;
+
+ if( p_dec->fmt_in.i_codec != VLC_CODEC_TX3G )
+ return VLC_EGENERIC;
+
+ p_dec->pf_decode_sub = Decode;
+
+ p_dec->fmt_out.i_cat = SPU_ES;
+ p_dec->fmt_out.i_codec = 0;
+
+ return VLC_SUCCESS;
+}
+
+/*****************************************************************************
+ * Close:
+ *****************************************************************************/
+static void Close( vlc_object_t *p_this )
+{
+ VLC_UNUSED(p_this);
+}
+
+#define FONT_FACE_BOLD 0x1
+#define FONT_FACE_ITALIC 0x2
+#define FONT_FACE_UNDERLINE 0x4
+
+typedef struct segment_t segment_t;
+
+typedef struct
+{
+ uint8_t i_fontsize; // % height
+ uint32_t i_color; //ARGB
+ uint8_t i_flags; //atom flags
+} segment_style_t;
+
+struct segment_t
+{
+ char *psz_string;
+ unsigned int i_size;
+ segment_t *p_next;
+ /* styles applied to that segment */
+ segment_style_t styles;
+};
+
+
+static void SegmentFree( segment_t *p_segment )
+{
+ if ( p_segment )
+ {
+ free( p_segment->psz_string );
+ free( p_segment );
+ }
+}
+
+static void SegmentDoSplit( segment_t *p_segment, uint16_t i_start, uint16_t i_end,
+ segment_t **pp_segment_left,
+ segment_t **pp_segment_middle,
+ segment_t **pp_segment_right )
+{
+ segment_t *p_segment_left = *pp_segment_left;
+ segment_t *p_segment_right = *pp_segment_right;
+ segment_t *p_segment_middle = *pp_segment_middle;
+ p_segment_left = p_segment_middle = p_segment_right = NULL;
+
+ if ( (p_segment->i_size - i_start < 1) || (p_segment->i_size - i_end < 1) )
+ return;
+
+ if ( i_start > 0 )
+ {
+ p_segment_left = calloc( 1, sizeof(segment_t) );
+ if ( !p_segment_left ) goto error;
+ memcpy( &p_segment_left->styles, &p_segment->styles, sizeof(segment_style_t) );
+ p_segment_left->psz_string = strndup( p_segment->psz_string, i_start );
+ p_segment_left->i_size = strlen( p_segment_left->psz_string );
+ }
+
+ p_segment_middle = calloc( 1, sizeof(segment_t) );
+ if ( !p_segment_middle ) goto error;
+ memcpy( &p_segment_middle->styles, &p_segment->styles, sizeof(segment_style_t) );
+ p_segment_middle->psz_string = strndup( p_segment->psz_string + i_start, i_end - i_start + 1 );
+ p_segment_middle->i_size = strlen( p_segment_middle->psz_string );
+
+ if ( i_end < (p_segment->i_size - 1) )
+ {
+ p_segment_right = calloc( 1, sizeof(segment_t) );
+ if ( !p_segment_right ) goto error;
+ memcpy( &p_segment_right->styles, &p_segment->styles, sizeof(segment_style_t) );
+ p_segment_right->psz_string = strndup( p_segment->psz_string + i_end + 1, p_segment->i_size - i_end - 1 );
+ p_segment_right->i_size = strlen( p_segment_right->psz_string );
+ }
+
+ if ( p_segment_left ) p_segment_left->p_next = p_segment_middle;
+ if ( p_segment_right ) p_segment_middle->p_next = p_segment_right;
+
+ *pp_segment_left = p_segment_left;
+ *pp_segment_middle = p_segment_middle;
+ *pp_segment_right = p_segment_right;
+
+ return;
+
+error:
+ SegmentFree( p_segment_left );
+ SegmentFree( p_segment_middle );
+ SegmentFree( p_segment_right );
+}
+
+static void HtmlAppend( char **ppsz_dst, const char *psz_src,
+ const segment_style_t *p_styles )
+{
+ if ( !ppsz_dst ) return;
+ int i_ignore; VLC_UNUSED(i_ignore);
+ char *psz_subtext = NULL;
+ char *psz_text = NULL;
+ char *psz_fontsize = NULL;
+ char *psz_color = NULL;
+
+ if ( p_styles->i_color & 0xFF000000 ) //ARGB
+ i_ignore = asprintf( &psz_color, " color=\"#%6x\"",
+ p_styles->i_color & 0x00FFFFFF );
+
+ /* STYLE_DEFAULT_FONT_SIZE = ~ 10% height */
+ if ( p_styles->i_fontsize > 0 )
+ i_ignore = asprintf( &psz_fontsize, " size=\"%u\"", p_styles->i_fontsize / 10 * STYLE_DEFAULT_FONT_SIZE );
+
+ i_ignore = asprintf( &psz_subtext, "%s%s%s%s%s%s%s",
+ ( p_styles->i_flags & FONT_FACE_UNDERLINE ) ? "<u>" : "",
+ ( p_styles->i_flags & FONT_FACE_BOLD ) ? "<b>" : "",
+ ( p_styles->i_flags & FONT_FACE_ITALIC ) ? "<i>" : "",
+ psz_src,
+ ( p_styles->i_flags & FONT_FACE_BOLD ) ? "</b>" : "",
+ ( p_styles->i_flags & FONT_FACE_ITALIC ) ? "</i>" : "",
+ ( p_styles->i_flags & FONT_FACE_UNDERLINE ) ? "</u>" : ""
+ );
+
+ if ( psz_color || psz_fontsize )
+ {
+ i_ignore = asprintf( &psz_text, "<font%s%s>%s</font>",
+ psz_color ? psz_color : "",
+ psz_fontsize ? psz_fontsize : "",
+ psz_subtext );
+ free( psz_subtext );
+ }
+ else
+ {
+ psz_text = psz_subtext;
+ }
+
+ free( psz_fontsize );
+ free( psz_color );
+
+ if ( *ppsz_dst )
+ {
+ char *psz_dst = *ppsz_dst;
+ i_ignore = asprintf( ppsz_dst, "%s%s", psz_dst, psz_text );
+ free( psz_dst );
+ free( psz_text );
+ }
+ else
+ *ppsz_dst = psz_text;
+}
+
+static bool SegmentSplit( segment_t *p_prev, segment_t **pp_segment,
+ const uint16_t i_start, const uint16_t i_end,
+ const segment_style_t *p_styles )
+{
+ segment_t *p_segment_left = NULL, *p_segment_middle = NULL, *p_segment_right = NULL;
+
+ if ( (*pp_segment)->i_size == 0 ) return false;
+ if ( i_start > i_end ) return false;
+ if ( (size_t)(i_end - i_start) > (*pp_segment)->i_size - 1 ) return false;
+ if ( i_end > (*pp_segment)->i_size - 1 ) return false;
+
+ SegmentDoSplit( *pp_segment, i_start, i_end, &p_segment_left, &p_segment_middle, &p_segment_right );
+ if ( !p_segment_middle )
+ {
+ /* Failed */
+ SegmentFree( p_segment_left );
+ SegmentFree( p_segment_right );
+ return false;
+ }
+
+ segment_t *p_next = (*pp_segment)->p_next;
+ SegmentFree( *pp_segment );
+ *pp_segment = ( p_segment_left ) ? p_segment_left : p_segment_middle ;
+ if ( p_prev ) p_prev->p_next = *pp_segment;
+
+ if ( p_segment_right )
+ p_segment_right->p_next = p_next;
+ else
+ p_segment_middle->p_next = p_next;
+
+ p_segment_middle->styles = *p_styles;
+
+ return true;
+}
+
+/* Creates a new segment using the given style and split existing ones according
+ to the start & end offsets */
+static void ApplySegmentStyle( segment_t **pp_segment, const uint16_t i_absstart,
+ const uint16_t i_absend, const segment_style_t *p_styles )
+{
+ /* find the matching segment */
+ uint16_t i_curstart = 0;
+ segment_t *p_prev = NULL;
+ segment_t *p_cur = *pp_segment;
+ while ( p_cur )
+ {
+ uint16_t i_curend = i_curstart + p_cur->i_size - 1;
+ if ( (i_absstart >= i_curstart) && (i_absend <= i_curend) )
+ {
+ /* segment found */
+ if ( !SegmentSplit( p_prev, &p_cur, i_absstart - i_curstart,
+ i_absend - i_curstart, p_styles ) )
+ return;
+ if ( !p_prev ) *pp_segment = p_cur;
+ break;
+ }
+ else
+ {
+ i_curstart += p_cur->i_size;
+ p_prev = p_cur;
+ p_cur = p_cur->p_next;
+ }
+ }
+}
+
+static char *SegmentsToHtml( segment_t *p_head )
+{
+ char *psz_dst = NULL;
+ char *psz_ret = NULL;
+ while( p_head )
+ {
+ HtmlAppend( &psz_dst, p_head->psz_string, &p_head->styles );
+ p_head = p_head->p_next;
+ }
+ int i_ignore = asprintf( &psz_ret, "<text>%s</text>", psz_dst );
+ VLC_UNUSED( i_ignore );
+ free( psz_dst );
+ return psz_ret;
+}
+
+/*****************************************************************************
+ * Decode:
+ *****************************************************************************/
+static subpicture_t *Decode( decoder_t *p_dec, block_t **pp_block )
+{
+ block_t *p_block;
+ subpicture_t *p_spu = NULL;
+
+ if( ( pp_block == NULL ) || ( *pp_block == NULL ) ) return NULL;
+ p_block = *pp_block;
+ *pp_block = NULL;
+
+ if( ( p_block->i_flags & (BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED) ) ||
+ p_block->i_buffer < sizeof(uint16_t) )
+ {
+ block_Release( p_block );
+ return NULL;
+ }
+
+ uint8_t *p_buf = p_block->p_buffer;
+
+ /* Read our raw string and create the styled segment for HTML */
+ uint16_t i_psz_length = GetWBE( p_buf );
+ char *psz_subtitle = malloc( i_psz_length + 1 );
+ if ( !psz_subtitle ) return NULL;
+ memcpy( psz_subtitle, p_block->p_buffer + sizeof(uint16_t), i_psz_length );
+ psz_subtitle[ i_psz_length ] = '\0';
+ p_buf += i_psz_length + sizeof(uint16_t);
+
+ for( uint16_t i=0; i < i_psz_length; i++ )
+ if ( psz_subtitle[i] == '\r' ) psz_subtitle[i] = '\n';
+
+ segment_t *p_segment = calloc( 1, sizeof(segment_t) );
+ if ( !p_segment )
+ {
+ free( psz_subtitle );
+ return NULL;
+ }
+ p_segment->psz_string = strdup( psz_subtitle );
+ p_segment->i_size = strlen( psz_subtitle );
+ if ( p_dec->fmt_in.subs.p_style )
+ {
+ p_segment->styles.i_color = p_dec->fmt_in.subs.p_style->i_font_color;
+ p_segment->styles.i_color |= p_dec->fmt_in.subs.p_style->i_font_alpha << 24;
+ if ( p_dec->fmt_in.subs.p_style->i_style_flags & STYLE_BOLD )
+ p_segment->styles.i_flags |= FONT_FACE_BOLD;
+ if ( p_dec->fmt_in.subs.p_style->i_style_flags & STYLE_ITALIC )
+ p_segment->styles.i_flags |= FONT_FACE_ITALIC;
+ if ( p_dec->fmt_in.subs.p_style->i_style_flags & STYLE_UNDERLINE )
+ p_segment->styles.i_flags |= FONT_FACE_UNDERLINE;
+ p_segment->styles.i_fontsize = p_dec->fmt_in.subs.p_style->i_font_size;
+ }
+
+ if ( !p_segment->psz_string )
+ {
+ SegmentFree( p_segment );
+ free( psz_subtitle );
+ return NULL;
+ }
+
+ /* Create the subpicture unit */
+ p_spu = decoder_NewSubpictureText( p_dec );
+ if( !p_spu )
+ {
+ free( psz_subtitle );
+ SegmentFree( p_segment );
+ return NULL;
+ }
+ subpicture_updater_sys_t *p_spu_sys = p_spu->updater.p_sys;
+
+ /* Parse our styles */
+ while( (size_t)(p_buf - p_block->p_buffer) + 8 < p_block->i_buffer )
+ {
+ uint32_t i_atomsize = GetDWBE( p_buf );
+ vlc_fourcc_t i_atomtype = VLC_FOURCC(p_buf[4],p_buf[5],p_buf[6],p_buf[7]);
+ p_buf += 8;
+ switch( i_atomtype )
+ {
+
+ case VLC_FOURCC('s','t','y','l'):
+ {
+ if ( (size_t)(p_buf - p_block->p_buffer) < 14 ) break;
+ uint16_t i_nbrecords = GetWBE(p_buf);
+ uint16_t i_cur_record = 0;
+ p_buf += 2;
+ while( i_cur_record++ < i_nbrecords )
+ {
+ if ( (size_t)(p_buf - p_block->p_buffer) < 12 ) break;
+ uint16_t i_start = __MIN( GetWBE(p_buf), i_psz_length - 1 );
+ uint16_t i_end = __MIN( GetWBE(p_buf + 2), i_psz_length - 1 );
+
+ segment_style_t style;
+ style.i_flags = p_buf[6];
+ style.i_fontsize = p_buf[7];
+ style.i_color = GetDWBE(p_buf+8) >> 8;// RGBA -> ARGB
+ style.i_color |= (GetDWBE(p_buf+8) & 0xFF) << 24;
+ ApplySegmentStyle( &p_segment, i_start, i_end, &style );
+
+ if ( i_nbrecords == 1 )
+ {
+ switch( p_buf[6] )
+ {
+ case FONT_FACE_BOLD:
+ p_spu_sys->style_flags.i_value = STYLE_BOLD;
+ p_spu_sys->style_flags.b_set = true;
+ break;
+ case FONT_FACE_ITALIC:
+ p_spu_sys->style_flags.i_value = STYLE_ITALIC;
+ p_spu_sys->style_flags.b_set = true;
+ break;
+ case FONT_FACE_UNDERLINE:
+ p_spu_sys->style_flags.i_value = STYLE_UNDERLINE;
+ p_spu_sys->style_flags.b_set = true;
+ break;
+ default:
+ break;
+ }
+ p_spu_sys->i_font_height_percent = p_buf[7] * 5;
+ p_spu_sys->font_color.i_value = GetDWBE(p_buf+8) >> 8;// RGBA -> ARGB
+ p_spu_sys->font_color.i_value |= (GetDWBE(p_buf+8) & 0xFF) << 24;
+ p_spu_sys->font_color.b_set = true;
+ }
+
+ p_buf += 12;
+ }
+ } break;
+
+ case VLC_FOURCC('d','r','p','o'):
+ if ( (size_t)(p_buf - p_block->p_buffer) < 4 ) break;
+ p_spu_sys->i_drop_shadow = __MAX( GetWBE(p_buf), GetWBE(p_buf+2) );
+ break;
+
+ case VLC_FOURCC('d','r','p','t'):
+ if ( (size_t)(p_buf - p_block->p_buffer) < 2 ) break;
+ p_spu_sys->i_drop_shadow_alpha = GetWBE(p_buf);
+ break;
+
+ default:
+ break;
+
+ }
+ p_buf += i_atomsize;
+ }
+
+ p_spu->i_start = p_block->i_pts;
+ p_spu->i_stop = p_block->i_pts + p_block->i_length;
+ p_spu->b_ephemer = (p_block->i_length == 0);
+ p_spu->b_absolute = false;
+
+ p_spu_sys->align = SUBPICTURE_ALIGN_BOTTOM;
+ p_spu_sys->text = psz_subtitle;
+ p_spu_sys->html = SegmentsToHtml( p_segment );
+
+ block_Release( p_block );
+
+ while ( p_segment )
+ {
+ segment_t *p_prev = p_segment;
+ p_segment = p_segment->p_next;
+ SegmentFree( p_prev );
+ }
+
+ return p_spu;
+}
--
1.8.5.3
More information about the vlc-devel
mailing list