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