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