Commit | Line | Data |
---|---|---|
9146ca37 MR |
1 | /* |
2 | * Ogg bitstream support | |
3 | * Luca Barbato <lu_zero@gentoo.org> | |
4 | * Based on tcvp implementation | |
115329f1 | 5 | * |
9146ca37 MR |
6 | */ |
7 | ||
8 | /** | |
9 | Copyright (C) 2005 Michael Ahlberg, Måns Rullgård | |
10 | ||
11 | Permission is hereby granted, free of charge, to any person | |
12 | obtaining a copy of this software and associated documentation | |
13 | files (the "Software"), to deal in the Software without | |
14 | restriction, including without limitation the rights to use, copy, | |
15 | modify, merge, publish, distribute, sublicense, and/or sell copies | |
16 | of the Software, and to permit persons to whom the Software is | |
17 | furnished to do so, subject to the following conditions: | |
18 | ||
19 | The above copyright notice and this permission notice shall be | |
20 | included in all copies or substantial portions of the Software. | |
21 | ||
22 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
23 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
24 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
25 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT | |
26 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | |
27 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
28 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |
29 | DEALINGS IN THE SOFTWARE. | |
30 | **/ | |
31 | ||
32 | ||
33 | #include <stdio.h> | |
a0ddef24 | 34 | #include "oggdec.h" |
9146ca37 MR |
35 | #include "avformat.h" |
36 | ||
37 | #define MAX_PAGE_SIZE 65307 | |
38 | #define DECODER_BUFFER_SIZE MAX_PAGE_SIZE | |
39 | ||
40 | static ogg_codec_t *ogg_codecs[] = { | |
41 | &vorbis_codec, | |
1ed923ea | 42 | &theora_codec, |
bcfc40ae | 43 | &flac_codec, |
880e3ef4 | 44 | &old_flac_codec, |
e1203ac0 MR |
45 | &ogm_video_codec, |
46 | &ogm_audio_codec, | |
47 | &ogm_old_codec, | |
9146ca37 MR |
48 | NULL |
49 | }; | |
50 | ||
9146ca37 MR |
51 | //FIXME We could avoid some structure duplication |
52 | static int | |
53 | ogg_save (AVFormatContext * s) | |
54 | { | |
55 | ogg_t *ogg = s->priv_data; | |
56 | ogg_state_t *ost = | |
ad3aa874 | 57 | av_malloc(sizeof (*ost) + (ogg->nstreams-1) * sizeof (*ogg->streams)); |
9146ca37 MR |
58 | int i; |
59 | ost->pos = url_ftell (&s->pb);; | |
60 | ost->curidx = ogg->curidx; | |
61 | ost->next = ogg->state; | |
20be72c8 | 62 | ost->nstreams = ogg->nstreams; |
9146ca37 MR |
63 | memcpy(ost->streams, ogg->streams, ogg->nstreams * sizeof(*ogg->streams)); |
64 | ||
65 | for (i = 0; i < ogg->nstreams; i++){ | |
66 | ogg_stream_t *os = ogg->streams + i; | |
67 | os->buf = av_malloc (os->bufsize); | |
68 | memset (os->buf, 0, os->bufsize); | |
69 | memcpy (os->buf, ost->streams[i].buf, os->bufpos); | |
70 | } | |
71 | ||
72 | ogg->state = ost; | |
73 | ||
74 | return 0; | |
75 | } | |
76 | ||
77 | static int | |
78 | ogg_restore (AVFormatContext * s, int discard) | |
79 | { | |
80 | ogg_t *ogg = s->priv_data; | |
81 | ByteIOContext *bc = &s->pb; | |
82 | ogg_state_t *ost = ogg->state; | |
83 | int i; | |
84 | ||
85 | if (!ost) | |
86 | return 0; | |
87 | ||
88 | ogg->state = ost->next; | |
89 | ||
90 | if (!discard){ | |
91 | for (i = 0; i < ogg->nstreams; i++) | |
92 | av_free (ogg->streams[i].buf); | |
93 | ||
94 | url_fseek (bc, ost->pos, SEEK_SET); | |
95 | ogg->curidx = ost->curidx; | |
20be72c8 MR |
96 | ogg->nstreams = ost->nstreams; |
97 | memcpy(ogg->streams, ost->streams, | |
98 | ost->nstreams * sizeof(*ogg->streams)); | |
9146ca37 MR |
99 | } |
100 | ||
101 | av_free (ost); | |
102 | ||
103 | return 0; | |
104 | } | |
105 | ||
106 | static int | |
107 | ogg_reset (ogg_t * ogg) | |
108 | { | |
109 | int i; | |
110 | ||
111 | for (i = 0; i < ogg->nstreams; i++){ | |
112 | ogg_stream_t *os = ogg->streams + i; | |
113 | os->bufpos = 0; | |
114 | os->pstart = 0; | |
115 | os->psize = 0; | |
116 | os->granule = -1; | |
117 | os->lastgp = -1; | |
118 | os->nsegs = 0; | |
119 | os->segp = 0; | |
120 | } | |
121 | ||
122 | ogg->curidx = -1; | |
123 | ||
124 | return 0; | |
125 | } | |
126 | ||
127 | static ogg_codec_t * | |
2d2f443d | 128 | ogg_find_codec (uint8_t * buf, int size) |
9146ca37 MR |
129 | { |
130 | int i; | |
131 | ||
132 | for (i = 0; ogg_codecs[i]; i++) | |
133 | if (size >= ogg_codecs[i]->magicsize && | |
134 | !memcmp (buf, ogg_codecs[i]->magic, ogg_codecs[i]->magicsize)) | |
135 | return ogg_codecs[i]; | |
136 | ||
137 | return NULL; | |
138 | } | |
139 | ||
140 | static int | |
141 | ogg_find_stream (ogg_t * ogg, int serial) | |
142 | { | |
143 | int i; | |
144 | ||
145 | for (i = 0; i < ogg->nstreams; i++) | |
146 | if (ogg->streams[i].serial == serial) | |
147 | return i; | |
148 | ||
149 | return -1; | |
150 | } | |
151 | ||
152 | static int | |
153 | ogg_new_stream (AVFormatContext * s, uint32_t serial) | |
154 | { | |
155 | ||
156 | ogg_t *ogg = s->priv_data; | |
157 | int idx = ogg->nstreams++; | |
158 | AVStream *st; | |
159 | ogg_stream_t *os; | |
160 | ||
161 | ogg->streams = av_realloc (ogg->streams, | |
162 | ogg->nstreams * sizeof (*ogg->streams)); | |
163 | memset (ogg->streams + idx, 0, sizeof (*ogg->streams)); | |
164 | os = ogg->streams + idx; | |
165 | os->serial = serial; | |
166 | os->bufsize = DECODER_BUFFER_SIZE; | |
40c5e1fa | 167 | os->buf = av_malloc(os->bufsize); |
9146ca37 MR |
168 | os->header = -1; |
169 | ||
170 | st = av_new_stream (s, idx); | |
171 | if (!st) | |
769e10f0 | 172 | return AVERROR(ENOMEM); |
9146ca37 MR |
173 | |
174 | av_set_pts_info(st, 64, 1, 1000000); | |
9146ca37 MR |
175 | |
176 | return idx; | |
177 | } | |
178 | ||
179 | static int | |
12a195e3 MR |
180 | ogg_new_buf(ogg_t *ogg, int idx) |
181 | { | |
182 | ogg_stream_t *os = ogg->streams + idx; | |
ea02862a | 183 | uint8_t *nb = av_malloc(os->bufsize); |
12a195e3 MR |
184 | int size = os->bufpos - os->pstart; |
185 | if(os->buf){ | |
186 | memcpy(nb, os->buf + os->pstart, size); | |
187 | av_free(os->buf); | |
188 | } | |
189 | os->buf = nb; | |
190 | os->bufpos = size; | |
191 | os->pstart = 0; | |
192 | ||
193 | return 0; | |
194 | } | |
195 | ||
196 | static int | |
9146ca37 MR |
197 | ogg_read_page (AVFormatContext * s, int *str) |
198 | { | |
199 | ByteIOContext *bc = &s->pb; | |
200 | ogg_t *ogg = s->priv_data; | |
201 | ogg_stream_t *os; | |
202 | int i = 0; | |
203 | int flags, nsegs; | |
204 | uint64_t gp; | |
205 | uint32_t serial; | |
206 | uint32_t seq; | |
207 | uint32_t crc; | |
208 | int size, idx; | |
191e8ca7 | 209 | uint8_t sync[4]; |
9146ca37 MR |
210 | int sp = 0; |
211 | ||
212 | if (get_buffer (bc, sync, 4) < 4) | |
213 | return -1; | |
214 | ||
215 | do{ | |
216 | int c; | |
217 | ||
218 | if (sync[sp & 3] == 'O' && | |
219 | sync[(sp + 1) & 3] == 'g' && | |
220 | sync[(sp + 2) & 3] == 'g' && sync[(sp + 3) & 3] == 'S') | |
221 | break; | |
222 | ||
223 | c = url_fgetc (bc); | |
224 | if (c < 0) | |
225 | return -1; | |
226 | sync[sp++ & 3] = c; | |
227 | }while (i++ < MAX_PAGE_SIZE); | |
228 | ||
229 | if (i >= MAX_PAGE_SIZE){ | |
230 | av_log (s, AV_LOG_INFO, "ogg, can't find sync word\n"); | |
231 | return -1; | |
232 | } | |
233 | ||
234 | if (url_fgetc (bc) != 0) /* version */ | |
235 | return -1; | |
236 | ||
237 | flags = url_fgetc (bc); | |
238 | gp = get_le64 (bc); | |
239 | serial = get_le32 (bc); | |
240 | seq = get_le32 (bc); | |
241 | crc = get_le32 (bc); | |
242 | nsegs = url_fgetc (bc); | |
243 | ||
244 | idx = ogg_find_stream (ogg, serial); | |
245 | if (idx < 0){ | |
246 | idx = ogg_new_stream (s, serial); | |
247 | if (idx < 0) | |
248 | return -1; | |
249 | } | |
250 | ||
251 | os = ogg->streams + idx; | |
252 | ||
40c5e1fa | 253 | if(os->psize > 0) |
12a195e3 MR |
254 | ogg_new_buf(ogg, idx); |
255 | ||
9146ca37 MR |
256 | if (get_buffer (bc, os->segments, nsegs) < nsegs) |
257 | return -1; | |
258 | ||
259 | os->nsegs = nsegs; | |
260 | os->segp = 0; | |
261 | ||
262 | size = 0; | |
263 | for (i = 0; i < nsegs; i++) | |
264 | size += os->segments[i]; | |
265 | ||
266 | if (flags & OGG_FLAG_CONT){ | |
267 | if (!os->psize){ | |
268 | while (os->segp < os->nsegs){ | |
269 | int seg = os->segments[os->segp++]; | |
270 | os->pstart += seg; | |
271 | if (seg < 255) | |
272 | break; | |
273 | } | |
274 | } | |
275 | }else{ | |
276 | os->psize = 0; | |
277 | } | |
278 | ||
279 | if (os->bufsize - os->bufpos < size){ | |
2d2f443d | 280 | uint8_t *nb = av_malloc (os->bufsize *= 2); |
9146ca37 MR |
281 | memcpy (nb, os->buf, os->bufpos); |
282 | av_free (os->buf); | |
283 | os->buf = nb; | |
284 | } | |
285 | ||
286 | if (get_buffer (bc, os->buf + os->bufpos, size) < size) | |
287 | return -1; | |
288 | ||
289 | os->lastgp = os->granule; | |
290 | os->bufpos += size; | |
291 | os->granule = gp; | |
292 | os->flags = flags; | |
293 | ||
294 | if (str) | |
295 | *str = idx; | |
296 | ||
297 | return 0; | |
298 | } | |
299 | ||
300 | static int | |
12a195e3 | 301 | ogg_packet (AVFormatContext * s, int *str, int *dstart, int *dsize) |
9146ca37 MR |
302 | { |
303 | ogg_t *ogg = s->priv_data; | |
304 | int idx; | |
305 | ogg_stream_t *os; | |
306 | int complete = 0; | |
307 | int segp = 0, psize = 0; | |
308 | ||
309 | #if 0 | |
310 | av_log (s, AV_LOG_DEBUG, "ogg_packet: curidx=%i\n", ogg->curidx); | |
311 | #endif | |
312 | ||
313 | do{ | |
314 | idx = ogg->curidx; | |
315 | ||
316 | while (idx < 0){ | |
317 | if (ogg_read_page (s, &idx) < 0) | |
318 | return -1; | |
319 | } | |
320 | ||
321 | os = ogg->streams + idx; | |
322 | ||
323 | #if 0 | |
324 | av_log (s, AV_LOG_DEBUG, | |
325 | "ogg_packet: idx=%d pstart=%d psize=%d segp=%d nsegs=%d\n", | |
326 | idx, os->pstart, os->psize, os->segp, os->nsegs); | |
327 | #endif | |
328 | ||
329 | if (!os->codec){ | |
330 | if (os->header < 0){ | |
331 | os->codec = ogg_find_codec (os->buf, os->bufpos); | |
332 | if (!os->codec){ | |
333 | os->header = 0; | |
334 | return 0; | |
335 | } | |
336 | }else{ | |
337 | return 0; | |
338 | } | |
339 | } | |
340 | ||
341 | segp = os->segp; | |
342 | psize = os->psize; | |
343 | ||
344 | while (os->segp < os->nsegs){ | |
345 | int ss = os->segments[os->segp++]; | |
346 | os->psize += ss; | |
347 | if (ss < 255){ | |
348 | complete = 1; | |
349 | break; | |
350 | } | |
351 | } | |
352 | ||
353 | if (!complete && os->segp == os->nsegs){ | |
9146ca37 MR |
354 | ogg->curidx = -1; |
355 | } | |
356 | }while (!complete); | |
357 | ||
358 | #if 0 | |
359 | av_log (s, AV_LOG_DEBUG, | |
360 | "ogg_packet: idx %i, frame size %i, start %i\n", | |
361 | idx, os->psize, os->pstart); | |
362 | #endif | |
363 | ||
364 | ogg->curidx = idx; | |
365 | ||
366 | if (os->header < 0){ | |
367 | int hdr = os->codec->header (s, idx); | |
368 | if (!hdr){ | |
369 | os->header = os->seq; | |
370 | os->segp = segp; | |
371 | os->psize = psize; | |
372 | ogg->headers = 1; | |
373 | }else{ | |
374 | os->pstart += os->psize; | |
375 | os->psize = 0; | |
376 | } | |
377 | } | |
378 | ||
379 | if (os->header > -1 && os->seq > os->header){ | |
e1a794b2 | 380 | os->pflags = 0; |
9146ca37 MR |
381 | if (os->codec && os->codec->packet) |
382 | os->codec->packet (s, idx); | |
383 | if (str) | |
384 | *str = idx; | |
12a195e3 MR |
385 | if (dstart) |
386 | *dstart = os->pstart; | |
387 | if (dsize) | |
388 | *dsize = os->psize; | |
389 | os->pstart += os->psize; | |
390 | os->psize = 0; | |
9146ca37 MR |
391 | } |
392 | ||
393 | os->seq++; | |
394 | if (os->segp == os->nsegs) | |
395 | ogg->curidx = -1; | |
396 | ||
397 | return 0; | |
398 | } | |
399 | ||
400 | static int | |
401 | ogg_get_headers (AVFormatContext * s) | |
402 | { | |
403 | ogg_t *ogg = s->priv_data; | |
404 | ||
405 | do{ | |
12a195e3 | 406 | if (ogg_packet (s, NULL, NULL, NULL) < 0) |
9146ca37 MR |
407 | return -1; |
408 | }while (!ogg->headers); | |
409 | ||
410 | #if 0 | |
411 | av_log (s, AV_LOG_DEBUG, "found headers\n"); | |
412 | #endif | |
413 | ||
414 | return 0; | |
415 | } | |
416 | ||
417 | static uint64_t | |
418 | ogg_gptopts (AVFormatContext * s, int i, uint64_t gp) | |
419 | { | |
1ed923ea MR |
420 | ogg_t *ogg = s->priv_data; |
421 | ogg_stream_t *os = ogg->streams + i; | |
9146ca37 MR |
422 | uint64_t pts = AV_NOPTS_VALUE; |
423 | ||
1ed923ea | 424 | if(os->codec->gptopts){ |
bb270c08 | 425 | pts = os->codec->gptopts(s, i, gp); |
3644cb8f | 426 | } else { |
1ed923ea | 427 | pts = gp; |
9146ca37 MR |
428 | } |
429 | ||
430 | return pts; | |
431 | } | |
432 | ||
433 | ||
434 | static int | |
435 | ogg_get_length (AVFormatContext * s) | |
436 | { | |
437 | ogg_t *ogg = s->priv_data; | |
9146ca37 | 438 | int idx = -1, i; |
56466d7b | 439 | offset_t size, end; |
69599eea MR |
440 | |
441 | if(s->pb.is_streamed) | |
442 | return 0; | |
9146ca37 MR |
443 | |
444 | // already set | |
445 | if (s->duration != AV_NOPTS_VALUE) | |
446 | return 0; | |
447 | ||
56466d7b MR |
448 | size = url_fsize(&s->pb); |
449 | if(size < 0) | |
450 | return 0; | |
451 | end = size > MAX_PAGE_SIZE? size - MAX_PAGE_SIZE: size; | |
452 | ||
9146ca37 | 453 | ogg_save (s); |
56466d7b | 454 | url_fseek (&s->pb, end, SEEK_SET); |
9146ca37 MR |
455 | |
456 | while (!ogg_read_page (s, &i)){ | |
e22f2aaf MR |
457 | if (ogg->streams[i].granule != -1 && ogg->streams[i].granule != 0 && |
458 | ogg->streams[i].codec) | |
9146ca37 MR |
459 | idx = i; |
460 | } | |
461 | ||
462 | if (idx != -1){ | |
463 | s->streams[idx]->duration = | |
464 | ogg_gptopts (s, idx, ogg->streams[idx].granule); | |
465 | } | |
466 | ||
56466d7b | 467 | ogg->size = size; |
9146ca37 | 468 | ogg_restore (s, 0); |
22ffac70 | 469 | ogg_save (s); |
595da759 | 470 | while (!ogg_read_page (s, &i)) { |
22ffac70 RD |
471 | if (i == idx && ogg->streams[i].granule != -1 && ogg->streams[i].granule != 0) |
472 | break; | |
473 | } | |
474 | if (i == idx) { | |
475 | s->streams[idx]->start_time = ogg_gptopts (s, idx, ogg->streams[idx].granule); | |
476 | s->streams[idx]->duration -= s->streams[idx]->start_time; | |
477 | } | |
478 | ogg_restore (s, 0); | |
9146ca37 MR |
479 | |
480 | return 0; | |
481 | } | |
482 | ||
483 | ||
484 | static int | |
485 | ogg_read_header (AVFormatContext * s, AVFormatParameters * ap) | |
486 | { | |
487 | ogg_t *ogg = s->priv_data; | |
488 | ogg->curidx = -1; | |
489 | //linear headers seek from start | |
490 | if (ogg_get_headers (s) < 0){ | |
491 | return -1; | |
492 | } | |
493 | ||
494 | //linear granulepos seek from end | |
495 | ogg_get_length (s); | |
496 | ||
497 | //fill the extradata in the per codec callbacks | |
498 | return 0; | |
499 | } | |
500 | ||
501 | ||
502 | static int | |
503 | ogg_read_packet (AVFormatContext * s, AVPacket * pkt) | |
504 | { | |
505 | ogg_t *ogg; | |
506 | ogg_stream_t *os; | |
507 | int idx = -1; | |
12a195e3 | 508 | int pstart, psize; |
9146ca37 | 509 | |
115329f1 | 510 | //Get an ogg packet |
9146ca37 | 511 | do{ |
12a195e3 | 512 | if (ogg_packet (s, &idx, &pstart, &psize) < 0) |
6f3e0b21 | 513 | return AVERROR(EIO); |
9146ca37 MR |
514 | }while (idx < 0 || !s->streams[idx]); |
515 | ||
516 | ogg = s->priv_data; | |
517 | os = ogg->streams + idx; | |
518 | ||
519 | //Alloc a pkt | |
12a195e3 | 520 | if (av_new_packet (pkt, psize) < 0) |
6f3e0b21 | 521 | return AVERROR(EIO); |
9146ca37 | 522 | pkt->stream_index = idx; |
12a195e3 | 523 | memcpy (pkt->data, os->buf + pstart, psize); |
9146ca37 MR |
524 | if (os->lastgp != -1LL){ |
525 | pkt->pts = ogg_gptopts (s, idx, os->lastgp); | |
526 | os->lastgp = -1; | |
527 | } | |
12a195e3 | 528 | |
e1a794b2 MR |
529 | pkt->flags = os->pflags; |
530 | ||
12a195e3 | 531 | return psize; |
9146ca37 MR |
532 | } |
533 | ||
534 | ||
535 | static int | |
536 | ogg_read_close (AVFormatContext * s) | |
537 | { | |
538 | ogg_t *ogg = s->priv_data; | |
539 | int i; | |
540 | ||
541 | for (i = 0; i < ogg->nstreams; i++){ | |
542 | av_free (ogg->streams[i].buf); | |
1ed923ea | 543 | av_free (ogg->streams[i].private); |
9146ca37 MR |
544 | } |
545 | av_free (ogg->streams); | |
546 | return 0; | |
547 | } | |
548 | ||
549 | ||
9146ca37 MR |
550 | static int64_t |
551 | ogg_read_timestamp (AVFormatContext * s, int stream_index, int64_t * pos_arg, | |
552 | int64_t pos_limit) | |
553 | { | |
554 | ogg_t *ogg = s->priv_data; | |
555 | ByteIOContext *bc = &s->pb; | |
a1f29b95 RD |
556 | int64_t pts = AV_NOPTS_VALUE; |
557 | int i; | |
558 | url_fseek(bc, *pos_arg, SEEK_SET); | |
559 | while (url_ftell(bc) < pos_limit && !ogg_read_page (s, &i)) { | |
560 | if (ogg->streams[i].granule != -1 && ogg->streams[i].granule != 0 && | |
561 | ogg->streams[i].codec && i == stream_index) { | |
562 | pts = ogg_gptopts(s, i, ogg->streams[i].granule); | |
563 | // FIXME: this is the position of the packet after the one with above | |
564 | // pts. | |
565 | *pos_arg = url_ftell(bc); | |
566 | break; | |
567 | } | |
568 | } | |
569 | ogg_reset(ogg); | |
570 | return pts; | |
9146ca37 | 571 | } |
9146ca37 | 572 | |
2e70e4aa MR |
573 | static int ogg_probe(AVProbeData *p) |
574 | { | |
2e70e4aa MR |
575 | if (p->buf[0] == 'O' && p->buf[1] == 'g' && |
576 | p->buf[2] == 'g' && p->buf[3] == 'S' && | |
577 | p->buf[4] == 0x0 && p->buf[5] <= 0x7 ) | |
578 | return AVPROBE_SCORE_MAX; | |
579 | else | |
580 | return 0; | |
581 | } | |
582 | ||
ff70e601 | 583 | AVInputFormat ogg_demuxer = { |
9146ca37 MR |
584 | "ogg", |
585 | "Ogg", | |
586 | sizeof (ogg_t), | |
2e70e4aa | 587 | ogg_probe, |
9146ca37 MR |
588 | ogg_read_header, |
589 | ogg_read_packet, | |
590 | ogg_read_close, | |
ce3132be | 591 | NULL, |
a1f29b95 | 592 | ogg_read_timestamp, |
9146ca37 MR |
593 | .extensions = "ogg", |
594 | }; |