Mercurial > libavformat.hg
comparison oggdec.c @ 2714:b22ba392ac21 libavformat
Rename ogg2.[ch] to oggdec.[ch].
author | diego |
---|---|
date | Wed, 07 Nov 2007 20:22:32 +0000 |
parents | ogg2.c@d9c256445c14 |
children | 2b101e9d25c0 |
comparison
equal
deleted
inserted
replaced
2713:d9c256445c14 | 2714:b22ba392ac21 |
---|---|
1 /* | |
2 * Ogg bitstream support | |
3 * Luca Barbato <lu_zero@gentoo.org> | |
4 * Based on tcvp implementation | |
5 * | |
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> | |
34 #include "oggdec.h" | |
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, | |
42 &theora_codec, | |
43 &flac_codec, | |
44 &old_flac_codec, | |
45 &ogm_video_codec, | |
46 &ogm_audio_codec, | |
47 &ogm_old_codec, | |
48 NULL | |
49 }; | |
50 | |
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 = | |
57 av_malloc(sizeof (*ost) + (ogg->nstreams-1) * sizeof (*ogg->streams)); | |
58 int i; | |
59 ost->pos = url_ftell (&s->pb);; | |
60 ost->curidx = ogg->curidx; | |
61 ost->next = ogg->state; | |
62 ost->nstreams = ogg->nstreams; | |
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; | |
96 ogg->nstreams = ost->nstreams; | |
97 memcpy(ogg->streams, ost->streams, | |
98 ost->nstreams * sizeof(*ogg->streams)); | |
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 * | |
128 ogg_find_codec (uint8_t * buf, int size) | |
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; | |
167 os->buf = av_malloc(os->bufsize); | |
168 os->header = -1; | |
169 | |
170 st = av_new_stream (s, idx); | |
171 if (!st) | |
172 return AVERROR(ENOMEM); | |
173 | |
174 av_set_pts_info(st, 64, 1, 1000000); | |
175 | |
176 return idx; | |
177 } | |
178 | |
179 static int | |
180 ogg_new_buf(ogg_t *ogg, int idx) | |
181 { | |
182 ogg_stream_t *os = ogg->streams + idx; | |
183 uint8_t *nb = av_malloc(os->bufsize); | |
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 | |
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; | |
209 uint8_t sync[4]; | |
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 | |
253 if(os->psize > 0) | |
254 ogg_new_buf(ogg, idx); | |
255 | |
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){ | |
280 uint8_t *nb = av_malloc (os->bufsize *= 2); | |
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 | |
301 ogg_packet (AVFormatContext * s, int *str, int *dstart, int *dsize) | |
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){ | |
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){ | |
380 if (os->codec && os->codec->packet) | |
381 os->codec->packet (s, idx); | |
382 if (str) | |
383 *str = idx; | |
384 if (dstart) | |
385 *dstart = os->pstart; | |
386 if (dsize) | |
387 *dsize = os->psize; | |
388 os->pstart += os->psize; | |
389 os->psize = 0; | |
390 } | |
391 | |
392 os->seq++; | |
393 if (os->segp == os->nsegs) | |
394 ogg->curidx = -1; | |
395 | |
396 return 0; | |
397 } | |
398 | |
399 static int | |
400 ogg_get_headers (AVFormatContext * s) | |
401 { | |
402 ogg_t *ogg = s->priv_data; | |
403 | |
404 do{ | |
405 if (ogg_packet (s, NULL, NULL, NULL) < 0) | |
406 return -1; | |
407 }while (!ogg->headers); | |
408 | |
409 #if 0 | |
410 av_log (s, AV_LOG_DEBUG, "found headers\n"); | |
411 #endif | |
412 | |
413 return 0; | |
414 } | |
415 | |
416 static uint64_t | |
417 ogg_gptopts (AVFormatContext * s, int i, uint64_t gp) | |
418 { | |
419 ogg_t *ogg = s->priv_data; | |
420 ogg_stream_t *os = ogg->streams + i; | |
421 uint64_t pts = AV_NOPTS_VALUE; | |
422 | |
423 if(os->codec->gptopts){ | |
424 pts = os->codec->gptopts(s, i, gp); | |
425 } else { | |
426 pts = gp; | |
427 } | |
428 | |
429 return pts; | |
430 } | |
431 | |
432 | |
433 static int | |
434 ogg_get_length (AVFormatContext * s) | |
435 { | |
436 ogg_t *ogg = s->priv_data; | |
437 int idx = -1, i; | |
438 offset_t size, end; | |
439 | |
440 if(s->pb.is_streamed) | |
441 return 0; | |
442 | |
443 // already set | |
444 if (s->duration != AV_NOPTS_VALUE) | |
445 return 0; | |
446 | |
447 size = url_fsize(&s->pb); | |
448 if(size < 0) | |
449 return 0; | |
450 end = size > MAX_PAGE_SIZE? size - MAX_PAGE_SIZE: size; | |
451 | |
452 ogg_save (s); | |
453 url_fseek (&s->pb, end, SEEK_SET); | |
454 | |
455 while (!ogg_read_page (s, &i)){ | |
456 if (ogg->streams[i].granule != -1 && ogg->streams[i].granule != 0 && | |
457 ogg->streams[i].codec) | |
458 idx = i; | |
459 } | |
460 | |
461 if (idx != -1){ | |
462 s->streams[idx]->duration = | |
463 ogg_gptopts (s, idx, ogg->streams[idx].granule); | |
464 } | |
465 | |
466 ogg->size = size; | |
467 ogg_restore (s, 0); | |
468 ogg_save (s); | |
469 while (!ogg_read_page (s, &i)) { | |
470 if (i == idx && ogg->streams[i].granule != -1 && ogg->streams[i].granule != 0) | |
471 break; | |
472 } | |
473 if (i == idx) { | |
474 s->streams[idx]->start_time = ogg_gptopts (s, idx, ogg->streams[idx].granule); | |
475 s->streams[idx]->duration -= s->streams[idx]->start_time; | |
476 } | |
477 ogg_restore (s, 0); | |
478 | |
479 return 0; | |
480 } | |
481 | |
482 | |
483 static int | |
484 ogg_read_header (AVFormatContext * s, AVFormatParameters * ap) | |
485 { | |
486 ogg_t *ogg = s->priv_data; | |
487 ogg->curidx = -1; | |
488 //linear headers seek from start | |
489 if (ogg_get_headers (s) < 0){ | |
490 return -1; | |
491 } | |
492 | |
493 //linear granulepos seek from end | |
494 ogg_get_length (s); | |
495 | |
496 //fill the extradata in the per codec callbacks | |
497 return 0; | |
498 } | |
499 | |
500 | |
501 static int | |
502 ogg_read_packet (AVFormatContext * s, AVPacket * pkt) | |
503 { | |
504 ogg_t *ogg; | |
505 ogg_stream_t *os; | |
506 int idx = -1; | |
507 int pstart, psize; | |
508 | |
509 //Get an ogg packet | |
510 do{ | |
511 if (ogg_packet (s, &idx, &pstart, &psize) < 0) | |
512 return AVERROR(EIO); | |
513 }while (idx < 0 || !s->streams[idx]); | |
514 | |
515 ogg = s->priv_data; | |
516 os = ogg->streams + idx; | |
517 | |
518 //Alloc a pkt | |
519 if (av_new_packet (pkt, psize) < 0) | |
520 return AVERROR(EIO); | |
521 pkt->stream_index = idx; | |
522 memcpy (pkt->data, os->buf + pstart, psize); | |
523 if (os->lastgp != -1LL){ | |
524 pkt->pts = ogg_gptopts (s, idx, os->lastgp); | |
525 os->lastgp = -1; | |
526 } | |
527 | |
528 return psize; | |
529 } | |
530 | |
531 | |
532 static int | |
533 ogg_read_close (AVFormatContext * s) | |
534 { | |
535 ogg_t *ogg = s->priv_data; | |
536 int i; | |
537 | |
538 for (i = 0; i < ogg->nstreams; i++){ | |
539 av_free (ogg->streams[i].buf); | |
540 av_free (ogg->streams[i].private); | |
541 } | |
542 av_free (ogg->streams); | |
543 return 0; | |
544 } | |
545 | |
546 | |
547 static int64_t | |
548 ogg_read_timestamp (AVFormatContext * s, int stream_index, int64_t * pos_arg, | |
549 int64_t pos_limit) | |
550 { | |
551 ogg_t *ogg = s->priv_data; | |
552 ByteIOContext *bc = &s->pb; | |
553 int64_t pts = AV_NOPTS_VALUE; | |
554 int i; | |
555 url_fseek(bc, *pos_arg, SEEK_SET); | |
556 while (url_ftell(bc) < pos_limit && !ogg_read_page (s, &i)) { | |
557 if (ogg->streams[i].granule != -1 && ogg->streams[i].granule != 0 && | |
558 ogg->streams[i].codec && i == stream_index) { | |
559 pts = ogg_gptopts(s, i, ogg->streams[i].granule); | |
560 // FIXME: this is the position of the packet after the one with above | |
561 // pts. | |
562 *pos_arg = url_ftell(bc); | |
563 break; | |
564 } | |
565 } | |
566 ogg_reset(ogg); | |
567 return pts; | |
568 } | |
569 | |
570 static int ogg_probe(AVProbeData *p) | |
571 { | |
572 if (p->buf[0] == 'O' && p->buf[1] == 'g' && | |
573 p->buf[2] == 'g' && p->buf[3] == 'S' && | |
574 p->buf[4] == 0x0 && p->buf[5] <= 0x7 ) | |
575 return AVPROBE_SCORE_MAX; | |
576 else | |
577 return 0; | |
578 } | |
579 | |
580 AVInputFormat ogg_demuxer = { | |
581 "ogg", | |
582 "Ogg", | |
583 sizeof (ogg_t), | |
584 ogg_probe, | |
585 ogg_read_header, | |
586 ogg_read_packet, | |
587 ogg_read_close, | |
588 NULL, | |
589 ogg_read_timestamp, | |
590 .extensions = "ogg", | |
591 }; |