[MPlayer-dev-eng] [PATCH] Tags support for SubRip and MicroDVD subtitles

ubitux ubitux at gmail.com
Mon May 17 00:26:53 CEST 2010


Hi,

Well, I've spend my week end on supporting tags for SubRip and MicroDVD
subtitles, and it seems to work fine.

So as expected, I used the libass for rendering. So the patch add a new
file specially focused on the subtitles convertion.

Here is what is supported:


   *SubRip*

 - Basic tags: italic, bold, underline, strike-through
 - Nested font tags with size, color and face attributes
 - When tag is not recognized, it is not stripped but display, so a
   subtitle with <Hello> inside will be displayed.

You can test it with this kind of subtitle:
        This <font color="#112233" size="6" face="Arial">could be <b>the</b> <font size="35" face="Times New Roman">m<font color="#000000">o</font>st</font> difficult thing to</font>implement.

It will gives:
        This {\c&H332211&}{\fs6}{\fnArial}could be {\b1}the{\b0} {\fs35}{\fnTimes New Roman}m{\c&H000000&}o{\c&H332211&}st{\fs6}{\fnArial}


   *MicroDVD subtitles*

 - Style, color, font name, font size, position, coordinates
 - Persistence

You can test it something like that:

        {667}{705}{Y:i}{o:123,231}Qu’y a-t-il|dans un nom ?
        {711}{758}{y:s}{C:00ff00}{f:Times New Roman}Ce que nous|appelons une|{c:ff00c0}rose,
        {763}{826}{Y:i}{s:12}embaumerait autant|sous un|autre nom.
        {831}{869}{y:ib}Roméo|{Y:u}et|{y:i,z}Juliette


Well, anyway, I attach the full patch so you can test it. Don't forget to
use -ass if you want to get the good rendering. Also, I give the patch in
two versions: SVN one and Git one (thanks to Uoti Urpala for the
ass_enabled trick) in order to get more testing chances :)

Please test it and send me feedback,

Best regards,

-- 
ubitux
-------------- next part --------------
Index: Makefile
===================================================================
--- Makefile	(revision 31179)
+++ Makefile	(working copy)
@@ -344,6 +344,7 @@
               playtreeparser.c \
               spudec.c \
               sub_cc.c \
+              subassconvert.c \
               subopt-helper.c \
               subreader.c \
               vobsub.c \
