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