memset when category is >=7, part of cook multichannel
[FFMpeg-mirror/ordered_chapters.git] / libavformat / oggdec.c
blob28dc56eeb93b1d121dd76560e33921b944932a81
1 /*
2 * Ogg bitstream support
3 * Luca Barbato <lu_zero@gentoo.org>
4 * Based on tcvp implementation
6 */
8 /**
9 Copyright (C) 2005 Michael Ahlberg, Måns Rullgård
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:
19 The above copyright notice and this permission notice shall be
20 included in all copies or substantial portions of the Software.
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 **/
33 #include <stdio.h>
34 #include "oggdec.h"
35 #include "avformat.h"
37 #define MAX_PAGE_SIZE 65307
38 #define DECODER_BUFFER_SIZE MAX_PAGE_SIZE
40 static const struct ogg_codec * const ogg_codecs[] = {
41 &ff_speex_codec,
42 &ff_vorbis_codec,
43 &ff_theora_codec,
44 &ff_flac_codec,
45 &ff_old_flac_codec,
46 &ff_ogm_video_codec,
47 &ff_ogm_audio_codec,
48 &ff_ogm_text_codec,
49 &ff_ogm_old_codec,
50 NULL
53 //FIXME We could avoid some structure duplication
54 static int
55 ogg_save (AVFormatContext * s)
57 struct ogg *ogg = s->priv_data;
58 struct ogg_state *ost =
59 av_malloc(sizeof (*ost) + (ogg->nstreams-1) * sizeof (*ogg->streams));
60 int i;
61 ost->pos = url_ftell (s->pb);
62 ost->curidx = ogg->curidx;
63 ost->next = ogg->state;
64 ost->nstreams = ogg->nstreams;
65 memcpy(ost->streams, ogg->streams, ogg->nstreams * sizeof(*ogg->streams));
67 for (i = 0; i < ogg->nstreams; i++){
68 struct ogg_stream *os = ogg->streams + i;
69 os->buf = av_malloc (os->bufsize);
70 memset (os->buf, 0, os->bufsize);
71 memcpy (os->buf, ost->streams[i].buf, os->bufpos);
74 ogg->state = ost;
76 return 0;
79 static int
80 ogg_restore (AVFormatContext * s, int discard)
82 struct ogg *ogg = s->priv_data;
83 ByteIOContext *bc = s->pb;
84 struct ogg_state *ost = ogg->state;
85 int i;
87 if (!ost)
88 return 0;
90 ogg->state = ost->next;
92 if (!discard){
93 for (i = 0; i < ogg->nstreams; i++)
94 av_free (ogg->streams[i].buf);
96 url_fseek (bc, ost->pos, SEEK_SET);
97 ogg->curidx = ost->curidx;
98 ogg->nstreams = ost->nstreams;
99 memcpy(ogg->streams, ost->streams,
100 ost->nstreams * sizeof(*ogg->streams));
103 av_free (ost);
105 return 0;
108 static int
109 ogg_reset (struct ogg * ogg)
111 int i;
113 for (i = 0; i < ogg->nstreams; i++){
114 struct ogg_stream *os = ogg->streams + i;
115 os->bufpos = 0;
116 os->pstart = 0;
117 os->psize = 0;
118 os->granule = -1;
119 os->lastgp = -1;
120 os->nsegs = 0;
121 os->segp = 0;
124 ogg->curidx = -1;
126 return 0;
129 static const struct ogg_codec *
130 ogg_find_codec (uint8_t * buf, int size)
132 int i;
134 for (i = 0; ogg_codecs[i]; i++)
135 if (size >= ogg_codecs[i]->magicsize &&
136 !memcmp (buf, ogg_codecs[i]->magic, ogg_codecs[i]->magicsize))
137 return ogg_codecs[i];
139 return NULL;
142 static int
143 ogg_find_stream (struct ogg * ogg, int serial)
145 int i;
147 for (i = 0; i < ogg->nstreams; i++)
148 if (ogg->streams[i].serial == serial)
149 return i;
151 return -1;
154 static int
155 ogg_new_stream (AVFormatContext * s, uint32_t serial)
158 struct ogg *ogg = s->priv_data;
159 int idx = ogg->nstreams++;
160 AVStream *st;
161 struct ogg_stream *os;
163 ogg->streams = av_realloc (ogg->streams,
164 ogg->nstreams * sizeof (*ogg->streams));
165 memset (ogg->streams + idx, 0, sizeof (*ogg->streams));
166 os = ogg->streams + idx;
167 os->serial = serial;
168 os->bufsize = DECODER_BUFFER_SIZE;
169 os->buf = av_malloc(os->bufsize);
170 os->header = -1;
172 st = av_new_stream (s, idx);
173 if (!st)
174 return AVERROR(ENOMEM);
176 av_set_pts_info(st, 64, 1, 1000000);
178 return idx;
181 static int
182 ogg_new_buf(struct ogg *ogg, int idx)
184 struct ogg_stream *os = ogg->streams + idx;
185 uint8_t *nb = av_malloc(os->bufsize);
186 int size = os->bufpos - os->pstart;
187 if(os->buf){
188 memcpy(nb, os->buf + os->pstart, size);
189 av_free(os->buf);
191 os->buf = nb;
192 os->bufpos = size;
193 os->pstart = 0;
195 return 0;
198 static int
199 ogg_read_page (AVFormatContext * s, int *str)
201 ByteIOContext *bc = s->pb;
202 struct ogg *ogg = s->priv_data;
203 struct ogg_stream *os;
204 int i = 0;
205 int flags, nsegs;
206 uint64_t gp;
207 uint32_t serial;
208 uint32_t seq;
209 uint32_t crc;
210 int size, idx;
211 uint8_t sync[4];
212 int sp = 0;
214 if (get_buffer (bc, sync, 4) < 4)
215 return -1;
218 int c;
220 if (sync[sp & 3] == 'O' &&
221 sync[(sp + 1) & 3] == 'g' &&
222 sync[(sp + 2) & 3] == 'g' && sync[(sp + 3) & 3] == 'S')
223 break;
225 c = url_fgetc (bc);
226 if (c < 0)
227 return -1;
228 sync[sp++ & 3] = c;
229 }while (i++ < MAX_PAGE_SIZE);
231 if (i >= MAX_PAGE_SIZE){
232 av_log (s, AV_LOG_INFO, "ogg, can't find sync word\n");
233 return -1;
236 if (url_fgetc (bc) != 0) /* version */
237 return -1;
239 flags = url_fgetc (bc);
240 gp = get_le64 (bc);
241 serial = get_le32 (bc);
242 seq = get_le32 (bc);
243 crc = get_le32 (bc);
244 nsegs = url_fgetc (bc);
246 idx = ogg_find_stream (ogg, serial);
247 if (idx < 0){
248 idx = ogg_new_stream (s, serial);
249 if (idx < 0)
250 return -1;
253 os = ogg->streams + idx;
255 if(os->psize > 0)
256 ogg_new_buf(ogg, idx);
258 if (get_buffer (bc, os->segments, nsegs) < nsegs)
259 return -1;
261 os->nsegs = nsegs;
262 os->segp = 0;
264 size = 0;
265 for (i = 0; i < nsegs; i++)
266 size += os->segments[i];
268 if (flags & OGG_FLAG_CONT){
269 if (!os->psize){
270 while (os->segp < os->nsegs){
271 int seg = os->segments[os->segp++];
272 os->pstart += seg;
273 if (seg < 255)
274 break;
277 }else{
278 os->psize = 0;
281 if (os->bufsize - os->bufpos < size){
282 uint8_t *nb = av_malloc (os->bufsize *= 2);
283 memcpy (nb, os->buf, os->bufpos);
284 av_free (os->buf);
285 os->buf = nb;
288 if (get_buffer (bc, os->buf + os->bufpos, size) < size)
289 return -1;
291 os->lastgp = os->granule;
292 os->bufpos += size;
293 os->granule = gp;
294 os->flags = flags;
296 if (str)
297 *str = idx;
299 return 0;
302 static int
303 ogg_packet (AVFormatContext * s, int *str, int *dstart, int *dsize)
305 struct ogg *ogg = s->priv_data;
306 int idx;
307 struct ogg_stream *os;
308 int complete = 0;
309 int segp = 0, psize = 0;
311 #if 0
312 av_log (s, AV_LOG_DEBUG, "ogg_packet: curidx=%i\n", ogg->curidx);
313 #endif
316 idx = ogg->curidx;
318 while (idx < 0){
319 if (ogg_read_page (s, &idx) < 0)
320 return -1;
323 os = ogg->streams + idx;
325 #if 0
326 av_log (s, AV_LOG_DEBUG,
327 "ogg_packet: idx=%d pstart=%d psize=%d segp=%d nsegs=%d\n",
328 idx, os->pstart, os->psize, os->segp, os->nsegs);
329 #endif
331 if (!os->codec){
332 if (os->header < 0){
333 os->codec = ogg_find_codec (os->buf, os->bufpos);
334 if (!os->codec){
335 os->header = 0;
336 return 0;
338 }else{
339 return 0;
343 segp = os->segp;
344 psize = os->psize;
346 while (os->segp < os->nsegs){
347 int ss = os->segments[os->segp++];
348 os->psize += ss;
349 if (ss < 255){
350 complete = 1;
351 break;
355 if (!complete && os->segp == os->nsegs){
356 ogg->curidx = -1;
358 }while (!complete);
360 #if 0
361 av_log (s, AV_LOG_DEBUG,
362 "ogg_packet: idx %i, frame size %i, start %i\n",
363 idx, os->psize, os->pstart);
364 #endif
366 ogg->curidx = idx;
368 if (os->header < 0){
369 int hdr = os->codec->header (s, idx);
370 if (!hdr){
371 os->header = os->seq;
372 os->segp = segp;
373 os->psize = psize;
374 ogg->headers = 1;
375 }else{
376 os->pstart += os->psize;
377 os->psize = 0;
381 if (os->header > -1 && os->seq > os->header){
382 os->pflags = 0;
383 if (os->codec && os->codec->packet)
384 os->codec->packet (s, idx);
385 if (str)
386 *str = idx;
387 if (dstart)
388 *dstart = os->pstart;
389 if (dsize)
390 *dsize = os->psize;
391 os->pstart += os->psize;
392 os->psize = 0;
395 os->seq++;
396 if (os->segp == os->nsegs)
397 ogg->curidx = -1;
399 return 0;
402 static int
403 ogg_get_headers (AVFormatContext * s)
405 struct ogg *ogg = s->priv_data;
408 if (ogg_packet (s, NULL, NULL, NULL) < 0)
409 return -1;
410 }while (!ogg->headers);
412 #if 0
413 av_log (s, AV_LOG_DEBUG, "found headers\n");
414 #endif
416 return 0;
419 static uint64_t
420 ogg_gptopts (AVFormatContext * s, int i, uint64_t gp)
422 struct ogg *ogg = s->priv_data;
423 struct ogg_stream *os = ogg->streams + i;
424 uint64_t pts = AV_NOPTS_VALUE;
426 if(os->codec->gptopts){
427 pts = os->codec->gptopts(s, i, gp);
428 } else {
429 pts = gp;
432 return pts;
436 static int
437 ogg_get_length (AVFormatContext * s)
439 struct ogg *ogg = s->priv_data;
440 int idx = -1, i;
441 int64_t size, end;
443 if(url_is_streamed(s->pb))
444 return 0;
446 // already set
447 if (s->duration != AV_NOPTS_VALUE)
448 return 0;
450 size = url_fsize(s->pb);
451 if(size < 0)
452 return 0;
453 end = size > MAX_PAGE_SIZE? size - MAX_PAGE_SIZE: size;
455 ogg_save (s);
456 url_fseek (s->pb, end, SEEK_SET);
458 while (!ogg_read_page (s, &i)){
459 if (ogg->streams[i].granule != -1 && ogg->streams[i].granule != 0 &&
460 ogg->streams[i].codec)
461 idx = i;
464 if (idx != -1){
465 s->streams[idx]->duration =
466 ogg_gptopts (s, idx, ogg->streams[idx].granule);
469 ogg->size = size;
470 ogg_restore (s, 0);
472 return 0;
476 static int
477 ogg_read_header (AVFormatContext * s, AVFormatParameters * ap)
479 struct ogg *ogg = s->priv_data;
480 ogg->curidx = -1;
481 //linear headers seek from start
482 if (ogg_get_headers (s) < 0){
483 return -1;
486 //linear granulepos seek from end
487 ogg_get_length (s);
489 //fill the extradata in the per codec callbacks
490 return 0;
494 static int
495 ogg_read_packet (AVFormatContext * s, AVPacket * pkt)
497 struct ogg *ogg;
498 struct ogg_stream *os;
499 int idx = -1;
500 int pstart, psize;
502 //Get an ogg packet
504 if (ogg_packet (s, &idx, &pstart, &psize) < 0)
505 return AVERROR(EIO);
506 }while (idx < 0 || !s->streams[idx]);
508 ogg = s->priv_data;
509 os = ogg->streams + idx;
511 //Alloc a pkt
512 if (av_new_packet (pkt, psize) < 0)
513 return AVERROR(EIO);
514 pkt->stream_index = idx;
515 memcpy (pkt->data, os->buf + pstart, psize);
516 if (os->lastgp != -1LL){
517 pkt->pts = ogg_gptopts (s, idx, os->lastgp);
518 os->lastgp = -1;
521 pkt->flags = os->pflags;
523 return psize;
527 static int
528 ogg_read_close (AVFormatContext * s)
530 struct ogg *ogg = s->priv_data;
531 int i;
533 for (i = 0; i < ogg->nstreams; i++){
534 av_free (ogg->streams[i].buf);
535 av_free (ogg->streams[i].private);
537 av_free (ogg->streams);
538 return 0;
542 static int64_t
543 ogg_read_timestamp (AVFormatContext * s, int stream_index, int64_t * pos_arg,
544 int64_t pos_limit)
546 struct ogg *ogg = s->priv_data;
547 ByteIOContext *bc = s->pb;
548 int64_t pts = AV_NOPTS_VALUE;
549 int i;
550 url_fseek(bc, *pos_arg, SEEK_SET);
551 while (url_ftell(bc) < pos_limit && !ogg_read_page (s, &i)) {
552 if (ogg->streams[i].granule != -1 && ogg->streams[i].granule != 0 &&
553 ogg->streams[i].codec && i == stream_index) {
554 pts = ogg_gptopts(s, i, ogg->streams[i].granule);
555 // FIXME: this is the position of the packet after the one with above
556 // pts.
557 *pos_arg = url_ftell(bc);
558 break;
561 ogg_reset(ogg);
562 return pts;
565 static int ogg_probe(AVProbeData *p)
567 if (p->buf[0] == 'O' && p->buf[1] == 'g' &&
568 p->buf[2] == 'g' && p->buf[3] == 'S' &&
569 p->buf[4] == 0x0 && p->buf[5] <= 0x7 )
570 return AVPROBE_SCORE_MAX;
571 else
572 return 0;
575 AVInputFormat ogg_demuxer = {
576 "ogg",
577 NULL_IF_CONFIG_SMALL("Ogg"),
578 sizeof (struct ogg),
579 ogg_probe,
580 ogg_read_header,
581 ogg_read_packet,
582 ogg_read_close,
583 NULL,
584 ogg_read_timestamp,
585 .extensions = "ogg",