OggExtractor.cpp revision abd1f4f870925d6776dbe4b930b759a1ab6595ca
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    status_t init();
80
81    sp<MetaData> getFileMetaData() { return mFileMeta; }
82
83private:
84    struct Page {
85        uint64_t mGranulePosition;
86        uint32_t mSerialNo;
87        uint32_t mPageNo;
88        uint8_t mFlags;
89        uint8_t mNumSegments;
90        uint8_t mLace[255];
91    };
92
93    sp<DataSource> mSource;
94    off_t mOffset;
95    Page mCurrentPage;
96    size_t mCurrentPageSize;
97    size_t mNextLaceIndex;
98
99    off_t mFirstDataOffset;
100
101    vorbis_info mVi;
102    vorbis_comment mVc;
103
104    sp<MetaData> mMeta;
105    sp<MetaData> mFileMeta;
106
107    ssize_t readPage(off_t offset, Page *page);
108    status_t findNextPage(off_t startOffset, off_t *pageOffset);
109
110    status_t verifyHeader(
111            MediaBuffer *buffer, uint8_t type);
112
113    void parseFileMetaData();
114    void extractAlbumArt(const void *data, size_t size);
115
116    MyVorbisExtractor(const MyVorbisExtractor &);
117    MyVorbisExtractor &operator=(const MyVorbisExtractor &);
118};
119
120////////////////////////////////////////////////////////////////////////////////
121
122OggSource::OggSource(const sp<OggExtractor> &extractor)
123    : mExtractor(extractor),
124      mStarted(false) {
125}
126
127OggSource::~OggSource() {
128    if (mStarted) {
129        stop();
130    }
131}
132
133sp<MetaData> OggSource::getFormat() {
134    return mExtractor->mImpl->getFormat();
135}
136
137status_t OggSource::start(MetaData *params) {
138    if (mStarted) {
139        return INVALID_OPERATION;
140    }
141
142    mStarted = true;
143
144    return OK;
145}
146
147status_t OggSource::stop() {
148    mStarted = false;
149
150    return OK;
151}
152
153status_t OggSource::read(
154        MediaBuffer **out, const ReadOptions *options) {
155    *out = NULL;
156
157    int64_t seekTimeUs;
158    ReadOptions::SeekMode mode;
159    if (options && options->getSeekTo(&seekTimeUs, &mode)) {
160        off_t pos = seekTimeUs * mExtractor->mImpl->approxBitrate() / 8000000ll;
161        LOGI("seeking to offset %ld", pos);
162
163        if (mExtractor->mImpl->seekToOffset(pos) != OK) {
164            return ERROR_END_OF_STREAM;
165        }
166    }
167
168    MediaBuffer *packet;
169    status_t err = mExtractor->mImpl->readNextPacket(&packet);
170
171    if (err != OK) {
172        return err;
173    }
174
175#if 0
176    int64_t timeUs;
177    if (packet->meta_data()->findInt64(kKeyTime, &timeUs)) {
178        LOGI("found time = %lld us", timeUs);
179    } else {
180        LOGI("NO time");
181    }
182#endif
183
184    *out = packet;
185
186    return OK;
187}
188
189////////////////////////////////////////////////////////////////////////////////
190
191MyVorbisExtractor::MyVorbisExtractor(const sp<DataSource> &source)
192    : mSource(source),
193      mOffset(0),
194      mCurrentPageSize(0),
195      mNextLaceIndex(0),
196      mFirstDataOffset(-1) {
197    mCurrentPage.mNumSegments = 0;
198
199    vorbis_info_init(&mVi);
200    vorbis_comment_init(&mVc);
201}
202
203MyVorbisExtractor::~MyVorbisExtractor() {
204    vorbis_comment_clear(&mVc);
205    vorbis_info_clear(&mVi);
206}
207
208sp<MetaData> MyVorbisExtractor::getFormat() const {
209    return mMeta;
210}
211
212status_t MyVorbisExtractor::findNextPage(
213        off_t startOffset, off_t *pageOffset) {
214    *pageOffset = startOffset;
215
216    for (;;) {
217        char signature[4];
218        ssize_t n = mSource->readAt(*pageOffset, &signature, 4);
219
220        if (n < 4) {
221            *pageOffset = 0;
222
223            return (n < 0) ? n : (status_t)ERROR_END_OF_STREAM;
224        }
225
226        if (!memcmp(signature, "OggS", 4)) {
227            if (*pageOffset > startOffset) {
228                LOGV("skipped %ld bytes of junk to reach next frame",
229                     *pageOffset - startOffset);
230            }
231
232            return OK;
233        }
234
235        ++*pageOffset;
236    }
237}
238
239status_t MyVorbisExtractor::seekToOffset(off_t offset) {
240    if (mFirstDataOffset >= 0 && offset < mFirstDataOffset) {
241        // Once we know where the actual audio data starts (past the headers)
242        // don't ever seek to anywhere before that.
243        offset = mFirstDataOffset;
244    }
245
246    off_t pageOffset;
247    status_t err = findNextPage(offset, &pageOffset);
248
249    if (err != OK) {
250        return err;
251    }
252
253    mOffset = pageOffset;
254
255    mCurrentPageSize = 0;
256    mCurrentPage.mNumSegments = 0;
257    mNextLaceIndex = 0;
258
259    // XXX what if new page continues packet from last???
260
261    return OK;
262}
263
264ssize_t MyVorbisExtractor::readPage(off_t offset, Page *page) {
265    uint8_t header[27];
266    if (mSource->readAt(offset, header, sizeof(header))
267            < (ssize_t)sizeof(header)) {
268        LOGE("failed to read %d bytes at offset 0x%08lx", sizeof(header), offset);
269
270        return ERROR_IO;
271    }
272
273    if (memcmp(header, "OggS", 4)) {
274        return ERROR_MALFORMED;
275    }
276
277    if (header[4] != 0) {
278        // Wrong version.
279
280        return ERROR_UNSUPPORTED;
281    }
282
283    page->mFlags = header[5];
284
285    if (page->mFlags & ~7) {
286        // Only bits 0-2 are defined in version 0.
287        return ERROR_MALFORMED;
288    }
289
290    page->mGranulePosition = U64LE_AT(&header[6]);
291
292#if 0
293    printf("granulePosition = %llu (0x%llx)\n",
294           page->mGranulePosition, page->mGranulePosition);
295#endif
296
297    page->mSerialNo = U32LE_AT(&header[14]);
298    page->mPageNo = U32LE_AT(&header[18]);
299
300    page->mNumSegments = header[26];
301    if (mSource->readAt(
302                offset + sizeof(header), page->mLace, page->mNumSegments)
303            < (ssize_t)page->mNumSegments) {
304        return ERROR_IO;
305    }
306
307    size_t totalSize = 0;;
308    for (size_t i = 0; i < page->mNumSegments; ++i) {
309        totalSize += page->mLace[i];
310    }
311
312#if 0
313    String8 tmp;
314    for (size_t i = 0; i < page->mNumSegments; ++i) {
315        char x[32];
316        sprintf(x, "%s%u", i > 0 ? ", " : "", (unsigned)page->mLace[i]);
317
318        tmp.append(x);
319    }
320
321    LOGV("%c %s", page->mFlags & 1 ? '+' : ' ', tmp.string());
322#endif
323
324    return sizeof(header) + page->mNumSegments + totalSize;
325}
326
327status_t MyVorbisExtractor::readNextPacket(MediaBuffer **out) {
328    *out = NULL;
329
330    MediaBuffer *buffer = NULL;
331    int64_t timeUs = -1;
332
333    for (;;) {
334        size_t i;
335        size_t packetSize = 0;
336        bool gotFullPacket = false;
337        for (i = mNextLaceIndex; i < mCurrentPage.mNumSegments; ++i) {
338            uint8_t lace = mCurrentPage.mLace[i];
339
340            packetSize += lace;
341
342            if (lace < 255) {
343                gotFullPacket = true;
344                ++i;
345                break;
346            }
347        }
348
349        if (mNextLaceIndex < mCurrentPage.mNumSegments) {
350            off_t dataOffset = mOffset + 27 + mCurrentPage.mNumSegments;
351            for (size_t j = 0; j < mNextLaceIndex; ++j) {
352                dataOffset += mCurrentPage.mLace[j];
353            }
354
355            size_t fullSize = packetSize;
356            if (buffer != NULL) {
357                fullSize += buffer->range_length();
358            }
359            MediaBuffer *tmp = new MediaBuffer(fullSize);
360            if (buffer != NULL) {
361                memcpy(tmp->data(), buffer->data(), buffer->range_length());
362                tmp->set_range(0, buffer->range_length());
363                buffer->release();
364            } else {
365                // XXX Not only is this not technically the correct time for
366                // this packet, we also stamp every packet in this page
367                // with the same time. This needs fixing later.
368                timeUs = mCurrentPage.mGranulePosition * 1000000ll / mVi.rate;
369                tmp->set_range(0, 0);
370            }
371            buffer = tmp;
372
373            ssize_t n = mSource->readAt(
374                    dataOffset,
375                    (uint8_t *)buffer->data() + buffer->range_length(),
376                    packetSize);
377
378            if (n < (ssize_t)packetSize) {
379                LOGE("failed to read %d bytes at 0x%08lx", packetSize, dataOffset);
380                return ERROR_IO;
381            }
382
383            buffer->set_range(0, fullSize);
384
385            mNextLaceIndex = i;
386
387            if (gotFullPacket) {
388                // We've just read the entire packet.
389
390                if (timeUs >= 0) {
391                    buffer->meta_data()->setInt64(kKeyTime, timeUs);
392                }
393
394                *out = buffer;
395
396                return OK;
397            }
398
399            // fall through, the buffer now contains the start of the packet.
400        }
401
402        CHECK_EQ(mNextLaceIndex, mCurrentPage.mNumSegments);
403
404        mOffset += mCurrentPageSize;
405        ssize_t n = readPage(mOffset, &mCurrentPage);
406
407        if (n <= 0) {
408            if (buffer) {
409                buffer->release();
410                buffer = NULL;
411            }
412
413            LOGE("readPage returned %ld", n);
414
415            return n < 0 ? n : (status_t)ERROR_END_OF_STREAM;
416        }
417
418        mCurrentPageSize = n;
419        mNextLaceIndex = 0;
420
421        if (buffer != NULL) {
422            if ((mCurrentPage.mFlags & 1) == 0) {
423                // This page does not continue the packet, i.e. the packet
424                // is already complete.
425
426                if (timeUs >= 0) {
427                    buffer->meta_data()->setInt64(kKeyTime, timeUs);
428                }
429
430                *out = buffer;
431
432                return OK;
433            }
434        }
435    }
436}
437
438status_t MyVorbisExtractor::init() {
439    mMeta = new MetaData;
440    mMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_VORBIS);
441
442    MediaBuffer *packet;
443    status_t err;
444    if ((err = readNextPacket(&packet)) != OK) {
445        return err;
446    }
447    LOGV("read packet of size %d\n", packet->range_length());
448    err = verifyHeader(packet, 1);
449    packet->release();
450    packet = NULL;
451    if (err != OK) {
452        return err;
453    }
454
455    if ((err = readNextPacket(&packet)) != OK) {
456        return err;
457    }
458    LOGV("read packet of size %d\n", packet->range_length());
459    err = verifyHeader(packet, 3);
460    packet->release();
461    packet = NULL;
462    if (err != OK) {
463        return err;
464    }
465
466    if ((err = readNextPacket(&packet)) != OK) {
467        return err;
468    }
469    LOGV("read packet of size %d\n", packet->range_length());
470    err = verifyHeader(packet, 5);
471    packet->release();
472    packet = NULL;
473    if (err != OK) {
474        return err;
475    }
476
477    mFirstDataOffset = mOffset + mCurrentPageSize;
478
479    return OK;
480}
481
482status_t MyVorbisExtractor::verifyHeader(
483        MediaBuffer *buffer, uint8_t type) {
484    const uint8_t *data =
485        (const uint8_t *)buffer->data() + buffer->range_offset();
486
487    size_t size = buffer->range_length();
488
489    if (size < 7 || data[0] != type || memcmp(&data[1], "vorbis", 6)) {
490        return ERROR_MALFORMED;
491    }
492
493    ogg_buffer buf;
494    buf.data = (uint8_t *)data;
495    buf.size = size;
496    buf.refcount = 1;
497    buf.ptr.owner = NULL;
498
499    ogg_reference ref;
500    ref.buffer = &buf;
501    ref.begin = 0;
502    ref.length = size;
503    ref.next = NULL;
504
505    oggpack_buffer bits;
506    oggpack_readinit(&bits, &ref);
507
508    CHECK_EQ(oggpack_read(&bits, 8), type);
509    for (size_t i = 0; i < 6; ++i) {
510        oggpack_read(&bits, 8);  // skip 'vorbis'
511    }
512
513    switch (type) {
514        case 1:
515        {
516            CHECK_EQ(0, _vorbis_unpack_info(&mVi, &bits));
517
518            mMeta->setData(kKeyVorbisInfo, 0, data, size);
519            mMeta->setInt32(kKeySampleRate, mVi.rate);
520            mMeta->setInt32(kKeyChannelCount, mVi.channels);
521
522            LOGV("lower-bitrate = %ld", mVi.bitrate_lower);
523            LOGV("upper-bitrate = %ld", mVi.bitrate_upper);
524            LOGV("nominal-bitrate = %ld", mVi.bitrate_nominal);
525            LOGV("window-bitrate = %ld", mVi.bitrate_window);
526
527            off_t size;
528            if (mSource->getSize(&size) == OK) {
529                uint64_t bps = approxBitrate();
530
531                mMeta->setInt64(kKeyDuration, size * 8000000ll / bps);
532            }
533            break;
534        }
535
536        case 3:
537        {
538            if (0 != _vorbis_unpack_comment(&mVc, &bits)) {
539                return ERROR_MALFORMED;
540            }
541
542            parseFileMetaData();
543            break;
544        }
545
546        case 5:
547        {
548            if (0 != _vorbis_unpack_books(&mVi, &bits)) {
549                return ERROR_MALFORMED;
550            }
551
552            mMeta->setData(kKeyVorbisBooks, 0, data, size);
553            break;
554        }
555    }
556
557    return OK;
558}
559
560uint64_t MyVorbisExtractor::approxBitrate() {
561    if (mVi.bitrate_nominal != 0) {
562        return mVi.bitrate_nominal;
563    }
564
565    return (mVi.bitrate_lower + mVi.bitrate_upper) / 2;
566}
567
568void MyVorbisExtractor::parseFileMetaData() {
569    mFileMeta = new MetaData;
570    mFileMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_CONTAINER_OGG);
571
572    struct {
573        const char *const mTag;
574        uint32_t mKey;
575    } kMap[] = {
576        { "TITLE", kKeyTitle },
577        { "ARTIST", kKeyArtist },
578        { "ALBUM", kKeyAlbum },
579        { "COMPOSER", kKeyComposer },
580        { "GENRE", kKeyGenre },
581        { "AUTHOR", kKeyAuthor },
582        { "TRACKNUMBER", kKeyCDTrackNumber },
583        { "DISCNUMBER", kKeyDiscNumber },
584        { "DATE", kKeyDate },
585        { "LYRICIST", kKeyWriter },
586        { "METADATA_BLOCK_PICTURE", kKeyAlbumArt },
587    };
588
589    for (int i = 0; i < mVc.comments; ++i) {
590        const char *comment = mVc.user_comments[i];
591
592        for (size_t j = 0; j < sizeof(kMap) / sizeof(kMap[0]); ++j) {
593            size_t tagLen = strlen(kMap[j].mTag);
594            if (!strncasecmp(kMap[j].mTag, comment, tagLen)
595                    && comment[tagLen] == '=') {
596                if (kMap[j].mKey == kKeyAlbumArt) {
597                    extractAlbumArt(
598                            &comment[tagLen + 1],
599                            mVc.comment_lengths[i] - tagLen - 1);
600                } else {
601                    mFileMeta->setCString(kMap[j].mKey, &comment[tagLen + 1]);
602                }
603            }
604        }
605
606    }
607
608#if 0
609    for (int i = 0; i < mVc.comments; ++i) {
610        LOGI("comment #%d: '%s'", i + 1, mVc.user_comments[i]);
611    }
612#endif
613}
614
615// The returned buffer should be free()d.
616static uint8_t *DecodeBase64(const char *s, size_t size, size_t *outSize) {
617    *outSize = 0;
618
619    if ((size % 4) != 0) {
620        return NULL;
621    }
622
623    size_t n = size;
624    size_t padding = 0;
625    if (n >= 1 && s[n - 1] == '=') {
626        padding = 1;
627
628        if (n >= 2 && s[n - 2] == '=') {
629            padding = 2;
630        }
631    }
632
633    size_t outLen = 3 * size / 4 - padding;
634
635    *outSize = outLen;
636
637    void *buffer = malloc(outLen);
638
639    uint8_t *out = (uint8_t *)buffer;
640    size_t j = 0;
641    uint32_t accum = 0;
642    for (size_t i = 0; i < n; ++i) {
643        char c = s[i];
644        unsigned value;
645        if (c >= 'A' && c <= 'Z') {
646            value = c - 'A';
647        } else if (c >= 'a' && c <= 'z') {
648            value = 26 + c - 'a';
649        } else if (c >= '0' && c <= '9') {
650            value = 52 + c - '0';
651        } else if (c == '+') {
652            value = 62;
653        } else if (c == '/') {
654            value = 63;
655        } else if (c != '=') {
656            return NULL;
657        } else {
658            if (i < n - padding) {
659                return NULL;
660            }
661
662            value = 0;
663        }
664
665        accum = (accum << 6) | value;
666
667        if (((i + 1) % 4) == 0) {
668            out[j++] = (accum >> 16);
669
670            if (j < outLen) { out[j++] = (accum >> 8) & 0xff; }
671            if (j < outLen) { out[j++] = accum & 0xff; }
672
673            accum = 0;
674        }
675    }
676
677    return (uint8_t *)buffer;
678}
679
680void MyVorbisExtractor::extractAlbumArt(const void *data, size_t size) {
681    LOGV("extractAlbumArt from '%s'", (const char *)data);
682
683    size_t flacSize;
684    uint8_t *flac = DecodeBase64((const char *)data, size, &flacSize);
685
686    if (flac == NULL) {
687        LOGE("malformed base64 encoded data.");
688        return;
689    }
690
691    LOGV("got flac of size %d", flacSize);
692
693    uint32_t picType;
694    uint32_t typeLen;
695    uint32_t descLen;
696    uint32_t dataLen;
697    char type[128];
698
699    if (flacSize < 8) {
700        goto exit;
701    }
702
703    picType = U32_AT(flac);
704
705    if (picType != 3) {
706        // This is not a front cover.
707        goto exit;
708    }
709
710    typeLen = U32_AT(&flac[4]);
711    if (typeLen + 1 > sizeof(type)) {
712        goto exit;
713    }
714
715    if (flacSize < 8 + typeLen) {
716        goto exit;
717    }
718
719    memcpy(type, &flac[8], typeLen);
720    type[typeLen] = '\0';
721
722    LOGV("picType = %d, type = '%s'", picType, type);
723
724    if (!strcmp(type, "-->")) {
725        // This is not inline cover art, but an external url instead.
726        goto exit;
727    }
728
729    descLen = U32_AT(&flac[8 + typeLen]);
730
731    if (flacSize < 32 + typeLen + descLen) {
732        goto exit;
733    }
734
735    dataLen = U32_AT(&flac[8 + typeLen + 4 + descLen + 16]);
736
737    if (flacSize < 32 + typeLen + descLen + dataLen) {
738        goto exit;
739    }
740
741    LOGV("got image data, %d trailing bytes",
742         flacSize - 32 - typeLen - descLen - dataLen);
743
744    mFileMeta->setData(
745            kKeyAlbumArt, 0, &flac[8 + typeLen + 4 + descLen + 20], dataLen);
746
747    mFileMeta->setCString(kKeyAlbumArtMIME, type);
748
749exit:
750    free(flac);
751    flac = NULL;
752}
753
754////////////////////////////////////////////////////////////////////////////////
755
756OggExtractor::OggExtractor(const sp<DataSource> &source)
757    : mDataSource(source),
758      mInitCheck(NO_INIT),
759      mImpl(NULL) {
760    mImpl = new MyVorbisExtractor(mDataSource);
761    mInitCheck = mImpl->seekToOffset(0);
762
763    if (mInitCheck == OK) {
764        mInitCheck = mImpl->init();
765    }
766}
767
768OggExtractor::~OggExtractor() {
769    delete mImpl;
770    mImpl = NULL;
771}
772
773size_t OggExtractor::countTracks() {
774    return mInitCheck != OK ? 0 : 1;
775}
776
777sp<MediaSource> OggExtractor::getTrack(size_t index) {
778    if (index >= 1) {
779        return NULL;
780    }
781
782    return new OggSource(this);
783}
784
785sp<MetaData> OggExtractor::getTrackMetaData(
786        size_t index, uint32_t flags) {
787    if (index >= 1) {
788        return NULL;
789    }
790
791    return mImpl->getFormat();
792}
793
794sp<MetaData> OggExtractor::getMetaData() {
795    return mImpl->getFileMetaData();
796}
797
798bool SniffOgg(
799        const sp<DataSource> &source, String8 *mimeType, float *confidence) {
800    char tmp[4];
801    if (source->readAt(0, tmp, 4) < 4 || memcmp(tmp, "OggS", 4)) {
802        return false;
803    }
804
805    mimeType->setTo(MEDIA_MIMETYPE_CONTAINER_OGG);
806    *confidence = 0.2f;
807
808    return true;
809}
810
811}  // namespace android
812