Browse code

SubViewer demuxer and decoder.

Clément Bœsch authored on 2012/07/14 06:42:35
Showing 13 changed files
... ...
@@ -25,8 +25,7 @@ version next:
25 25
 - RTMPTE protocol support
26 26
 - showwaves filter
27 27
 - LucasArts SMUSH playback support
28
-- SAMI demuxer and decoder
29
-- RealText demuxer and decoder
28
+- SAMI, RealText and SubViewer demuxers and decoders
30 29
 - Heart Of Darkness PAF playback support
31 30
 - iec61883 device
32 31
 - asettb filter
... ...
@@ -885,6 +885,7 @@ performance on systems without hardware floating point support).
885 885
 @item RealText         @tab   @tab X @tab   @tab X
886 886
 @item SAMI             @tab   @tab X @tab   @tab X
887 887
 @item SubRip (SRT)     @tab X @tab X @tab X @tab X
888
+@item SubViewer        @tab   @tab X @tab   @tab X
888 889
 @item 3GPP Timed Text  @tab   @tab   @tab   @tab X
889 890
 @item XSUB             @tab   @tab   @tab X @tab X
890 891
 @end multitable
... ...
@@ -426,6 +426,7 @@ OBJS-$(CONFIG_SONIC_LS_ENCODER)        += sonic.o
426 426
 OBJS-$(CONFIG_SP5X_DECODER)            += sp5xdec.o mjpegdec.o mjpeg.o
427 427
 OBJS-$(CONFIG_SRT_DECODER)             += srtdec.o ass.o
428 428
 OBJS-$(CONFIG_SRT_ENCODER)             += srtenc.o ass_split.o
429
+OBJS-$(CONFIG_SUBVIEWER_DECODER)       += subviewerdec.o ass.o
429 430
 OBJS-$(CONFIG_SUNRAST_DECODER)         += sunrast.o
430 431
 OBJS-$(CONFIG_SUNRAST_ENCODER)         += sunrastenc.o
431 432
 OBJS-$(CONFIG_SVQ1_DECODER)            += svq1dec.o svq1.o h263.o \
... ...
@@ -414,6 +414,7 @@ void avcodec_register_all(void)
414 414
     REGISTER_DECODER (REALTEXT, realtext);
415 415
     REGISTER_DECODER (SAMI, sami);
416 416
     REGISTER_ENCDEC  (SRT, srt);
417
+    REGISTER_DECODER (SUBVIEWER, subviewer);
417 418
     REGISTER_ENCDEC  (XSUB, xsub);
418 419
 
419 420
     /* external libraries */
