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