yading@11
|
1 /*
|
yading@11
|
2 * Copyright (c) 2012 Clément Bœsch
|
yading@11
|
3 *
|
yading@11
|
4 * This file is part of FFmpeg.
|
yading@11
|
5 *
|
yading@11
|
6 * FFmpeg is free software; you can redistribute it and/or
|
yading@11
|
7 * modify it under the terms of the GNU Lesser General Public
|
yading@11
|
8 * License as published by the Free Software Foundation; either
|
yading@11
|
9 * version 2.1 of the License, or (at your option) any later version.
|
yading@11
|
10 *
|
yading@11
|
11 * FFmpeg is distributed in the hope that it will be useful,
|
yading@11
|
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
yading@11
|
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
yading@11
|
14 * Lesser General Public License for more details.
|
yading@11
|
15 *
|
yading@11
|
16 * You should have received a copy of the GNU Lesser General Public
|
yading@11
|
17 * License along with FFmpeg; if not, write to the Free Software
|
yading@11
|
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
yading@11
|
19 */
|
yading@11
|
20
|
yading@11
|
21 /**
|
yading@11
|
22 * @file
|
yading@11
|
23 * RealText subtitle demuxer
|
yading@11
|
24 * @see http://service.real.com/help/library/guides/ProductionGuide/prodguide/htmfiles/realtext.htm
|
yading@11
|
25 */
|
yading@11
|
26
|
yading@11
|
27 #include "avformat.h"
|
yading@11
|
28 #include "internal.h"
|
yading@11
|
29 #include "subtitles.h"
|
yading@11
|
30 #include "libavutil/avstring.h"
|
yading@11
|
31 #include "libavutil/bprint.h"
|
yading@11
|
32 #include "libavutil/intreadwrite.h"
|
yading@11
|
33
|
yading@11
|
34 typedef struct {
|
yading@11
|
35 FFDemuxSubtitlesQueue q;
|
yading@11
|
36 } RealTextContext;
|
yading@11
|
37
|
yading@11
|
38 static int realtext_probe(AVProbeData *p)
|
yading@11
|
39 {
|
yading@11
|
40 const unsigned char *ptr = p->buf;
|
yading@11
|
41
|
yading@11
|
42 if (AV_RB24(ptr) == 0xEFBBBF)
|
yading@11
|
43 ptr += 3; /* skip UTF-8 BOM */
|
yading@11
|
44 return !av_strncasecmp(ptr, "<window", 7) ? AVPROBE_SCORE_MAX/2 : 0;
|
yading@11
|
45 }
|
yading@11
|
46
|
yading@11
|
47 static int read_ts(const char *s)
|
yading@11
|
48 {
|
yading@11
|
49 int hh, mm, ss, ms;
|
yading@11
|
50
|
yading@11
|
51 if (sscanf(s, "%u:%u:%u.%u", &hh, &mm, &ss, &ms) == 4) return (hh*3600 + mm*60 + ss) * 100 + ms;
|
yading@11
|
52 if (sscanf(s, "%u:%u:%u" , &hh, &mm, &ss ) == 3) return (hh*3600 + mm*60 + ss) * 100;
|
yading@11
|
53 if (sscanf(s, "%u:%u.%u", &mm, &ss, &ms) == 3) return ( mm*60 + ss) * 100 + ms;
|
yading@11
|
54 if (sscanf(s, "%u:%u" , &mm, &ss ) == 2) return ( mm*60 + ss) * 100;
|
yading@11
|
55 if (sscanf(s, "%u.%u", &ss, &ms) == 2) return ( ss) * 100 + ms;
|
yading@11
|
56 return strtol(s, NULL, 10) * 100;
|
yading@11
|
57 }
|
yading@11
|
58
|
yading@11
|
59 static int realtext_read_header(AVFormatContext *s)
|
yading@11
|
60 {
|
yading@11
|
61 RealTextContext *rt = s->priv_data;
|
yading@11
|
62 AVStream *st = avformat_new_stream(s, NULL);
|
yading@11
|
63 AVBPrint buf;
|
yading@11
|
64 char c = 0;
|
yading@11
|
65 int res = 0, duration = read_ts("60"); // default duration is 60 seconds
|
yading@11
|
66
|
yading@11
|
67 if (!st)
|
yading@11
|
68 return AVERROR(ENOMEM);
|
yading@11
|
69 avpriv_set_pts_info(st, 64, 1, 100);
|
yading@11
|
70 st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE;
|
yading@11
|
71 st->codec->codec_id = AV_CODEC_ID_REALTEXT;
|
yading@11
|
72
|
yading@11
|
73 av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
|
yading@11
|
74
|
yading@11
|
75 while (!url_feof(s->pb)) {
|
yading@11
|
76 AVPacket *sub;
|
yading@11
|
77 const int64_t pos = avio_tell(s->pb) - (c != 0);
|
yading@11
|
78 int n = ff_smil_extract_next_chunk(s->pb, &buf, &c);
|
yading@11
|
79
|
yading@11
|
80 if (n == 0)
|
yading@11
|
81 break;
|
yading@11
|
82
|
yading@11
|
83 if (!av_strncasecmp(buf.str, "<window", 7)) {
|
yading@11
|
84 /* save header to extradata */
|
yading@11
|
85 const char *p = ff_smil_get_attr_ptr(buf.str, "duration");
|
yading@11
|
86
|
yading@11
|
87 if (p)
|
yading@11
|
88 duration = read_ts(p);
|
yading@11
|
89 st->codec->extradata = av_strdup(buf.str);
|
yading@11
|
90 if (!st->codec->extradata) {
|
yading@11
|
91 res = AVERROR(ENOMEM);
|
yading@11
|
92 goto end;
|
yading@11
|
93 }
|
yading@11
|
94 st->codec->extradata_size = buf.len + 1;
|
yading@11
|
95 } else {
|
yading@11
|
96 /* if we just read a <time> tag, introduce a new event, otherwise merge
|
yading@11
|
97 * with the previous one */
|
yading@11
|
98 int merge = !av_strncasecmp(buf.str, "<time", 5) ? 0 : 1;
|
yading@11
|
99 sub = ff_subtitles_queue_insert(&rt->q, buf.str, buf.len, merge);
|
yading@11
|
100 if (!sub) {
|
yading@11
|
101 res = AVERROR(ENOMEM);
|
yading@11
|
102 goto end;
|
yading@11
|
103 }
|
yading@11
|
104 if (!merge) {
|
yading@11
|
105 const char *begin = ff_smil_get_attr_ptr(buf.str, "begin");
|
yading@11
|
106 const char *end = ff_smil_get_attr_ptr(buf.str, "end");
|
yading@11
|
107
|
yading@11
|
108 sub->pos = pos;
|
yading@11
|
109 sub->pts = begin ? read_ts(begin) : 0;
|
yading@11
|
110 sub->duration = end ? (read_ts(end) - sub->pts) : duration;
|
yading@11
|
111 }
|
yading@11
|
112 }
|
yading@11
|
113 av_bprint_clear(&buf);
|
yading@11
|
114 }
|
yading@11
|
115 ff_subtitles_queue_finalize(&rt->q);
|
yading@11
|
116
|
yading@11
|
117 end:
|
yading@11
|
118 av_bprint_finalize(&buf, NULL);
|
yading@11
|
119 return res;
|
yading@11
|
120 }
|
yading@11
|
121
|
yading@11
|
122 static int realtext_read_packet(AVFormatContext *s, AVPacket *pkt)
|
yading@11
|
123 {
|
yading@11
|
124 RealTextContext *rt = s->priv_data;
|
yading@11
|
125 return ff_subtitles_queue_read_packet(&rt->q, pkt);
|
yading@11
|
126 }
|
yading@11
|
127
|
yading@11
|
128 static int realtext_read_seek(AVFormatContext *s, int stream_index,
|
yading@11
|
129 int64_t min_ts, int64_t ts, int64_t max_ts, int flags)
|
yading@11
|
130 {
|
yading@11
|
131 RealTextContext *rt = s->priv_data;
|
yading@11
|
132 return ff_subtitles_queue_seek(&rt->q, s, stream_index,
|
yading@11
|
133 min_ts, ts, max_ts, flags);
|
yading@11
|
134 }
|
yading@11
|
135
|
yading@11
|
136 static int realtext_read_close(AVFormatContext *s)
|
yading@11
|
137 {
|
yading@11
|
138 RealTextContext *rt = s->priv_data;
|
yading@11
|
139 ff_subtitles_queue_clean(&rt->q);
|
yading@11
|
140 return 0;
|
yading@11
|
141 }
|
yading@11
|
142
|
yading@11
|
143 AVInputFormat ff_realtext_demuxer = {
|
yading@11
|
144 .name = "realtext",
|
yading@11
|
145 .long_name = NULL_IF_CONFIG_SMALL("RealText subtitle format"),
|
yading@11
|
146 .priv_data_size = sizeof(RealTextContext),
|
yading@11
|
147 .read_probe = realtext_probe,
|
yading@11
|
148 .read_header = realtext_read_header,
|
yading@11
|
149 .read_packet = realtext_read_packet,
|
yading@11
|
150 .read_seek2 = realtext_read_seek,
|
yading@11
|
151 .read_close = realtext_read_close,
|
yading@11
|
152 .extensions = "rt",
|
yading@11
|
153 };
|