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
40static 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
51};
52
53//FIXME We could avoid some structure duplication
54static int
55ogg_save (AVFormatContext * s)
56{
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));
66
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);
72    }
73
74    ogg->state = ost;
75
76    return 0;
77}
78
79static int
80ogg_restore (AVFormatContext * s, int discard)
81{
82    struct ogg *ogg = s->priv_data;
83    ByteIOContext *bc = s->pb;
84    struct ogg_state *ost = ogg->state;
85    int i;
86
87    if (!ost)
88        return 0;
89
90    ogg->state = ost->next;
91
92    if (!discard){
93        for (i = 0; i < ogg->nstreams; i++)
94            av_free (ogg->streams[i].buf);
95
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));
101    }
102
103    av_free (ost);
104
105    return 0;
106}
107
108static int
109ogg_reset (struct ogg * ogg)
110{
111    int i;
112
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;
122    }
123
124    ogg->curidx = -1;
125
126    return 0;
127}
128
129static const struct ogg_codec *
130ogg_find_codec (uint8_t * buf, int size)
131{
132    int i;
133
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];
138
139    return NULL;
140}
141
142static int
143ogg_find_stream (struct ogg * ogg, int serial)
144{
145    int i;
146
147    for (i = 0; i < ogg->nstreams; i++)
148        if (ogg->streams[i].serial == serial)
149            return i;
150
151    return -1;
152}
153
154static int
155ogg_new_stream (AVFormatContext * s, uint32_t serial)
156{
157
158    struct ogg *ogg = s->priv_data;
159    int idx = ogg->nstreams++;
160    AVStream *st;
161    struct ogg_stream *os;
162
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;
171
172    st = av_new_stream (s, idx);
173    if (!st)
174        return AVERROR(ENOMEM);
175
176    av_set_pts_info(st, 64, 1, 1000000);
177
178    return idx;
179}
180
181static int
182ogg_new_buf(struct ogg *ogg, int idx)
183{
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);
190    }
191    os->buf = nb;
192    os->bufpos = size;
193    os->pstart = 0;
194
195    return 0;
196}
197
198static int
199ogg_read_page (AVFormatContext * s, int *str)
200{
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;
213
214    if (get_buffer (bc, sync, 4) < 4)
215        return -1;
216
217    do{
218        int c;
219
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;
224
225        c = url_fgetc (bc);
226        if (c < 0)
227            return -1;
228        sync[sp++ & 3] = c;
229    }while (i++ < MAX_PAGE_SIZE);
230
231    if (i >= MAX_PAGE_SIZE){
232        av_log (s, AV_LOG_INFO, "ogg, can't find sync word\n");
233        return -1;
234    }
235
236    if (url_fgetc (bc) != 0)      /* version */
237        return -1;
238
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);
245
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;
251    }
252
253    os = ogg->streams + idx;
254
255    if(os->psize > 0)
256        ogg_new_buf(ogg, idx);
257
258    if (get_buffer (bc, os->segments, nsegs) < nsegs)
259        return -1;
260
261    os->nsegs = nsegs;
262    os->segp = 0;
263
264    size = 0;
265    for (i = 0; i < nsegs; i++)
266        size += os->segments[i];
267
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;
275            }
276        }
277    }else{
278        os->psize = 0;
279    }
280
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;
286    }
287
288    if (get_buffer (bc, os->buf + os->bufpos, size) < size)
289        return -1;
290
291    os->lastgp = os->granule;
292    os->bufpos += size;
293    os->granule = gp;
294    os->flags = flags;
295
296    if (str)
297        *str = idx;
298
299    return 0;
300}
301
302static int
303ogg_packet (AVFormatContext * s, int *str, int *dstart, int *dsize)
304{
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;
310
311#if 0
312    av_log (s, AV_LOG_DEBUG, "ogg_packet: curidx=%i\n", ogg->curidx);
313#endif
314
315    do{
316        idx = ogg->curidx;
317
318        while (idx < 0){
319            if (ogg_read_page (s, &idx) < 0)
320                return -1;
321        }
322
323        os = ogg->streams + idx;
324
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
330
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;
337                }
338            }else{
339                return 0;
340            }
341        }
342
343        segp = os->segp;
344        psize = os->psize;
345
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;
352            }
353        }
354
355        if (!complete && os->segp == os->nsegs){
356            ogg->curidx = -1;
357        }
358    }while (!complete);
359
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
365
366    ogg->curidx = idx;
367
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;
378        }
379    }
380
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;
393    }
394
395    os->seq++;
396    if (os->segp == os->nsegs)
397        ogg->curidx = -1;
398
399    return 0;
400}
401
402static int
403ogg_get_headers (AVFormatContext * s)
404{
405    struct ogg *ogg = s->priv_data;
406
407    do{
408        if (ogg_packet (s, NULL, NULL, NULL) < 0)
409            return -1;
410    }while (!ogg->headers);
411
412#if 0
413    av_log (s, AV_LOG_DEBUG, "found headers\n");
414#endif
415
416    return 0;
417}
418
419static uint64_t
420ogg_gptopts (AVFormatContext * s, int i, uint64_t gp)
421{
422    struct ogg *ogg = s->priv_data;
423    struct ogg_stream *os = ogg->streams + i;
424    uint64_t pts = AV_NOPTS_VALUE;
425
426    if(os->codec->gptopts){
427        pts = os->codec->gptopts(s, i, gp);
428    } else {
429        pts = gp;
430    }
431
432    return pts;
433}
434
435
436static int
437ogg_get_length (AVFormatContext * s)
438{
439    struct ogg *ogg = s->priv_data;
440    int idx = -1, i;
441    int64_t size, end;
442
443    if(url_is_streamed(s->pb))
444        return 0;
445
446// already set
447    if (s->duration != AV_NOPTS_VALUE)
448        return 0;
449
450    size = url_fsize(s->pb);
451    if(size < 0)
452        return 0;
453    end = size > MAX_PAGE_SIZE? size - MAX_PAGE_SIZE: size;
454
455    ogg_save (s);
456    url_fseek (s->pb, end, SEEK_SET);
457
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;
462    }
463
464    if (idx != -1){
465        s->streams[idx]->duration =
466            ogg_gptopts (s, idx, ogg->streams[idx].granule);
467    }
468
469    ogg->size = size;
470    ogg_restore (s, 0);
471
472    return 0;
473}
474
475
476static int
477ogg_read_header (AVFormatContext * s, AVFormatParameters * ap)
478{
479    struct ogg *ogg = s->priv_data;
480    int i;
481    ogg->curidx = -1;
482    //linear headers seek from start
483    if (ogg_get_headers (s) < 0){
484        return -1;
485    }
486
487    for (i = 0; i < ogg->nstreams; i++)
488        if (ogg->streams[i].header < 0)
489            ogg->streams[i].codec = NULL;
490
491    //linear granulepos seek from end
492    ogg_get_length (s);
493
494    //fill the extradata in the per codec callbacks
495    return 0;
496}
497
498
499static int
500ogg_read_packet (AVFormatContext * s, AVPacket * pkt)
501{
502    struct ogg *ogg;
503    struct ogg_stream *os;
504    int idx = -1;
505    int pstart, psize;
506
507    //Get an ogg packet
508    do{
509        if (ogg_packet (s, &idx, &pstart, &psize) < 0)
510            return AVERROR(EIO);
511    }while (idx < 0 || !s->streams[idx]);
512
513    ogg = s->priv_data;
514    os = ogg->streams + idx;
515
516    //Alloc a pkt
517    if (av_new_packet (pkt, psize) < 0)
518        return AVERROR(EIO);
519    pkt->stream_index = idx;
520    memcpy (pkt->data, os->buf + pstart, psize);
521    if (os->lastgp != -1LL){
522        pkt->pts = ogg_gptopts (s, idx, os->lastgp);
523        os->lastgp = -1;
524    }
525
526    pkt->flags = os->pflags;
527
528    return psize;
529}
530
531
532static int
533ogg_read_close (AVFormatContext * s)
534{
535    struct ogg *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
547static int64_t
548ogg_read_timestamp (AVFormatContext * s, int stream_index, int64_t * pos_arg,
549                    int64_t pos_limit)
550{
551    struct ogg *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
570static 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
580AVInputFormat ogg_demuxer = {
581    "ogg",
582    NULL_IF_CONFIG_SMALL("Ogg"),
583    sizeof (struct ogg),
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};
592