OggExtractor.cpp revision a5750e0dad9e90f2195ce36f2c4457fa04b2b83e
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/foundation/ADebug.h>
25#include <media/stagefright/DataSource.h>
26#include <media/stagefright/MediaBuffer.h>
27#include <media/stagefright/MediaBufferGroup.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        ALOGI("found time = %lld us", timeUs);
193    } else {
194        ALOGI("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                ALOGV("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        ALOGV("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    ALOGV("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        ALOGV("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    ALOGV("seeking to entry %zu / %zu 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    ssize_t n;
382    if ((n = mSource->readAt(offset, header, sizeof(header)))
383            < (ssize_t)sizeof(header)) {
384        ALOGV("failed to read %zu bytes at offset 0x%016llx, got %zd bytes",
385             sizeof(header), offset, n);
386
387        if (n < 0) {
388            return n;
389        } else if (n == 0) {
390            return ERROR_END_OF_STREAM;
391        } else {
392            return ERROR_IO;
393        }
394    }
395
396    if (memcmp(header, "OggS", 4)) {
397        return ERROR_MALFORMED;
398    }
399
400    if (header[4] != 0) {
401        // Wrong version.
402
403        return ERROR_UNSUPPORTED;
404    }
405
406    page->mFlags = header[5];
407
408    if (page->mFlags & ~7) {
409        // Only bits 0-2 are defined in version 0.
410        return ERROR_MALFORMED;
411    }
412
413    page->mGranulePosition = U64LE_AT(&header[6]);
414
415#if 0
416    printf("granulePosition = %llu (0x%llx)\n",
417           page->mGranulePosition, page->mGranulePosition);
418#endif
419
420    page->mSerialNo = U32LE_AT(&header[14]);
421    page->mPageNo = U32LE_AT(&header[18]);
422
423    page->mNumSegments = header[26];
424    if (mSource->readAt(
425                offset + sizeof(header), page->mLace, page->mNumSegments)
426            < (ssize_t)page->mNumSegments) {
427        return ERROR_IO;
428    }
429
430    size_t totalSize = 0;;
431    for (size_t i = 0; i < page->mNumSegments; ++i) {
432        totalSize += page->mLace[i];
433    }
434
435#if 0
436    String8 tmp;
437    for (size_t i = 0; i < page->mNumSegments; ++i) {
438        char x[32];
439        sprintf(x, "%s%u", i > 0 ? ", " : "", (unsigned)page->mLace[i]);
440
441        tmp.append(x);
442    }
443
444    ALOGV("%c %s", page->mFlags & 1 ? '+' : ' ', tmp.string());
445#endif
446
447    return sizeof(header) + page->mNumSegments + totalSize;
448}
449
450status_t MyVorbisExtractor::readNextPacket(MediaBuffer **out) {
451    *out = NULL;
452
453    MediaBuffer *buffer = NULL;
454    int64_t timeUs = -1;
455
456    for (;;) {
457        size_t i;
458        size_t packetSize = 0;
459        bool gotFullPacket = false;
460        for (i = mNextLaceIndex; i < mCurrentPage.mNumSegments; ++i) {
461            uint8_t lace = mCurrentPage.mLace[i];
462
463            packetSize += lace;
464
465            if (lace < 255) {
466                gotFullPacket = true;
467                ++i;
468                break;
469            }
470        }
471
472        if (mNextLaceIndex < mCurrentPage.mNumSegments) {
473            off64_t dataOffset = mOffset + 27 + mCurrentPage.mNumSegments;
474            for (size_t j = 0; j < mNextLaceIndex; ++j) {
475                dataOffset += mCurrentPage.mLace[j];
476            }
477
478            size_t fullSize = packetSize;
479            if (buffer != NULL) {
480                fullSize += buffer->range_length();
481            }
482            MediaBuffer *tmp = new MediaBuffer(fullSize);
483            if (buffer != NULL) {
484                memcpy(tmp->data(), buffer->data(), buffer->range_length());
485                tmp->set_range(0, buffer->range_length());
486                buffer->release();
487            } else {
488                // XXX Not only is this not technically the correct time for
489                // this packet, we also stamp every packet in this page
490                // with the same time. This needs fixing later.
491
492                if (mVi.rate) {
493                    // Rate may not have been initialized yet if we're currently
494                    // reading the configuration packets...
495                    // Fortunately, the timestamp doesn't matter for those.
496                    timeUs = mCurrentPage.mGranulePosition * 1000000ll / mVi.rate;
497                }
498                tmp->set_range(0, 0);
499            }
500            buffer = tmp;
501
502            ssize_t n = mSource->readAt(
503                    dataOffset,
504                    (uint8_t *)buffer->data() + buffer->range_length(),
505                    packetSize);
506
507            if (n < (ssize_t)packetSize) {
508                ALOGV("failed to read %zu bytes at 0x%016llx, got %zd bytes",
509                     packetSize, dataOffset, n);
510                return ERROR_IO;
511            }
512
513            buffer->set_range(0, fullSize);
514
515            mNextLaceIndex = i;
516
517            if (gotFullPacket) {
518                // We've just read the entire packet.
519
520                if (timeUs >= 0) {
521                    buffer->meta_data()->setInt64(kKeyTime, timeUs);
522                }
523
524                if (mFirstPacketInPage) {
525                    buffer->meta_data()->setInt32(
526                            kKeyValidSamples, mCurrentPageSamples);
527                    mFirstPacketInPage = false;
528                }
529
530                *out = buffer;
531
532                return OK;
533            }
534
535            // fall through, the buffer now contains the start of the packet.
536        }
537
538        CHECK_EQ(mNextLaceIndex, mCurrentPage.mNumSegments);
539
540        mOffset += mCurrentPageSize;
541        ssize_t n = readPage(mOffset, &mCurrentPage);
542
543        if (n <= 0) {
544            if (buffer) {
545                buffer->release();
546                buffer = NULL;
547            }
548
549            ALOGV("readPage returned %zd", n);
550
551            return n < 0 ? n : (status_t)ERROR_END_OF_STREAM;
552        }
553
554        mCurrentPageSamples =
555            mCurrentPage.mGranulePosition - mPrevGranulePosition;
556        mFirstPacketInPage = true;
557
558        mPrevGranulePosition = mCurrentPage.mGranulePosition;
559
560        mCurrentPageSize = n;
561        mNextLaceIndex = 0;
562
563        if (buffer != NULL) {
564            if ((mCurrentPage.mFlags & 1) == 0) {
565                // This page does not continue the packet, i.e. the packet
566                // is already complete.
567
568                if (timeUs >= 0) {
569                    buffer->meta_data()->setInt64(kKeyTime, timeUs);
570                }
571
572                buffer->meta_data()->setInt32(
573                        kKeyValidSamples, mCurrentPageSamples);
574                mFirstPacketInPage = false;
575
576                *out = buffer;
577
578                return OK;
579            }
580        }
581    }
582}
583
584status_t MyVorbisExtractor::init() {
585    mMeta = new MetaData;
586    mMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_VORBIS);
587
588    MediaBuffer *packet;
589    status_t err;
590    if ((err = readNextPacket(&packet)) != OK) {
591        return err;
592    }
593    ALOGV("read packet of size %zu\n", packet->range_length());
594    err = verifyHeader(packet, 1);
595    packet->release();
596    packet = NULL;
597    if (err != OK) {
598        return err;
599    }
600
601    if ((err = readNextPacket(&packet)) != OK) {
602        return err;
603    }
604    ALOGV("read packet of size %zu\n", packet->range_length());
605    err = verifyHeader(packet, 3);
606    packet->release();
607    packet = NULL;
608    if (err != OK) {
609        return err;
610    }
611
612    if ((err = readNextPacket(&packet)) != OK) {
613        return err;
614    }
615    ALOGV("read packet of size %zu\n", packet->range_length());
616    err = verifyHeader(packet, 5);
617    packet->release();
618    packet = NULL;
619    if (err != OK) {
620        return err;
621    }
622
623    mFirstDataOffset = mOffset + mCurrentPageSize;
624
625    off64_t size;
626    uint64_t lastGranulePosition;
627    if (!(mSource->flags() & DataSource::kIsCachingDataSource)
628            && mSource->getSize(&size) == OK
629            && findPrevGranulePosition(size, &lastGranulePosition) == OK) {
630        // Let's assume it's cheap to seek to the end.
631        // The granule position of the final page in the stream will
632        // give us the exact duration of the content, something that
633        // we can only approximate using avg. bitrate if seeking to
634        // the end is too expensive or impossible (live streaming).
635
636        int64_t durationUs = lastGranulePosition * 1000000ll / mVi.rate;
637
638        mMeta->setInt64(kKeyDuration, durationUs);
639
640        buildTableOfContents();
641    }
642
643    return OK;
644}
645
646void MyVorbisExtractor::buildTableOfContents() {
647    off64_t offset = mFirstDataOffset;
648    Page page;
649    ssize_t pageSize;
650    while ((pageSize = readPage(offset, &page)) > 0) {
651        mTableOfContents.push();
652
653        TOCEntry &entry =
654            mTableOfContents.editItemAt(mTableOfContents.size() - 1);
655
656        entry.mPageOffset = offset;
657        entry.mTimeUs = page.mGranulePosition * 1000000ll / mVi.rate;
658
659        offset += (size_t)pageSize;
660    }
661
662    // Limit the maximum amount of RAM we spend on the table of contents,
663    // if necessary thin out the table evenly to trim it down to maximum
664    // size.
665
666    static const size_t kMaxTOCSize = 8192;
667    static const size_t kMaxNumTOCEntries = kMaxTOCSize / sizeof(TOCEntry);
668
669    size_t numerator = mTableOfContents.size();
670
671    if (numerator > kMaxNumTOCEntries) {
672        size_t denom = numerator - kMaxNumTOCEntries;
673
674        size_t accum = 0;
675        for (ssize_t i = mTableOfContents.size() - 1; i >= 0; --i) {
676            accum += denom;
677            if (accum >= numerator) {
678                mTableOfContents.removeAt(i);
679                accum -= numerator;
680            }
681        }
682    }
683}
684
685status_t MyVorbisExtractor::verifyHeader(
686        MediaBuffer *buffer, uint8_t type) {
687    const uint8_t *data =
688        (const uint8_t *)buffer->data() + buffer->range_offset();
689
690    size_t size = buffer->range_length();
691
692    if (size < 7 || data[0] != type || memcmp(&data[1], "vorbis", 6)) {
693        return ERROR_MALFORMED;
694    }
695
696    ogg_buffer buf;
697    buf.data = (uint8_t *)data;
698    buf.size = size;
699    buf.refcount = 1;
700    buf.ptr.owner = NULL;
701
702    ogg_reference ref;
703    ref.buffer = &buf;
704    ref.begin = 0;
705    ref.length = size;
706    ref.next = NULL;
707
708    oggpack_buffer bits;
709    oggpack_readinit(&bits, &ref);
710
711    CHECK_EQ(oggpack_read(&bits, 8), type);
712    for (size_t i = 0; i < 6; ++i) {
713        oggpack_read(&bits, 8);  // skip 'vorbis'
714    }
715
716    switch (type) {
717        case 1:
718        {
719            CHECK_EQ(0, _vorbis_unpack_info(&mVi, &bits));
720
721            mMeta->setData(kKeyVorbisInfo, 0, data, size);
722            mMeta->setInt32(kKeySampleRate, mVi.rate);
723            mMeta->setInt32(kKeyChannelCount, mVi.channels);
724
725            ALOGV("lower-bitrate = %ld", mVi.bitrate_lower);
726            ALOGV("upper-bitrate = %ld", mVi.bitrate_upper);
727            ALOGV("nominal-bitrate = %ld", mVi.bitrate_nominal);
728            ALOGV("window-bitrate = %ld", mVi.bitrate_window);
729
730            off64_t size;
731            if (mSource->getSize(&size) == OK) {
732                uint64_t bps = approxBitrate();
733                if (bps != 0) {
734                    mMeta->setInt64(kKeyDuration, size * 8000000ll / bps);
735                }
736            }
737            break;
738        }
739
740        case 3:
741        {
742            if (0 != _vorbis_unpack_comment(&mVc, &bits)) {
743                return ERROR_MALFORMED;
744            }
745
746            parseFileMetaData();
747            break;
748        }
749
750        case 5:
751        {
752            if (0 != _vorbis_unpack_books(&mVi, &bits)) {
753                return ERROR_MALFORMED;
754            }
755
756            mMeta->setData(kKeyVorbisBooks, 0, data, size);
757            break;
758        }
759    }
760
761    return OK;
762}
763
764uint64_t MyVorbisExtractor::approxBitrate() {
765    if (mVi.bitrate_nominal != 0) {
766        return mVi.bitrate_nominal;
767    }
768
769    return (mVi.bitrate_lower + mVi.bitrate_upper) / 2;
770}
771
772void MyVorbisExtractor::parseFileMetaData() {
773    mFileMeta = new MetaData;
774    mFileMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_CONTAINER_OGG);
775
776    for (int i = 0; i < mVc.comments; ++i) {
777        const char *comment = mVc.user_comments[i];
778        size_t commentLength = mVc.comment_lengths[i];
779        parseVorbisComment(mFileMeta, comment, commentLength);
780        //ALOGI("comment #%d: '%s'", i + 1, mVc.user_comments[i]);
781    }
782}
783
784void parseVorbisComment(
785        const sp<MetaData> &fileMeta, const char *comment, size_t commentLength)
786{
787    struct {
788        const char *const mTag;
789        uint32_t mKey;
790    } kMap[] = {
791        { "TITLE", kKeyTitle },
792        { "ARTIST", kKeyArtist },
793        { "ALBUMARTIST", kKeyAlbumArtist },
794        { "ALBUM ARTIST", kKeyAlbumArtist },
795        { "COMPILATION", kKeyCompilation },
796        { "ALBUM", kKeyAlbum },
797        { "COMPOSER", kKeyComposer },
798        { "GENRE", kKeyGenre },
799        { "AUTHOR", kKeyAuthor },
800        { "TRACKNUMBER", kKeyCDTrackNumber },
801        { "DISCNUMBER", kKeyDiscNumber },
802        { "DATE", kKeyDate },
803        { "LYRICIST", kKeyWriter },
804        { "METADATA_BLOCK_PICTURE", kKeyAlbumArt },
805        { "ANDROID_LOOP", kKeyAutoLoop },
806    };
807
808        for (size_t j = 0; j < sizeof(kMap) / sizeof(kMap[0]); ++j) {
809            size_t tagLen = strlen(kMap[j].mTag);
810            if (!strncasecmp(kMap[j].mTag, comment, tagLen)
811                    && comment[tagLen] == '=') {
812                if (kMap[j].mKey == kKeyAlbumArt) {
813                    extractAlbumArt(
814                            fileMeta,
815                            &comment[tagLen + 1],
816                            commentLength - tagLen - 1);
817                } else if (kMap[j].mKey == kKeyAutoLoop) {
818                    if (!strcasecmp(&comment[tagLen + 1], "true")) {
819                        fileMeta->setInt32(kKeyAutoLoop, true);
820                    }
821                } else {
822                    fileMeta->setCString(kMap[j].mKey, &comment[tagLen + 1]);
823                }
824            }
825        }
826
827}
828
829// The returned buffer should be free()d.
830static uint8_t *DecodeBase64(const char *s, size_t size, size_t *outSize) {
831    *outSize = 0;
832
833    if ((size % 4) != 0) {
834        return NULL;
835    }
836
837    size_t n = size;
838    size_t padding = 0;
839    if (n >= 1 && s[n - 1] == '=') {
840        padding = 1;
841
842        if (n >= 2 && s[n - 2] == '=') {
843            padding = 2;
844        }
845    }
846
847    size_t outLen = 3 * size / 4 - padding;
848
849    *outSize = outLen;
850
851    void *buffer = malloc(outLen);
852
853    uint8_t *out = (uint8_t *)buffer;
854    size_t j = 0;
855    uint32_t accum = 0;
856    for (size_t i = 0; i < n; ++i) {
857        char c = s[i];
858        unsigned value;
859        if (c >= 'A' && c <= 'Z') {
860            value = c - 'A';
861        } else if (c >= 'a' && c <= 'z') {
862            value = 26 + c - 'a';
863        } else if (c >= '0' && c <= '9') {
864            value = 52 + c - '0';
865        } else if (c == '+') {
866            value = 62;
867        } else if (c == '/') {
868            value = 63;
869        } else if (c != '=') {
870            return NULL;
871        } else {
872            if (i < n - padding) {
873                return NULL;
874            }
875
876            value = 0;
877        }
878
879        accum = (accum << 6) | value;
880
881        if (((i + 1) % 4) == 0) {
882            out[j++] = (accum >> 16);
883
884            if (j < outLen) { out[j++] = (accum >> 8) & 0xff; }
885            if (j < outLen) { out[j++] = accum & 0xff; }
886
887            accum = 0;
888        }
889    }
890
891    return (uint8_t *)buffer;
892}
893
894static void extractAlbumArt(
895        const sp<MetaData> &fileMeta, const void *data, size_t size) {
896    ALOGV("extractAlbumArt from '%s'", (const char *)data);
897
898    size_t flacSize;
899    uint8_t *flac = DecodeBase64((const char *)data, size, &flacSize);
900
901    if (flac == NULL) {
902        ALOGE("malformed base64 encoded data.");
903        return;
904    }
905
906    ALOGV("got flac of size %zu", flacSize);
907
908    uint32_t picType;
909    uint32_t typeLen;
910    uint32_t descLen;
911    uint32_t dataLen;
912    char type[128];
913
914    if (flacSize < 8) {
915        goto exit;
916    }
917
918    picType = U32_AT(flac);
919
920    if (picType != 3) {
921        // This is not a front cover.
922        goto exit;
923    }
924
925    typeLen = U32_AT(&flac[4]);
926    if (typeLen + 1 > sizeof(type)) {
927        goto exit;
928    }
929
930    if (flacSize < 8 + typeLen) {
931        goto exit;
932    }
933
934    memcpy(type, &flac[8], typeLen);
935    type[typeLen] = '\0';
936
937    ALOGV("picType = %d, type = '%s'", picType, type);
938
939    if (!strcmp(type, "-->")) {
940        // This is not inline cover art, but an external url instead.
941        goto exit;
942    }
943
944    descLen = U32_AT(&flac[8 + typeLen]);
945
946    if (flacSize < 32 + typeLen + descLen) {
947        goto exit;
948    }
949
950    dataLen = U32_AT(&flac[8 + typeLen + 4 + descLen + 16]);
951
952    if (flacSize < 32 + typeLen + descLen + dataLen) {
953        goto exit;
954    }
955
956    ALOGV("got image data, %zu trailing bytes",
957         flacSize - 32 - typeLen - descLen - dataLen);
958
959    fileMeta->setData(
960            kKeyAlbumArt, 0, &flac[8 + typeLen + 4 + descLen + 20], dataLen);
961
962    fileMeta->setCString(kKeyAlbumArtMIME, type);
963
964exit:
965    free(flac);
966    flac = NULL;
967}
968
969////////////////////////////////////////////////////////////////////////////////
970
971OggExtractor::OggExtractor(const sp<DataSource> &source)
972    : mDataSource(source),
973      mInitCheck(NO_INIT),
974      mImpl(NULL) {
975    mImpl = new MyVorbisExtractor(mDataSource);
976    mInitCheck = mImpl->seekToOffset(0);
977
978    if (mInitCheck == OK) {
979        mInitCheck = mImpl->init();
980    }
981}
982
983OggExtractor::~OggExtractor() {
984    delete mImpl;
985    mImpl = NULL;
986}
987
988size_t OggExtractor::countTracks() {
989    return mInitCheck != OK ? 0 : 1;
990}
991
992sp<MediaSource> OggExtractor::getTrack(size_t index) {
993    if (index >= 1) {
994        return NULL;
995    }
996
997    return new OggSource(this);
998}
999
1000sp<MetaData> OggExtractor::getTrackMetaData(
1001        size_t index, uint32_t /* flags */) {
1002    if (index >= 1) {
1003        return NULL;
1004    }
1005
1006    return mImpl->getFormat();
1007}
1008
1009sp<MetaData> OggExtractor::getMetaData() {
1010    return mImpl->getFileMetaData();
1011}
1012
1013bool SniffOgg(
1014        const sp<DataSource> &source, String8 *mimeType, float *confidence,
1015        sp<AMessage> *) {
1016    char tmp[4];
1017    if (source->readAt(0, tmp, 4) < 4 || memcmp(tmp, "OggS", 4)) {
1018        return false;
1019    }
1020
1021    mimeType->setTo(MEDIA_MIMETYPE_CONTAINER_OGG);
1022    *confidence = 0.2f;
1023
1024    return true;
1025}
1026
1027}  // namespace android
1028