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

ubitux ubitux at gmail.com
Tue May 18 23:54:36 CEST 2010


Hi,

According to what was said on IRC, and the feedback I got here, here is a
new version of the patch, with those changes:

* Pointed by Uoti Urpala *
- [SubRip] stack pointer size check incorrect,
- [SubRip] return the structure pointer instead of field in the function to get the
  last matching tag. This avoid cast & ugliness,
- cosmetics: switch/case alignment.

* Pointed by Kazuo Teramoto *
- [SubRip] Applied (with some minor changes) the color patch.

* Personnal improvements *
- [SubRip] Handle multilines tags. I've added a new function to get the
  old one clear in subreader.c,
- Remove "nested tags" handle for persistent tags since it's totally
  useless and slower.

Finally, a SubRip subtitle like this:

        This <font color="#112233" size="6" face="Arial">could be
        the <font size="35" face="Times New Roman">m<font color="#000000">o</font>st</font>
        difficult thing to</font>implement.
        <font color="blue">Qu’y a-t-il <i>dans un <s>nom</s> ?</i>

Will become:

        This {\c&H332211&}{\fs6}{\fnArial}could be\Nthe {\fs35}{\fnTimes New Roman}m{\c&H000000&}o{\c&H332211&}st{\fs6}{\fnArial}\Ndifficult thing to{\fs}{\c}{\fn}implement.\N{\c&HFF0000&}Qu’y a-t-il {\i1}dans un {\s1}nom{\s0} ?{\i0}

I was looking for implementing the SubRip coordinates, but I just don't
understand how it can be handled:

        X1:100 X2:100 Y1:100 Y2:100

Why two coordinates? I just don't know. If anyone want me to implement it,
just tell me how it works.

