/src/ffmpeg/libavformat/subtitles.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2012-2013 Clément Bœsch <u pkh me> |
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 | | #include "avformat.h" |
22 | | #include "subtitles.h" |
23 | | #include "avio_internal.h" |
24 | | #include "libavutil/avassert.h" |
25 | | #include "libavutil/avstring.h" |
26 | | #include "libavutil/mem.h" |
27 | | |
28 | | void ff_text_init_avio(void *s, FFTextReader *r, AVIOContext *pb) |
29 | 5.76M | { |
30 | 5.76M | int i; |
31 | 5.76M | r->pb = pb; |
32 | 5.76M | r->buf_pos = r->buf_len = 0; |
33 | 5.76M | r->type = FF_UTF_8; |
34 | 17.2M | for (i = 0; i < 2; i++) |
35 | 11.5M | r->buf[r->buf_len++] = avio_r8(r->pb); |
36 | 5.76M | if (strncmp("\xFF\xFE", r->buf, 2) == 0) { |
37 | 65.3k | r->type = FF_UTF16LE; |
38 | 65.3k | r->buf_pos += 2; |
39 | 5.69M | } else if (strncmp("\xFE\xFF", r->buf, 2) == 0) { |
40 | 94.8k | r->type = FF_UTF16BE; |
41 | 94.8k | r->buf_pos += 2; |
42 | 5.60M | } else { |
43 | 5.60M | r->buf[r->buf_len++] = avio_r8(r->pb); |
44 | 5.60M | if (strncmp("\xEF\xBB\xBF", r->buf, 3) == 0) { |
45 | | // UTF8 |
46 | 4.90k | r->buf_pos += 3; |
47 | 4.90k | } |
48 | 5.60M | } |
49 | 5.76M | if (s && (r->type == FF_UTF16LE || r->type == FF_UTF16BE)) |
50 | 2.50k | av_log(s, AV_LOG_INFO, |
51 | 2.50k | "UTF16 is automatically converted to UTF8, do not specify a character encoding\n"); |
52 | 5.76M | } |
53 | | |
54 | | void ff_text_init_buf(FFTextReader *r, const void *buf, size_t size) |
55 | 5.74M | { |
56 | 5.74M | ffio_init_read_context(&r->buf_pb, buf, size); |
57 | 5.74M | ff_text_init_avio(NULL, r, &r->buf_pb.pub); |
58 | 5.74M | } |
59 | | |
60 | | int64_t ff_text_pos(FFTextReader *r) |
61 | 18.0M | { |
62 | 18.0M | return avio_tell(r->pb) - r->buf_len + r->buf_pos; |
63 | 18.0M | } |
64 | | |
65 | | int ff_text_r8(FFTextReader *r) |
66 | 436M | { |
67 | 436M | uint32_t val; |
68 | 436M | uint8_t tmp; |
69 | 436M | if (r->buf_pos < r->buf_len) |
70 | 99.0M | return r->buf[r->buf_pos++]; |
71 | 337M | if (r->type == FF_UTF16LE) { |
72 | 14.5M | GET_UTF16(val, avio_rl16(r->pb), return 0;) |
73 | 322M | } else if (r->type == FF_UTF16BE) { |
74 | 21.5M | GET_UTF16(val, avio_rb16(r->pb), return 0;) |
75 | 301M | } else { |
76 | 301M | return avio_r8(r->pb); |
77 | 301M | } |
78 | 36.0M | if (!val) |
79 | 244k | return 0; |
80 | 35.8M | r->buf_pos = 0; |
81 | 35.8M | r->buf_len = 0; |
82 | 35.8M | PUT_UTF8(val, tmp, r->buf[r->buf_len++] = tmp;) |
83 | 35.8M | return r->buf[r->buf_pos++]; // buf_len is at least 1 |
84 | 36.0M | } |
85 | | |
86 | | void ff_text_read(FFTextReader *r, char *buf, size_t size) |
87 | 4.79M | { |
88 | 73.7M | for ( ; size > 0; size--) |
89 | 68.9M | *buf++ = ff_text_r8(r); |
90 | 4.79M | } |
91 | | |
92 | | int ff_text_eof(FFTextReader *r) |
93 | 29.5M | { |
94 | 29.5M | return r->buf_pos >= r->buf_len && avio_feof(r->pb); |
95 | 29.5M | } |
96 | | |
97 | | int ff_text_peek_r8(FFTextReader *r) |
98 | 29.1M | { |
99 | 29.1M | int c; |
100 | 29.1M | if (r->buf_pos < r->buf_len) |
101 | 16.8M | return r->buf[r->buf_pos]; |
102 | 12.2M | c = ff_text_r8(r); |
103 | 12.2M | if (!avio_feof(r->pb)) { |
104 | 12.2M | r->buf_pos = 0; |
105 | 12.2M | r->buf_len = 1; |
106 | 12.2M | r->buf[0] = c; |
107 | 12.2M | } |
108 | 12.2M | return c; |
109 | 29.1M | } |
110 | | |
111 | | AVPacket *ff_subtitles_queue_insert(FFDemuxSubtitlesQueue *q, |
112 | | const uint8_t *event, size_t len, int merge) |
113 | 29.6M | { |
114 | 29.6M | AVPacket **subs, *sub; |
115 | | |
116 | 29.6M | av_assert1(event || len == 0); |
117 | | |
118 | 29.6M | if (merge && q->nb_subs > 0) { |
119 | | /* merge with previous event */ |
120 | | |
121 | 3.54M | int old_len; |
122 | 3.54M | sub = q->subs[q->nb_subs - 1]; |
123 | 3.54M | old_len = sub->size; |
124 | 3.54M | if (event) { |
125 | 3.54M | if (av_grow_packet(sub, len) < 0) |
126 | 0 | return NULL; |
127 | 3.54M | memcpy(sub->data + old_len, event, len); |
128 | 3.54M | } |
129 | 26.1M | } else { |
130 | | /* new event */ |
131 | | |
132 | 26.1M | if (q->nb_subs >= INT_MAX/sizeof(*q->subs) - 1) |
133 | 0 | return NULL; |
134 | 26.1M | subs = av_fast_realloc(q->subs, &q->allocated_size, |
135 | 26.1M | (q->nb_subs + 1) * sizeof(*q->subs)); |
136 | 26.1M | if (!subs) |
137 | 0 | return NULL; |
138 | 26.1M | q->subs = subs; |
139 | 26.1M | sub = av_packet_alloc(); |
140 | 26.1M | if (!sub) |
141 | 0 | return NULL; |
142 | 26.1M | if (event) { |
143 | 25.5M | if (av_new_packet(sub, len) < 0) { |
144 | 0 | av_packet_free(&sub); |
145 | 0 | return NULL; |
146 | 0 | } |
147 | 25.5M | memcpy(sub->data, event, len); |
148 | 25.5M | } |
149 | 26.1M | sub->flags |= AV_PKT_FLAG_KEY; |
150 | 26.1M | sub->pts = sub->dts = 0; |
151 | 26.1M | subs[q->nb_subs++] = sub; |
152 | 26.1M | } |
153 | 29.6M | return sub; |
154 | 29.6M | } |
155 | | |
156 | | AVPacket *ff_subtitles_queue_insert_bprint(FFDemuxSubtitlesQueue *q, |
157 | | const AVBPrint *event, int merge) |
158 | 7.32M | { |
159 | 7.32M | if (!av_bprint_is_complete(event)) |
160 | 0 | return NULL; |
161 | 7.32M | return ff_subtitles_queue_insert(q, event->str, event->len, merge); |
162 | 7.32M | } |
163 | | |
164 | | static int cmp_pkt_sub_ts_pos(const void *a, const void *b) |
165 | 282M | { |
166 | 282M | const AVPacket *s1 = *(const AVPacket **)a; |
167 | 282M | const AVPacket *s2 = *(const AVPacket **)b; |
168 | 282M | if (s1->pts == s2->pts) |
169 | 178M | return FFDIFFSIGN(s1->pos, s2->pos); |
170 | 103M | return FFDIFFSIGN(s1->pts , s2->pts); |
171 | 282M | } |
172 | | |
173 | | static int cmp_pkt_sub_pos_ts(const void *a, const void *b) |
174 | 0 | { |
175 | 0 | const AVPacket *s1 = *(const AVPacket **)a; |
176 | 0 | const AVPacket *s2 = *(const AVPacket **)b; |
177 | 0 | if (s1->pos == s2->pos) { |
178 | 0 | if (s1->pts == s2->pts) |
179 | 0 | return 0; |
180 | 0 | return s1->pts > s2->pts ? 1 : -1; |
181 | 0 | } |
182 | 0 | return s1->pos > s2->pos ? 1 : -1; |
183 | 0 | } |
184 | | |
185 | | static void drop_dups(void *log_ctx, FFDemuxSubtitlesQueue *q) |
186 | 26.3k | { |
187 | 26.3k | int i, drop = 0; |
188 | | |
189 | 25.8M | for (i = 1; i < q->nb_subs; i++) { |
190 | 25.7M | const int last_id = i - 1 - drop; |
191 | 25.7M | const AVPacket *last = q->subs[last_id]; |
192 | | |
193 | 25.7M | if (q->subs[i]->pts == last->pts && |
194 | 24.2M | q->subs[i]->duration == last->duration && |
195 | 18.9M | q->subs[i]->stream_index == last->stream_index && |
196 | 18.9M | !strcmp(q->subs[i]->data, last->data)) { |
197 | | |
198 | 10.7M | av_packet_free(&q->subs[i]); |
199 | 10.7M | drop++; |
200 | 15.0M | } else if (drop) { |
201 | 13.6M | q->subs[last_id + 1] = q->subs[i]; |
202 | 13.6M | q->subs[i] = NULL; |
203 | 13.6M | } |
204 | 25.7M | } |
205 | | |
206 | 26.3k | if (drop) { |
207 | 3.38k | q->nb_subs -= drop; |
208 | 3.38k | av_log(log_ctx, AV_LOG_WARNING, "Dropping %d duplicated subtitle events\n", drop); |
209 | 3.38k | } |
210 | 26.3k | } |
211 | | |
212 | | void ff_subtitles_queue_finalize(void *log_ctx, FFDemuxSubtitlesQueue *q) |
213 | 42.5k | { |
214 | 42.5k | int i; |
215 | | |
216 | 42.5k | if (!q->nb_subs) |
217 | 15.4k | return; |
218 | | |
219 | 27.0k | qsort(q->subs, q->nb_subs, sizeof(*q->subs), |
220 | 27.0k | q->sort == SUB_SORT_TS_POS ? cmp_pkt_sub_ts_pos |
221 | 27.0k | : cmp_pkt_sub_pos_ts); |
222 | 26.0M | for (i = 0; i < q->nb_subs; i++) |
223 | 26.0M | if (q->subs[i]->duration < 0 && i < q->nb_subs - 1 && q->subs[i + 1]->pts - (uint64_t)q->subs[i]->pts <= INT64_MAX) |
224 | 11.4M | q->subs[i]->duration = q->subs[i + 1]->pts - q->subs[i]->pts; |
225 | | |
226 | 27.0k | if (!q->keep_duplicates) |
227 | 26.3k | drop_dups(log_ctx, q); |
228 | 27.0k | } |
229 | | |
230 | | int ff_subtitles_queue_read_packet(FFDemuxSubtitlesQueue *q, AVPacket *pkt) |
231 | 4.16M | { |
232 | 4.16M | AVPacket *sub; |
233 | 4.16M | int ret; |
234 | | |
235 | 4.16M | if (q->current_sub_idx == q->nb_subs) |
236 | 40.9k | return AVERROR_EOF; |
237 | 4.12M | sub = q->subs[q->current_sub_idx]; |
238 | 4.12M | if ((ret = av_packet_ref(pkt, sub)) < 0) { |
239 | 0 | return ret; |
240 | 0 | } |
241 | | |
242 | 4.12M | pkt->dts = pkt->pts; |
243 | 4.12M | q->current_sub_idx++; |
244 | 4.12M | return 0; |
245 | 4.12M | } |
246 | | |
247 | | static int search_sub_ts(const FFDemuxSubtitlesQueue *q, int64_t ts) |
248 | 0 | { |
249 | 0 | int s1 = 0, s2 = q->nb_subs - 1; |
250 | |
|
251 | 0 | if (s2 < s1) |
252 | 0 | return AVERROR(ERANGE); |
253 | | |
254 | 0 | for (;;) { |
255 | 0 | int mid; |
256 | |
|
257 | 0 | if (s1 == s2) |
258 | 0 | return s1; |
259 | 0 | if (s1 == s2 - 1) |
260 | 0 | return q->subs[s1]->pts <= q->subs[s2]->pts ? s1 : s2; |
261 | 0 | mid = (s1 + s2) / 2; |
262 | 0 | if (q->subs[mid]->pts <= ts) |
263 | 0 | s1 = mid; |
264 | 0 | else |
265 | 0 | s2 = mid; |
266 | 0 | } |
267 | 0 | } |
268 | | |
269 | | int ff_subtitles_queue_seek(FFDemuxSubtitlesQueue *q, AVFormatContext *s, int stream_index, |
270 | | int64_t min_ts, int64_t ts, int64_t max_ts, int flags) |
271 | 0 | { |
272 | 0 | if (flags & AVSEEK_FLAG_BYTE) { |
273 | 0 | return AVERROR(ENOSYS); |
274 | 0 | } else if (flags & AVSEEK_FLAG_FRAME) { |
275 | 0 | if (ts < 0 || ts >= q->nb_subs) |
276 | 0 | return AVERROR(ERANGE); |
277 | 0 | q->current_sub_idx = ts; |
278 | 0 | } else { |
279 | 0 | int i, idx = search_sub_ts(q, ts); |
280 | 0 | int64_t ts_selected; |
281 | |
|
282 | 0 | if (idx < 0) |
283 | 0 | return idx; |
284 | 0 | for (i = idx; i < q->nb_subs && q->subs[i]->pts < min_ts; i++) |
285 | 0 | if (stream_index == -1 || q->subs[i]->stream_index == stream_index) |
286 | 0 | idx = i; |
287 | 0 | for (i = idx; i > 0 && q->subs[i]->pts > max_ts; i--) |
288 | 0 | if (stream_index == -1 || q->subs[i]->stream_index == stream_index) |
289 | 0 | idx = i; |
290 | |
|
291 | 0 | ts_selected = q->subs[idx]->pts; |
292 | 0 | if (ts_selected < min_ts || ts_selected > max_ts) |
293 | 0 | return AVERROR(ERANGE); |
294 | | |
295 | | /* look back in the latest subtitles for overlapping subtitles */ |
296 | 0 | for (i = idx - 1; i >= 0; i--) { |
297 | 0 | int64_t pts = q->subs[i]->pts; |
298 | 0 | if (q->subs[i]->duration <= 0 || |
299 | 0 | (stream_index != -1 && q->subs[i]->stream_index != stream_index)) |
300 | 0 | continue; |
301 | 0 | if (pts >= min_ts && pts > ts_selected - q->subs[i]->duration) |
302 | 0 | idx = i; |
303 | 0 | else |
304 | 0 | break; |
305 | 0 | } |
306 | | |
307 | | /* If the queue is used to store multiple subtitles streams (like with |
308 | | * VobSub) and the stream index is not specified, we need to make sure |
309 | | * to focus on the smallest file position offset for a same timestamp; |
310 | | * queue is ordered by pts and then filepos, so we can take the first |
311 | | * entry for a given timestamp. */ |
312 | 0 | if (stream_index == -1) |
313 | 0 | while (idx > 0 && q->subs[idx - 1]->pts == q->subs[idx]->pts) |
314 | 0 | idx--; |
315 | |
|
316 | 0 | q->current_sub_idx = idx; |
317 | 0 | } |
318 | 0 | return 0; |
319 | 0 | } |
320 | | |
321 | | void ff_subtitles_queue_clean(FFDemuxSubtitlesQueue *q) |
322 | 45.4k | { |
323 | 45.4k | int i; |
324 | | |
325 | 15.4M | for (i = 0; i < q->nb_subs; i++) |
326 | 15.3M | av_packet_free(&q->subs[i]); |
327 | 45.4k | av_freep(&q->subs); |
328 | 45.4k | q->nb_subs = q->allocated_size = q->current_sub_idx = 0; |
329 | 45.4k | } |
330 | | |
331 | | int ff_subtitles_read_packet(AVFormatContext *s, AVPacket *pkt) |
332 | 2.79M | { |
333 | 2.79M | FFDemuxSubtitlesQueue *q = s->priv_data; |
334 | 2.79M | return ff_subtitles_queue_read_packet(q, pkt); |
335 | 2.79M | } |
336 | | |
337 | | int ff_subtitles_read_seek(AVFormatContext *s, int stream_index, |
338 | | int64_t min_ts, int64_t ts, int64_t max_ts, int flags) |
339 | 0 | { |
340 | 0 | FFDemuxSubtitlesQueue *q = s->priv_data; |
341 | 0 | return ff_subtitles_queue_seek(q, s, stream_index, |
342 | 0 | min_ts, ts, max_ts, flags); |
343 | 0 | } |
344 | | |
345 | | int ff_subtitles_read_close(AVFormatContext *s) |
346 | 33.8k | { |
347 | 33.8k | FFDemuxSubtitlesQueue *q = s->priv_data; |
348 | 33.8k | ff_subtitles_queue_clean(q); |
349 | 33.8k | return 0; |
350 | 33.8k | } |
351 | | |
352 | | int ff_smil_extract_next_text_chunk(FFTextReader *tr, AVBPrint *buf, char *c) |
353 | 4.53M | { |
354 | 4.53M | int i = 0; |
355 | 4.53M | char end_chr; |
356 | | |
357 | 4.53M | if (!*c) // cached char? |
358 | 4.25M | *c = ff_text_r8(tr); |
359 | 4.53M | if (!*c) |
360 | 864 | return 0; |
361 | | |
362 | 4.53M | end_chr = *c == '<' ? '>' : '<'; |
363 | 106M | do { |
364 | 106M | av_bprint_chars(buf, *c, 1); |
365 | 106M | *c = ff_text_r8(tr); |
366 | 106M | if (i == INT_MAX) |
367 | 0 | return AVERROR_INVALIDDATA; |
368 | 106M | i++; |
369 | 106M | } while (*c != end_chr && *c); |
370 | 4.53M | if (end_chr == '>') { |
371 | 4.08M | av_bprint_chars(buf, '>', 1); |
372 | 4.08M | *c = 0; |
373 | 4.08M | } |
374 | 4.53M | return av_bprint_is_complete(buf) ? i : AVERROR(ENOMEM); |
375 | 4.53M | } |
376 | | |
377 | | const char *ff_smil_get_attr_ptr(const char *s, const char *attr) |
378 | 3.84M | { |
379 | 3.84M | int in_quotes = 0; |
380 | 3.84M | const size_t len = strlen(attr); |
381 | | |
382 | 7.72M | while (*s) { |
383 | 33.1M | while (*s) { |
384 | 29.2M | if (!in_quotes && av_isspace(*s)) |
385 | 63.8k | break; |
386 | 29.2M | in_quotes ^= *s == '"'; // XXX: support escaping? |
387 | 29.2M | s++; |
388 | 29.2M | } |
389 | 3.99M | while (av_isspace(*s)) |
390 | 99.1k | s++; |
391 | 3.89M | if (!av_strncasecmp(s, attr, len) && s[len] == '=') |
392 | 14.0k | return s + len + 1 + (s[len + 1] == '"'); |
393 | 3.89M | } |
394 | 3.83M | return NULL; |
395 | 3.84M | } |
396 | | |
397 | | static inline int is_eol(char c) |
398 | 67.9M | { |
399 | 67.9M | return c == '\r' || c == '\n'; |
400 | 67.9M | } |
401 | | |
402 | | int ff_subtitles_read_text_chunk(FFTextReader *tr, AVBPrint *buf) |
403 | 3.16M | { |
404 | 3.16M | char eol_buf[5], last_was_cr = 0; |
405 | 3.16M | int n = 0, i = 0, nb_eol = 0; |
406 | | |
407 | 3.16M | av_bprint_clear(buf); |
408 | | |
409 | 67.9M | for (;;) { |
410 | 67.9M | char c = ff_text_r8(tr); |
411 | | |
412 | 67.9M | if (!c) |
413 | 3.16M | break; |
414 | | |
415 | | /* ignore all initial line breaks */ |
416 | 64.7M | if (n == 0 && is_eol(c)) |
417 | 8.60k | continue; |
418 | | |
419 | | /* line break buffering: we don't want to add the trailing \r\n */ |
420 | 64.7M | if (is_eol(c)) { |
421 | 2.04M | nb_eol += c == '\n' || last_was_cr; |
422 | 2.04M | if (nb_eol == 2) |
423 | 5.32k | break; |
424 | 2.04M | eol_buf[i++] = c; |
425 | 2.04M | if (i == sizeof(eol_buf) - 1) |
426 | 0 | break; |
427 | 2.04M | last_was_cr = c == '\r'; |
428 | 2.04M | continue; |
429 | 2.04M | } |
430 | | |
431 | | /* only one line break followed by data: we flush the line breaks |
432 | | * buffer */ |
433 | 62.7M | if (i) { |
434 | 2.02M | eol_buf[i] = 0; |
435 | 2.02M | av_bprintf(buf, "%s", eol_buf); |
436 | 2.02M | i = nb_eol = 0; |
437 | 2.02M | } |
438 | | |
439 | 62.7M | av_bprint_chars(buf, c, 1); |
440 | 62.7M | n++; |
441 | 62.7M | } |
442 | 3.16M | return av_bprint_is_complete(buf) ? 0 : AVERROR(ENOMEM); |
443 | 3.16M | } |
444 | | |
445 | | int ff_subtitles_read_chunk(AVIOContext *pb, AVBPrint *buf) |
446 | 3.16M | { |
447 | 3.16M | FFTextReader tr; |
448 | 3.16M | tr.buf_pos = tr.buf_len = 0; |
449 | 3.16M | tr.type = 0; |
450 | 3.16M | tr.pb = pb; |
451 | 3.16M | return ff_subtitles_read_text_chunk(&tr, buf); |
452 | 3.16M | } |
453 | | |
454 | | ptrdiff_t ff_subtitles_read_line(FFTextReader *tr, char *buf, size_t size) |
455 | 14.1M | { |
456 | 14.1M | size_t cur = 0; |
457 | 14.1M | if (!size) |
458 | 0 | return 0; |
459 | 14.1M | buf[0] = '\0'; |
460 | 113M | while (cur + 1 < size) { |
461 | 113M | unsigned char c = ff_text_r8(tr); |
462 | 113M | if (!c) |
463 | 12.4M | return ff_text_eof(tr) ? cur : AVERROR_INVALIDDATA; |
464 | 100M | if (c == '\r' || c == '\n') |
465 | 1.69M | break; |
466 | 99.0M | buf[cur++] = c; |
467 | 99.0M | buf[cur] = '\0'; |
468 | 99.0M | } |
469 | 4.26M | while (ff_text_peek_r8(tr) == '\r') |
470 | 2.53M | ff_text_r8(tr); |
471 | 1.73M | if (ff_text_peek_r8(tr) == '\n') |
472 | 81.6k | ff_text_r8(tr); |
473 | 1.73M | return cur; |
474 | 14.1M | } |