Statistics
| Branch: | Revision:

ffmpeg / libavformat / oggdec.c @ c9da676d

History | View | Annotate | Download (13.2 KB)

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 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
54
static int
55
ogg_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

    
79
static int
80
ogg_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

    
108
static int
109
ogg_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

    
129
static const struct ogg_codec *
130
ogg_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

    
142
static int
143
ogg_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

    
154
static int
155
ogg_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

    
181
static int
182
ogg_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

    
198
static int
199
ogg_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

    
302
static int
303
ogg_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

    
402
static int
403
ogg_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

    
419
static uint64_t
420
ogg_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

    
436
static int
437
ogg_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: 0;
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

    
476
static int
477
ogg_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

    
499
static int
500
ogg_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

    
532
static int
533
ogg_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

    
547
static int64_t
548
ogg_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

    
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
    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
    .metadata_conv = ff_vorbiscomment_metadata_conv,
592
};