2 * Copyright (c) 2012-2013 Clément Bœsch <u pkh me>
4 * This file is part of FFmpeg.
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.
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.
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
22 #include "subtitles.h"
23 #include "avio_internal.h"
24 #include "libavutil/avassert.h"
25 #include "libavutil/avstring.h"
27 void ff_text_init_avio(FFTextReader
*r
, AVIOContext
*pb
)
31 r
->buf_pos
= r
->buf_len
= 0;
33 for (i
= 0; i
< 2; i
++)
34 r
->buf
[r
->buf_len
++] = avio_r8(r
->pb
);
35 if (strncmp("\xFF\xFE", r
->buf
, 2) == 0) {
38 } else if (strncmp("\xFE\xFF", r
->buf
, 2) == 0) {
42 r
->buf
[r
->buf_len
++] = avio_r8(r
->pb
);
43 if (strncmp("\xEF\xBB\xBF", r
->buf
, 3) == 0) {
50 void ff_text_init_buf(FFTextReader
*r
, void *buf
, size_t size
)
52 memset(&r
->buf_pb
, 0, sizeof(r
->buf_pb
));
53 ffio_init_context(&r
->buf_pb
, buf
, size
, 0, NULL
, NULL
, NULL
, NULL
);
54 ff_text_init_avio(r
, &r
->buf_pb
);
57 int64_t ff_text_pos(FFTextReader
*r
)
59 return avio_tell(r
->pb
) - r
->buf_len
+ r
->buf_pos
;
62 int ff_text_r8(FFTextReader
*r
)
66 if (r
->buf_pos
< r
->buf_len
)
67 return r
->buf
[r
->buf_pos
++];
68 if (r
->type
== FF_UTF16LE
) {
69 GET_UTF16(val
, avio_rl16(r
->pb
), return 0;)
70 } else if (r
->type
== FF_UTF16BE
) {
71 GET_UTF16(val
, avio_rb16(r
->pb
), return 0;)
73 return avio_r8(r
->pb
);
79 PUT_UTF8(val
, tmp
, r
->buf
[r
->buf_len
++] = tmp
;)
80 return r
->buf
[r
->buf_pos
++]; // buf_len is at least 1
83 void ff_text_read(FFTextReader
*r
, char *buf
, size_t size
)
85 for ( ; size
> 0; size
--)
86 *buf
++ = ff_text_r8(r
);
89 int ff_text_eof(FFTextReader
*r
)
91 return r
->buf_pos
>= r
->buf_len
&& avio_feof(r
->pb
);
94 int ff_text_peek_r8(FFTextReader
*r
)
97 if (r
->buf_pos
< r
->buf_len
)
98 return r
->buf
[r
->buf_pos
];
100 if (!avio_feof(r
->pb
)) {
108 AVPacket
*ff_subtitles_queue_insert(FFDemuxSubtitlesQueue
*q
,
109 const uint8_t *event
, int len
, int merge
)
111 AVPacket
*subs
, *sub
;
113 if (merge
&& q
->nb_subs
> 0) {
114 /* merge with previous event */
117 sub
= &q
->subs
[q
->nb_subs
- 1];
119 if (av_grow_packet(sub
, len
) < 0)
121 memcpy(sub
->data
+ old_len
, event
, len
);
125 if (q
->nb_subs
>= INT_MAX
/sizeof(*q
->subs
) - 1)
127 subs
= av_fast_realloc(q
->subs
, &q
->allocated_size
,
128 (q
->nb_subs
+ 1) * sizeof(*q
->subs
));
132 sub
= &subs
[q
->nb_subs
++];
133 if (av_new_packet(sub
, len
) < 0)
135 sub
->flags
|= AV_PKT_FLAG_KEY
;
136 sub
->pts
= sub
->dts
= 0;
137 memcpy(sub
->data
, event
, len
);
142 static int cmp_pkt_sub_ts_pos(const void *a
, const void *b
)
144 const AVPacket
*s1
= a
;
145 const AVPacket
*s2
= b
;
146 if (s1
->pts
== s2
->pts
) {
147 if (s1
->pos
== s2
->pos
)
149 return s1
->pos
> s2
->pos
? 1 : -1;
151 return s1
->pts
> s2
->pts
? 1 : -1;
154 static int cmp_pkt_sub_pos_ts(const void *a
, const void *b
)
156 const AVPacket
*s1
= a
;
157 const AVPacket
*s2
= b
;
158 if (s1
->pos
== s2
->pos
) {
159 if (s1
->pts
== s2
->pts
)
161 return s1
->pts
> s2
->pts
? 1 : -1;
163 return s1
->pos
> s2
->pos
? 1 : -1;
166 void ff_subtitles_queue_finalize(FFDemuxSubtitlesQueue
*q
)
170 qsort(q
->subs
, q
->nb_subs
, sizeof(*q
->subs
),
171 q
->sort
== SUB_SORT_TS_POS
? cmp_pkt_sub_ts_pos
172 : cmp_pkt_sub_pos_ts
);
173 for (i
= 0; i
< q
->nb_subs
; i
++)
174 if (q
->subs
[i
].duration
== -1 && i
< q
->nb_subs
- 1)
175 q
->subs
[i
].duration
= q
->subs
[i
+ 1].pts
- q
->subs
[i
].pts
;
178 int ff_subtitles_queue_read_packet(FFDemuxSubtitlesQueue
*q
, AVPacket
*pkt
)
180 AVPacket
*sub
= q
->subs
+ q
->current_sub_idx
;
182 if (q
->current_sub_idx
== q
->nb_subs
)
184 if (av_copy_packet(pkt
, sub
) < 0) {
185 return AVERROR(ENOMEM
);
189 q
->current_sub_idx
++;
193 static int search_sub_ts(const FFDemuxSubtitlesQueue
*q
, int64_t ts
)
195 int s1
= 0, s2
= q
->nb_subs
- 1;
198 return AVERROR(ERANGE
);
206 return q
->subs
[s1
].pts
<= q
->subs
[s2
].pts
? s1
: s2
;
208 if (q
->subs
[mid
].pts
<= ts
)
215 int ff_subtitles_queue_seek(FFDemuxSubtitlesQueue
*q
, AVFormatContext
*s
, int stream_index
,
216 int64_t min_ts
, int64_t ts
, int64_t max_ts
, int flags
)
218 if (flags
& AVSEEK_FLAG_BYTE
) {
219 return AVERROR(ENOSYS
);
220 } else if (flags
& AVSEEK_FLAG_FRAME
) {
221 if (ts
< 0 || ts
>= q
->nb_subs
)
222 return AVERROR(ERANGE
);
223 q
->current_sub_idx
= ts
;
225 int i
, idx
= search_sub_ts(q
, ts
);
230 for (i
= idx
; i
< q
->nb_subs
&& q
->subs
[i
].pts
< min_ts
; i
++)
231 if (stream_index
== -1 || q
->subs
[i
].stream_index
== stream_index
)
233 for (i
= idx
; i
> 0 && q
->subs
[i
].pts
> max_ts
; i
--)
234 if (stream_index
== -1 || q
->subs
[i
].stream_index
== stream_index
)
237 ts_selected
= q
->subs
[idx
].pts
;
238 if (ts_selected
< min_ts
|| ts_selected
> max_ts
)
239 return AVERROR(ERANGE
);
241 /* look back in the latest subtitles for overlapping subtitles */
242 for (i
= idx
- 1; i
>= 0; i
--) {
243 int64_t pts
= q
->subs
[i
].pts
;
244 if (q
->subs
[i
].duration
<= 0 ||
245 (stream_index
!= -1 && q
->subs
[i
].stream_index
!= stream_index
))
247 if (pts
>= min_ts
&& pts
> ts_selected
- q
->subs
[i
].duration
)
253 /* If the queue is used to store multiple subtitles streams (like with
254 * VobSub) and the stream index is not specified, we need to make sure
255 * to focus on the smallest file position offset for a same timestamp;
256 * queue is ordered by pts and then filepos, so we can take the first
257 * entry for a given timestamp. */
258 if (stream_index
== -1)
259 while (idx
> 0 && q
->subs
[idx
- 1].pts
== q
->subs
[idx
].pts
)
262 q
->current_sub_idx
= idx
;
267 void ff_subtitles_queue_clean(FFDemuxSubtitlesQueue
*q
)
271 for (i
= 0; i
< q
->nb_subs
; i
++)
272 av_free_packet(&q
->subs
[i
]);
274 q
->nb_subs
= q
->allocated_size
= q
->current_sub_idx
= 0;
277 int ff_smil_extract_next_text_chunk(FFTextReader
*tr
, AVBPrint
*buf
, char *c
)
282 if (!*c
) // cached char?
287 end_chr
= *c
== '<' ? '>' : '<';
289 av_bprint_chars(buf
, *c
, 1);
292 } while (*c
!= end_chr
&& *c
);
293 if (end_chr
== '>') {
294 av_bprint_chars(buf
, '>', 1);
300 const char *ff_smil_get_attr_ptr(const char *s
, const char *attr
)
303 const int len
= strlen(attr
);
307 if (!in_quotes
&& av_isspace(*s
))
309 in_quotes
^= *s
== '"'; // XXX: support escaping?
312 while (av_isspace(*s
))
314 if (!av_strncasecmp(s
, attr
, len
) && s
[len
] == '=')
315 return s
+ len
+ 1 + (s
[len
+ 1] == '"');
320 static inline int is_eol(char c
)
322 return c
== '\r' || c
== '\n';
325 void ff_subtitles_read_text_chunk(FFTextReader
*tr
, AVBPrint
*buf
)
327 char eol_buf
[5], last_was_cr
= 0;
328 int n
= 0, i
= 0, nb_eol
= 0;
330 av_bprint_clear(buf
);
333 char c
= ff_text_r8(tr
);
338 /* ignore all initial line breaks */
339 if (n
== 0 && is_eol(c
))
342 /* line break buffering: we don't want to add the trailing \r\n */
344 nb_eol
+= c
== '\n' || last_was_cr
;
348 if (i
== sizeof(eol_buf
) - 1)
350 last_was_cr
= c
== '\r';
354 /* only one line break followed by data: we flush the line breaks
358 av_bprintf(buf
, "%s", eol_buf
);
362 av_bprint_chars(buf
, c
, 1);
367 void ff_subtitles_read_chunk(AVIOContext
*pb
, AVBPrint
*buf
)
370 tr
.buf_pos
= tr
.buf_len
= 0;
373 ff_subtitles_read_text_chunk(&tr
, buf
);
376 ptrdiff_t ff_subtitles_read_line(FFTextReader
*tr
, char *buf
, size_t size
)
381 while (cur
+ 1 < size
) {
382 unsigned char c
= ff_text_r8(tr
);
384 return ff_text_eof(tr
) ? cur
: AVERROR_INVALIDDATA
;
385 if (c
== '\r' || c
== '\n')
390 if (ff_text_peek_r8(tr
) == '\r')
392 if (ff_text_peek_r8(tr
) == '\n')