[MPlayer-dev-eng] [PATCH] Tags support for SubRip and MicroDVD subtitles [v3]
ubitux
ubitux at gmail.com
Fri Jun 25 20:36:19 CEST 2010
On Wed, Jun 23, 2010 at 06:39:41PM +0200, Reimar Döffinger wrote:
> On Wed, Jun 23, 2010 at 07:55:09AM +0200, ubitux wrote:
>
>
> A few (preferably doxygen-compatible) comments/function documentation
> would be a really good idea.
>
Sure. I added doxygen-doc on subassconvert* public functions.
> > [...]
>
> return str1.len - str2.len;
> maybe?
>
> > [...]
>
> same.
>
> > [...]
>
> I'm not convinced these are really a good thing for readability.
>
Since the bstr code is not from me, I'm not sure about the consequences of
any modification; I don't really want to change it since it works.
> > +#include <stdbool.h>
>
> Please avoid bool, not all compilers are 100% compliant there, and for
> those that are bool often has a performance impact.
> Plus, it is not used anywhere else in MPlayer IIRC.
>
Changed.
> > +#define NB_ELEMS(s) (sizeof(s) / sizeof((s)[0]))
>
> FF_ARRAY_ELEMS from libavutil/common.h
>
I didn't find anything to do that in mplayer includes, so I didn't look
elsewhere, my bad. Fixed.
> > + ret = vsnprintf(dst->buf + dst->len, dst->bufsize - dst->len, fmt, va);
> > + if (ret < 0)
> > + return;
> > + dst->len += ret;
> > + if (dst->len > dst->bufsize)
> > + dst->len = dst->bufsize;
> > + va_end(va);
>
> There's a return before the va_end, sure that is guaranteed to work?
Fixed.
> I'd say slower but more reliable would be
> dst->len += strlen(dst->buf + dst->len);
>
Huh? Why is it more reliable?
> > +static int indexof(const char *s, int c)
> > +{
> > + char *f = strchr(s, c);
> > + return f ? (f - s) : -1;
> > +}
>
> Wouldn't just using strcspn result in more obvious code?
>
Replace strchr by strcspn? I'm not sure I would be more obvious. Also, if
the question is to replace indexof with strcspn, it's not a good idea; I
really need an indexof() function.
> > +struct font_tag {
> > + int size;
> > + uint32_t color;
> > + struct bstr face;
> > + bool has_size : 1;
> > + bool has_color : 1;
> > + bool has_face : 1;
>
> I still shudder from the last time I ended up debugging gcc miscompiling
> bitfields, please for my sake don't use them unless it's really important.
It's not bitfield in order to do some nasty C hacks but to clarify the
code. They are really not harmful at all, and code is much more clear.
> You can probably save more memory (on 64 bit systems) by putting bstr first.
>
Ok, changed.
> > +static const struct tag_conv {
> > + char *from;
> > + char *to;
>
> const char *
>
Done.
> > +} subrip_basic_tags[] = {
> > + {"<i>", "{\\i1}"}, {"</i>", "{\\i0}"},
> > + {"<b>", "{\\b1}"}, {"</b>", "{\\b0}"},
> > + {"<u>", "{\\u1}"}, {"</u>", "{\\u0}"},
> > + {"<s>", "{\\s1}"}, {"</s>", "{\\s0}"},
> > + {"{", "\\{"}, {"}", "\\}"},
> > + {"\n", "\\N"}
>
> Also considering these e.g.
> char from[8];
> char to[8];
> would be nicer in some ways.
>
I'm not sure about loosing some code flexibility just for a trivial
optimization…
> > +static const struct {
> > + char *s;
>
> basically same comment
Done.
> Also 0-terminating the arrays might actually result in nicer-looking loops
> than using the NB_ELEMS or whatever.
>
It's a matter of tastes; I don't see the problem with FF_ARRAY_ELEMS-stuff
since it's faster and memory-less.
> > +// Color, Font, Size, cHarset, stYle, Position, cOordinate
> > +#define MICRODVD_TAGS "cfshyYpo"
>
> Maybe use an enum?
>
No, this string is really used in the MicroDVD design. It would be painful
to redo everything…
> > + for (i = sizeof(MICRODVD_TAGS) - 2; i; i--) {
>
> This kind of thing isn't really great for understanding the code.
>
I can explain that; in fact I was considerating nested tags at first, so I
was doing a LIFO code. After changing it, I kept that stuff because the
output markup was closing according to that loop: I mean it was clear to
pass here, and then show a good behavior. Do you mind if I let it that
way?
> > + int a1, a2, a3, a4, b1, b2, b3, b4, j = 0;
>
> A bit better names please :-)
>
Sorry, I just copy/paste the original sub_read_line_subviewer and kept the
major part of the code. I changed that.
> > + 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;
>
> len = p - line;
> possibly even
> len = strcspn(line, "\n\r");
> p[len] = 0;
> if (strncspn(line, " \t") == len) // whitespace-only
> break;
>
>
I don't understand the way you use strcspn… I'm not sure about modifying
that… But maybe when subreader.c will be prettyfied I'll try to improve
string parsing. ATM, I really think it would be problematic to change it
while adding the sub ASS converter.
> > + if (!(j + 1 + len < sizeof(full_line) - 1))
> > + break;
>
> Why not just >= instead of ! <
> Also on 64 bit systems I think this could overflow, it should instead be
> len >= sizeof(full_line) - j - 2
>
Done. But I didn't understand why it would be problematic on 64 bits.
> > + char converted_line[LINE_LEN + 1];
>
> Maybe it makes sense to malloc a few of these? I think there's a lot
> of these on the stack in the end, and even one is not exactly small...
>
I don't think there is more than 4 of them. There is no recursion at all
so…
> > [...]
>
> Please do not reindent existing code, that makes it nearly impossible
> to see what you changed and thus fixing regressions needlessly grows
> from a few-minutes job to a one-hour job.
I swear I tried to avoid the 2-spaces-decal but I didn't find any
satisfying solution for that: I really need to put it in a scope. I didn't
changed the code, I just shift the block to add a if-condition around.
--
ubitux
-------------- next part --------------
Index: Makefile
===================================================================
--- Makefile (revision 31559)
+++ Makefile (working copy)
@@ -327,6 +327,7 @@
SRCS_COMMON-$(ZR) += libmpcodecs/vd_zrmjpeg.c \
libmpcodecs/vf_zrmjpeg.c
SRCS_COMMON = asxparser.c \
+ bstr.c \
codec-cfg.c \
cpudetect.c \
edl.c \
@@ -343,6 +344,7 @@
playtreeparser.c \
spudec.c \
sub_cc.c \
+ subassconvert.c \
subopt-helper.c \
subreader.c \
vobsub.c \
Index: bstr.c
===================================================================
--- bstr.c (revision 0)
+++ bstr.c (revision 0)
@@ -0,0 +1,52 @@
+/*
+ * 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 <libavutil/avutil.h>
+
+#include "bstr.h"
+
+int bstrcmp(struct bstr str1, struct bstr str2)
+{
+ int ret = memcmp(str1.start, str2.start, FFMIN(str1.len, str2.len));
+
+ if (!ret) {
+ if (str1.len == str2.len)
+ return 0;
+ else if (str1.len > str2.len)
+ return 1;
+ else
+ return -1;
+ }
+ return ret;
+}
+
+int bstrcasecmp(struct bstr str1, struct bstr str2)
+{
+ int ret = strncasecmp(str1.start, str2.start, FFMIN(str1.len, str2.len));
+
+ if (!ret) {
+ if (str1.len == str2.len)
+ return 0;
+ else if (str1.len > str2.len)
+ return 1;
+ else
+ return -1;
+ }
+ return ret;
+}
Index: bstr.h
===================================================================
--- bstr.h (revision 0)
+++ bstr.h (revision 0)
@@ -0,0 +1,39 @@
+/*
+ * 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_BSTR_H
+#define MPLAYER_BSTR_H
+
+#include <stdint.h>
+#include <string.h>
+#include <sys/types.h>
+
+struct bstr {
+ const uint8_t *start;
+ size_t len;
+};
+
+int bstrcmp(struct bstr str1, struct bstr str2);
+int bstrcasecmp(struct bstr str1, struct bstr str2);
+
+// Create bstr compound literal from null-terminated string
+#define BSTR(s) (struct bstr){(s), (s) ? strlen(s) : 0}
+// create a pair (not single value!) for "%.*s" printf syntax
+#define BSTR_P(bstr) (int)((bstr).len), (bstr).start
+
+#endif /* MPLAYER_BSTR_H */
Index: subassconvert.c
===================================================================
--- subassconvert.c (revision 0)
+++ subassconvert.c (revision 0)
@@ -0,0 +1,518 @@
+/*
+ * 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 <stdio.h>
+#include <stdarg.h>
+
+#include "mp_msg.h"
+#include "bstr.h"
+#include "subassconvert.h"
+#include "libavutil/common.h"
+
+struct line {
+ char *buf;
+ int bufsize;
+ int len;
+};
+
+#ifdef __GNUC__
+static void append_text(struct line *dst, char *fmt, ...) __attribute__ ((format(printf, 2, 3)));
+#endif
+
+static void append_text(struct line *dst, char *fmt, ...)
+{
+ va_list va;
+ int ret;
+
+ va_start(va, fmt);
+ ret = vsnprintf(dst->buf + dst->len, dst->bufsize - dst->len, fmt, va);
+ if (ret < 0)
+ goto end;
+ dst->len += ret;
+ if (dst->len > dst->bufsize)
+ dst->len = dst->bufsize;
+end:
+ va_end(va);
+}
+
+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.
+ *
+ */
+
+struct font_tag {
+ struct bstr face;
+ int size;
+ uint32_t color;
+ int has_size : 1;
+ int has_color : 1;
+ int has_face : 1;
+};
+
+static const struct tag_conv {
+ const char *from;
+ const char *to;
+} subrip_basic_tags[] = {
+ {"<i>", "{\\i1}"}, {"</i>", "{\\i0}"},
+ {"<b>", "{\\b1}"}, {"</b>", "{\\b0}"},
+ {"<u>", "{\\u1}"}, {"</u>", "{\\u0}"},
+ {"<s>", "{\\s1}"}, {"</s>", "{\\s0}"},
+ {"{", "\\{"}, {"}", "\\}"},
+ {"\n", "\\N"}
+};
+
+static const struct {
+ const char *s;
+ uint32_t v;
+} subrip_web_colors[] = {
+ /* 16 named html colors in BGR format */
+ {"red", 0x0000ff}, {"blue", 0xff0000}, {"lime", 0x00ff00},
+ {"aqua", 0xffff00}, {"purple", 0x800080}, {"yellow", 0x00ffff},
+ {"fuchsia", 0xff00ff}, {"white", 0xffffff}, {"gray", 0x808080},
+ {"maroon", 0x000080}, {"olive", 0x008080}, {"black", 0x000000},
+ {"silver", 0xc0c0c0}, {"teal", 0x808000}, {"green", 0x008000},
+ {"navy", 0x800000}
+};
+
+#define SUBRIP_MAX_STACKED_FONT_TAGS 16
+
+/**
+ * \brief Convert SubRip lines into ASS markup
+ * \param orig original SubRip lines. The content will remain untouched.
+ * \param dest ASS markup destination buffer.
+ * \param dest_buffer_size maximum size for the destination buffer.
+ */
+void subassconvert_subrip(const char *orig, char *dest, int dest_buffer_size)
+{
+ /* line is not const to avoid warnings with strtol, etc.
+ * orig content won't be changed */
+ char *line = (char *)orig;
+ struct line new_line = {
+ .buf = dest,
+ .bufsize = dest_buffer_size,
+ };
+ struct font_tag font_stack[SUBRIP_MAX_STACKED_FONT_TAGS + 1];
+ int sp = 0;
+
+ font_stack[0] = (struct font_tag){}; // type with all defaults
+ while (*line && new_line.len < new_line.bufsize - 1) {
+ char *orig_line = line;
+
+ for (int i = 0; i < FF_ARRAY_ELEMS(subrip_basic_tags); i++) {
+ const struct tag_conv *tag = &subrip_basic_tags[i];
+ int from_len = strlen(tag->from);
+ if (strncmp(line, tag->from, from_len) == 0) {
+ append_text(&new_line, "%s", tag->to);
+ line += from_len;
+ }
+ }
+
+ if (strncmp(line, "</font>", 7) == 0) {
+ /* Closing font tag */
+ line += 7;
+
+ if (sp > 0) {
+ struct font_tag *tag = &font_stack[sp];
+ struct font_tag *last_tag = &tag[-1];
+ sp--;
+
+ if (tag->has_size) {
+ if (!last_tag->has_size)
+ append_text(&new_line, "{\\fs}");
+ else if (last_tag->size != tag->size)
+ append_text(&new_line, "{\\fs%d}", last_tag->size);
+ }
+
+ if (tag->has_color) {
+ if (!last_tag->has_color)
+ append_text(&new_line, "{\\c}");
+ else if (last_tag->color != tag->color)
+ append_text(&new_line, "{\\c&H%06X&}", last_tag->color);
+ }
+
+ if (tag->has_face) {
+ if (!last_tag->has_face)
+ append_text(&new_line, "{\\fn}");
+ else if (bstrcmp(last_tag->face, tag->face) != 0)
+ append_text(&new_line, "{\\fn%.*s}",
+ BSTR_P(last_tag->face));
+ }
+ }
+ } else if (strncmp(line, "<font ", 6) == 0
+ && sp + 1 < FF_ARRAY_ELEMS(font_stack)) {
+ /* Opening font tag */
+ char *potential_font_tag_start = line;
+ int len_backup = new_line.len;
+ struct font_tag *tag = &font_stack[sp + 1];
+ int has_valid_attr = 0;
+
+ *tag = tag[-1]; // keep values from previous tag
+ line += 6;
+
+ while (*line && *line != '>') {
+ if (strncmp(line, "size=\"", 6) == 0) {
+ line += 6;
+ tag->size = strtol(line, &line, 10);
+ if (*line != '"')
+ break;
+ append_text(&new_line, "{\\fs%d}", tag->size);
+ tag->has_size = 1;
+ has_valid_attr = 1;
+ } else if (strncmp(line, "color=\"", 7) == 0) {
+ line += 7;
+ if (*line == '#') {
+ // #RRGGBB format
+ line++;
+ tag->color = strtol(line, &line, 16) & 0x00ffffff;
+ if (*line != '"')
+ break;
+ tag->color = ((tag->color & 0xff) << 16) |
+ (tag->color & 0xff00) |
+ ((tag->color & 0xff0000) >> 16);
+ } else {
+ // Standard web colors
+ int i, len = indexof(line, '"');
+ if (len <= 0)
+ break;
+ for (i = 0; i < FF_ARRAY_ELEMS(subrip_web_colors); i++) {
+ const char *color = subrip_web_colors[i].s;
+ if (strlen(color) == len
+ && strncasecmp(line, color, len) == 0) {
+ tag->color = subrip_web_colors[i].v;
+ break;
+ }
+ }
+
+ if (i == FF_ARRAY_ELEMS(subrip_web_colors)) {
+ /* We didn't find any matching color */
+ line = strchr(line, '"'); // can't be NULL, see above
+ mp_msg(MSGT_SUBREADER, MSGL_WARN,
+ "[SubRip] Unknown font color in subtitle: %s\n", orig);
+ append_text(&new_line, "{\\c}");
+ line += 2;
+ continue;
+ }
+
+ line += len;
+ }
+ append_text(&new_line, "{\\c&H%06X&}", tag->color);
+ tag->has_color = 1;
+ has_valid_attr = 1;
+ } else if (strncmp(line, "face=\"", 6) == 0) {
+ /* Font face attribute */
+ int len;
+ line += 6;
+ len = indexof(line, '"');
+ if (len <= 0)
+ break;
+ tag->face.start = line;
+ tag->face.len = len;
+ line += len;
+ append_text(&new_line, "{\\fn%.*s}", BSTR_P(tag->face));
+ tag->has_face = 1;
+ has_valid_attr = 1;
+ }
+ line++;
+ }
+
+ if (!has_valid_attr || *line != '>') { /* Not valid font tag */
+ line = potential_font_tag_start;
+ new_line.len = len_backup;
+ } else {
+ sp++;
+ line++;
+ }
+ }
+
+ /* Tag conversion code didn't match */
+ if (line == orig_line)
+ new_line.buf[new_line.len++] = *line++;
+ }
+ new_line.buf[new_line.len] = 0;
+}
+
+
+/*
+ * MicroDVD
+ *
+ * Based on the specifications found here:
+ * https://trac.videolan.org/vlc/ticket/1825#comment:6
+ */
+
+struct microdvd_tag {
+ char key;
+ int persistent;
+ uint32_t data1;
+ uint32_t data2;
+ struct bstr data_string;
+};
+
+#define MICRODVD_PERSISTENT_OFF 0
+#define MICRODVD_PERSISTENT_ON 1
+#define MICRODVD_PERSISTENT_OPENED 2
+
+// Color, Font, Size, cHarset, stYle, Position, cOordinate
+#define MICRODVD_TAGS "cfshyYpo"
+
+static void microdvd_set_tag(struct microdvd_tag *tags, struct microdvd_tag tag)
+{
+ int tag_index = indexof(MICRODVD_TAGS, tag.key);
+
+ if (tag_index < 0)
+ return;
+ memcpy(&tags[tag_index], &tag, sizeof(tag));
+}
+
+// italic, bold, underline, strike-through
+#define MICRODVD_STYLES "ibus"
+
+static char *microdvd_load_tags(struct microdvd_tag *tags, char *s)
+{
+ while (*s == '{') {
+ char *start = s;
+ char tag_char = *(s + 1);
+ struct microdvd_tag 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;
+ /* We must distinguish persistent and non-persistent styles
+ * to handle this kind of style tags: {y:ib}{Y:us} */
+ tag.key = tag_char;
+ break;
+
+ /* Color */
+ case 'C':
+ tag.persistent = MICRODVD_PERSISTENT_ON;
+ case 'c':
+ tag.data1 = strtol(s, &s, 16) & 0x00ffffff;
+ if (*s != '}')
+ break;
+ tag.key = 'c';
+ break;
+
+ /* Font name */
+ case 'F':
+ tag.persistent = MICRODVD_PERSISTENT_ON;
+ case 'f':
+ {
+ int len = indexof(s, '}');
+ if (len < 0)
+ break;
+ tag.data_string.start = s;
+ tag.data_string.len = len;
+ s += len;
+ tag.key = 'f';
+ break;
+ }
+
+ /* Font size */
+ case 'S':
+ tag.persistent = MICRODVD_PERSISTENT_ON;
+ case 's':
+ tag.data1 = strtol(s, &s, 10);
+ if (*s != '}')
+ break;
+ tag.key = 's';
+ break;
+
+ /* Charset */
+ case 'H':
+ {
+ //TODO: not yet handled, just parsed.
+ int len = indexof(s, '}');
+ if (len < 0)
+ break;
+ tag.data_string.start = s;
+ tag.data_string.len = len;
+ s += len;
+ 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 = strtol(s, &s, 10);
+ if (*s != ',')
+ break;
+ s++;
+ tag.data2 = strtol(s, &s, 10);
+ if (*s != '}')
+ break;
+ tag.key = 'o';
+ break;
+
+ default: /* Unknown tag, we considere it's text */
+ break;
+ }
+
+ if (tag.key == 0)
+ return start;
+
+ microdvd_set_tag(tags, tag);
+ s++;
+ }
+ return s;
+}
+
+static void microdvd_open_tags(struct line *new_line, struct microdvd_tag *tags)
+{
+ for (int i = 0; i < sizeof(MICRODVD_TAGS) - 1; i++) {
+ if (tags[i].persistent == MICRODVD_PERSISTENT_OPENED)
+ continue;
+ switch (tags[i].key) {
+ case 'Y':
+ case 'y':
+ for (int sidx = 0; sidx < sizeof(MICRODVD_STYLES) - 1; sidx++)
+ if (tags[i].data1 & (1 << sidx))
+ append_text(new_line, "{\\%c1}", MICRODVD_STYLES[sidx]);
+ break;
+
+ case 'c':
+ append_text(new_line, "{\\c&H%06X&}", tags[i].data1);
+ break;
+
+ case 'f':
+ append_text(new_line, "{\\fn%.*s}", BSTR_P(tags[i].data_string));
+ break;
+
+ case 's':
+ append_text(new_line, "{\\fs%d}", tags[i].data1);
+ break;
+
+ case 'p':
+ if (tags[i].data1 == 0)
+ append_text(new_line, "{\\an8}");
+ break;
+
+ case 'o':
+ append_text(new_line, "{\\pos(%d,%d)}",
+ tags[i].data1, tags[i].data2);
+ break;
+ }
+ if (tags[i].persistent == MICRODVD_PERSISTENT_ON)
+ tags[i].persistent = MICRODVD_PERSISTENT_OPENED;
+ }
+}
+
+static void microdvd_close_no_persistent_tags(struct line *new_line,
+ struct microdvd_tag *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':
+ for (int sidx = sizeof(MICRODVD_STYLES) - 2; sidx >= 0; sidx--)
+ if (tags[i].data1 & (1 << sidx))
+ append_text(new_line, "{\\%c0}", MICRODVD_STYLES[sidx]);
+ break;
+
+ case 'c':
+ append_text(new_line, "{\\c}");
+ break;
+
+ case 'f':
+ append_text(new_line, "{\\fn}");
+ break;
+
+ case 's':
+ append_text(new_line, "{\\fs}");
+ break;
+ }
+ tags[i].key = 0;
+ }
+}
+
+/**
+ * \brief Convert MicroDVD lines into ASS markup
+ * \param orig original MicroDVD line. The content will remain untouched.
+ * \param dest ASS markup destination buffer.
+ * \param dest_buffer_size maximum size for the destination buffer.
+ */
+void subassconvert_microdvd(const char *orig, char *dest, int dest_buffer_size)
+{
+ /* line is not const to avoid warnings with strtol, etc.
+ * orig content won't be changed */
+ char *line = (char *)orig;
+ struct line new_line = {
+ .buf = dest,
+ .bufsize = dest_buffer_size,
+ };
+ struct microdvd_tag tags[sizeof(MICRODVD_TAGS) - 1] = {};
+
+ while (*line) {
+ line = microdvd_load_tags(tags, line);
+ microdvd_open_tags(&new_line, tags);
+
+ while (*line && *line != '|')
+ new_line.buf[new_line.len++] = *line++;
+
+ if (*line == '|') {
+ microdvd_close_no_persistent_tags(&new_line, tags);
+ append_text(&new_line, "\\N");
+ line++;
+ }
+ }
+ new_line.buf[new_line.len] = 0;
+}
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(const char *orig, char *dest, int dest_buffer_size);
+void subassconvert_microdvd(const char *orig, char *dest, int dest_buffer_size);
+
+#endif
Index: subreader.c
===================================================================
--- subreader.c (revision 31559)
+++ subreader.c (working copy)
@@ -33,10 +33,12 @@
#include "mp_msg.h"
#include "mpcommon.h"
#include "subreader.h"
+#include "subassconvert.h"
#include "libvo/sub.h"
#include "stream/stream.h"
#include "libavutil/common.h"
#include "libavutil/avstring.h"
+#include "libass/ass_mp.h"
#ifdef CONFIG_ENCA
#include <enca.h>
@@ -296,7 +298,11 @@
"{%ld}{%ld}%[^\r\n]",
&(current->start), &(current->end), line2) < 3));
- p=line2;
+ if (ass_enabled) {
+ subassconvert_microdvd(line2, line, LINE_LEN + 1);
+ p = line;
+ } else
+ p = line2;
next=p, i=0;
while ((next =sub_readtext (next, &(current->text[i])))) {
@@ -365,12 +371,70 @@
return current;
}
+static subtitle *sub_ass_read_line_subviewer(stream_t *st, subtitle *current, int utf16)
+{
+ int h1, m1, s1, ms1, h2, m2, s2, ms2, 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",
+ &h1, &m1, &s1, &sep, &ms1, &h2, &m2, &s2, &sep, &ms2) < 10)
+ continue;
+
+ current->start = h1 * 360000 + m1 * 6000 + s1 * 100 + ms1 / 10;
+ current->end = h2 * 360000 + m2 * 6000 + s2 * 100 + ms2 / 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 (len >= sizeof(full_line) - j - 2)
+ break;
+
+ if (j != 0)
+ 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]) {
+ char converted_line[LINE_LEN + 1];
+ subassconvert_subrip(full_line, converted_line, LINE_LEN + 1);
+ current->text[0] = strdup(converted_line);
+ current->lines = 1;
+ }
+ }
+ 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)
@@ -2290,12 +2354,13 @@
* \param txt text to parse
* \param len length of text in txt
* \param endpts pts at which this subtitle text should be removed again
+ * \param strip_markup if strip markup is set (!= 0), markup tags like <b></b> are ignored
*
* <> and {} are interpreted as comment delimiters, "\n", "\N", '\n', '\r'
* and '\0' are interpreted as newlines, duplicate, leading and trailing
* newlines are ignored.
*/
-void sub_add_text(subtitle *sub, const char *txt, int len, double endpts) {
+void sub_add_text(subtitle *sub, const char *txt, int len, double endpts, int strip_markup) {
int comment = 0;
int double_newline = 1; // ignore newlines at the beginning
int i, pos;
@@ -2308,42 +2373,48 @@
buf = malloc(MAX_SUBLINE + 1);
sub->text[sub->lines] = buf;
sub->endpts[sub->lines] = endpts;
- for (i = 0; i < len && pos < MAX_SUBLINE; i++) {
- char c = txt[i];
- if (c == '<') comment |= 1;
- if (c == '{') comment |= 2;
- if (comment) {
- if (c == '}') comment &= ~2;
- if (c == '>') comment &= ~1;
- continue;
- }
- if (pos == MAX_SUBLINE - 1) {
- i--;
- c = 0;
- }
- if (c == '\\' && i + 1 < len) {
- c = txt[++i];
- if (c == 'n' || c == 'N') c = 0;
- }
- if (c == '\n' || c == '\r') c = 0;
- if (c) {
- double_newline = 0;
- buf[pos++] = c;
- } else if (!double_newline) {
- if (sub->lines >= SUB_MAX_TEXT - 1) {
- mp_msg(MSGT_VO, MSGL_WARN, "Too many subtitle lines\n");
- break;
+
+ if (!strip_markup) {
+ subassconvert_subrip(txt, buf, MAX_SUBLINE + 1);
+ sub->text[sub->lines] = buf;
+ } else {
+ for (i = 0; i < len && pos < MAX_SUBLINE; i++) {
+ char c = txt[i];
+ if (c == '<') comment |= 1;
+ if (c == '{') comment |= 2;
+ if (comment) {
+ if (c == '}') comment &= ~2;
+ if (c == '>') comment &= ~1;
+ continue;
}
- double_newline = 1;
- buf[pos] = 0;
- sub->lines++;
- pos = 0;
- buf = malloc(MAX_SUBLINE + 1);
- sub->text[sub->lines] = buf;
- sub->endpts[sub->lines] = endpts;
+ if (pos == MAX_SUBLINE - 1) {
+ i--;
+ c = 0;
+ }
+ if (c == '\\' && i + 1 < len) {
+ c = txt[++i];
+ if (c == 'n' || c == 'N') c = 0;
+ }
+ if (c == '\n' || c == '\r') c = 0;
+ if (c) {
+ double_newline = 0;
+ buf[pos++] = c;
+ } else if (!double_newline) {
+ if (sub->lines >= SUB_MAX_TEXT - 1) {
+ mp_msg(MSGT_VO, MSGL_WARN, "Too many subtitle lines\n");
+ break;
+ }
+ double_newline = 1;
+ buf[pos] = 0;
+ sub->lines++;
+ pos = 0;
+ buf = malloc(MAX_SUBLINE + 1);
+ sub->text[sub->lines] = buf;
+ sub->endpts[sub->lines] = endpts;
+ }
}
+ buf[pos] = 0;
}
- buf[pos] = 0;
if (sub->lines < SUB_MAX_TEXT &&
strlen(sub->text[sub->lines]))
sub->lines++;
Index: subreader.h
===================================================================
--- subreader.h (revision 31559)
+++ subreader.h (working copy)
@@ -105,7 +105,7 @@
void sub_free( sub_data * subd );
void find_sub(sub_data* subd,int key);
void step_sub(sub_data *subd, float pts, int movement);
-void sub_add_text(subtitle *sub, const char *txt, int len, double endpts);
+void sub_add_text(subtitle *sub, const char *txt, int len, double endpts, int strip_markup);
int sub_clear_text(subtitle *sub, double pts);
#endif /* MPLAYER_SUBREADER_H */
Index: libmpdemux/demux_ogg.c
===================================================================
--- libmpdemux/demux_ogg.c (revision 31559)
+++ libmpdemux/demux_ogg.c (working copy)
@@ -212,7 +212,7 @@
endpts = 1.0 + pts + (float)duration / 1000.0;
}
sub_clear_text(&ogg_sub, MP_NOPTS_VALUE);
- sub_add_text(&ogg_sub, &packet[lcv], pack->bytes - lcv, endpts);
+ sub_add_text(&ogg_sub, &packet[lcv], pack->bytes - lcv, endpts, 1);
}
mp_msg(MSGT_DEMUX, MSGL_DBG2, "Ogg sub lines: %d first: '%s'\n",
Index: mpcommon.c
===================================================================
--- mpcommon.c (revision 31559)
+++ mpcommon.c (working copy)
@@ -212,7 +212,7 @@
if (subpts != MP_NOPTS_VALUE) {
subtitle tmp_subs = {0};
if (endpts == MP_NOPTS_VALUE) endpts = subpts + 3;
- sub_add_text(&tmp_subs, packet, len, endpts);
+ sub_add_text(&tmp_subs, packet, len, endpts, 0);
tmp_subs.start = subpts * 100;
tmp_subs.end = endpts * 100;
ass_process_subtitle(ass_track, &tmp_subs);
@@ -239,7 +239,7 @@
len -= p - packet;
packet = p;
}
- sub_add_text(&subs, packet, len, endpts);
+ sub_add_text(&subs, packet, len, endpts, 1);
set_osd_subtitle(&subs);
}
if (d_dvdsub->non_interleaved)
More information about the MPlayer-dev-eng
mailing list