[vlc-devel] [PATCH] libass: srt support
Jean-Baptiste Kempf
jb at videolan.org
Sun Sep 11 19:54:14 CEST 2011
On Sun, Sep 11, 2011 at 03:38:10PM +0200, Mathieu Velten wrote :
> This is a patch which add srt parsing to the libass renderer (using
> parsing code from mplayer) : it allows to handle correctly all the TAG
> srts out there by converting "standard" HTML-like tags into ass tags.
Hmm, ok.
> Since libass has higher priority and it now supports srt it becomes
> the default decoder for srt, I don't know if this is ok for you.
libass costs a lot more CPU than the normal freetype one, so I am not
sure...
> +/* Default fonts */
> +#ifdef __APPLE__
> +# define FC_DEFAULT_FONT "Arial Black"
> +#elif defined( WIN32 )
> +# define FC_DEFAULT_FONT "Arial"
> +#elif defined( HAVE_MAEMO )
> +# define FC_DEFAULT_FONT "Nokia Sans"
> +#else
> +# define FC_DEFAULT_FONT "Sans"
> +#endif
> +
> /*****************************************************************************
> * Module descriptor
> *****************************************************************************/
> static int Create ( vlc_object_t * );
> static void Destroy( vlc_object_t * );
>
> +#ifdef HAVE_FONTCONFIG
> +#define FONT_LONGTEXT N_("Font family for the font you want to use")
> +#else
> +#define FONT_LONGTEXT N_("Fontfile for the font you want to use")
> +#endif
> +
> +#define FONT_TEXT N_("Font")
> +#define LINESPACING_TEXT N_("Line spacing")
> +#define LINESPACING_LONGTEXT N_("")
> +#define BORDERSTYLE_TEXT N_("Border style")
> +#define BORDERSTYLE_LONGTEXT N_("")
> +#define OUTLINE_TEXT N_("Outline")
> +#define OUTLINE_LONGTEXT N_("")
> +#define SHADOW_TEXT N_("Shadow")
> +#define SHADOW_LONGTEXT N_("")
> +#define LEFTMARGIN_TEXT N_("Left margin")
> +#define RIGHTMARGIN_TEXT N_("Right margin")
> +#define VERTICALMARGIN_TEXT N_("Vertical margin")
> +#define MARGIN_LONGTEXT N_("")
> +#define VERTICALALIGNMENT_TEXT N_("Vertical alignment")
> +#define HORIZONTALALIGNMENT_TEXT N_("Horizontal alignment")
> +#define ALIGNMENT_LONGTEXT N_("")
> +#define BOLD_TEXT N_("Bold")
> +#define ITALIC_TEXT N_("Italic")
> +#define FONTSIZE_TEXT N_("Font size")
> +#define FONTSIZE_LONGTEXT N_("")
> +#define TEXTCOLOR_TEXT N_("Text color")
> +#define TEXTTRANSPARENCY_TEXT N_("Text transparency")
> +#define BORDERCOLOR_TEXT N_("Border color")
> +#define BORDERTRANSPARENCY_TEXT N_("Border transparency")
> +#define SHADOWCOLOR_TEXT N_("Shadow color")
> +#define SHADOWTRANSPARENCY_TEXT N_("Shadow transparency")
> +#define COLOR_LONGTEXT N_("")
> +#define TRANSPARENCY_LONGTEXT N_("0 = totally opaque, 255 = transparent")
> +#define FORCE_STYLE_TEXT N_("Override default style for ASS subtitles")
> +
> +#define BORDER_OUTLINE 1
> +#define BORDER_OPAQUE_BOX 3
> +
> +static int const pi_border_style[] = { BORDER_OUTLINE, BORDER_OPAQUE_BOX };
> +static const char *const ppsz_border_style_text[] = {
> + N_("Outline"), N_("Opaque box") };
> +
> +static int const pi_vertical_alignment[] = { VALIGN_TOP, VALIGN_CENTER, VALIGN_SUB };
> +static const char *const ppsz_vertical_alignment_text[] = {
> + N_("Top"), N_("Center"), N_("Sub") };
> +
> +static int const pi_horizontal_alignment[] = { HALIGN_LEFT, HALIGN_CENTER, HALIGN_RIGHT };
> +static const char *const ppsz_horizontal_alignment_text[] = {
> + N_("Left"), N_("Center"), N_("Right") };
> +
> +static const int pi_color_values[] = {
> + 0x000000, 0x808080, 0xC0C0C0, 0xFFFFFF, 0x800000,
> + 0xFF0000, 0xFF00FF, 0xFFFF00, 0x008080, 0x008000, 0x008080,
> + 0x00FF00, 0x800080, 0x000080, 0x0000FF, 0x00FFFF };
> +
> +static const char *const ppsz_color_descriptions[] = {
> + N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
> + N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
> + N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
> +
> +
> vlc_module_begin ()
> set_shortname( N_("Subtitles (advanced)"))
> set_description( N_("Subtitle renderers using libass") )
> set_capability( "decoder", 100 )
> set_category( CAT_INPUT )
> set_subcategory( SUBCAT_INPUT_SCODEC )
> +
> + add_font( "libass-font", FC_DEFAULT_FONT, FONT_TEXT, FONT_LONGTEXT,
> + false )
> +
> + add_integer( "libass-fontsize", 18, FONTSIZE_TEXT,
> + FONTSIZE_LONGTEXT, false )
> +
> + add_bool( "libass-bold", true, BOLD_TEXT, BOLD_TEXT, false )
> +
> + add_bool( "libass-italic", false, ITALIC_TEXT, ITALIC_TEXT, false )
> +
> + /* hook to the color values list, with default 0xffffff = white */
> + add_integer_with_range( "libass-text-color", 0xFFFFFF, 0x000000, 0xFFFFFF, TEXTCOLOR_TEXT,
> + COLOR_LONGTEXT, false )
> + change_integer_list( pi_color_values, ppsz_color_descriptions )
> + add_integer_with_range( "libass-text-transparency", 0x00, 0x00, 0xFF, TEXTTRANSPARENCY_TEXT,
> + TRANSPARENCY_LONGTEXT, false )
> + add_integer_with_range( "libass-border-color", 0x000000, 0x000000, 0xFFFFFF, BORDERCOLOR_TEXT,
> + COLOR_LONGTEXT, false )
> + change_integer_list( pi_color_values, ppsz_color_descriptions )
> + add_integer_with_range( "libass-border-transparency", 0x00, 0x00, 0xFF, BORDERTRANSPARENCY_TEXT,
> + TRANSPARENCY_LONGTEXT, false )
> + add_integer_with_range( "libass-shadow-color", 0x000000, 0x000000, 0xFFFFFF, SHADOWCOLOR_TEXT,
> + COLOR_LONGTEXT, false )
> + change_integer_list( pi_color_values, ppsz_color_descriptions )
> + add_integer_with_range( "libass-shadow-transparency", 0x80, 0x00, 0xFF, SHADOWTRANSPARENCY_TEXT,
> + TRANSPARENCY_LONGTEXT, false )
> +
> + add_integer( "libass-border-style", 2, BORDERSTYLE_TEXT,
> + BORDERSTYLE_LONGTEXT, false )
> + change_integer_list( pi_border_style, ppsz_border_style_text )
> +
> + add_float_with_range( "libass-outline", 1.0, 0.0, 4.0,
> + OUTLINE_TEXT, OUTLINE_LONGTEXT, false )
> +
> + add_float_with_range( "libass-shadow", 1.0, 0.0, 4.0,
> + SHADOW_TEXT, SHADOW_LONGTEXT, false )
> +
> + add_integer( "libass-line-spacing", 0, LINESPACING_TEXT,
> + LINESPACING_LONGTEXT, false )
> +
> + add_integer( "libass-left-margin", 20, LEFTMARGIN_TEXT,
> + MARGIN_LONGTEXT, false )
> + add_integer( "libass-right-margin", 20, RIGHTMARGIN_TEXT,
> + MARGIN_LONGTEXT, false )
> + add_integer( "libass-vertical-margin", 20, VERTICALMARGIN_TEXT,
> + MARGIN_LONGTEXT, false )
> +
> + add_integer( "libass-horizontal-alignment", HALIGN_CENTER, HORIZONTALALIGNMENT_TEXT,
> + ALIGNMENT_LONGTEXT, false )
> + change_integer_list( pi_horizontal_alignment, ppsz_horizontal_alignment_text )
> +
> + add_integer( "libass-vertical-alignment", VALIGN_SUB, VERTICALALIGNMENT_TEXT,
> + ALIGNMENT_LONGTEXT, false )
> + change_integer_list( pi_vertical_alignment, ppsz_vertical_alignment_text )
> +
> + add_bool( "libass-force-style", false, FORCE_STYLE_TEXT, FORCE_STYLE_TEXT, false )
> +
NO WAY.
Duplicating options that are used in freetype is just a no-go.
> + p_sys->s_fontfamily = var_CreateGetString( p_dec, "libass-font" );
> + p_sys->i_font_size = var_CreateGetInteger( p_dec, "libass-fontsize" );
> + p_sys->b_bold = var_CreateGetBool( p_dec, "libass-bold" );
> + p_sys->b_italic = var_CreateGetBool( p_dec, "libass-italic" );
> + p_sys->i_text_color = var_CreateGetInteger( p_dec, "libass-text-color" ) << 8 | var_CreateGetInteger( p_dec, "libass-text-transparency" );
> + p_sys->i_border_color = var_CreateGetInteger( p_dec, "libass-border-color" ) << 8 | var_CreateGetInteger( p_dec, "libass-border-transparency" );
> + p_sys->i_shadow_color = var_CreateGetInteger( p_dec, "libass-shadow-color" ) << 8 | var_CreateGetInteger( p_dec, "libass-shadow-transparency" );
> + p_sys->i_border_style = var_CreateGetInteger( p_dec, "libass-border-style" );
> + p_sys->f_outline = var_CreateGetFloat( p_dec, "libass-outline" );
> + p_sys->f_shadow = var_CreateGetFloat( p_dec, "libass-shadow" );
> + p_sys->i_left_margin = var_CreateGetInteger( p_dec, "libass-left-margin" );
> + p_sys->i_right_margin = var_CreateGetInteger( p_dec, "libass-right-margin" );
> + p_sys->i_vertical_margin = var_CreateGetInteger( p_dec, "libass-vertical-margin" );
> + p_sys->i_horizontal_alignment = var_CreateGetInteger( p_dec, "libass-horizontal-alignment" );
> + p_sys->i_vertical_alignment = var_CreateGetInteger( p_dec, "libass-vertical-alignment" );
> + p_sys->b_force_style = var_CreateGetBool( p_dec, "libass-force-style" );
THis shouldn't be var_CreateGet* I think.
> +#define FFMIN(a,b) ((a) > (b) ? (b) : (a))
Seriously? not __MIN ?
> + int i;
> + ASS_Event* evt = track->events + track->n_events - 1;
> +
> + for (i = 0; i<track->n_events - 1; ++i) // ignoring last event, it is the one we are comparing with
VLC is C99.
> +/*
> + * SubRip
> + *
> + * Support basic tags (italic, bold, underline, strike-through)
> + * and font tag with size, color and face attributes.
> + *
> + */
> +
> +struct font_tag {
> + struct bstr face;
> + int size;
> + uint32_t color;
> +};
> +
> +static const struct tag_conv {
> + const char *from;
> + const char *to;
> +} subrip_basic_tags[] = {
> + {"<i>", "{\\i1}"}, {"</i>", "{\\i0}"},
> + {"<b>", "{\\b1}"}, {"</b>", "{\\b0}"},
> + {"<u>", "{\\u1}"}, {"</u>", "{\\u0}"},
> + {"<s>", "{\\s1}"}, {"</s>", "{\\s0}"},
> +/* don't escape {} because it breaks ass in srt tags
> + {"{", "\\{"}, {"}", "\\}"}, */
> + {"\n", "\\N"}
> +};
> +
> +static const struct {
> + const char *s;
> + uint32_t v;
> +} subrip_web_colors[] = {
> + /* 16 named HTML colors in BGR format */
> + {"red", 0x0000ff}, {"blue", 0xff0000}, {"lime", 0x00ff00},
> + {"aqua", 0xffff00}, {"purple", 0x800080}, {"yellow", 0x00ffff},
> + {"fuchsia", 0xff00ff}, {"white", 0xffffff}, {"gray", 0x808080},
> + {"maroon", 0x000080}, {"olive", 0x008080}, {"black", 0x000000},
> + {"silver", 0xc0c0c0}, {"teal", 0x808000}, {"green", 0x008000},
> + {"navy", 0x800000}
> +};
Are you sure there are no regressions with the SubRip test suite?
> +void subassconvert_subrip(const char *orig, char *dest, size_t dest_buffer_size)
> +{
> + /* line is not const to avoid warnings with strtol, etc.
> + * orig content won't be changed */
> + char *line = (char *)orig;
> + struct line new_line = {
> + .buf = dest,
> + .bufsize = dest_buffer_size,
> + };
> + struct font_tag font_stack[SUBRIP_MAX_STACKED_FONT_TAGS];
> + int sp = 0;
> +
> + font_stack[0] = (struct font_tag){}; // type with all defaults
> + while (*line && new_line.len < new_line.bufsize - 1) {
> + char *orig_line = line;
> + int i;
> +
> + for (i = 0; i < FF_ARRAY_ELEMS(subrip_basic_tags); i++) {
> + const struct tag_conv *tag = &subrip_basic_tags[i];
> + int from_len = strlen(tag->from);
> + if (strncmp(line, tag->from, from_len) == 0) {
> + append_text(&new_line, "%s", tag->to);
> + line += from_len;
> + }
> + }
> +
> + if (strncmp(line, "</font>", 7) == 0) {
> + /* Closing font tag */
> + line += 7;
> +
> + if (sp > 0) {
> + struct font_tag *tag = &font_stack[sp];
> + struct font_tag *last_tag = &tag[-1];
> + sp--;
> +
> + if (tag->size) {
> + if (!last_tag->size)
> + append_text(&new_line, "{\\fs}");
> + else if (last_tag->size != tag->size)
> + append_text(&new_line, "{\\fs%d}", last_tag->size);
> + }
> +
> + if (tag->color & SUBRIP_FLAG_COLOR) {
> + if (!(last_tag->color & SUBRIP_FLAG_COLOR))
> + append_text(&new_line, "{\\c}");
> + else if (last_tag->color != tag->color)
> + append_text(&new_line, "{\\c&H%06X&}",
> + last_tag->color & 0xffffff);
> + }
> +
> + if (tag->face.len) {
> + if (!last_tag->face.len)
> + append_text(&new_line, "{\\fn}");
> + else if (bstrcmp(last_tag->face, tag->face) != 0)
> + append_text(&new_line, "{\\fn%.*s}",
> + BSTR_P(last_tag->face));
> + }
> + }
> + } else if (strncmp(line, "<font ", 6) == 0
> + && sp + 1 < FF_ARRAY_ELEMS(font_stack)) {
> + /* Opening font tag */
> + char *potential_font_tag_start = line;
> + int len_backup = new_line.len;
> + struct font_tag *tag = &font_stack[sp + 1];
> + int has_valid_attr = 0;
> + int has_comma = 0;
> +
> + *tag = tag[-1]; // keep values from previous tag
> + line += 6;
> +
> + while (*line && *line != '>') {
> + if ((has_comma = strncmp(line, "size=\"", 6) == 0) || strncmp(line, "size=", 5) == 0) {
> + line += 5;
> + if (has_comma)
> + line++;
> + tag->size = strtol(line, &line, 10);
> + if ((*line != '"' && *line != ' ' && *line != '>') || !tag->size)
> + break;
> + append_text(&new_line, "{\\fs%d}", tag->size);
> + has_valid_attr = 1;
> + } else if ((has_comma = strncmp(line, "color=\"", 7) == 0) || strncmp(line, "color=", 6) == 0) {
> + line += 6;
> + if (has_comma)
> + line++;
> + if (*line == '#') {
> + // #RRGGBB format
> + line++;
> + tag->color = strtol(line, &line, 16) & 0x00ffffff;
> + if (*line != '"' && *line != ' ' && *line != '>')
> + break;
> + tag->color = ((tag->color & 0xff) << 16) |
> + (tag->color & 0xff00) |
> + ((tag->color & 0xff0000) >> 16) |
> + SUBRIP_FLAG_COLOR;
> + } else {
> + // Standard web colors
> + int i, len = indexof(line, '"');
> + if (len <= 0)
> + len = indexof(line, ' ');
> + if (len <= 0)
> + len = indexof(line, '>');
> + if (len <= 0)
> + break;
> + for (i = 0; i < FF_ARRAY_ELEMS(subrip_web_colors); i++) {
> + const char *color = subrip_web_colors[i].s;
> + if (strlen(color) == len
> + && strncasecmp(line, color, len) == 0) {
> + tag->color = SUBRIP_FLAG_COLOR | subrip_web_colors[i].v;
> + break;
> + }
> + }
> +
> + line += len;
> +
> + if (i == FF_ARRAY_ELEMS(subrip_web_colors)) {
> + /* mp_msg(MSGT_SUBREADER, MSGL_WARN,
> + MSGTR_SUBTITLES_SubRip_UnknownFontColor, orig); */
> + append_text(&new_line, "{\\c}");
> + continue;
> + }
> + }
> + append_text(&new_line, "{\\c&H%06X&}", tag->color & 0xffffff);
> + has_valid_attr = 1;
> + } else if ((has_comma = strncmp(line, "face=\"", 6) == 0) || strncmp(line, "face=", 5) == 0) {
> + /* Font face attribute */
> + int len;
> + line += 5;
> + if (has_comma)
> + line++;
> + len = indexof(line, '"');
> + if (len <= 0)
> + len = indexof(line, ' ');
> + if (len <= 0)
> + len = indexof(line, '>');
> + if (len <= 0)
> + break;
> + tag->face.start = line;
> + tag->face.len = len;
> + line += len;
> + append_text(&new_line, "{\\fn%.*s}", BSTR_P(tag->face));
> + has_valid_attr = 1;
> + }
> + line++;
> + }
> +
> + if (!has_valid_attr || *line != '>') { /* Not valid font tag */
> + line = potential_font_tag_start;
> + new_line.len = len_backup;
> + } else {
> + sp++;
> + line++;
> + }
> + }
> +
> + /* Tag conversion code didn't match */
> + if (line == orig_line)
> + new_line.buf[new_line.len++] = *line++;
> + }
> + new_line.buf[new_line.len] = 0;
> +}
Has this code been fuzzed?
Best Regards,
--
Jean-Baptiste Kempf
http://www.jbkempf.com/ - +33 672 704 734
Sent from my Electronic Device
More information about the vlc-devel
mailing list