Once again, this mail is sent with both SVN and Git version, and I still
hope for new feedback/reviews.

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,579 @@
+/*
+ * 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;
+}
+
+static int indexof(const char *s, int c)
+{
+    char *f = strchr(s, c);
+
+    return (f) ? (f - s) : -1;
+}
+
+
+
+/*
+ *      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_tag_type;
+
+#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}"},
+};
+
+static const struct {
+    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}
+};
+
+#define SUBRIP_MAX_STACKED_FONT_TAGS    16
+
+static font_tag_type *subrip_get_last_matching_tag(font_tag_type *fstack, int sp, int flag)
+{
+    while (sp > 0) {
+        font_tag_type *tag;
+
+        sp--;
+        tag = &fstack[sp];
+        if (tag->field & flag)
+            return tag;
+    }
+    return NULL;
+}
+
+void subassconvert_subrip(char *line)
+{
+    char new_line[LINE_LEN + 1];
+    char *start_line = line;
+    int j = 0;
+    font_tag_type 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_tag_type *tag;
+
+                    sp--;
+                    tag = &font_stack[sp];
+
+                    if (tag->field & SUBRIP_FIELD_FONT_SIZE) {
+                        font_tag_type *last_tag = subrip_get_last_matching_tag(font_stack, sp, SUBRIP_FIELD_FONT_SIZE);
+
+                        if (last_tag == NULL) {
+                            insert_text_in_subdest(new_line, &j, "{\\fs}", sizeof("{\\fs}") - 1);
+                        } else {
+                            char stag[4 + sizeof(last_tag->size) + 2];
+
+                            sprintf(stag, "{\\fs%ld}", last_tag->size);
+                            insert_text_in_subdest(new_line, &j, stag, strlen(stag));
+                        }
+                    }
+
+                    if (tag->field & SUBRIP_FIELD_FONT_COLOR) {
+                        font_tag_type *last_tag = subrip_get_last_matching_tag(font_stack, sp, SUBRIP_FIELD_FONT_COLOR);
+
+                        if (last_tag == NULL) {
+                            insert_text_in_subdest(new_line, &j, "{\\c}", sizeof("{\\c}") - 1);
+                        } else {
+                            char ctag[] = "{\\c&Hbbggrr&}";
+
+                            sprintf(ctag, "{\\c&H%06X&}", last_tag->color);
+                            insert_text_in_subdest(new_line, &j, ctag, sizeof(ctag) - 1);
+                        }
+                    }
+
+                    if (tag->field & SUBRIP_FIELD_FONT_FACE) {
+                        font_tag_type *last_tag = subrip_get_last_matching_tag(font_stack, sp, SUBRIP_FIELD_FONT_FACE);
+
+                        if (last_tag == NULL) {
+                            insert_text_in_subdest(new_line, &j, "{\\fn}", sizeof("{\\fn}") - 1);
+                        } else {
+                            char ftag[4 + sizeof(last_tag->face) + 2];
+                            int len = sprintf(ftag, "{\\fn%s}", last_tag->face);
+
+                            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_tag_type *tag = &font_stack[sp];
+
+                if (sp < sizeof(font_stack) / 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=\"", 7) == 0) {
+                            char ctag[] = "{\\c&Hbbggrr&}";
+
+                            line += 7;
+
+                            /* #RRGGBB format */
+                            if (*line == '#') {
+                                line++;
+                                tag->color = (uint32_t)strtol(line, &line, 16) & 0x00ffffff;
+                                if (*line != '"')
+                                    break;
+                                tag->color = ((tag->color & 0xff) << 16) | (tag->color & 0xff00) | ((tag->color & 0xff0000) >> 16);
+
+                            /* Standard web colors */
+                            } else {
+                                int i, len = indexof(line, '"');
+
+                                if (len <= 0)
+                                    break;
+                                tag->color = 0x00ffffff;    // Let's put some white in case we didn't find any matching colors
+
+                                for (i = 0; i < sizeof(subrip_web_colors) / sizeof(*subrip_web_colors); i++) {
+                                    if(strncasecmp(line, subrip_web_colors[i].s, len) == 0) {
+                                        tag->color = subrip_web_colors[i].v;
+                                        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
+ */
+
+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 i;
+
+    for (i = 0; i < sizeof(MICRODVD_TAGS) - 1; i++) {
+        if (tags[i].persistent == MICRODVD_PERSISTENT_OPENED)
+            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);
+
+        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;}
@@ -358,12 +362,78 @@
     return current;
 }
 
+static subtitle *sub_ass_read_line_subviewer(stream_t *st, subtitle *current, int utf16)
+{
+    int a1, a2, a3, a4, b1, b2, b3, b4, j = 0;
+
+    while (!current->text[0]) {
+        char line[LINE_LEN + 1], full_line[LINE_LEN + 1], sep;
+        int i;
+
+        /* Parse SubRip header */
+        if (!stream_read_line(st, line, LINE_LEN, utf16))
+            return NULL;
+        if (sscanf(line, "%d:%d:%d%[,.:]%d --> %d:%d:%d%[,.:]%d",
+                     &a1, &a2, &a3, &sep, &a4, &b1, &b2, &b3, &sep, &b4) < 10)
+            continue;
+
+        current->start = a1 * 360000 + a2 * 6000 + a3 * 100 + a4 / 10;
+        current->end   = b1 * 360000 + b2 * 6000 + b3 * 100 + b4 / 10;
+
+        /* Concat lines */
+        full_line[0] = 0;
+        for (i = 0; i < SUB_MAX_TEXT; i++) {
+            int blank = 1, len = 0;
+            char *p;
+
+            if (!stream_read_line(st, line, LINE_LEN, utf16))
+                break;
+
+            for (p = line; *p != '\n' && *p != '\r' && *p; p++, len++)
+                if (*p != ' ' && *p != '\t')
+                    blank = 0;
+
+            if (blank)
+                break;
+
+            *p = 0;
+
+            if (!(j + 1 + len < sizeof(full_line) - 1))
+                break;
+
+            if (j != 0) {
+                full_line[j++] = '\\';
+                full_line[j++] = 'N';
+            }
+            strcpy(&full_line[j], line);
+            j += len;
+        }
+
+        /* Use the ASS/SSA converter to transform the whole lines */
+        if (full_line[0]) {
+            int len;
+
+            subassconvert_subrip(full_line);
+            len = strlen(full_line);
+            current->lines = 1;
+            current->text[0] = malloc(len + 1);
+            if (!current->text[0])
+                return ERR;
+            strncpy(current->text[0], full_line, len);
+            current->text[0][len] = '\0';
+        }
+    }
+    return current;
+}
+
 static subtitle *sub_read_line_subviewer(stream_t *st,subtitle *current, int utf16) {
     char line[LINE_LEN+1];
     int a1,a2,a3,a4,b1,b2,b3,b4;
     char *p=NULL;
     int i,len;
 
+    if (ass_enabled)
+        return sub_ass_read_line_subviewer(st, current, utf16);
     while (!current->text[0]) {
 	if (!stream_read_line (st, line, LINE_LEN, utf16)) return NULL;
 	if ((len=sscanf (line, "%d:%d:%d%[,.:]%d --> %d:%d:%d%[,.:]%d",&a1,&a2,&a3,(char *)&i,&a4,&b1,&b2,&b3,(char *)&i,&b4)) < 10)
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..978d9f4
--- /dev/null
+++ b/subassconvert.c
@@ -0,0 +1,579 @@
+/*
+ * 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;
+}
+
+static int indexof(const char *s, int c)
+{
+    char *f = strchr(s, c);
+
+    return (f) ? (f - s) : -1;
+}
+
+
+
+/*
+ *      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_tag_type;
+
+#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}"},
+};
+
+static const struct {
+    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}
+};
+
+#define SUBRIP_MAX_STACKED_FONT_TAGS    16
+
+static font_tag_type *subrip_get_last_matching_tag(font_tag_type *fstack, int sp, int flag)
+{
+    while (sp > 0) {
+        font_tag_type *tag;
+
+        sp--;
+        tag = &fstack[sp];
+        if (tag->field & flag)
+            return tag;
+    }
+    return NULL;
+}
+
+void subassconvert_subrip(char *line)
+{
+    char new_line[LINE_LEN + 1];
+    char *start_line = line;
+    int j = 0;
+    font_tag_type 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_tag_type *tag;
+
+                    sp--;
+                    tag = &font_stack[sp];
+
+                    if (tag->field & SUBRIP_FIELD_FONT_SIZE) {
+                        font_tag_type *last_tag = subrip_get_last_matching_tag(font_stack, sp, SUBRIP_FIELD_FONT_SIZE);
+
+                        if (last_tag == NULL) {
+                            insert_text_in_subdest(new_line, &j, "{\\fs}", sizeof("{\\fs}") - 1);
+                        } else {
+                            char stag[4 + sizeof(last_tag->size) + 2];
+
+                            sprintf(stag, "{\\fs%ld}", last_tag->size);
+                            insert_text_in_subdest(new_line, &j, stag, strlen(stag));
+                        }
+                    }
+
+                    if (tag->field & SUBRIP_FIELD_FONT_COLOR) {
+                        font_tag_type *last_tag = subrip_get_last_matching_tag(font_stack, sp, SUBRIP_FIELD_FONT_COLOR);
+
+                        if (last_tag == NULL) {
+                            insert_text_in_subdest(new_line, &j, "{\\c}", sizeof("{\\c}") - 1);
+                        } else {
+                            char ctag[] = "{\\c&Hbbggrr&}";
+
+                            sprintf(ctag, "{\\c&H%06X&}", last_tag->color);
+                            insert_text_in_subdest(new_line, &j, ctag, sizeof(ctag) - 1);
+                        }
+                    }
+
+                    if (tag->field & SUBRIP_FIELD_FONT_FACE) {
+                        font_tag_type *last_tag = subrip_get_last_matching_tag(font_stack, sp, SUBRIP_FIELD_FONT_FACE);
+
+                        if (last_tag == NULL) {
+                            insert_text_in_subdest(new_line, &j, "{\\fn}", sizeof("{\\fn}") - 1);
+                        } else {
+                            char ftag[4 + sizeof(last_tag->face) + 2];
+                            int len = sprintf(ftag, "{\\fn%s}", last_tag->face);
+
+                            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_tag_type *tag = &font_stack[sp];
+
+                if (sp < sizeof(font_stack) / 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=\"", 7) == 0) {
+                            char ctag[] = "{\\c&Hbbggrr&}";
+
+                            line += 7;
+
+                            /* #RRGGBB format */
+                            if (*line == '#') {
+                                line++;
+                                tag->color = (uint32_t)strtol(line, &line, 16) & 0x00ffffff;
+                                if (*line != '"')
+                                    break;
+                                tag->color = ((tag->color & 0xff) << 16) | (tag->color & 0xff00) | ((tag->color & 0xff0000) >> 16);
+
+                            /* Standard web colors */
+                            } else {
+                                int i, len = indexof(line, '"');
+
+                                if (len <= 0)
+                                    break;
+                                tag->color = 0x00ffffff;    // Let's put some white in case we didn't find any matching colors
+
+                                for (i = 0; i < sizeof(subrip_web_colors) / sizeof(*subrip_web_colors); i++) {
+                                    if(strncasecmp(line, subrip_web_colors[i].s, len) == 0) {
+                                        tag->color = subrip_web_colors[i].v;
+                                        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
+ */
+
+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 i;
+
+    for (i = 0; i < sizeof(MICRODVD_TAGS) - 1; i++) {
+        if (tags[i].persistent == MICRODVD_PERSISTENT_OPENED)
+            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);
+
+        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..5cc9998 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;}
@@ -358,12 +361,78 @@ static subtitle *sub_read_line_subrip(stream_t* st, subtitle *current, int utf16
     return current;
 }
 
+static subtitle *sub_ass_read_line_subviewer(stream_t *st, subtitle *current, int utf16)
+{
+    int a1, a2, a3, a4, b1, b2, b3, b4, j = 0;
+
+    while (!current->text[0]) {
+        char line[LINE_LEN + 1], full_line[LINE_LEN + 1], sep;
+        int i;
+
+        /* Parse SubRip header */
+        if (!stream_read_line(st, line, LINE_LEN, utf16))
+            return NULL;
+        if (sscanf(line, "%d:%d:%d%[,.:]%d --> %d:%d:%d%[,.:]%d",
+                     &a1, &a2, &a3, &sep, &a4, &b1, &b2, &b3, &sep, &b4) < 10)
+            continue;
+
+        current->start = a1 * 360000 + a2 * 6000 + a3 * 100 + a4 / 10;
+        current->end   = b1 * 360000 + b2 * 6000 + b3 * 100 + b4 / 10;
+
+        /* Concat lines */
+        full_line[0] = 0;
+        for (i = 0; i < SUB_MAX_TEXT; i++) {
+            int blank = 1, len = 0;
+            char *p;
+
+            if (!stream_read_line(st, line, LINE_LEN, utf16))
+                break;
+
+            for (p = line; *p != '\n' && *p != '\r' && *p; p++, len++)
+                if (*p != ' ' && *p != '\t')
+                    blank = 0;
+
+            if (blank)
+                break;
+
+            *p = 0;
+
+            if (!(j + 1 + len < sizeof(full_line) - 1))
+                break;
+
+            if (j != 0) {
+                full_line[j++] = '\\';
+                full_line[j++] = 'N';
+            }
+            strcpy(&full_line[j], line);
+            j += len;
+        }
+
+        /* Use the ASS/SSA converter to transform the whole lines */
+        if (full_line[0]) {
+            int len;
+
+            subassconvert_subrip(full_line);
+            len = strlen(full_line);
+            current->lines = 1;
+            current->text[0] = malloc(len + 1);
+            if (!current->text[0])
+                return ERR;
+            strncpy(current->text[0], full_line, len);
+            current->text[0][len] = '\0';
+        }
+    }
+    return current;
+}
+
 static subtitle *sub_read_line_subviewer(stream_t *st,subtitle *current, int utf16) {
     char line[LINE_LEN+1];
     int a1,a2,a3,a4,b1,b2,b3,b4;
     char *p=NULL;
     int i,len;
 
+    if (st->opts->ass_enabled)
+        return sub_ass_read_line_subviewer(st, current, utf16);
     while (!current->text[0]) {
 	if (!stream_read_line (st, line, LINE_LEN, utf16)) return NULL;
 	if ((len=sscanf (line, "%d:%d:%d%[,.:]%d --> %d:%d:%d%[,.:]%d",&a1,&a2,&a3,(char *)&i,&a4,&b1,&b2,&b3,(char *)&i,&b4)) < 10)
@@ -1352,7 +1421,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 +1451,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