Browse code

lavc: add MicroDVD decoder.

Based on my MicroDVD->ASS conversion code from MPlayer
(sub/subassconvert.c).

Clément Bœsch authored on 2012/04/15 04:46:01
Showing 6 changed files
... ...
@@ -27,6 +27,7 @@ version next:
27 27
 - ffmpeg -benchmark_all option
28 28
 - super2xsai filter ported from libmpcodecs
29 29
 - add libavresample audio conversion library for compatibility
30
+- MicroDVD decoder
30 31
 
31 32
 
32 33
 version 0.10:
... ...
@@ -837,7 +837,7 @@ performance on systems without hardware floating point support).
837 837
 @item SSA/ASS      @tab X @tab X @tab X @tab X
838 838
 @item DVB          @tab X @tab X @tab X @tab X
839 839
 @item DVD          @tab X @tab X @tab X @tab X
840
-@item MicroDVD     @tab X @tab X @tab   @tab
840
+@item MicroDVD     @tab X @tab X @tab   @tab X
841 841
 @item PGS          @tab   @tab   @tab   @tab X
842 842
 @item SubRip (SRT) @tab X @tab X @tab X @tab X
843 843
 @item XSUB         @tab   @tab   @tab X @tab X
... ...
@@ -239,6 +239,7 @@ OBJS-$(CONFIG_MACE3_DECODER)           += mace.o
239 239
 OBJS-$(CONFIG_MACE6_DECODER)           += mace.o
240 240
 OBJS-$(CONFIG_MDEC_DECODER)            += mdec.o mpeg12.o mpeg12data.o \
241 241
                                           mpegvideo.o error_resilience.o
242
+OBJS-$(CONFIG_MICRODVD_DECODER)        += microdvddec.o ass.o
242 243
 OBJS-$(CONFIG_MIMIC_DECODER)           += mimic.o
243 244
 OBJS-$(CONFIG_MJPEG_DECODER)           += mjpegdec.o mjpeg.o
244 245
 OBJS-$(CONFIG_MJPEG_ENCODER)           += mjpegenc.o mjpeg.o           \
... ...
@@ -397,6 +397,7 @@ void avcodec_register_all(void)
397 397
     REGISTER_ENCDEC  (ASS, ass);
398 398
     REGISTER_ENCDEC  (DVBSUB, dvbsub);
399 399
     REGISTER_ENCDEC  (DVDSUB, dvdsub);
400
+    REGISTER_DECODER (MICRODVD, microdvd);
400 401
     REGISTER_DECODER (PGSSUB, pgssub);
401 402
     REGISTER_ENCDEC  (SRT, srt);
402 403
     REGISTER_ENCDEC  (XSUB, xsub);
