OggExtractor.cpp revision 96f52cde23982f668592418a9548045237d5e327
1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17//#define LOG_NDEBUG 0
18#define LOG_TAG "OggExtractor"
19#include <utils/Log.h>
20
21#include "include/OggExtractor.h"
22
23#include <cutils/properties.h>
24#include <media/stagefright/DataSource.h>
25#include <media/stagefright/MediaBuffer.h>
26#include <media/stagefright/MediaBufferGroup.h>
27#include <media/stagefright/MediaDebug.h>
28#include <media/stagefright/MediaDefs.h>
29#include <media/stagefright/MediaErrors.h>
30#include <media/stagefright/MediaSource.h>
31#include <media/stagefright/MetaData.h>
32#include <media/stagefright/Utils.h>
33#include <utils/String8.h>
34
35extern "C" {
36    #include <Tremolo/codec_internal.h>
37
38    int _vorbis_unpack_books(vorbis_info *vi,oggpack_buffer *opb);
39    int _vorbis_unpack_info(vorbis_info *vi,oggpack_buffer *opb);
40    int _vorbis_unpack_comment(vorbis_comment *vc,oggpack_buffer *opb);
41}
42
43namespace android {
44
45struct OggSource : public MediaSource {
46    OggSource(const sp<OggExtractor> &extractor);
47
48    virtual sp<MetaData> getFormat();
49
50    virtual status_t start(MetaData *params = NULL);
51    virtual status_t stop();
52
53    virtual status_t read(
54            MediaBuffer **buffer, const ReadOptions *options = NULL);
55
56protected:
57    virtual ~OggSource();
58
59private:
60    sp<OggExtractor> mExtractor;
61    bool mStarted;
62
63    OggSource(const OggSource &);
64    OggSource &operator=(const OggSource &);
65};
66
67struct MyVorbisExtractor {
68    MyVorbisExtractor(const sp<DataSource> &source);
69    virtual ~MyVorbisExtractor();
70
71    sp<MetaData> getFormat() const;
72
73    // Returns an approximate bitrate in bits per second.
74    uint64_t approxBitrate();
75
76    status_t seekToOffset(off_t offset);
77    status_t readNextPacket(MediaBuffer **buffer);
78
79    void init();
80
81private:
82    struct Page {
83        uint64_t mGranulePosition;
84        uint32_t mSerialNo;
85        uint32_t mPageNo;
86        uint8_t mFlags;
87        uint8_t mNumSegments;
88        uint8_t mLace[255];
89    };
90
91    sp<DataSource> mSource;
92    off_t mOffset;
93    Page mCurrentPage;
94    size_t mCurrentPageSize;
95    size_t mNextLaceIndex;
96
97    off_t mFirstDataOffset;
98
99    vorbis_info mVi;
100    vorbis_comment mVc;
101
102    sp<MetaData> mMeta;
103
104    ssize_t readPage(off_t offset, Page *page);
105    status_t findNextPage(off_t startOffset, off_t *pageOffset);
106
107    void verifyHeader(
108            MediaBuffer *buffer, uint8_t type);
109
110    MyVorbisExtractor(const MyVorbisExtractor &);
111    MyVorbisExtractor &operator=(const MyVorbisExtractor &);
112};
113
114////////////////////////////////////////////////////////////////////////////////
115
116OggSource::OggSource(const sp<OggExtractor> &extractor)
117    : mExtractor(extractor),
118      mStarted(false) {
119}
120
121OggSource::~OggSource() {
122    if (mStarted) {
123        stop();
124    }
125}
126
127sp<MetaData> OggSource::getFormat() {
128    return mExtractor->mImpl->getFormat();
129}
130
131status_t OggSource::start(MetaData *params) {
132    if (mStarted) {
133        return INVALID_OPERATION;
134    }
135
136    mStarted = true;
137
138    return OK;
139}
140
141status_t OggSource::stop() {
142    mStarted = false;
143
144    return OK;
145}
146
147status_t OggSource::read(
148        MediaBuffer **out, const ReadOptions *options) {
149    *out = NULL;
150
151    int64_t seekTimeUs;
152    if (options && options->getSeekTo(&seekTimeUs)) {
153        off_t pos = seekTimeUs * mExtractor->mImpl->approxBitrate() / 8000000ll;
154        LOGI("seeking to offset %ld", pos);
155
156        if (mExtractor->mImpl->seekToOffset(pos) != OK) {
157            return ERROR_END_OF_STREAM;
158        }
159    }
160
161    MediaBuffer *packet;
162    status_t err = mExtractor->mImpl->readNextPacket(&packet);
163
164    if (err != OK) {
165        return err;
166    }
167
168#if 0
169    int64_t timeUs;
170    if (packet->meta_data()->findInt64(kKeyTime, &timeUs)) {
171        LOGI("found time = %lld us", timeUs);
172    } else {
173        LOGI("NO time");
174    }
175#endif
176
177    *out = packet;
178
179    return OK;
180}
181
182////////////////////////////////////////////////////////////////////////////////
183
184MyVorbisExtractor::MyVorbisExtractor(const sp<DataSource> &source)
185    : mSource(source),
186      mOffset(0),
187      mCurrentPageSize(0),
188      mNextLaceIndex(0),
189      mFirstDataOffset(-1) {
190    mCurrentPage.mNumSegments = 0;
191}
192
193MyVorbisExtractor::~MyVorbisExtractor() {
194}
195
196sp<MetaData> MyVorbisExtractor::getFormat() const {
197    return mMeta;
198}
199
200status_t MyVorbisExtractor::findNextPage(
201        off_t startOffset, off_t *pageOffset) {
202    *pageOffset = startOffset;
203
204    for (;;) {
205        char signature[4];
206        ssize_t n = mSource->readAt(*pageOffset, &signature, 4);
207
208        if (n < 4) {
209            *pageOffset = 0;
210
211            return (n < 0) ? n : (status_t)ERROR_END_OF_STREAM;
212        }
213
214        if (!memcmp(signature, "OggS", 4)) {
215            if (*pageOffset > startOffset) {
216                LOGV("skipped %ld bytes of junk to reach next frame",
217                     *pageOffset - startOffset);
218            }
219
220            return OK;
221        }
222
223        ++*pageOffset;
224    }
225}
226
227status_t MyVorbisExtractor::seekToOffset(off_t offset) {
228    if (mFirstDataOffset >= 0 && offset < mFirstDataOffset) {
229        // Once we know where the actual audio data starts (past the headers)
230        // don't ever seek to anywhere before that.
231        offset = mFirstDataOffset;
232    }
233
234    off_t pageOffset;
235    status_t err = findNextPage(offset, &pageOffset);
236
237    if (err != OK) {
238        return err;
239    }
240
241    mOffset = pageOffset;
242
243    mCurrentPageSize = 0;
244    mCurrentPage.mNumSegments = 0;
245    mNextLaceIndex = 0;
246
247    // XXX what if new page continues packet from last???
248
249    return OK;
250}
251
252ssize_t MyVorbisExtractor::readPage(off_t offset, Page *page) {
253    uint8_t header[27];
254    if (mSource->readAt(offset, header, sizeof(header))
255            < (ssize_t)sizeof(header)) {
256        LOGE("failed to read %d bytes at offset 0x%08lx", sizeof(header), offset);
257
258        return ERROR_IO;
259    }
260
261    if (memcmp(header, "OggS", 4)) {
262        return ERROR_MALFORMED;
263    }
264
265    if (header[4] != 0) {
266        // Wrong version.
267
268        return ERROR_UNSUPPORTED;
269    }
270
271    page->mFlags = header[5];
272
273    if (page->mFlags & ~7) {
274        // Only bits 0-2 are defined in version 0.
275        return ERROR_MALFORMED;
276    }
277
278    page->mGranulePosition = U64LE_AT(&header[6]);
279
280#if 0
281    printf("granulePosition = %llu (0x%llx)\n",
282           page->mGranulePosition, page->mGranulePosition);
283#endif
284
285    page->mSerialNo = U32LE_AT(&header[14]);
286    page->mPageNo = U32LE_AT(&header[18]);
287
288    page->mNumSegments = header[26];
289    if (mSource->readAt(
290                offset + sizeof(header), page->mLace, page->mNumSegments)
291            < (ssize_t)page->mNumSegments) {
292        return ERROR_IO;
293    }
294
295    size_t totalSize = 0;;
296    for (size_t i = 0; i < page->mNumSegments; ++i) {
297        totalSize += page->mLace[i];
298    }
299
300    String8 tmp;
301    for (size_t i = 0; i < page->mNumSegments; ++i) {
302        char x[32];
303        sprintf(x, "%s%u", i > 0 ? ", " : "", (unsigned)page->mLace[i]);
304
305        tmp.append(x);
306    }
307
308    LOGV("%c %s", page->mFlags & 1 ? '+' : ' ', tmp.string());
309
310    return sizeof(header) + page->mNumSegments + totalSize;
311}
312
313status_t MyVorbisExtractor::readNextPacket(MediaBuffer **out) {
314    *out = NULL;
315
316    MediaBuffer *buffer = NULL;
317    int64_t timeUs = -1;
318
319    for (;;) {
320        size_t i;
321        size_t packetSize = 0;
322        bool gotFullPacket = false;
323        for (i = mNextLaceIndex; i < mCurrentPage.mNumSegments; ++i) {
324            uint8_t lace = mCurrentPage.mLace[i];
325
326            packetSize += lace;
327
328            if (lace < 255) {
329                gotFullPacket = true;
330                ++i;
331                break;
332            }
333        }
334
335        if (mNextLaceIndex < mCurrentPage.mNumSegments) {
336            off_t dataOffset = mOffset + 27 + mCurrentPage.mNumSegments;
337            for (size_t j = 0; j < mNextLaceIndex; ++j) {
338                dataOffset += mCurrentPage.mLace[j];
339            }
340
341            size_t fullSize = packetSize;
342            if (buffer != NULL) {
343                fullSize += buffer->range_length();
344            }
345            MediaBuffer *tmp = new MediaBuffer(fullSize);
346            if (buffer != NULL) {
347                memcpy(tmp->data(), buffer->data(), buffer->range_length());
348                tmp->set_range(0, buffer->range_length());
349                buffer->release();
350            } else {
351                // XXX Not only is this not technically the correct time for
352                // this packet, we also stamp every packet in this page
353                // with the same time. This needs fixing later.
354                timeUs = mCurrentPage.mGranulePosition * 1000000ll / mVi.rate;
355                tmp->set_range(0, 0);
356            }
357            buffer = tmp;
358
359            ssize_t n = mSource->readAt(
360                    dataOffset,
361                    (uint8_t *)buffer->data() + buffer->range_length(),
362                    packetSize);
363
364            if (n < (ssize_t)packetSize) {
365                LOGE("failed to read %d bytes at 0x%08lx", packetSize, dataOffset);
366                return ERROR_IO;
367            }
368
369            buffer->set_range(0, fullSize);
370
371            mNextLaceIndex = i;
372
373            if (gotFullPacket) {
374                // We've just read the entire packet.
375
376                if (timeUs >= 0) {
377                    buffer->meta_data()->setInt64(kKeyTime, timeUs);
378                }
379
380                *out = buffer;
381
382                return OK;
383            }
384
385            // fall through, the buffer now contains the start of the packet.
386        }
387
388        CHECK_EQ(mNextLaceIndex, mCurrentPage.mNumSegments);
389
390        mOffset += mCurrentPageSize;
391        ssize_t n = readPage(mOffset, &mCurrentPage);
392
393        if (n <= 0) {
394            if (buffer) {
395                buffer->release();
396                buffer = NULL;
397            }
398
399            LOGE("readPage returned %ld", n);
400
401            return n < 0 ? n : (status_t)ERROR_END_OF_STREAM;
402        }
403
404        mCurrentPageSize = n;
405        mNextLaceIndex = 0;
406
407        if (buffer != NULL) {
408            if ((mCurrentPage.mFlags & 1) == 0) {
409                // This page does not continue the packet, i.e. the packet
410                // is already complete.
411
412                if (timeUs >= 0) {
413                    buffer->meta_data()->setInt64(kKeyTime, timeUs);
414                }
415
416                *out = buffer;
417
418                return OK;
419            }
420        }
421    }
422}
423
424void MyVorbisExtractor::init() {
425    vorbis_info_init(&mVi);
426
427    vorbis_comment mVc;
428
429    mMeta = new MetaData;
430    mMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_VORBIS);
431
432    MediaBuffer *packet;
433    CHECK_EQ(readNextPacket(&packet), OK);
434    LOGV("read packet of size %d\n", packet->range_length());
435    verifyHeader(packet, 1);
436    packet->release();
437    packet = NULL;
438
439    CHECK_EQ(readNextPacket(&packet), OK);
440    LOGV("read packet of size %d\n", packet->range_length());
441    verifyHeader(packet, 3);
442    packet->release();
443    packet = NULL;
444
445    CHECK_EQ(readNextPacket(&packet), OK);
446    LOGV("read packet of size %d\n", packet->range_length());
447    verifyHeader(packet, 5);
448    packet->release();
449    packet = NULL;
450
451    mFirstDataOffset = mOffset + mCurrentPageSize;
452}
453
454void MyVorbisExtractor::verifyHeader(
455        MediaBuffer *buffer, uint8_t type) {
456    const uint8_t *data =
457        (const uint8_t *)buffer->data() + buffer->range_offset();
458
459    size_t size = buffer->range_length();
460
461    CHECK(size >= 7);
462
463    CHECK_EQ(data[0], type);
464    CHECK(!memcmp(&data[1], "vorbis", 6));
465
466    ogg_buffer buf;
467    buf.data = (uint8_t *)data;
468    buf.size = size;
469    buf.refcount = 1;
470    buf.ptr.owner = NULL;
471
472    ogg_reference ref;
473    ref.buffer = &buf;
474    ref.begin = 0;
475    ref.length = size;
476    ref.next = NULL;
477
478    oggpack_buffer bits;
479    oggpack_readinit(&bits, &ref);
480
481    CHECK_EQ(oggpack_read(&bits, 8), type);
482    for (size_t i = 0; i < 6; ++i) {
483        oggpack_read(&bits, 8);  // skip 'vorbis'
484    }
485
486    switch (type) {
487        case 1:
488        {
489            CHECK_EQ(0, _vorbis_unpack_info(&mVi, &bits));
490
491            mMeta->setData(kKeyVorbisInfo, 0, data, size);
492            mMeta->setInt32(kKeySampleRate, mVi.rate);
493            mMeta->setInt32(kKeyChannelCount, mVi.channels);
494
495            LOGV("lower-bitrate = %ld", mVi.bitrate_lower);
496            LOGV("upper-bitrate = %ld", mVi.bitrate_upper);
497            LOGV("nominal-bitrate = %ld", mVi.bitrate_nominal);
498            LOGV("window-bitrate = %ld", mVi.bitrate_window);
499
500            off_t size;
501            if (mSource->getSize(&size) == OK) {
502                uint64_t bps = approxBitrate();
503
504                mMeta->setInt64(kKeyDuration, size * 8000000ll / bps);
505            }
506            break;
507        }
508
509        case 3:
510        {
511            CHECK_EQ(0, _vorbis_unpack_comment(&mVc, &bits));
512            break;
513        }
514
515        case 5:
516        {
517            CHECK_EQ(0, _vorbis_unpack_books(&mVi, &bits));
518
519            mMeta->setData(kKeyVorbisBooks, 0, data, size);
520            break;
521        }
522    }
523}
524
525uint64_t MyVorbisExtractor::approxBitrate() {
526    if (mVi.bitrate_nominal != 0) {
527        return mVi.bitrate_nominal;
528    }
529
530    return (mVi.bitrate_lower + mVi.bitrate_upper) / 2;
531}
532
533////////////////////////////////////////////////////////////////////////////////
534
535OggExtractor::OggExtractor(const sp<DataSource> &source)
536    : mDataSource(source),
537      mInitCheck(NO_INIT),
538      mImpl(NULL) {
539    mImpl = new MyVorbisExtractor(mDataSource);
540    CHECK_EQ(mImpl->seekToOffset(0), OK);
541    mImpl->init();
542
543    mInitCheck = OK;
544}
545
546OggExtractor::~OggExtractor() {
547    delete mImpl;
548    mImpl = NULL;
549}
550
551size_t OggExtractor::countTracks() {
552    return mInitCheck != OK ? 0 : 1;
553}
554
555sp<MediaSource> OggExtractor::getTrack(size_t index) {
556    if (index >= 1) {
557        return NULL;
558    }
559
560    return new OggSource(this);
561}
562
563sp<MetaData> OggExtractor::getTrackMetaData(
564        size_t index, uint32_t flags) {
565    if (index >= 1) {
566        return NULL;
567    }
568
569    return mImpl->getFormat();
570}
571
572sp<MetaData> OggExtractor::getMetaData() {
573    sp<MetaData> meta = new MetaData;
574
575    if (mInitCheck != OK) {
576        return meta;
577    }
578
579    meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_CONTAINER_OGG);
580
581    return meta;
582}
583
584bool SniffOgg(
585        const sp<DataSource> &source, String8 *mimeType, float *confidence) {
586    char tmp[4];
587    if (source->readAt(0, tmp, 4) < 4 || memcmp(tmp, "OggS", 4)) {
588        return false;
589    }
590
591    mimeType->setTo(MEDIA_MIMETYPE_CONTAINER_OGG);
592    *confidence = 0.2f;
593
594    return true;
595}
596
597}  // namespace android
598