... ...
@@ -438,6 +438,7 @@ enum CodecID {
438 438
     CODEC_ID_JACOSUB    = MKBETAG('J','S','U','B'),
439 439
     CODEC_ID_SAMI       = MKBETAG('S','A','M','I'),
440 440
     CODEC_ID_REALTEXT   = MKBETAG('R','T','X','T'),
441
+    CODEC_ID_SUBVIEWER  = MKBETAG('S','u','b','V'),
441 442
 
442 443
     /* other specific kind of codecs (generally used for attachments) */
443 444
     CODEC_ID_FIRST_UNKNOWN = 0x18000,           ///< A dummy ID pointing at the start of various fake codecs.
444 445
new file mode 100644
... ...
@@ -0,0 +1,78 @@
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
+ * SubViewer subtitle decoder
23
+ * @see https://en.wikipedia.org/wiki/SubViewer
24
+ */
25
+
26
+#include "avcodec.h"
27
+#include "ass.h"
28
+#include "libavutil/bprint.h"
29
+
30
+static int subviewer_event_to_ass(AVBPrint *buf, const char *p)
31
+{
32
+    while (*p) {
33
+        char c;
34
+
35
+        if (sscanf(p, "%*u:%*u:%*u.%*u,%*u:%*u:%*u.%*u%c", &c) == 1)
36
+            p += strcspn(p, "\n") + 1;
37
+        if (!strncmp(p, "[br]", 4)) {
38
+            av_bprintf(buf, "\\N");
39
+            p += 4;
40
+        } else {
41
+            if (p[0] == '\n' && p[1])
42
+                av_bprintf(buf, "\\N");
43
+            else if (*p != '\r')
44
+                av_bprint_chars(buf, *p, 1);
45
+            p++;
46
+        }
47
+    }
48
+
49
+    av_bprintf(buf, "\r\n");
50
+    return 0;
51
+}
52
+
53
+static int subviewer_decode_frame(AVCodecContext *avctx,
54
+                                  void *data, int *got_sub_ptr, AVPacket *avpkt)
55
+{
56
+    AVSubtitle *sub = data;
57
+    const char *ptr = avpkt->data;
58
+    AVBPrint buf;
59
+
60
+    av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
61
+    // note: no need to rescale pts & duration since they are in the same
62
+    // timebase as ASS (1/100)
63
+    if (ptr && avpkt->size > 0 && !subviewer_event_to_ass(&buf, ptr))
64
+        ff_ass_add_rect(sub, buf.str, avpkt->pts, avpkt->duration, 0);
65
+    *got_sub_ptr = sub->num_rects > 0;
66
+    av_bprint_finalize(&buf, NULL);
67
+    return avpkt->size;
68
+}
69
+
70
+AVCodec ff_subviewer_decoder = {
71
+    .name           = "subviewer",
72
+    .long_name      = NULL_IF_CONFIG_SMALL("SubViewer subtitle"),
73
+    .type           = AVMEDIA_TYPE_SUBTITLE,
74
+    .id             = CODEC_ID_SUBVIEWER,
75
+    .decode         = subviewer_decode_frame,
76
+    .init           = ff_ass_subtitle_header_default,
77
+};
... ...
@@ -27,8 +27,8 @@
27 27
  */
28 28
 
29 29
 #define LIBAVCODEC_VERSION_MAJOR 54
30
-#define LIBAVCODEC_VERSION_MINOR  44
31
-#define LIBAVCODEC_VERSION_MICRO 101
30
+#define LIBAVCODEC_VERSION_MINOR  45
31
+#define LIBAVCODEC_VERSION_MICRO 100
32 32
 
33 33
 #define LIBAVCODEC_VERSION_INT  AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
34 34
                                                LIBAVCODEC_VERSION_MINOR, \
... ...
@@ -324,6 +324,7 @@ OBJS-$(CONFIG_SPDIF_MUXER)               += spdif.o spdifenc.o
324 324
 OBJS-$(CONFIG_SRT_DEMUXER)               += srtdec.o
325 325
 OBJS-$(CONFIG_SRT_MUXER)                 += srtenc.o
326 326
 OBJS-$(CONFIG_STR_DEMUXER)               += psxstr.o
327
+OBJS-$(CONFIG_SUBVIEWER_DEMUXER)         += subviewerdec.o
327 328
 OBJS-$(CONFIG_SWF_DEMUXER)               += swfdec.o
328 329
 OBJS-$(CONFIG_SWF_MUXER)                 += swfenc.o
329 330
 OBJS-$(CONFIG_THP_DEMUXER)               += thp.o
... ...
@@ -229,6 +229,7 @@ void av_register_all(void)
229 229
     REGISTER_MUXDEMUX (SPDIF, spdif);
230 230
     REGISTER_MUXDEMUX (SRT, srt);
231 231
     REGISTER_DEMUXER  (STR, str);
232
+    REGISTER_DEMUXER  (SUBVIEWER, subviewer);
232 233
     REGISTER_MUXDEMUX (SWF, swf);
233 234
     REGISTER_MUXER    (TG2, tg2);
234 235
     REGISTER_MUXER    (TGP, tgp);
235 236
new file mode 100644
... ...
@@ -0,0 +1,177 @@
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
+ * SubViewer subtitle demuxer
23
+ * @see https://en.wikipedia.org/wiki/SubViewer
24
+ */
25
+
26
+#include "avformat.h"
27
+#include "internal.h"
28
+#include "subtitles.h"
29
+#include "libavutil/avstring.h"
30
+#include "libavutil/bprint.h"
31
+#include "libavutil/intreadwrite.h"
32
+
33
+typedef struct {
34
+    FFDemuxSubtitlesQueue q;
35
+} SubViewerContext;
36
+
37
+static int subviewer_probe(AVProbeData *p)
38
+{
39
+    char c;
40
+    const unsigned char *ptr = p->buf;
41
+
42
+    if (AV_RB24(ptr) == 0xEFBBBF)
43
+        ptr += 3;  /* skip UTF-8 BOM */
44
+    if (sscanf(ptr, "%*u:%*u:%*u.%*u,%*u:%*u:%*u.%*u%c", &c) == 1)
45
+        return AVPROBE_SCORE_MAX/2;
46
+    if (!strncmp(ptr, "[INFORMATION]", 13))
47
+        return AVPROBE_SCORE_MAX/3;
48
+    return 0;
49
+}
50
+
51
+static int read_ts(const char *s, int64_t *start, int *duration)
52
+{
53
+    int64_t end;
54
+    int hh1, mm1, ss1, ms1;
55
+    int hh2, mm2, ss2, ms2;
56
+
57
+    if (sscanf(s, "%u:%u:%u.%u,%u:%u:%u.%u",
58
+               &hh1, &mm1, &ss1, &ms1, &hh2, &mm2, &ss2, &ms2) == 8) {
59
+        end    = (hh2*3600 + mm2*60 + ss2) * 100 + ms2;
60
+        *start = (hh1*3600 + mm1*60 + ss1) * 100 + ms1;
61
+        *duration = end - *start;
62
+        return 0;
63
+    }
64
+    return -1;
65
+}
66
+
67
+static int subviewer_read_header(AVFormatContext *s)
68
+{
69
+    SubViewerContext *subviewer = s->priv_data;
70
+    AVStream *st = avformat_new_stream(s, NULL);
71
+    AVBPrint header;
72
+    int res = 0;
73
+
74
+    if (!st)
75
+        return AVERROR(ENOMEM);
76
+    avpriv_set_pts_info(st, 64, 1, 100);
77
+    st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE;
78
+    st->codec->codec_id   = CODEC_ID_SUBVIEWER;
79
+
80
+    av_bprint_init(&header, 0, AV_BPRINT_SIZE_UNLIMITED);
81
+
82
+    while (!url_feof(s->pb)) {
83
+        char line[2048];
84
+        const int64_t pos = avio_tell(s->pb);
85
+        int len = ff_get_line(s->pb, line, sizeof(line));
86
+
87
+        if (!len)
88
+            break;
89
+
90
+        if (line[0] == '[' && strncmp(line, "[br]", 4)) {
91
+
92
+            /* ignore event style, XXX: add to side_data? */
93
+            if (strstr(line, "[COLF]") || strstr(line, "[SIZE]") ||
94
+                strstr(line, "[FONT]") || strstr(line, "[STYLE]"))
95
+                continue;
96
+
97
+            if (!st->codec->extradata) { // header not finalized yet
98
+                av_bprintf(&header, "%s", line);
99
+                if (!strncmp(line, "[END INFORMATION]", 17) || !strncmp(line, "[SUBTITLE]", 10)) {
100
+                    /* end of header */
101
+                    av_bprint_finalize(&header, (char **)&st->codec->extradata);
102
+                    if (!st->codec->extradata) {
103
+                        res = AVERROR(ENOMEM);
104
+                        goto end;
105
+                    }
106
+                    st->codec->extradata_size = header.len + 1;
107
+                } else if (strncmp(line, "[INFORMATION]", 13)) {
108
+                    /* assume file metadata at this point */
109
+                    int i, j = 0;
110
+                    char key[32], value[128];
111
+
112
+                    for (i = 1; i < sizeof(key) - 1 && line[i] && line[i] != ']'; i++)
113
+                        key[i - 1] = av_tolower(line[i]);
114
+                    key[i - 1] = 0;
115
+
116
+                    if (line[i] == ']')
117
+                        i++;
118
+                    while (line[i] == ' ')
119
+                        i++;
120
+                    while (j < sizeof(value) - 1 && line[i] && !strchr("]\r\n", line[i]))
121
+                        value[j++] = line[i++];
122
+                    value[j] = 0;
123
+
124
+                    av_dict_set(&s->metadata, key, value, 0);
125
+                }
126
+            }
127
+        } else {
128
+            int64_t pts_start = AV_NOPTS_VALUE;
129
+            int duration = -1;
130
+            int timed_line = !read_ts(line, &pts_start, &duration);
131
+            AVPacket *sub;
132
+
133
+            sub = ff_subtitles_queue_insert(&subviewer->q, line, len, !timed_line);
134
+            if (!sub) {
135
+                res = AVERROR(ENOMEM);
136
+                goto end;
137
+            }
138
+            if (timed_line) {
139
+                sub->pos = pos;
140
+                sub->pts = pts_start;
141
+                sub->duration = duration;
142
+            }
143
+        }
144
+    }
145
+
146
+    ff_subtitles_queue_finalize(&subviewer->q);
147
+
148
+end:
149
+    av_bprint_finalize(&header, NULL);
150
+    return res;
151
+}
152
+
153
+static int subviewer_read_packet(AVFormatContext *s, AVPacket *pkt)
154
+{
155
+    SubViewerContext *subviewer = s->priv_data;
156
+    return ff_subtitles_queue_read_packet(&subviewer->q, pkt);
157
+}
158
+
159
+static int subviewer_read_close(AVFormatContext *s)
160
+{
161
+    SubViewerContext *subviewer = s->priv_data;
162
+    ff_subtitles_queue_clean(&subviewer->q);
163
+    return 0;
164
+}
165
+
166
+AVInputFormat ff_subviewer_demuxer = {
167
+    .name           = "subviewer",
168
+    .long_name      = NULL_IF_CONFIG_SMALL("SubViewer subtitle format"),
169
+    .priv_data_size = sizeof(SubViewerContext),
170
+    .read_probe     = subviewer_probe,
171
+    .read_header    = subviewer_read_header,
172
+    .read_packet    = subviewer_read_packet,
173
+    .read_close     = subviewer_read_close,
174
+    .flags          = AVFMT_GENERIC_INDEX,
175
+    .extensions     = "sub",
176
+};
... ...
@@ -30,8 +30,8 @@
30 30
 #include "libavutil/avutil.h"
31 31
 
32 32
 #define LIBAVFORMAT_VERSION_MAJOR 54
33
-#define LIBAVFORMAT_VERSION_MINOR 20
34
-#define LIBAVFORMAT_VERSION_MICRO 101
33
+#define LIBAVFORMAT_VERSION_MINOR 21
34
+#define LIBAVFORMAT_VERSION_MICRO 100
35 35
 
36 36
 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
37 37
                                                LIBAVFORMAT_VERSION_MINOR, \
... ...
@@ -16,5 +16,8 @@ fate-sub-sami: CMD = md5 -i $(SAMPLES)/sub/SAMI_capability_tester.smi -f ass
16 16
 FATE_SUBTITLES += fate-sub-srt
17 17
 fate-sub-srt: CMD = md5 -i $(SAMPLES)/sub/SubRip_capability_tester.srt -f ass
18 18
 
19
+FATE_SUBTITLES += fate-sub-subviewer
20
+fate-sub-subviewer: CMD = md5 -i $(SAMPLES)/sub/SubViewer_capability_tester.sub -f ass
21
+
19 22
 FATE_SAMPLES_FFMPEG += $(FATE_SUBTITLES)
20 23
 fate-subtitles: $(FATE_SUBTITLES)
21 24
new file mode 100644
... ...
@@ -0,0 +1 @@
0
+303c25863d2283928c19db58a53c93e2