403 404
new file mode 100644
... ...
@@ -0,0 +1,317 @@
0
+/*
1
+ * Copyright (c) 2012 Clément Bœsch
2
+ *
3
+ * This file is part of FFmpeg.
4
+ *
5
+ * FFmpeg is free software; you can redistribute it and/or
6
+ * modify it under the terms of the GNU Lesser General Public
7
+ * License as published by the Free Software Foundation; either
8
+ * version 2.1 of the License, or (at your option) any later version.
9
+ *
10
+ * FFmpeg is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
+ * Lesser General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU Lesser General Public
16
+ * License along with FFmpeg; if not, write to the Free Software
17
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
+ */
19
+
20
+/**
21
+ * @file
22
+ * MicroDVD subtitle decoder
23
+ *
24
+ * Based on the specifications found here:
25
+ * https://trac.videolan.org/vlc/ticket/1825#comment:6
26
+ */
27
+
28
+#include "libavutil/avstring.h"
29
+#include "libavutil/parseutils.h"
30
+#include "libavutil/bprint.h"
31
+#include "avcodec.h"
32
+#include "ass.h"
33
+
34
+static int indexof(const char *s, int c)
35
+{
36
+    char *f = strchr(s, c);
37
+    return f ? (f - s) : -1;
38
+}
39
+
40
+struct microdvd_tag {
41
+    char key;
42
+    int persistent;
43
+    uint32_t data1;
44
+    uint32_t data2;
45
+    char *data_string;
46
+    int data_string_len;
47
+};
48
+
49
+#define MICRODVD_PERSISTENT_OFF     0
50
+#define MICRODVD_PERSISTENT_ON      1
51
+#define MICRODVD_PERSISTENT_OPENED  2
52
+
53
+// Color, Font, Size, cHarset, stYle, Position, cOordinate
54
+#define MICRODVD_TAGS "cfshyYpo"
55
+
56
+static void microdvd_set_tag(struct microdvd_tag *tags, struct microdvd_tag tag)
57
+{
58
+    int tag_index = indexof(MICRODVD_TAGS, tag.key);
59
+
60
+    if (tag_index < 0)
61
+        return;
62
+    memcpy(&tags[tag_index], &tag, sizeof(tag));
63
+}
64
+
65
+// italic, bold, underline, strike-through
66
+#define MICRODVD_STYLES "ibus"
67
+
68
+static char *microdvd_load_tags(struct microdvd_tag *tags, char *s)
69
+{
70
+    while (*s == '{') {
71
+        char *start = s;
72
+        char tag_char = *(s + 1);
73
+        struct microdvd_tag tag = {0};
74
+
75
+        if (!tag_char || *(s + 2) != ':')
76
+            break;
77
+        s += 3;
78
+
79
+        switch (tag_char) {
80
+
81
+        /* Style */
82
+        case 'Y':
83
+            tag.persistent = MICRODVD_PERSISTENT_ON;
84
+        case 'y':
85
+            while (*s && *s != '}') {
86
+                int style_index = indexof(MICRODVD_STYLES, *s);
87
+
88
+                if (style_index >= 0)
89
+                    tag.data1 |= (1 << style_index);
90
+                s++;
91
+            }
92
+            if (*s != '}')
93
+                break;
94
+            /* We must distinguish persistent and non-persistent styles
95
+             * to handle this kind of style tags: {y:ib}{Y:us} */
96
+            tag.key = tag_char;
97
+            break;
98
+
99
+        /* Color */
100
+        case 'C':
101
+            tag.persistent = MICRODVD_PERSISTENT_ON;
102
+        case 'c':
103
+            tag.data1 = strtol(s, &s, 16) & 0x00ffffff;
104
+            if (*s != '}')
105
+                break;
106
+            tag.key = 'c';
107
+            break;
108
+
109
+        /* Font name */
110
+        case 'F':
111
+            tag.persistent = MICRODVD_PERSISTENT_ON;
112
+        case 'f': {
113
+            int len = indexof(s, '}');
114
+            if (len < 0)
115
+                break;
116
+            tag.data_string = s;
117
+            tag.data_string_len = len;
118
+            s += len;
119
+            tag.key = 'f';
120
+            break;
121
+        }
122
+
123
+        /* Font size */
124
+        case 'S':
125
+            tag.persistent = MICRODVD_PERSISTENT_ON;
126
+        case 's':
127
+            tag.data1 = strtol(s, &s, 10);
128
+            if (*s != '}')
129
+                break;
130
+            tag.key = 's';
131
+            break;
132
+
133
+        /* Charset */
134
+        case 'H': {
135
+            //TODO: not yet handled, just parsed.
136
+            int len = indexof(s, '}');
137
+            if (len < 0)
138
+                break;
139
+            tag.data_string = s;
140
+            tag.data_string_len = len;
141
+            s += len;
142
+            tag.key = 'h';
143
+            break;
144
+        }
145
+
146
+        /* Position */
147
+        case 'P':
148
+            tag.persistent = MICRODVD_PERSISTENT_ON;
149
+            tag.data1 = (*s++ == '1');
150
+            if (*s != '}')
151
+                break;
152
+            tag.key = 'p';
153
+            break;
154
+
155
+        /* Coordinates */
156
+        case 'o':
157
+            tag.persistent = MICRODVD_PERSISTENT_ON;
158
+            tag.data1 = strtol(s, &s, 10);
159
+            if (*s != ',')
160
+                break;
161
+            s++;
162
+            tag.data2 = strtol(s, &s, 10);
163
+            if (*s != '}')
164
+                break;
165
+            tag.key = 'o';
166
+            break;
167
+
168
+        default:    /* Unknown tag, we consider it's text */
169
+            break;
170
+        }
171
+
172
+        if (tag.key == 0)
173
+            return start;
174
+
175
+        microdvd_set_tag(tags, tag);
176
+        s++;
177
+    }
178
+    return s;
179
+}
180
+
181
+static void microdvd_open_tags(AVBPrint *new_line, struct microdvd_tag *tags)
182
+{
183
+    int i, sidx;
184
+    for (i = 0; i < sizeof(MICRODVD_TAGS) - 1; i++) {
185
+        if (tags[i].persistent == MICRODVD_PERSISTENT_OPENED)
186
+            continue;
187
+        switch (tags[i].key) {
188
+        case 'Y':
189
+        case 'y':
190
+            for (sidx = 0; sidx < sizeof(MICRODVD_STYLES) - 1; sidx++)
191
+                if (tags[i].data1 & (1 << sidx))
192
+                    av_bprintf(new_line, "{\\%c1}", MICRODVD_STYLES[sidx]);
193
+            break;
194
+
195
+        case 'c':
196
+            av_bprintf(new_line, "{\\c&H%06X&}", tags[i].data1);
197
+            break;
198
+
199
+        case 'f':
200
+            av_bprintf(new_line, "{\\fn%.*s}",
201
+                       tags[i].data_string_len, tags[i].data_string);
202
+            break;
203
+
204
+        case 's':
205
+            av_bprintf(new_line, "{\\fs%d}", tags[i].data1);
206
+            break;
207
+
208
+        case 'p':
209
+            if (tags[i].data1 == 0)
210
+                av_bprintf(new_line, "{\\an8}");
211
+            break;
212
+
213
+        case 'o':
214
+            av_bprintf(new_line, "{\\pos(%d,%d)}",
215
+                       tags[i].data1, tags[i].data2);
216
+            break;
217
+        }
218
+        if (tags[i].persistent == MICRODVD_PERSISTENT_ON)
219
+            tags[i].persistent = MICRODVD_PERSISTENT_OPENED;
220
+    }
221
+}
222
+
223
+static void microdvd_close_no_persistent_tags(AVBPrint *new_line,
224
+                                              struct microdvd_tag *tags)
225
+{
226
+    int i, sidx;
227
+
228
+    for (i = sizeof(MICRODVD_TAGS) - 2; i; i--) {
229
+        if (tags[i].persistent != MICRODVD_PERSISTENT_OFF)
230
+            continue;
231
+        switch (tags[i].key) {
232
+
233
+        case 'y':
234
+            for (sidx = sizeof(MICRODVD_STYLES) - 2; sidx >= 0; sidx--)
235
+                if (tags[i].data1 & (1 << sidx))
236
+                    av_bprintf(new_line, "{\\%c0}", MICRODVD_STYLES[sidx]);
237
+            break;
238
+
239
+        case 'c':
240
+            av_bprintf(new_line, "{\\c}");
241
+            break;
242
+
243
+        case 'f':
244
+            av_bprintf(new_line, "{\\fn}");
245
+            break;
246
+
247
+        case 's':
248
+            av_bprintf(new_line, "{\\fs}");
249
+            break;
250
+        }
251
+        tags[i].key = 0;
252
+    }
253
+}
254
+
255
+static int microdvd_decode_frame(AVCodecContext *avctx,
256
+                                 void *data, int *got_sub_ptr, AVPacket *avpkt)
257
+{
258
+    AVSubtitle *sub = data;
259
+    AVBPrint new_line;
260
+    char *decoded_sub;
261
+    char *line = avpkt->data;
262
+    char *end = avpkt->data + avpkt->size;
263
+    int64_t frame_start = avpkt->pts;
264
+    int64_t frame_end   = avpkt->pts + avpkt->duration;
265
+    int ts_start = av_rescale_q(frame_start, avctx->time_base, (AVRational){1,100});
266
+    int ts_end   = av_rescale_q(frame_end,   avctx->time_base, (AVRational){1,100});
267
+    struct microdvd_tag tags[sizeof(MICRODVD_TAGS) - 1] = {{0}};
268
+
269
+    if (avpkt->size <= 0)
270
+        return avpkt->size;
271
+
272
+    av_bprint_init(&new_line, 0, 2048);
273
+
274
+    // skip {frame_start}{frame_end}
275
+    line = strchr(line, '}'); if (!line) goto end; line++;
276
+    line = strchr(line, '}'); if (!line) goto end; line++;
277
+
278
+    // subtitle content
279
+    while (line < end && *line) {
280
+
281
+        // parse MicroDVD tags, and open them in ASS
282
+        line = microdvd_load_tags(tags, line);
283
+        microdvd_open_tags(&new_line, tags);
284
+
285
+        // simple copy until EOL or forced carriage return
286
+        while (line < end && *line && *line != '|') {
287
+            av_bprint_chars(&new_line, *line, 1);
288
+            line++;
289
+        }
290
+
291
+        // line split
292
+        if (line < end && *line == '|') {
293
+            microdvd_close_no_persistent_tags(&new_line, tags);
294
+            av_bprintf(&new_line, "\\N");
295
+            line++;
296
+        }
297
+    }
298
+
299
+end:
300
+    av_bprint_finalize(&new_line, &decoded_sub);
301
+    if (*decoded_sub)
302
+        ff_ass_add_rect(sub, decoded_sub, ts_start, ts_end, 0);
303
+    av_free(decoded_sub);
304
+
305
+    *got_sub_ptr = sub->num_rects > 0;
306
+    return avpkt->size;
307
+}
308
+
309
+AVCodec ff_microdvd_decoder = {
310
+    .name         = "microdvd",
311
+    .long_name    = NULL_IF_CONFIG_SMALL("MicroDVD subtitle"),
312
+    .type         = AVMEDIA_TYPE_SUBTITLE,
313
+    .id           = CODEC_ID_MICRODVD,
314
+    .init         = ff_ass_subtitle_header_default,
315
+    .decode       = microdvd_decode_frame,
316
+};
... ...
@@ -27,7 +27,7 @@
27 27
  */
28 28
 
29 29
 #define LIBAVCODEC_VERSION_MAJOR 54
30
-#define LIBAVCODEC_VERSION_MINOR  15
30
+#define LIBAVCODEC_VERSION_MINOR  16
31 31
 #define LIBAVCODEC_VERSION_MICRO 100
32 32
 
33 33
 #define LIBAVCODEC_VERSION_INT  AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \