OggExtractor.cpp revision 3fd91baee812919f53a85c5c05f32606313f8334
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 seekToTime(int64_t timeUs);
77    status_t seekToOffset(off64_t offset);
78    status_t readNextPacket(MediaBuffer **buffer);
79
80    status_t init();
81
82    sp<MetaData> getFileMetaData() { return mFileMeta; }
83
84private:
85    struct Page {
86        uint64_t mGranulePosition;
87        uint32_t mSerialNo;
88        uint32_t mPageNo;
89        uint8_t mFlags;
90        uint8_t mNumSegments;
91        uint8_t mLace[255];
92    };
93
94    struct TOCEntry {
95        off64_t mPageOffset;
96        int64_t mTimeUs;
97    };
98
99    sp<DataSource> mSource;
100    off64_t mOffset;
101    Page mCurrentPage;
102    uint64_t mPrevGranulePosition;
103    size_t mCurrentPageSize;
104    bool mFirstPacketInPage;
105    uint64_t mCurrentPageSamples;
106    size_t mNextLaceIndex;
107
108    off64_t mFirstDataOffset;
109
110    vorbis_info mVi;
111    vorbis_comment mVc;
112
113    sp<MetaData> mMeta;
114    sp<MetaData> mFileMeta;
115
116    Vector<TOCEntry> mTableOfContents;
117
118    ssize_t readPage(off64_t offset, Page *page);
119    status_t findNextPage(off64_t startOffset, off64_t *pageOffset);
120
121    status_t verifyHeader(
122            MediaBuffer *buffer, uint8_t type);
123
124    void parseFileMetaData();
125
126    status_t findPrevGranulePosition(off64_t pageOffset, uint64_t *granulePos);
127
128    void buildTableOfContents();
129
130    MyVorbisExtractor(const MyVorbisExtractor &);
131    MyVorbisExtractor &operator=(const MyVorbisExtractor &);
132};
133
134static void extractAlbumArt(
135        const sp<MetaData> &fileMeta, const void *data, size_t size);
136
137////////////////////////////////////////////////////////////////////////////////
138
139OggSource::OggSource(const sp<OggExtractor> &extractor)
140    : mExtractor(extractor),
141      mStarted(false) {
142}
143
144OggSource::~OggSource() {
145    if (mStarted) {
146        stop();
147    }
148}
149
150sp<MetaData> OggSource::getFormat() {
151    return mExtractor->mImpl->getFormat();
152}
153
154status_t OggSource::start(MetaData *params) {
155    if (mStarted) {
156        return INVALID_OPERATION;
157    }
158
159    mStarted = true;
160
161    return OK;
162}
163
164status_t OggSource::stop() {
165    mStarted = false;
166
167    return OK;
168}
169
170status_t OggSource::read(
171        MediaBuffer **out, const ReadOptions *options) {
172    *out = NULL;
173
174    int64_t seekTimeUs;
175    ReadOptions::SeekMode mode;
176    if (options && options->getSeekTo(&seekTimeUs, &mode)) {
177        if (mExtractor->mImpl->seekToTime(seekTimeUs) != OK) {
178            return ERROR_END_OF_STREAM;
179        }
180    }
181
182    MediaBuffer *packet;
183    status_t err = mExtractor->mImpl->readNextPacket(&packet);
184
185    if (err != OK) {
186        return err;
187    }
188
189#if 0
190    int64_t timeUs;
191    if (packet->meta_data()->findInt64(kKeyTime, &timeUs)) {
192        LOGI("found time = %lld us", timeUs);
193    } else {
194        LOGI("NO time");
195    }
196#endif
197
198    packet->meta_data()->setInt32(kKeyIsSyncFrame, 1);
199
200    *out = packet;
201
202    return OK;
203}
204
205////////////////////////////////////////////////////////////////////////////////
206
207MyVorbisExtractor::MyVorbisExtractor(const sp<DataSource> &source)
208    : mSource(source),
209      mOffset(0),
210      mPrevGranulePosition(0),
211      mCurrentPageSize(0),
212      mFirstPacketInPage(true),
213      mCurrentPageSamples(0),
214      mNextLaceIndex(0),
215      mFirstDataOffset(-1) {
216    mCurrentPage.mNumSegments = 0;
217
218    vorbis_info_init(&mVi);
219    vorbis_comment_init(&mVc);
220}
221
222MyVorbisExtractor::~MyVorbisExtractor() {
223    vorbis_comment_clear(&mVc);
224    vorbis_info_clear(&mVi);
225}
226
227sp<MetaData> MyVorbisExtractor::getFormat() const {
228    return mMeta;
229}
230
231status_t MyVorbisExtractor::findNextPage(
232        off64_t startOffset, off64_t *pageOffset) {
233    *pageOffset = startOffset;
234
235    for (;;) {
236        char signature[4];
237        ssize_t n = mSource->readAt(*pageOffset, &signature, 4);
238
239        if (n < 4) {
240            *pageOffset = 0;
241
242            return (n < 0) ? n : (status_t)ERROR_END_OF_STREAM;
243        }
244
245        if (!memcmp(signature, "OggS", 4)) {
246            if (*pageOffset > startOffset) {
247                LOGV("skipped %lld bytes of junk to reach next frame",
248                     *pageOffset - startOffset);
249            }
250
251            return OK;
252        }
253
254        ++*pageOffset;
255    }
256}
257
258// Given the offset of the "current" page, find the page immediately preceding
259// it (if any) and return its granule position.
260// To do this we back up from the "current" page's offset until we find any
261// page preceding it and then scan forward to just before the current page.
262status_t MyVorbisExtractor::findPrevGranulePosition(
263        off64_t pageOffset, uint64_t *granulePos) {
264    *granulePos = 0;
265
266    off64_t prevPageOffset = 0;
267    off64_t prevGuess = pageOffset;
268    for (;;) {
269        if (prevGuess >= 5000) {
270            prevGuess -= 5000;
271        } else {
272            prevGuess = 0;
273        }
274
275        LOGV("backing up %lld bytes", pageOffset - prevGuess);
276
277        status_t err = findNextPage(prevGuess, &prevPageOffset);
278        if (err != OK) {
279            return err;
280        }
281
282        if (prevPageOffset < pageOffset || prevGuess == 0) {
283            break;
284        }
285    }
286
287    if (prevPageOffset == pageOffset) {
288        // We did not find a page preceding this one.
289        return UNKNOWN_ERROR;
290    }
291
292    LOGV("prevPageOffset at %lld, pageOffset at %lld",
293         prevPageOffset, pageOffset);
294
295    for (;;) {
296        Page prevPage;
297        ssize_t n = readPage(prevPageOffset, &prevPage);
298
299        if (n <= 0) {
300            return (status_t)n;
301        }
302
303        prevPageOffset += n;
304
305        if (prevPageOffset == pageOffset) {
306            *granulePos = prevPage.mGranulePosition;
307            return OK;
308        }
309    }
310}
311
312status_t MyVorbisExtractor::seekToTime(int64_t timeUs) {
313    if (mTableOfContents.isEmpty()) {
314        // Perform approximate seeking based on avg. bitrate.
315
316        off64_t pos = timeUs * approxBitrate() / 8000000ll;
317
318        LOGV("seeking to offset %lld", pos);
319        return seekToOffset(pos);
320    }
321
322    size_t left = 0;
323    size_t right = mTableOfContents.size();
324    while (left < right) {
325        size_t center = left / 2 + right / 2 + (left & right & 1);
326
327        const TOCEntry &entry = mTableOfContents.itemAt(center);
328
329        if (timeUs < entry.mTimeUs) {
330            right = center;
331        } else if (timeUs > entry.mTimeUs) {
332            left = center + 1;
333        } else {
334            left = right = center;
335            break;
336        }
337    }
338
339    const TOCEntry &entry = mTableOfContents.itemAt(left);
340
341    LOGV("seeking to entry %d / %d at offset %lld",
342         left, mTableOfContents.size(), entry.mPageOffset);
343
344    return seekToOffset(entry.mPageOffset);
345}
346
347status_t MyVorbisExtractor::seekToOffset(off64_t offset) {
348    if (mFirstDataOffset >= 0 && offset < mFirstDataOffset) {
349        // Once we know where the actual audio data starts (past the headers)
350        // don't ever seek to anywhere before that.
351        offset = mFirstDataOffset;
352    }
353
354    off64_t pageOffset;
355    status_t err = findNextPage(offset, &pageOffset);
356
357    if (err != OK) {
358        return err;
359    }
360
361    // We found the page we wanted to seek to, but we'll also need
362    // the page preceding it to determine how many valid samples are on
363    // this page.
364    findPrevGranulePosition(pageOffset, &mPrevGranulePosition);
365
366    mOffset = pageOffset;
367
368    mCurrentPageSize = 0;
369    mFirstPacketInPage = true;
370    mCurrentPageSamples = 0;
371    mCurrentPage.mNumSegments = 0;
372    mNextLaceIndex = 0;
373
374    // XXX what if new page continues packet from last???
375
376    return OK;
377}
378
379ssize_t MyVorbisExtractor::readPage(off64_t offset, Page *page) {
380    uint8_t header[27];
381    if (mSource->readAt(offset, header, sizeof(header))
382            < (ssize_t)sizeof(header)) {
383        LOGV("failed to read %d bytes at offset 0x%016llx",
384             sizeof(header), offset);
385
386        return ERROR_IO;
387    }
388
389    if (memcmp(header, "OggS", 4)) {
390        return ERROR_MALFORMED;
391    }
392
393    if (header[4] != 0) {
394        // Wrong version.
395
396        return ERROR_UNSUPPORTED;
397    }
398
399    page->mFlags = header[5];
400
401    if (page->mFlags & ~7) {
402        // Only bits 0-2 are defined in version 0.
403        return ERROR_MALFORMED;
404    }
405
406    page->mGranulePosition = U64LE_AT(&header[6]);
407
408#if 0
409    printf("granulePosition = %llu (0x%llx)\n",
410           page->mGranulePosition, page->mGranulePosition);
411#endif
412
413    page->mSerialNo = U32LE_AT(&header[14]);
414    page->mPageNo = U32LE_AT(&header[18]);
415
416    page->mNumSegments = header[26];
417    if (mSource->readAt(
418                offset + sizeof(header), page->mLace, page->mNumSegments)
419            < (ssize_t)page->mNumSegments) {
420        return ERROR_IO;
421    }
422
423    size_t totalSize = 0;;
424    for (size_t i = 0; i < page->mNumSegments; ++i) {
425        totalSize += page->mLace[i];
426    }
427
428#if 0
429    String8 tmp;
430    for (size_t i = 0; i < page->mNumSegments; ++i) {
431        char x[32];
432        sprintf(x, "%s%u", i > 0 ? ", " : "", (unsigned)page->mLace[i]);
433
434        tmp.append(x);
435    }
436
437    LOGV("%c %s", page->mFlags & 1 ? '+' : ' ', tmp.string());
438#endif
439
440    return sizeof(header) + page->mNumSegments + totalSize;
441}
442
443status_t MyVorbisExtractor::readNextPacket(MediaBuffer **out) {
444    *out = NULL;
445
446    MediaBuffer *buffer = NULL;
447    int64_t timeUs = -1;
448
449    for (;;) {
450        size_t i;
451        size_t packetSize = 0;
452        bool gotFullPacket = false;
453        for (i = mNextLaceIndex; i < mCurrentPage.mNumSegments; ++i) {
454            uint8_t lace = mCurrentPage.mLace[i];
455
456            packetSize += lace;
457
458            if (lace < 255) {
459                gotFullPacket = true;
460                ++i;
461                break;
462            }
463        }
464
465        if (mNextLaceIndex < mCurrentPage.mNumSegments) {
466            off64_t dataOffset = mOffset + 27 + mCurrentPage.mNumSegments;
467            for (size_t j = 0; j < mNextLaceIndex; ++j) {
468                dataOffset += mCurrentPage.mLace[j];
469            }
470
471            size_t fullSize = packetSize;
472            if (buffer != NULL) {
473                fullSize += buffer->range_length();
474            }
475            MediaBuffer *tmp = new MediaBuffer(fullSize);
476            if (buffer != NULL) {
477                memcpy(tmp->data(), buffer->data(), buffer->range_length());
478                tmp->set_range(0, buffer->range_length());
479                buffer->release();
480            } else {
481                // XXX Not only is this not technically the correct time for
482                // this packet, we also stamp every packet in this page
483                // with the same time. This needs fixing later.
484
485                if (mVi.rate) {
486                    // Rate may not have been initialized yet if we're currently
487                    // reading the configuration packets...
488                    // Fortunately, the timestamp doesn't matter for those.
489                    timeUs = mCurrentPage.mGranulePosition * 1000000ll / mVi.rate;
490                }
491                tmp->set_range(0, 0);
492            }
493            buffer = tmp;
494
495            ssize_t n = mSource->readAt(
496                    dataOffset,
497                    (uint8_t *)buffer->data() + buffer->range_length(),
498                    packetSize);
499
500            if (n < (ssize_t)packetSize) {
501                LOGV("failed to read %d bytes at 0x%016llx",
502                     packetSize, dataOffset);
503                return ERROR_IO;
504            }
505
506            buffer->set_range(0, fullSize);
507
508            mNextLaceIndex = i;
509
510            if (gotFullPacket) {
511                // We've just read the entire packet.
512
513                if (timeUs >= 0) {
514                    buffer->meta_data()->setInt64(kKeyTime, timeUs);
515                }
516
517                if (mFirstPacketInPage) {
518                    buffer->meta_data()->setInt32(
519                            kKeyValidSamples, mCurrentPageSamples);
520                    mFirstPacketInPage = false;
521                }
522
523                *out = buffer;
524
525                return OK;
526            }
527
528            // fall through, the buffer now contains the start of the packet.
529        }
530
531        CHECK_EQ(mNextLaceIndex, mCurrentPage.mNumSegments);
532
533        mOffset += mCurrentPageSize;
534        ssize_t n = readPage(mOffset, &mCurrentPage);
535
536        if (n <= 0) {
537            if (buffer) {
538                buffer->release();
539                buffer = NULL;
540            }
541
542            LOGV("readPage returned %ld", n);
543
544            return n < 0 ? n : (status_t)ERROR_END_OF_STREAM;
545        }
546
547        mCurrentPageSamples =
548            mCurrentPage.mGranulePosition - mPrevGranulePosition;
549        mFirstPacketInPage = true;
550
551        mPrevGranulePosition = mCurrentPage.mGranulePosition;
552
553        mCurrentPageSize = n;
554        mNextLaceIndex = 0;
555
556        if (buffer != NULL) {
557            if ((mCurrentPage.mFlags & 1) == 0) {
558                // This page does not continue the packet, i.e. the packet
559                // is already complete.
560
561                if (timeUs >= 0) {
562                    buffer->meta_data()->setInt64(kKeyTime, timeUs);
563                }
564
565                buffer->meta_data()->setInt32(
566                        kKeyValidSamples, mCurrentPageSamples);
567                mFirstPacketInPage = false;
568
569                *out = buffer;
570
571                return OK;
572            }
573        }
574    }
575}
576
577status_t MyVorbisExtractor::init() {
578    mMeta = new MetaData;
579    mMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_VORBIS);
580
581    MediaBuffer *packet;
582    status_t err;
583    if ((err = readNextPacket(&packet)) != OK) {
584        return err;
585    }
586    LOGV("read packet of size %d\n", packet->range_length());
587    err = verifyHeader(packet, 1);
588    packet->release();
589    packet = NULL;
590    if (err != OK) {
591        return err;
592    }
593
594    if ((err = readNextPacket(&packet)) != OK) {
595        return err;
596    }
597    LOGV("read packet of size %d\n", packet->range_length());
598    err = verifyHeader(packet, 3);
599    packet->release();
600    packet = NULL;
601    if (err != OK) {
602        return err;
603    }
604
605    if ((err = readNextPacket(&packet)) != OK) {
606        return err;
607    }
608    LOGV("read packet of size %d\n", packet->range_length());
609    err = verifyHeader(packet, 5);
610    packet->release();
611    packet = NULL;
612    if (err != OK) {
613        return err;
614    }
615
616    mFirstDataOffset = mOffset + mCurrentPageSize;
617
618    off64_t size;
619    uint64_t lastGranulePosition;
620    if (!(mSource->flags() & DataSource::kIsCachingDataSource)
621            && mSource->getSize(&size) == OK
622            && findPrevGranulePosition(size, &lastGranulePosition) == OK) {
623        // Let's assume it's cheap to seek to the end.
624        // The granule position of the final page in the stream will
625        // give us the exact duration of the content, something that
626        // we can only approximate using avg. bitrate if seeking to
627        // the end is too expensive or impossible (live streaming).
628
629        int64_t durationUs = lastGranulePosition * 1000000ll / mVi.rate;
630
631        mMeta->setInt64(kKeyDuration, durationUs);
632
633        buildTableOfContents();
634    }
635
636    return OK;
637}
638
639void MyVorbisExtractor::buildTableOfContents() {
640    off64_t offset = mFirstDataOffset;
641    Page page;
642    ssize_t pageSize;
643    while ((pageSize = readPage(offset, &page)) > 0) {
644        mTableOfContents.push();
645
646        TOCEntry &entry =
647            mTableOfContents.editItemAt(mTableOfContents.size() - 1);
648
649        entry.mPageOffset = offset;
650        entry.mTimeUs = page.mGranulePosition * 1000000ll / mVi.rate;
651
652        offset += (size_t)pageSize;
653    }
654
655    // Limit the maximum amount of RAM we spend on the table of contents,
656    // if necessary thin out the table evenly to trim it down to maximum
657    // size.
658
659    static const size_t kMaxTOCSize = 8192;
660    static const size_t kMaxNumTOCEntries = kMaxTOCSize / sizeof(TOCEntry);
661
662    size_t numerator = mTableOfContents.size();
663
664    if (numerator > kMaxNumTOCEntries) {
665        size_t denom = numerator - kMaxNumTOCEntries;
666
667        size_t accum = 0;
668        for (ssize_t i = mTableOfContents.size() - 1; i >= 0; --i) {
669            accum += denom;
670            if (accum >= numerator) {
671                mTableOfContents.removeAt(i);
672                accum -= numerator;
673            }
674        }
675    }
676}
677
678status_t MyVorbisExtractor::verifyHeader(
679        MediaBuffer *buffer, uint8_t type) {
680    const uint8_t *data =
681        (const uint8_t *)buffer->data() + buffer->range_offset();
682
683    size_t size = buffer->range_length();
684
685    if (size < 7 || data[0] != type || memcmp(&data[1], "vorbis", 6)) {
686        return ERROR_MALFORMED;
687    }
688
689    ogg_buffer buf;
690    buf.data = (uint8_t *)data;
691    buf.size = size;
692    buf.refcount = 1;
693    buf.ptr.owner = NULL;
694
695    ogg_reference ref;
696    ref.buffer = &buf;
697    ref.begin = 0;
698    ref.length = size;
699    ref.next = NULL;
700
701    oggpack_buffer bits;
702    oggpack_readinit(&bits, &ref);
703
704    CHECK_EQ(oggpack_read(&bits, 8), type);
705    for (size_t i = 0; i < 6; ++i) {
706        oggpack_read(&bits, 8);  // skip 'vorbis'
707    }
708
709    switch (type) {
710        case 1:
711        {
712            CHECK_EQ(0, _vorbis_unpack_info(&mVi, &bits));
713
714            mMeta->setData(kKeyVorbisInfo, 0, data, size);
715            mMeta->setInt32(kKeySampleRate, mVi.rate);
716            mMeta->setInt32(kKeyChannelCount, mVi.channels);
717
718            LOGV("lower-bitrate = %ld", mVi.bitrate_lower);
719            LOGV("upper-bitrate = %ld", mVi.bitrate_upper);
720            LOGV("nominal-bitrate = %ld", mVi.bitrate_nominal);
721            LOGV("window-bitrate = %ld", mVi.bitrate_window);
722
723            off64_t size;
724            if (mSource->getSize(&size) == OK) {
725                uint64_t bps = approxBitrate();
726
727                mMeta->setInt64(kKeyDuration, size * 8000000ll / bps);
728            }
729            break;
730        }
731
732        case 3:
733        {
734            if (0 != _vorbis_unpack_comment(&mVc, &bits)) {
735                return ERROR_MALFORMED;
736            }
737
738            parseFileMetaData();
739            break;
740        }
741
742        case 5:
743        {
744            if (0 != _vorbis_unpack_books(&mVi, &bits)) {
745                return ERROR_MALFORMED;
746            }
747
748            mMeta->setData(kKeyVorbisBooks, 0, data, size);
749            break;
750        }
751    }
752
753    return OK;
754}
755
756uint64_t MyVorbisExtractor::approxBitrate() {
757    if (mVi.bitrate_nominal != 0) {
758        return mVi.bitrate_nominal;
759    }
760
761    return (mVi.bitrate_lower + mVi.bitrate_upper) / 2;
762}
763
764void MyVorbisExtractor::parseFileMetaData() {
765    mFileMeta = new MetaData;
766    mFileMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_CONTAINER_OGG);
767
768    for (int i = 0; i < mVc.comments; ++i) {
769        const char *comment = mVc.user_comments[i];
770        size_t commentLength = mVc.comment_lengths[i];
771        parseVorbisComment(mFileMeta, comment, commentLength);
772        //LOGI("comment #%d: '%s'", i + 1, mVc.user_comments[i]);
773    }
774}
775
776void parseVorbisComment(
777        const sp<MetaData> &fileMeta, const char *comment, size_t commentLength)
778{
779    struct {
780        const char *const mTag;
781        uint32_t mKey;
782    } kMap[] = {
783        { "TITLE", kKeyTitle },
784        { "ARTIST", kKeyArtist },
785        { "ALBUMARTIST", kKeyAlbumArtist },
786        { "ALBUM ARTIST", kKeyAlbumArtist },
787        { "COMPILATION", kKeyCompilation },
788        { "ALBUM", kKeyAlbum },
789        { "COMPOSER", kKeyComposer },
790        { "GENRE", kKeyGenre },
791        { "AUTHOR", kKeyAuthor },
792        { "TRACKNUMBER", kKeyCDTrackNumber },
793        { "DISCNUMBER", kKeyDiscNumber },
794        { "DATE", kKeyDate },
795        { "LYRICIST", kKeyWriter },
796        { "METADATA_BLOCK_PICTURE", kKeyAlbumArt },
797        { "ANDROID_LOOP", kKeyAutoLoop },
798    };
799
800        for (size_t j = 0; j < sizeof(kMap) / sizeof(kMap[0]); ++j) {
801            size_t tagLen = strlen(kMap[j].mTag);
802            if (!strncasecmp(kMap[j].mTag, comment, tagLen)
803                    && comment[tagLen] == '=') {
804                if (kMap[j].mKey == kKeyAlbumArt) {
805                    extractAlbumArt(
806                            fileMeta,
807                            &comment[tagLen + 1],
808                            commentLength - tagLen - 1);
809                } else if (kMap[j].mKey == kKeyAutoLoop) {
810                    if (!strcasecmp(&comment[tagLen + 1], "true")) {
811                        fileMeta->setInt32(kKeyAutoLoop, true);
812                    }
813                } else {
814                    fileMeta->setCString(kMap[j].mKey, &comment[tagLen + 1]);
815                }
816            }
817        }
818
819}
820
821// The returned buffer should be free()d.
822static uint8_t *DecodeBase64(const char *s, size_t size, size_t *outSize) {
823    *outSize = 0;
824
825    if ((size % 4) != 0) {
826        return NULL;
827    }
828
829    size_t n = size;
830    size_t padding = 0;
831    if (n >= 1 && s[n - 1] == '=') {
832        padding = 1;
833
834        if (n >= 2 && s[n - 2] == '=') {
835            padding = 2;
836        }
837    }
838
839    size_t outLen = 3 * size / 4 - padding;
840
841    *outSize = outLen;
842
843    void *buffer = malloc(outLen);
844
845    uint8_t *out = (uint8_t *)buffer;
846    size_t j = 0;
847    uint32_t accum = 0;
848    for (size_t i = 0; i < n; ++i) {
849        char c = s[i];
850        unsigned value;
851        if (c >= 'A' && c <= 'Z') {
852            value = c - 'A';
853        } else if (c >= 'a' && c <= 'z') {
854            value = 26 + c - 'a';
855        } else if (c >= '0' && c <= '9') {
856            value = 52 + c - '0';
857        } else if (c == '+') {
858            value = 62;
859        } else if (c == '/') {
860            value = 63;
861        } else if (c != '=') {
862            return NULL;
863        } else {
864            if (i < n - padding) {
865                return NULL;
866            }
867
868            value = 0;
869        }
870
871        accum = (accum << 6) | value;
872
873        if (((i + 1) % 4) == 0) {
874            out[j++] = (accum >> 16);
875
876            if (j < outLen) { out[j++] = (accum >> 8) & 0xff; }
877            if (j < outLen) { out[j++] = accum & 0xff; }
878
879            accum = 0;
880        }
881    }
882
883    return (uint8_t *)buffer;
884}
885
886static void extractAlbumArt(
887        const sp<MetaData> &fileMeta, const void *data, size_t size) {
888    LOGV("extractAlbumArt from '%s'", (const char *)data);
889
890    size_t flacSize;
891    uint8_t *flac = DecodeBase64((const char *)data, size, &flacSize);
892
893    if (flac == NULL) {
894        LOGE("malformed base64 encoded data.");
895        return;
896    }
897
898    LOGV("got flac of size %d", flacSize);
899
900    uint32_t picType;
901    uint32_t typeLen;
902    uint32_t descLen;
903    uint32_t dataLen;
904    char type[128];
905
906    if (flacSize < 8) {
907        goto exit;
908    }
909
910    picType = U32_AT(flac);
911
912    if (picType != 3) {
913        // This is not a front cover.
914        goto exit;
915    }
916
917    typeLen = U32_AT(&flac[4]);
918    if (typeLen + 1 > sizeof(type)) {
919        goto exit;
920    }
921
922    if (flacSize < 8 + typeLen) {
923        goto exit;
924    }
925
926    memcpy(type, &flac[8], typeLen);
927    type[typeLen] = '\0';
928
929    LOGV("picType = %d, type = '%s'", picType, type);
930
931    if (!strcmp(type, "-->")) {
932        // This is not inline cover art, but an external url instead.
933        goto exit;
934    }
935
936    descLen = U32_AT(&flac[8 + typeLen]);
937
938    if (flacSize < 32 + typeLen + descLen) {
939        goto exit;
940    }
941
942    dataLen = U32_AT(&flac[8 + typeLen + 4 + descLen + 16]);
943
944    if (flacSize < 32 + typeLen + descLen + dataLen) {
945        goto exit;
946    }
947
948    LOGV("got image data, %d trailing bytes",
949         flacSize - 32 - typeLen - descLen - dataLen);
950
951    fileMeta->setData(
952            kKeyAlbumArt, 0, &flac[8 + typeLen + 4 + descLen + 20], dataLen);
953
954    fileMeta->setCString(kKeyAlbumArtMIME, type);
955
956exit:
957    free(flac);
958    flac = NULL;
959}
960
961////////////////////////////////////////////////////////////////////////////////
962
963OggExtractor::OggExtractor(const sp<DataSource> &source)
964    : mDataSource(source),
965      mInitCheck(NO_INIT),
966      mImpl(NULL) {
967    mImpl = new MyVorbisExtractor(mDataSource);
968    mInitCheck = mImpl->seekToOffset(0);
969
970    if (mInitCheck == OK) {
971        mInitCheck = mImpl->init();
972    }
973}
974
975OggExtractor::~OggExtractor() {
976    delete mImpl;
977    mImpl = NULL;
978}
979
980size_t OggExtractor::countTracks() {
981    return mInitCheck != OK ? 0 : 1;
982}
983
984sp<MediaSource> OggExtractor::getTrack(size_t index) {
985    if (index >= 1) {
986        return NULL;
987    }
988
989    return new OggSource(this);
990}
991
992sp<MetaData> OggExtractor::getTrackMetaData(
993        size_t index, uint32_t flags) {
994    if (index >= 1) {
995        return NULL;
996    }
997
998    return mImpl->getFormat();
999}
1000
1001sp<MetaData> OggExtractor::getMetaData() {
1002    return mImpl->getFileMetaData();
1003}
1004
1005bool SniffOgg(
1006        const sp<DataSource> &source, String8 *mimeType, float *confidence,
1007        sp<AMessage> *) {
1008    char tmp[4];
1009    if (source->readAt(0, tmp, 4) < 4 || memcmp(tmp, "OggS", 4)) {
1010        return false;
1011    }
1012
1013    mimeType->setTo(MEDIA_MIMETYPE_CONTAINER_OGG);
1014    *confidence = 0.2f;
1015
1016    return true;
1017}
1018
1019}  // namespace android
1020