Index: subassconvert.c
===================================================================
--- subassconvert.c	(revision 0)
+++ subassconvert.c	(revision 0)
@@ -0,0 +1,552 @@
+/*
+ * Subtitles converter to SSA/ASS in order to allow special formatting
+ *
+ * This file is part of MPlayer.
+ *
+ * MPlayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MPlayer 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with MPlayer; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "subreader.h"
+#include "subassconvert.h"
+
+#define SUB_MAX_FONTSTRING_LEN  32
+
+static int insert_text_in_subdest(char *dst, int *j, char *src, int srclen)
+{
+    if (!(*j + srclen + 1 < LINE_LEN))
+        return -1;
+    strcpy(&dst[*j], src);
+    *j += srclen;
+    return 0;
+}
+
+
+/*
+ *      SubRip
+ *
+ *      Support basic tags (italic, bold, underline, strike-through)
+ *      and font tag with size, color and face attributes.
+ *
+ */
+
+typedef struct {
+    int field;
+    long int size;
+    uint32_t color;
+    char face[SUB_MAX_FONTSTRING_LEN];
+} font_stack_tag;
+
+#define SUBRIP_FIELD_FONT_SIZE  (1 << 0)
+#define SUBRIP_FIELD_FONT_COLOR (1 << 1)
+#define SUBRIP_FIELD_FONT_FACE  (1 << 2)
+
+static const struct {
+    char *from;
+    char *to;
+} subrip_basic_tags[] = {
+    {"<i>", "{\\i1}"}, {"</i>", "{\\i0}"},
+    {"<b>", "{\\b1}"}, {"</b>", "{\\b0}"},
+    {"<u>", "{\\u1}"}, {"</u>", "{\\u0}"},
+    {"<s>", "{\\s1}"}, {"</s>", "{\\s0}"}
+};
+
+#define SUBRIP_MAX_STACKED_FONT_TAGS    16
+
+static void *subrip_get_last_tag_setting(font_stack_tag *fstack, int sp, int flag)
+{
+    while (sp > 0) {
+        font_stack_tag *tag;
+
+        sp--;
+        tag = &fstack[sp];
+        switch (tag->field & flag) {
+            case SUBRIP_FIELD_FONT_SIZE:
+                return &tag->size;
+            case SUBRIP_FIELD_FONT_COLOR:
+                return &tag->color;
+            case SUBRIP_FIELD_FONT_FACE:
+                return tag->face;
+            default:
+                break;
+        }
+    }
+    return NULL;
+}
+
+void subassconvert_subrip(char *line)
+{
+    char new_line[LINE_LEN + 1];
+    char *start_line = line;
+    int j = 0;
+    font_stack_tag font_stack[SUBRIP_MAX_STACKED_FONT_TAGS];
+    int sp = 0;
+
+    while (*line && j < sizeof(new_line) - 1) {
+        int n, match = 0;
+
+        /* Basic tags */
+        for (n = 0; n < (int)(sizeof(subrip_basic_tags) / sizeof(*subrip_basic_tags)); n++) {
+            int from_len = strlen(subrip_basic_tags[n].from);
+            int to_len = strlen(subrip_basic_tags[n].to);
+
+            if (strncmp(line, subrip_basic_tags[n].from, from_len) == 0) {
+                insert_text_in_subdest(new_line, &j, subrip_basic_tags[n].to, to_len);
+                line += from_len;
+                match = 1;
+                break;
+            }
+        }
+
+        if (!match) {
+
+            /* Closing font tag */
+            if (strncmp(line, "</font>", 7) == 0) {
+                line += 7;
+                match = 1;
+
+                if (sp > 0) {
+                    font_stack_tag *tag;
+
+                    sp--;
+                    tag = &font_stack[sp];
+
+                    if (tag->field & SUBRIP_FIELD_FONT_SIZE) {
+                        void *last_setting_ptr = subrip_get_last_tag_setting(font_stack, sp, SUBRIP_FIELD_FONT_SIZE);
+
+                        if (last_setting_ptr == NULL) {
+                            insert_text_in_subdest(new_line, &j, "{\\fs}", sizeof("{\\fs}") - 1);
+                        } else {
+                            char stag[4 + sizeof(tag->size) + 2];
+
+                            sprintf(stag, "{\\fs%ld}", *(long int *)last_setting_ptr);
+                            insert_text_in_subdest(new_line, &j, stag, strlen(stag));
+                        }
+                    }
+
+                    if (tag->field & SUBRIP_FIELD_FONT_COLOR) {
+                        void *last_setting_ptr = subrip_get_last_tag_setting(font_stack, sp, SUBRIP_FIELD_FONT_COLOR);
+
+                        if (last_setting_ptr == NULL) {
+                            insert_text_in_subdest(new_line, &j, "{\\c}", sizeof("{\\c}") - 1);
+                        } else {
+                            char ctag[] = "{\\c&Hbbggrr&}";
+
+                            sprintf(ctag, "{\\c&H%06X&}", *(uint32_t *)last_setting_ptr);
+                            insert_text_in_subdest(new_line, &j, ctag, sizeof(ctag) - 1);
+                        }
+                    }
+
+                    if (tag->field & SUBRIP_FIELD_FONT_FACE) {
+                        void *last_setting_ptr = subrip_get_last_tag_setting(font_stack, sp, SUBRIP_FIELD_FONT_FACE);
+
+                        if (last_setting_ptr == NULL) {
+                            insert_text_in_subdest(new_line, &j, "{\\fn}", sizeof("{\\fn}") - 1);
+                        } else {
+                            char ftag[4 + sizeof(tag->face) + 2];
+                            int len = sprintf(ftag, "{\\fn%s}", (char *)last_setting_ptr);
+
+                            insert_text_in_subdest(new_line, &j, ftag, len);
+                        }
+                    }
+                }
+
+            /* Opening font tag */
+            } else if (strncmp(line, "<font ", 6) == 0) {
+                char *potential_font_tag_start = line;
+                int j_backup = j;
+                font_stack_tag *tag = &font_stack[sp];
+
+                if (sp < sizeof(font_stack)) {
+                    tag->field = 0;
+                    line += 6;
+
+                    while (*line && *line != '>') {
+
+                        /* Size attribute */
+                        if (strncmp(line, "size=\"", 6) == 0) {
+                            char stag[4 + sizeof(tag->size) + 2];
+                            int len;
+
+                            line += 6;
+                            tag->size = strtol(line, &line, 10);
+                            if (*line != '"')
+                                break;
+                            len = sprintf(stag, "{\\fs%ld}", tag->size);
+                            insert_text_in_subdest(new_line, &j, stag, len);
+                            tag->field |= SUBRIP_FIELD_FONT_SIZE;
+
+                            /* Color attribute */
+                        } else if (strncmp(line, "color=\"#", 8) == 0) {
+                            char ctag[] = "{\\c&Hbbggrr&}";
+
+                            line += 8;
+                            tag->color = (uint32_t)strtol(line, &line, 16) & 0x00ffffff;
+                            tag->color = ((tag->color & 0xff) << 16) | (tag->color & 0xff00) | ((tag->color & 0xff0000) >> 16);
+                            if (*line != '"')
+                                break;
+                            sprintf(ctag, "{\\c&H%06X&}", tag->color);
+                            insert_text_in_subdest(new_line, &j, ctag, sizeof(ctag) - 1);
+                            tag->field |= SUBRIP_FIELD_FONT_COLOR;
+
+                            /* Font face attribute */
+                        } else if (strncmp(line, "face=\"", 6) == 0) {
+                            int i = 0, len;
+                            char ftag[4 + sizeof(tag->face) + 2];
+
+                            line += 6;
+                            while (*line && *line != '"' && i < sizeof(tag->face) - 1)
+                                tag->face[i++] = *line++;
+                            tag->face[i] = 0;
+                            if (*line != '"')
+                                break;
+                            len = sprintf(ftag, "{\\fn%s}", tag->face);
+                            insert_text_in_subdest(new_line, &j, ftag, len);
+                            tag->field |= SUBRIP_FIELD_FONT_FACE;
+                        }
+
+                        line++;
+                    }
+
+                    if (!tag->field || *line != '>') {      /* Not valid font tag */
+                        line = potential_font_tag_start;
+                        j = j_backup;
+                    } else {
+                        match = 1;
+                        sp++;
+                        line++;
+                    }
+                }
+            }
+        }
+
+        /* Normal text */
+        if (!match)
+            new_line[j++] = *line++;
+    }
+    new_line[j] = 0;
+    strcpy(start_line, new_line);
+}
+
+
+/*
+ *      MicroDVD
+ *
+ *      Based on the specifications found here:
+ *      https://trac.videolan.org/vlc/ticket/1825#comment:6
+ */
+
+static int indexof(const char *s, int c)
+{
+    char *f = strchr(s, c);
+
+    return (f) ? (f - s) : -1;
+}
+
+typedef struct {
+    char key;
+    int persistent;
+    uint32_t data1;
+    uint32_t data2;
+    char data_string[SUB_MAX_FONTSTRING_LEN];
+} microdvd_tag_type;
+
+#define MICRODVD_DEAD_TAG           {MICRODVD_TAG_DISABLED, MICRODVD_PERSISTENT_OFF, 0, 0, ""}
+
+#define MICRODVD_PERSISTENT_OFF     0
+#define MICRODVD_PERSISTENT_ON      1
+#define MICRODVD_PERSISTENT_OPENED  2
+
+#define MICRODVD_TAGS               "cfshyYpo"   /* Color, Font, Size, cHarset, stYle, Position, cOordinate */
+
+static void microdvd_set_tag(microdvd_tag_type *tags, microdvd_tag_type tag)
+{
+    int tag_index = indexof(MICRODVD_TAGS, tag.key);
+
+    if (tag_index < 0)
+        return;
+    memcpy(&tags[tag_index], &tag, sizeof(tag));
+}
+
+#define MICRODVD_STYLES             "ibus"      /* italic, bold, underline, strike-through */
+#define MICRODVD_TAG_DISABLED       '.'
+
+static char *microdvd_load_tags(microdvd_tag_type *tags, char *s)
+{
+    while (*s == '{') {
+        char *start = s;
+        char tag_char = *(s + 1);
+        microdvd_tag_type tag = MICRODVD_DEAD_TAG;
+
+        if (!tag_char || *(s + 2) != ':')
+            break;
+        s += 3;
+
+        switch (tag_char) {
+
+            /* Style */
+            case 'Y':
+                tag.persistent = MICRODVD_PERSISTENT_ON;
+            case 'y':
+                while (*s && *s != '}') {
+                    int style_index = indexof(MICRODVD_STYLES, *s);
+
+                    if (style_index >= 0)
+                        tag.data1 |= (1 << style_index);
+                    s++;
+                }
+                if (*s != '}')
+                    break;
+                tag.key = tag_char;   /* We must distinct persistent and non-persistent styles
+                                         to handle that kind of style tags: {y:ib}{Y:us} */
+                break;
+
+            /* Color */
+            case 'C':
+                tag.persistent = MICRODVD_PERSISTENT_ON;
+            case 'c':
+                tag.data1 = (uint32_t)strtol(s, &s, 16) & 0x00ffffff;
+                if (*s != '}')
+                    break;
+                tag.key = 'c';
+                break;
+
+            /* Font name */
+            case 'F':
+                tag.persistent = MICRODVD_PERSISTENT_ON;
+            case 'f':
+            {
+                int i = 0;
+
+                while (*s && *s != '}' && i < sizeof(tag.data_string))
+                    tag.data_string[i++] = *s++;
+                if (*s != '}')
+                    break;
+                tag.key = 'f';
+                break;
+            }
+
+            /* Font size */
+            case 'S':
+                tag.persistent = MICRODVD_PERSISTENT_ON;
+            case 's':
+            {
+                tag.data1 = (uint32_t)strtol(s, &s, 10);
+                if (*s != '}' || tag.data1 > 72)
+                    break;
+                tag.key = 's';
+                break;
+            }
+
+            /* Charset */
+            case 'H':
+            {
+                int i = 0;
+
+                //TODO: not yet handled, just parsed.
+                while (*s && *s != '}' && i < sizeof(tag.data_string))
+                    tag.data_string[i++] = *s++;
+                if (*s != '}')
+                    break;
+                tag.key = 'h';
+                break;
+            }
+
+            /* Position */
+            case 'P':
+                tag.persistent = MICRODVD_PERSISTENT_ON;
+                tag.data1 = (*s++ == '1');
+                if (*s != '}')
+                    break;
+                tag.key = 'p';
+                break;
+
+            /* Coordinates */
+            case 'o':
+                tag.persistent = MICRODVD_PERSISTENT_ON;
+                tag.data1 = (uint32_t)strtol(s, &s, 10);
+                if (*s != ',')
+                    break;
+                s++;
+                tag.data2 = (uint32_t)strtol(s, &s, 10);
+                if (*s != '}')
+                    break;
+                tag.key = 'o';
+                break;
+
+            default:    /* Unknown tag, we considere it's text */
+                break;
+        }
+
+        if (tag.key == MICRODVD_TAG_DISABLED)
+            return start;
+
+        microdvd_set_tag(tags, tag);
+        s++;
+    }
+    return s;
+}
+
+static void microdvd_open_tags(char *new_line, int *j, microdvd_tag_type *tags, int persistence)
+{
+    int i;
+
+    for (i = 0; i < sizeof(MICRODVD_TAGS) - 1; i++) {
+        if (tags[i].persistent != persistence)
+            continue;
+        switch (tags[i].key) {
+            case 'Y':
+            case 'y':
+            {
+                int style_index = 0;
+                char ytag[] = "{\\.1}";
+
+                while (style_index < sizeof(MICRODVD_STYLES) - 1) {
+                    int style_mask = (1 << style_index);
+
+                    if (tags[i].data1 & style_mask) {
+                        ytag[2] = MICRODVD_STYLES[style_index];
+                        insert_text_in_subdest(new_line, j, ytag, sizeof(ytag) - 1);
+                    }
+                    style_index++;
+                }
+                break;
+            }
+
+            case 'c':
+            {
+                char ctag[] = "{\\c&Hbbggrr&}";
+
+                sprintf(ctag, "{\\c&H%06X&}", tags[i].data1);
+                insert_text_in_subdest(new_line, j, ctag, sizeof(ctag) - 1);
+                break;
+            }
+
+            case 'f':
+            {
+                char ftag[4 + sizeof(tags[0].data_string) + 2];
+                int len = sprintf(ftag, "{\\fn%s}", tags[i].data_string);
+
+                insert_text_in_subdest(new_line, j, ftag, len);
+                break;
+            }
+
+            case 's':
+            {
+                char stag[4 + 2 + 2];
+                int len = sprintf(stag, "{\\fs%d}", tags[i].data1);
+
+                insert_text_in_subdest(new_line, j, stag, len);
+                break;
+            }
+
+            case 'p':
+                if (tags[i].data1 == 0)
+                    insert_text_in_subdest(new_line, j, "{\\an8}", sizeof("{\\an8}") - 1);
+                break;
+
+            case 'o':
+            {
+                char otag[5 + sizeof(tags[0].data1) * 2 + 3];
+                int len = sprintf(otag, "{\\pos(%d,%d)}", tags[i].data1, tags[i].data2);
+
+                insert_text_in_subdest(new_line, j, otag, len);
+                break;
+            }
+
+            default:
+                break;
+        }
+        if (tags[i].persistent == MICRODVD_PERSISTENT_ON)
+            tags[i].persistent = MICRODVD_PERSISTENT_OPENED;
+    }
+}
+
+static void microdvd_close_no_persistent_tags(char *new_line, int *j, microdvd_tag_type *tags)
+{
+    int i;
+
+    for (i = sizeof(MICRODVD_TAGS) - 2; i; i--) {
+        if (tags[i].persistent != MICRODVD_PERSISTENT_OFF)
+            continue;
+        switch (tags[i].key) {
+            case 'y':
+            {
+                int style_index = sizeof(MICRODVD_STYLES) - 2;
+                char ctag[] = "{\\.0}";
+
+                while (style_index >= 0) {
+                    int style_mask = (1 << style_index);
+
+                    if (tags[i].data1 & style_mask) {
+                        ctag[2] = MICRODVD_STYLES[style_index];
+                        insert_text_in_subdest(new_line, j, ctag, sizeof(ctag) - 1);
+                    }
+                    style_index--;
+                }
+                break;
+            }
+
+            case 'c':
+                insert_text_in_subdest(new_line, j, "{\\c}", sizeof("{\\c}") - 1);
+                break;
+
+            case 'f':
+                insert_text_in_subdest(new_line, j, "{\\fn}", sizeof("{\\fn}") - 1);
+                break;
+
+            case 's':
+                insert_text_in_subdest(new_line, j, "{\\fs}", sizeof("{\\fs}") - 1);
+                break;
+
+            default:
+                break;
+        }
+        tags[i].key = MICRODVD_TAG_DISABLED;
+    }
+}
+
+void subassconvert_microdvd(char *line)
+{
+    int i, j = 0;
+    char new_line[LINE_LEN + 1];
+    char *line_start = line;
+    microdvd_tag_type tags[sizeof(MICRODVD_TAGS) - 1];
+    const microdvd_tag_type dead_tag = MICRODVD_DEAD_TAG;
+
+    for (i = 0; MICRODVD_TAGS[i]; i++)
+        memcpy(&tags[i], &dead_tag, sizeof(dead_tag));
+
+    while (*line) {
+        line = microdvd_load_tags(tags, line);
+        microdvd_open_tags(new_line, &j, tags, MICRODVD_PERSISTENT_ON);
+        microdvd_open_tags(new_line, &j, tags, MICRODVD_PERSISTENT_OFF);
+
+        while (*line && *line != '|')
+            new_line[j++] = *line++;
+
+        if (*line == '|') {
+            microdvd_close_no_persistent_tags(new_line, &j, tags);
+            insert_text_in_subdest(new_line, &j, "\\N", sizeof("\\N") - 1);
+            line++;
+        }
+    }
+
+    new_line[j] = 0;
+    strcpy(line_start, new_line);
+}
Index: subassconvert.h
===================================================================
--- subassconvert.h	(revision 0)
+++ subassconvert.h	(revision 0)
@@ -0,0 +1,27 @@
+/*
+ * Header for subtitles converter to SSA/ASS
+ *
+ * This file is part of MPlayer.
+ *
+ * MPlayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MPlayer 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with MPlayer; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPLAYER_SUBASSCONVERT_H
+#define MPLAYER_SUBASSCONVERT_H
+
+void subassconvert_subrip(char *line);
+void subassconvert_microdvd(char *line);
+
+#endif
Index: subreader.c
===================================================================
--- subreader.c	(revision 31179)
+++ subreader.c	(working copy)
@@ -36,6 +36,9 @@
 #include "libavutil/common.h"
 #include "libavutil/avstring.h"
 
+#include "libass/ass_mp.h"
+#include "subassconvert.h"
+
 #ifdef CONFIG_ENCA
 #include <enca.h>
 #endif
@@ -55,8 +58,6 @@
 
 extern char* dvdsub_lang;
 
-/* Maximal length of line of a subtitle */
-#define LINE_LEN 1000
 static float mpsub_position=0;
 static float mpsub_multiplier=1.;
 static int sub_slacktime = 20000; //20 sec
@@ -291,6 +292,9 @@
 
     p=line2;
 
+    if (ass_enabled)
+        subassconvert_microdvd(p);
+
     next=p, i=0;
     while ((next =sub_readtext (next, &(current->text[i])))) {
         if (current->text[i]==ERR) {return ERR;}
@@ -373,6 +377,10 @@
 	for (i=0; i<SUB_MAX_TEXT;) {
 	    int blank = 1;
 	    if (!stream_read_line (st, line, LINE_LEN, utf16)) break;
+
+	    if (ass_enabled)
+		subassconvert_subrip(line);
+
 	    len=0;
 	    for (p=line; *p!='\n' && *p!='\r' && *p; p++,len++)
 		if (*p != ' ' && *p != '\t')
@@ -384,17 +392,19 @@
 		//strncpy (current->text[i], line, len); current->text[i][len]='\0';
                 for(; j<len; j++) {
 		    /* let's filter html tags ::atmos */
-		    if(line[j]=='>') {
-			skip=0;
-			continue;
+		    if (!ass_enabled) {
+			if(line[j]=='>') {
+			    skip=0;
+			    continue;
+			}
+			if(line[j]=='<') {
+			    skip=1;
+			    continue;
+			}
+			if(skip) {
+			    continue;
+			}
 		    }
-		    if(line[j]=='<') {
-			skip=1;
-			continue;
-		    }
-		    if(skip) {
-			continue;
-		    }
 		    *curptr=line[j];
 		    curptr++;
 		}
Index: subreader.h
===================================================================
--- subreader.h	(revision 31179)
+++ subreader.h	(working copy)
@@ -44,6 +44,9 @@
 #define SUB_JACOSUB  12
 #define SUB_MPL2     13
 
+/* Maximal length of line of a subtitle */
+#define LINE_LEN 1000
+
 // One of the SUB_* constant above
 extern int sub_format;
 
-------------- next part --------------
diff --git a/Makefile b/Makefile
index d9cb34d..68f399e 100644
--- a/Makefile
+++ b/Makefile
@@ -348,6 +348,7 @@ SRCS_COMMON = asxparser.c \
               playtreeparser.c \
               spudec.c \
               sub_cc.c \
+              subassconvert.c \
               subopt-helper.c \
               subreader.c \
               talloc.c \
diff --git a/mencoder.c b/mencoder.c
index dc3d870..f7d08b4 100644
--- a/mencoder.c
+++ b/mencoder.c
@@ -346,7 +346,7 @@ static void add_subtitles(char *filename, float fps, int silent)
 
     if (!filename) return;
 
-    subd = sub_read_file(filename, fps);
+    subd = sub_read_file(filename, fps, &opts);
 #ifdef CONFIG_ASS
     if (opts.ass_enabled)
 #ifdef CONFIG_ICONV
diff --git a/mplayer.c b/mplayer.c
index beb1af5..356f6d1 100644
--- a/mplayer.c
+++ b/mplayer.c
@@ -1119,7 +1119,7 @@ void add_subtitles(struct MPContext *mpctx, char *filename, float fps, int noerr
         asst = ass_read_stream(ass_library, filename, 0);
 #endif
         if (!asst) {
-            subd = sub_read_file(filename, fps);
+            subd = sub_read_file(filename, fps, &mpctx->opts);
             if (subd) {
                 asst = ass_read_subdata(ass_library, subd, fps);
                 if (asst) {
@@ -1130,7 +1130,7 @@ void add_subtitles(struct MPContext *mpctx, char *filename, float fps, int noerr
         }
     } else
 #endif
-        subd = sub_read_file(filename, fps);
+        subd = sub_read_file(filename, fps, &mpctx->opts);
 
 
     if (!asst && !subd) {
diff --git a/subassconvert.c b/subassconvert.c
new file mode 100644
index 0000000..41df121
--- /dev/null
+++ b/subassconvert.c
@@ -0,0 +1,552 @@
+/*
+ * Subtitles converter to SSA/ASS in order to allow special formatting
+ *
+ * This file is part of MPlayer.
+ *
+ * MPlayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MPlayer 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with MPlayer; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "subreader.h"
+#include "subassconvert.h"
+
+#define SUB_MAX_FONTSTRING_LEN  32
+
+static int insert_text_in_subdest(char *dst, int *j, char *src, int srclen)
+{
+    if (!(*j + srclen + 1 < LINE_LEN))
+        return -1;
+    strcpy(&dst[*j], src);
+    *j += srclen;
+    return 0;
+}
+
+
+/*
+ *      SubRip
+ *
+ *      Support basic tags (italic, bold, underline, strike-through)
+ *      and font tag with size, color and face attributes.
+ *
+ */
+
+typedef struct {
+    int field;
+    long int size;
+    uint32_t color;
+    char face[SUB_MAX_FONTSTRING_LEN];
+} font_stack_tag;
+
+#define SUBRIP_FIELD_FONT_SIZE  (1 << 0)
+#define SUBRIP_FIELD_FONT_COLOR (1 << 1)
+#define SUBRIP_FIELD_FONT_FACE  (1 << 2)
+
+static const struct {
+    char *from;
+    char *to;
+} subrip_basic_tags[] = {
+    {"<i>", "{\\i1}"}, {"</i>", "{\\i0}"},
+    {"<b>", "{\\b1}"}, {"</b>", "{\\b0}"},
+    {"<u>", "{\\u1}"}, {"</u>", "{\\u0}"},
+    {"<s>", "{\\s1}"}, {"</s>", "{\\s0}"}
+};
+
+#define SUBRIP_MAX_STACKED_FONT_TAGS    16
+
+static void *subrip_get_last_tag_setting(font_stack_tag *fstack, int sp, int flag)
+{
+    while (sp > 0) {
+        font_stack_tag *tag;
+
+        sp--;
+        tag = &fstack[sp];
+        switch (tag->field & flag) {
+            case SUBRIP_FIELD_FONT_SIZE:
+                return &tag->size;
+            case SUBRIP_FIELD_FONT_COLOR:
+                return &tag->color;
+            case SUBRIP_FIELD_FONT_FACE:
+                return tag->face;
+            default:
+                break;
+        }
+    }
+    return NULL;
+}
+
+void subassconvert_subrip(char *line)
+{
+    char new_line[LINE_LEN + 1];
+    char *start_line = line;
+    int j = 0;
+    font_stack_tag font_stack[SUBRIP_MAX_STACKED_FONT_TAGS];
+    int sp = 0;
+
+    while (*line && j < sizeof(new_line) - 1) {
+        int n, match = 0;
+
+        /* Basic tags */
+        for (n = 0; n < (int)(sizeof(subrip_basic_tags) / sizeof(*subrip_basic_tags)); n++) {
+            int from_len = strlen(subrip_basic_tags[n].from);
+            int to_len = strlen(subrip_basic_tags[n].to);
+
+            if (strncmp(line, subrip_basic_tags[n].from, from_len) == 0) {
+                insert_text_in_subdest(new_line, &j, subrip_basic_tags[n].to, to_len);
+                line += from_len;
+                match = 1;
+                break;
+            }
+        }
+
+        if (!match) {
+
+            /* Closing font tag */
+            if (strncmp(line, "</font>", 7) == 0) {
+                line += 7;
+                match = 1;
+
+                if (sp > 0) {
+                    font_stack_tag *tag;
+
+                    sp--;
+                    tag = &font_stack[sp];
+
+                    if (tag->field & SUBRIP_FIELD_FONT_SIZE) {
+                        void *last_setting_ptr = subrip_get_last_tag_setting(font_stack, sp, SUBRIP_FIELD_FONT_SIZE);
+
+                        if (last_setting_ptr == NULL) {
+                            insert_text_in_subdest(new_line, &j, "{\\fs}", sizeof("{\\fs}") - 1);
+                        } else {
+                            char stag[4 + sizeof(tag->size) + 2];
+
+                            sprintf(stag, "{\\fs%ld}", *(long int *)last_setting_ptr);
+                            insert_text_in_subdest(new_line, &j, stag, strlen(stag));
+                        }
+                    }
+
+                    if (tag->field & SUBRIP_FIELD_FONT_COLOR) {
+                        void *last_setting_ptr = subrip_get_last_tag_setting(font_stack, sp, SUBRIP_FIELD_FONT_COLOR);
+
+                        if (last_setting_ptr == NULL) {
+                            insert_text_in_subdest(new_line, &j, "{\\c}", sizeof("{\\c}") - 1);
+                        } else {
+                            char ctag[] = "{\\c&Hbbggrr&}";
+
+                            sprintf(ctag, "{\\c&H%06X&}", *(uint32_t *)last_setting_ptr);
+                            insert_text_in_subdest(new_line, &j, ctag, sizeof(ctag) - 1);
+                        }
+                    }
+
+                    if (tag->field & SUBRIP_FIELD_FONT_FACE) {
+                        void *last_setting_ptr = subrip_get_last_tag_setting(font_stack, sp, SUBRIP_FIELD_FONT_FACE);
+
+                        if (last_setting_ptr == NULL) {
+                            insert_text_in_subdest(new_line, &j, "{\\fn}", sizeof("{\\fn}") - 1);
+                        } else {
+                            char ftag[4 + sizeof(tag->face) + 2];
+                            int len = sprintf(ftag, "{\\fn%s}", (char *)last_setting_ptr);
+
+                            insert_text_in_subdest(new_line, &j, ftag, len);
+                        }
+                    }
+                }
+
+            /* Opening font tag */
+            } else if (strncmp(line, "<font ", 6) == 0) {
+                char *potential_font_tag_start = line;
+                int j_backup = j;
+                font_stack_tag *tag = &font_stack[sp];
+
+                if (sp < sizeof(font_stack)) {
+                    tag->field = 0;
+                    line += 6;
+
+                    while (*line && *line != '>') {
+
+                        /* Size attribute */
+                        if (strncmp(line, "size=\"", 6) == 0) {
+                            char stag[4 + sizeof(tag->size) + 2];
+                            int len;
+
+                            line += 6;
+                            tag->size = strtol(line, &line, 10);
+                            if (*line != '"')
+                                break;
+                            len = sprintf(stag, "{\\fs%ld}", tag->size);
+                            insert_text_in_subdest(new_line, &j, stag, len);
+                            tag->field |= SUBRIP_FIELD_FONT_SIZE;
+
+                            /* Color attribute */
+                        } else if (strncmp(line, "color=\"#", 8) == 0) {
+                            char ctag[] = "{\\c&Hbbggrr&}";
+
+                            line += 8;
+                            tag->color = (uint32_t)strtol(line, &line, 16) & 0x00ffffff;
+                            tag->color = ((tag->color & 0xff) << 16) | (tag->color & 0xff00) | ((tag->color & 0xff0000) >> 16);
+                            if (*line != '"')
+                                break;
+                            sprintf(ctag, "{\\c&H%06X&}", tag->color);
+                            insert_text_in_subdest(new_line, &j, ctag, sizeof(ctag) - 1);
+                            tag->field |= SUBRIP_FIELD_FONT_COLOR;
+
+                            /* Font face attribute */
+                        } else if (strncmp(line, "face=\"", 6) == 0) {
+                            int i = 0, len;
+                            char ftag[4 + sizeof(tag->face) + 2];
+
+                            line += 6;
+                            while (*line && *line != '"' && i < sizeof(tag->face) - 1)
+                                tag->face[i++] = *line++;
+                            tag->face[i] = 0;
+                            if (*line != '"')
+                                break;
+                            len = sprintf(ftag, "{\\fn%s}", tag->face);
+                            insert_text_in_subdest(new_line, &j, ftag, len);
+                            tag->field |= SUBRIP_FIELD_FONT_FACE;
+                        }
+
+                        line++;
+                    }
+
+                    if (!tag->field || *line != '>') {      /* Not valid font tag */
+                        line = potential_font_tag_start;
+                        j = j_backup;
+                    } else {
+                        match = 1;
+                        sp++;
+                        line++;
+                    }
+                }
+            }
+        }
+
+        /* Normal text */
+        if (!match)
+            new_line[j++] = *line++;
+    }
+    new_line[j] = 0;
+    strcpy(start_line, new_line);
+}
+
+
+/*
+ *      MicroDVD
+ *
+ *      Based on the specifications found here:
+ *      https://trac.videolan.org/vlc/ticket/1825#comment:6
+ */
+
+static int indexof(const char *s, int c)
+{
+    char *f = strchr(s, c);
+
+    return (f) ? (f - s) : -1;
+}
+
+typedef struct {
+    char key;
+    int persistent;
+    uint32_t data1;
+    uint32_t data2;
+    char data_string[SUB_MAX_FONTSTRING_LEN];
+} microdvd_tag_type;
+
+#define MICRODVD_DEAD_TAG           {MICRODVD_TAG_DISABLED, MICRODVD_PERSISTENT_OFF, 0, 0, ""}
+
+#define MICRODVD_PERSISTENT_OFF     0
+#define MICRODVD_PERSISTENT_ON      1
+#define MICRODVD_PERSISTENT_OPENED  2
+
+#define MICRODVD_TAGS               "cfshyYpo"   /* Color, Font, Size, cHarset, stYle, Position, cOordinate */
+
+static void microdvd_set_tag(microdvd_tag_type *tags, microdvd_tag_type tag)
+{
+    int tag_index = indexof(MICRODVD_TAGS, tag.key);
+
+    if (tag_index < 0)
+        return;
+    memcpy(&tags[tag_index], &tag, sizeof(tag));
+}
+
+#define MICRODVD_STYLES             "ibus"      /* italic, bold, underline, strike-through */
+#define MICRODVD_TAG_DISABLED       '.'
+
+static char *microdvd_load_tags(microdvd_tag_type *tags, char *s)
+{
+    while (*s == '{') {
+        char *start = s;
+        char tag_char = *(s + 1);
+        microdvd_tag_type tag = MICRODVD_DEAD_TAG;
+
+        if (!tag_char || *(s + 2) != ':')
+            break;
+        s += 3;
+
+        switch (tag_char) {
+
+            /* Style */
+            case 'Y':
+                tag.persistent = MICRODVD_PERSISTENT_ON;
+            case 'y':
+                while (*s && *s != '}') {
+                    int style_index = indexof(MICRODVD_STYLES, *s);
+
+                    if (style_index >= 0)
+                        tag.data1 |= (1 << style_index);
+                    s++;
+                }
+                if (*s != '}')
+                    break;
+                tag.key = tag_char;   /* We must distinct persistent and non-persistent styles
+                                         to handle that kind of style tags: {y:ib}{Y:us} */
+                break;
+
+            /* Color */
+            case 'C':
+                tag.persistent = MICRODVD_PERSISTENT_ON;
+            case 'c':
+                tag.data1 = (uint32_t)strtol(s, &s, 16) & 0x00ffffff;
+                if (*s != '}')
+                    break;
+                tag.key = 'c';
+                break;
+
+            /* Font name */
+            case 'F':
+                tag.persistent = MICRODVD_PERSISTENT_ON;
+            case 'f':
+            {
+                int i = 0;
+
+                while (*s && *s != '}' && i < sizeof(tag.data_string))
+                    tag.data_string[i++] = *s++;
+                if (*s != '}')
+                    break;
+                tag.key = 'f';
+                break;
+            }
+
+            /* Font size */
+            case 'S':
+                tag.persistent = MICRODVD_PERSISTENT_ON;
+            case 's':
+            {
+                tag.data1 = (uint32_t)strtol(s, &s, 10);
+                if (*s != '}' || tag.data1 > 72)
+                    break;
+                tag.key = 's';
+                break;
+            }
+
+            /* Charset */
+            case 'H':
+            {
+                int i = 0;
+
+                //TODO: not yet handled, just parsed.
+                while (*s && *s != '}' && i < sizeof(tag.data_string))
+                    tag.data_string[i++] = *s++;
+                if (*s != '}')
+                    break;
+                tag.key = 'h';
+                break;
+            }
+
+            /* Position */
+            case 'P':
+                tag.persistent = MICRODVD_PERSISTENT_ON;
+                tag.data1 = (*s++ == '1');
+                if (*s != '}')
+                    break;
+                tag.key = 'p';
+                break;
+
+            /* Coordinates */
+            case 'o':
+                tag.persistent = MICRODVD_PERSISTENT_ON;
+                tag.data1 = (uint32_t)strtol(s, &s, 10);
+                if (*s != ',')
+                    break;
+                s++;
+                tag.data2 = (uint32_t)strtol(s, &s, 10);
+                if (*s != '}')
+                    break;
+                tag.key = 'o';
+                break;
+
+            default:    /* Unknown tag, we considere it's text */
+                break;
+        }
+
+        if (tag.key == MICRODVD_TAG_DISABLED)
+            return start;
+
+        microdvd_set_tag(tags, tag);
+        s++;
+    }
+    return s;
+}
+
+static void microdvd_open_tags(char *new_line, int *j, microdvd_tag_type *tags, int persistence)
+{
+    int i;
+
+    for (i = 0; i < sizeof(MICRODVD_TAGS) - 1; i++) {
+        if (tags[i].persistent != persistence)
+            continue;
+        switch (tags[i].key) {
+            case 'Y':
+            case 'y':
+            {
+                int style_index = 0;
+                char ytag[] = "{\\.1}";
+
+                while (style_index < sizeof(MICRODVD_STYLES) - 1) {
+                    int style_mask = (1 << style_index);
+
+                    if (tags[i].data1 & style_mask) {
+                        ytag[2] = MICRODVD_STYLES[style_index];
+                        insert_text_in_subdest(new_line, j, ytag, sizeof(ytag) - 1);
+                    }
+                    style_index++;
+                }
+                break;
+            }
+
+            case 'c':
+            {
+                char ctag[] = "{\\c&Hbbggrr&}";
+
+                sprintf(ctag, "{\\c&H%06X&}", tags[i].data1);
+                insert_text_in_subdest(new_line, j, ctag, sizeof(ctag) - 1);
+                break;
+            }
+
+            case 'f':
+            {
+                char ftag[4 + sizeof(tags[0].data_string) + 2];
+                int len = sprintf(ftag, "{\\fn%s}", tags[i].data_string);
+
+                insert_text_in_subdest(new_line, j, ftag, len);
+                break;
+            }
+
+            case 's':
+            {
+                char stag[4 + 2 + 2];
+                int len = sprintf(stag, "{\\fs%d}", tags[i].data1);
+
+                insert_text_in_subdest(new_line, j, stag, len);
+                break;
+            }
+
+            case 'p':
+                if (tags[i].data1 == 0)
+                    insert_text_in_subdest(new_line, j, "{\\an8}", sizeof("{\\an8}") - 1);
+                break;
+
+            case 'o':
+            {
+                char otag[5 + sizeof(tags[0].data1) * 2 + 3];
+                int len = sprintf(otag, "{\\pos(%d,%d)}", tags[i].data1, tags[i].data2);
+
+                insert_text_in_subdest(new_line, j, otag, len);
+                break;
+            }
+
+            default:
+                break;
+        }
+        if (tags[i].persistent == MICRODVD_PERSISTENT_ON)
+            tags[i].persistent = MICRODVD_PERSISTENT_OPENED;
+    }
+}
+
+static void microdvd_close_no_persistent_tags(char *new_line, int *j, microdvd_tag_type *tags)
+{
+    int i;
+
+    for (i = sizeof(MICRODVD_TAGS) - 2; i; i--) {
+        if (tags[i].persistent != MICRODVD_PERSISTENT_OFF)
+            continue;
+        switch (tags[i].key) {
+            case 'y':
+            {
+                int style_index = sizeof(MICRODVD_STYLES) - 2;
+                char ctag[] = "{\\.0}";
+
+                while (style_index >= 0) {
+                    int style_mask = (1 << style_index);
+
+                    if (tags[i].data1 & style_mask) {
+                        ctag[2] = MICRODVD_STYLES[style_index];
+                        insert_text_in_subdest(new_line, j, ctag, sizeof(ctag) - 1);
+                    }
+                    style_index--;
+                }
+                break;
+            }
+
+            case 'c':
+                insert_text_in_subdest(new_line, j, "{\\c}", sizeof("{\\c}") - 1);
+                break;
+
+            case 'f':
+                insert_text_in_subdest(new_line, j, "{\\fn}", sizeof("{\\fn}") - 1);
+                break;
+
+            case 's':
+                insert_text_in_subdest(new_line, j, "{\\fs}", sizeof("{\\fs}") - 1);
+                break;
+
+            default:
+                break;
+        }
+        tags[i].key = MICRODVD_TAG_DISABLED;
+    }
+}
+
+void subassconvert_microdvd(char *line)
+{
+    int i, j = 0;
+    char new_line[LINE_LEN + 1];
+    char *line_start = line;
+    microdvd_tag_type tags[sizeof(MICRODVD_TAGS) - 1];
+    const microdvd_tag_type dead_tag = MICRODVD_DEAD_TAG;
+
+    for (i = 0; MICRODVD_TAGS[i]; i++)
+        memcpy(&tags[i], &dead_tag, sizeof(dead_tag));
+
+    while (*line) {
+        line = microdvd_load_tags(tags, line);
+        microdvd_open_tags(new_line, &j, tags, MICRODVD_PERSISTENT_ON);
+        microdvd_open_tags(new_line, &j, tags, MICRODVD_PERSISTENT_OFF);
+
+        while (*line && *line != '|')
+            new_line[j++] = *line++;
+
+        if (*line == '|') {
+            microdvd_close_no_persistent_tags(new_line, &j, tags);
+            insert_text_in_subdest(new_line, &j, "\\N", sizeof("\\N") - 1);
+            line++;
+        }
+    }
+
+    new_line[j] = 0;
+    strcpy(line_start, new_line);
+}
diff --git a/subassconvert.h b/subassconvert.h
new file mode 100644
index 0000000..d1be265
--- /dev/null
+++ b/subassconvert.h
@@ -0,0 +1,27 @@
+/*
+ * Header for subtitles converter to SSA/ASS
+ *
+ * This file is part of MPlayer.
+ *
+ * MPlayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MPlayer 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with MPlayer; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPLAYER_SUBASSCONVERT_H
+#define MPLAYER_SUBASSCONVERT_H
+
+void subassconvert_subrip(char *line);
+void subassconvert_microdvd(char *line);
+
+#endif
diff --git a/subreader.c b/subreader.c
index 394cc41..2075d06 100644
--- a/subreader.c
+++ b/subreader.c
@@ -32,6 +32,8 @@
 #include "config.h"
 #include "mp_msg.h"
 #include "subreader.h"
+#include "subassconvert.h"
+#include "options.h"
 #include "stream/stream.h"
 #include "libavutil/common.h"
 #include "libavutil/avstring.h"
@@ -55,8 +57,6 @@ int fribidi_flip_commas = 0;    ///flip comma when fribidi is used
 
 extern char* dvdsub_lang;
 
-/* Maximal length of line of a subtitle */
-#define LINE_LEN 1000
 static float mpsub_position=0;
 static float mpsub_multiplier=1.;
 static int sub_slacktime = 20000; //20 sec
@@ -291,6 +291,9 @@ static subtitle *sub_read_line_microdvd(stream_t *st,subtitle *current, int utf1
 
     p=line2;
 
+    if (st->opts->ass_enabled)
+        subassconvert_microdvd(p);
+
     next=p, i=0;
     while ((next =sub_readtext (next, &(current->text[i])))) {
         if (current->text[i]==ERR) {return ERR;}
@@ -373,6 +376,10 @@ static subtitle *sub_read_line_subviewer(stream_t *st,subtitle *current, int utf
 	for (i=0; i<SUB_MAX_TEXT;) {
 	    int blank = 1;
 	    if (!stream_read_line (st, line, LINE_LEN, utf16)) break;
+
+	    if (st->opts->ass_enabled)
+		subassconvert_subrip(line);
+
 	    len=0;
 	    for (p=line; *p!='\n' && *p!='\r' && *p; p++,len++)
 		if (*p != ' ' && *p != '\t')
@@ -384,16 +391,18 @@ static subtitle *sub_read_line_subviewer(stream_t *st,subtitle *current, int utf
 		//strncpy (current->text[i], line, len); current->text[i][len]='\0';
                 for(; j<len; j++) {
 		    /* let's filter html tags ::atmos */
-		    if(line[j]=='>') {
-			skip=0;
-			continue;
-		    }
-		    if(line[j]=='<') {
-			skip=1;
-			continue;
-		    }
-		    if(skip) {
-			continue;
+		    if (!st->opts->ass_enabled) {
+			if(line[j]=='>') {
+			    skip=0;
+			    continue;
+			}
+			if(line[j]=='<') {
+			    skip=1;
+			    continue;
+			}
+			if(skip) {
+			    continue;
+			}
 		    }
 		    *curptr=line[j];
 		    curptr++;
@@ -1352,7 +1361,8 @@ const char* guess_cp(stream_t *st, const char *preferred_language, const char *f
 #undef MAX_GUESS_BUFFER_SIZE
 #endif
 
-sub_data* sub_read_file (char *filename, float fps) {
+sub_data* sub_read_file(char *filename, float fps, struct MPOpts *opts)
+{
     int utf16;
     stream_t* fd;
     int n_max, n_first, i, j, sub_first, sub_orig;
@@ -1381,6 +1391,7 @@ sub_data* sub_read_file (char *filename, float fps) {
     if(filename==NULL) return NULL; //qnx segfault
     i = 0;
     fd=open_stream (filename, NULL, &i); if (!fd) return NULL;
+    fd->opts = opts;
 
     sub_format = SUB_INVALID;
     for (utf16 = 0; sub_format == SUB_INVALID && utf16 < 3; utf16++) {
diff --git a/subreader.h b/subreader.h
index f1706dc..e5704fd 100644
--- a/subreader.h
+++ b/subreader.h
@@ -44,6 +44,9 @@ extern int sub_match_fuzziness;
 #define SUB_JACOSUB  12
 #define SUB_MPL2     13
 
+/* Maximal length of line of a subtitle */
+#define LINE_LEN 1000
+
 // One of the SUB_* constant above
 extern int sub_format;
 
@@ -84,7 +87,8 @@ extern char *fribidi_charset;
 extern int flip_hebrew;
 extern int fribidi_flip_commas;
 
-sub_data* sub_read_file (char *filename, float pts);
+struct MPOpts;
+sub_data* sub_read_file (char *filename, float pts, struct MPOpts *opts);
 subtitle* subcp_recode (subtitle *sub);
 // enca_fd is the file enca uses to determine the codepage.
 // setting to NULL disables enca.


More information about the MPlayer-dev-eng mailing list