blob: 5e79e788816d24150a0d212e8e51a68b87c85959 [file] [log] [blame]
Andreas Huberee7ff202010-05-07 10:35:13 -07001/*
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>
James Dongf1d5aa12012-02-06 23:46:37 -080024#include <media/stagefright/foundation/ADebug.h>
Andreas Huberee7ff202010-05-07 10:35:13 -070025#include <media/stagefright/DataSource.h>
26#include <media/stagefright/MediaBuffer.h>
27#include <media/stagefright/MediaBufferGroup.h>
Andreas Huberee7ff202010-05-07 10:35:13 -070028#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
Andreas Huber3fd91ba2011-03-02 11:10:10 -080076 status_t seekToTime(int64_t timeUs);
James Dongc7fc37a2010-11-16 14:04:54 -080077 status_t seekToOffset(off64_t offset);
Andreas Huberee7ff202010-05-07 10:35:13 -070078 status_t readNextPacket(MediaBuffer **buffer);
79
Andreas Huber5a4001d2010-05-26 12:59:06 -070080 status_t init();
Andreas Huberee7ff202010-05-07 10:35:13 -070081
Andreas Huber4595be92010-05-25 11:20:29 -070082 sp<MetaData> getFileMetaData() { return mFileMeta; }
83
Andreas Huberee7ff202010-05-07 10:35:13 -070084private:
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
Andreas Huber3fd91ba2011-03-02 11:10:10 -080094 struct TOCEntry {
95 off64_t mPageOffset;
96 int64_t mTimeUs;
97 };
98
Andreas Huberee7ff202010-05-07 10:35:13 -070099 sp<DataSource> mSource;
James Dongc7fc37a2010-11-16 14:04:54 -0800100 off64_t mOffset;
Andreas Huberee7ff202010-05-07 10:35:13 -0700101 Page mCurrentPage;
Andreas Huberdb622222010-09-28 09:16:21 -0700102 uint64_t mPrevGranulePosition;
Andreas Huberee7ff202010-05-07 10:35:13 -0700103 size_t mCurrentPageSize;
Andreas Huberdb622222010-09-28 09:16:21 -0700104 bool mFirstPacketInPage;
105 uint64_t mCurrentPageSamples;
Andreas Huberee7ff202010-05-07 10:35:13 -0700106 size_t mNextLaceIndex;
107
James Dongc7fc37a2010-11-16 14:04:54 -0800108 off64_t mFirstDataOffset;
Andreas Huber96f52cd2010-05-11 10:11:55 -0700109
Andreas Huberee7ff202010-05-07 10:35:13 -0700110 vorbis_info mVi;
111 vorbis_comment mVc;
112
113 sp<MetaData> mMeta;
Andreas Huber4595be92010-05-25 11:20:29 -0700114 sp<MetaData> mFileMeta;
Andreas Huberee7ff202010-05-07 10:35:13 -0700115
Andreas Huber3fd91ba2011-03-02 11:10:10 -0800116 Vector<TOCEntry> mTableOfContents;
117
James Dongc7fc37a2010-11-16 14:04:54 -0800118 ssize_t readPage(off64_t offset, Page *page);
119 status_t findNextPage(off64_t startOffset, off64_t *pageOffset);
Andreas Huberee7ff202010-05-07 10:35:13 -0700120
Andreas Huber5a4001d2010-05-26 12:59:06 -0700121 status_t verifyHeader(
Andreas Huberee7ff202010-05-07 10:35:13 -0700122 MediaBuffer *buffer, uint8_t type);
123
Andreas Huber4595be92010-05-25 11:20:29 -0700124 void parseFileMetaData();
Andreas Huber4595be92010-05-25 11:20:29 -0700125
Andreas Huber3fd91ba2011-03-02 11:10:10 -0800126 status_t findPrevGranulePosition(off64_t pageOffset, uint64_t *granulePos);
127
128 void buildTableOfContents();
Andreas Huberdb622222010-09-28 09:16:21 -0700129
Andreas Huberee7ff202010-05-07 10:35:13 -0700130 MyVorbisExtractor(const MyVorbisExtractor &);
131 MyVorbisExtractor &operator=(const MyVorbisExtractor &);
132};
133
Glenn Kasten856990b2011-01-13 11:17:00 -0800134static void extractAlbumArt(
135 const sp<MetaData> &fileMeta, const void *data, size_t size);
136
Andreas Huberee7ff202010-05-07 10:35:13 -0700137////////////////////////////////////////////////////////////////////////////////
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;
Andreas Huberabd1f4f2010-07-20 15:04:28 -0700175 ReadOptions::SeekMode mode;
176 if (options && options->getSeekTo(&seekTimeUs, &mode)) {
Andreas Huber3fd91ba2011-03-02 11:10:10 -0800177 if (mExtractor->mImpl->seekToTime(seekTimeUs) != OK) {
Andreas Huberee7ff202010-05-07 10:35:13 -0700178 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)) {
Steve Blockdf64d152012-01-04 20:05:49 +0000192 ALOGI("found time = %lld us", timeUs);
Andreas Huberee7ff202010-05-07 10:35:13 -0700193 } else {
Steve Blockdf64d152012-01-04 20:05:49 +0000194 ALOGI("NO time");
Andreas Huberee7ff202010-05-07 10:35:13 -0700195 }
196#endif
197
Andreas Huber8bf59e72010-08-06 14:13:10 -0700198 packet->meta_data()->setInt32(kKeyIsSyncFrame, 1);
199
Andreas Huberee7ff202010-05-07 10:35:13 -0700200 *out = packet;
201
202 return OK;
203}
204
205////////////////////////////////////////////////////////////////////////////////
206
207MyVorbisExtractor::MyVorbisExtractor(const sp<DataSource> &source)
208 : mSource(source),
209 mOffset(0),
Andreas Huberdb622222010-09-28 09:16:21 -0700210 mPrevGranulePosition(0),
Andreas Huberee7ff202010-05-07 10:35:13 -0700211 mCurrentPageSize(0),
Andreas Huberdb622222010-09-28 09:16:21 -0700212 mFirstPacketInPage(true),
213 mCurrentPageSamples(0),
Andreas Huber96f52cd2010-05-11 10:11:55 -0700214 mNextLaceIndex(0),
215 mFirstDataOffset(-1) {
Andreas Huberee7ff202010-05-07 10:35:13 -0700216 mCurrentPage.mNumSegments = 0;
Andreas Huber4595be92010-05-25 11:20:29 -0700217
218 vorbis_info_init(&mVi);
219 vorbis_comment_init(&mVc);
Andreas Huberee7ff202010-05-07 10:35:13 -0700220}
221
222MyVorbisExtractor::~MyVorbisExtractor() {
Andreas Huber4595be92010-05-25 11:20:29 -0700223 vorbis_comment_clear(&mVc);
224 vorbis_info_clear(&mVi);
Andreas Huberee7ff202010-05-07 10:35:13 -0700225}
226
227sp<MetaData> MyVorbisExtractor::getFormat() const {
228 return mMeta;
229}
230
231status_t MyVorbisExtractor::findNextPage(
James Dongc7fc37a2010-11-16 14:04:54 -0800232 off64_t startOffset, off64_t *pageOffset) {
Andreas Huberee7ff202010-05-07 10:35:13 -0700233 *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) {
Steve Block3856b092011-10-20 11:56:00 +0100247 ALOGV("skipped %lld bytes of junk to reach next frame",
Andreas Huberee7ff202010-05-07 10:35:13 -0700248 *pageOffset - startOffset);
249 }
250
251 return OK;
252 }
253
254 ++*pageOffset;
255 }
256}
257
Andreas Huberdb622222010-09-28 09:16:21 -0700258// 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.
Andreas Huber3fd91ba2011-03-02 11:10:10 -0800262status_t MyVorbisExtractor::findPrevGranulePosition(
263 off64_t pageOffset, uint64_t *granulePos) {
264 *granulePos = 0;
265
James Dongc7fc37a2010-11-16 14:04:54 -0800266 off64_t prevPageOffset = 0;
267 off64_t prevGuess = pageOffset;
Andreas Huberdb622222010-09-28 09:16:21 -0700268 for (;;) {
269 if (prevGuess >= 5000) {
270 prevGuess -= 5000;
271 } else {
272 prevGuess = 0;
273 }
274
Steve Block3856b092011-10-20 11:56:00 +0100275 ALOGV("backing up %lld bytes", pageOffset - prevGuess);
Andreas Huberdb622222010-09-28 09:16:21 -0700276
Andreas Huber3fd91ba2011-03-02 11:10:10 -0800277 status_t err = findNextPage(prevGuess, &prevPageOffset);
278 if (err != OK) {
279 return err;
280 }
Andreas Huberdb622222010-09-28 09:16:21 -0700281
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.
Andreas Huber3fd91ba2011-03-02 11:10:10 -0800289 return UNKNOWN_ERROR;
Andreas Huberdb622222010-09-28 09:16:21 -0700290 }
291
Steve Block3856b092011-10-20 11:56:00 +0100292 ALOGV("prevPageOffset at %lld, pageOffset at %lld",
Andreas Huber3fd91ba2011-03-02 11:10:10 -0800293 prevPageOffset, pageOffset);
Andreas Huberdb622222010-09-28 09:16:21 -0700294
295 for (;;) {
296 Page prevPage;
297 ssize_t n = readPage(prevPageOffset, &prevPage);
298
299 if (n <= 0) {
Andreas Huber3fd91ba2011-03-02 11:10:10 -0800300 return (status_t)n;
Andreas Huberdb622222010-09-28 09:16:21 -0700301 }
302
303 prevPageOffset += n;
304
305 if (prevPageOffset == pageOffset) {
Andreas Huber3fd91ba2011-03-02 11:10:10 -0800306 *granulePos = prevPage.mGranulePosition;
307 return OK;
Andreas Huberdb622222010-09-28 09:16:21 -0700308 }
309 }
310}
311
Andreas Huber3fd91ba2011-03-02 11:10:10 -0800312status_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
Steve Block3856b092011-10-20 11:56:00 +0100318 ALOGV("seeking to offset %lld", pos);
Andreas Huber3fd91ba2011-03-02 11:10:10 -0800319 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
Steve Block3856b092011-10-20 11:56:00 +0100341 ALOGV("seeking to entry %d / %d at offset %lld",
Andreas Huber3fd91ba2011-03-02 11:10:10 -0800342 left, mTableOfContents.size(), entry.mPageOffset);
343
344 return seekToOffset(entry.mPageOffset);
345}
346
James Dongc7fc37a2010-11-16 14:04:54 -0800347status_t MyVorbisExtractor::seekToOffset(off64_t offset) {
Andreas Huber96f52cd2010-05-11 10:11:55 -0700348 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
James Dongc7fc37a2010-11-16 14:04:54 -0800354 off64_t pageOffset;
Andreas Huberee7ff202010-05-07 10:35:13 -0700355 status_t err = findNextPage(offset, &pageOffset);
356
357 if (err != OK) {
358 return err;
359 }
360
Andreas Huberdb622222010-09-28 09:16:21 -0700361 // 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.
Andreas Huber3fd91ba2011-03-02 11:10:10 -0800364 findPrevGranulePosition(pageOffset, &mPrevGranulePosition);
Andreas Huberdb622222010-09-28 09:16:21 -0700365
Andreas Huberee7ff202010-05-07 10:35:13 -0700366 mOffset = pageOffset;
367
368 mCurrentPageSize = 0;
Andreas Huberdb622222010-09-28 09:16:21 -0700369 mFirstPacketInPage = true;
370 mCurrentPageSamples = 0;
Andreas Huberee7ff202010-05-07 10:35:13 -0700371 mCurrentPage.mNumSegments = 0;
372 mNextLaceIndex = 0;
373
374 // XXX what if new page continues packet from last???
375
376 return OK;
377}
378
James Dongc7fc37a2010-11-16 14:04:54 -0800379ssize_t MyVorbisExtractor::readPage(off64_t offset, Page *page) {
Andreas Huberee7ff202010-05-07 10:35:13 -0700380 uint8_t header[27];
Andreas Huberdf659ac2011-05-10 16:14:51 -0700381 ssize_t n;
382 if ((n = mSource->readAt(offset, header, sizeof(header)))
Andreas Huberee7ff202010-05-07 10:35:13 -0700383 < (ssize_t)sizeof(header)) {
Steve Block3856b092011-10-20 11:56:00 +0100384 ALOGV("failed to read %d bytes at offset 0x%016llx, got %ld bytes",
Andreas Huberdf659ac2011-05-10 16:14:51 -0700385 sizeof(header), offset, n);
Andreas Huberee7ff202010-05-07 10:35:13 -0700386
Andreas Huberdf659ac2011-05-10 16:14:51 -0700387 if (n < 0) {
388 return n;
389 } else if (n == 0) {
390 return ERROR_END_OF_STREAM;
391 } else {
392 return ERROR_IO;
393 }
Andreas Huberee7ff202010-05-07 10:35:13 -0700394 }
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
Andreas Huber5a4001d2010-05-26 12:59:06 -0700435#if 0
Andreas Huberee7ff202010-05-07 10:35:13 -0700436 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
Steve Block3856b092011-10-20 11:56:00 +0100444 ALOGV("%c %s", page->mFlags & 1 ? '+' : ' ', tmp.string());
Andreas Huber5a4001d2010-05-26 12:59:06 -0700445#endif
Andreas Huberee7ff202010-05-07 10:35:13 -0700446
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) {
James Dongc7fc37a2010-11-16 14:04:54 -0800473 off64_t dataOffset = mOffset + 27 + mCurrentPage.mNumSegments;
Andreas Huberee7ff202010-05-07 10:35:13 -0700474 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();
Andreas Huberecaccb92010-08-05 10:00:10 -0700487 } else {
Andreas Huberee7ff202010-05-07 10:35:13 -0700488 // 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.
Andreas Huberecaccb92010-08-05 10:00:10 -0700491
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 }
Andreas Huberee7ff202010-05-07 10:35:13 -0700498 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) {
Steve Block3856b092011-10-20 11:56:00 +0100508 ALOGV("failed to read %d bytes at 0x%016llx, got %ld bytes",
Andreas Huberdf659ac2011-05-10 16:14:51 -0700509 packetSize, dataOffset, n);
Andreas Huberee7ff202010-05-07 10:35:13 -0700510 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
Andreas Huberdb622222010-09-28 09:16:21 -0700524 if (mFirstPacketInPage) {
525 buffer->meta_data()->setInt32(
526 kKeyValidSamples, mCurrentPageSamples);
527 mFirstPacketInPage = false;
528 }
529
Andreas Huberee7ff202010-05-07 10:35:13 -0700530 *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
Steve Block3856b092011-10-20 11:56:00 +0100549 ALOGV("readPage returned %ld", n);
Andreas Huberee7ff202010-05-07 10:35:13 -0700550
551 return n < 0 ? n : (status_t)ERROR_END_OF_STREAM;
552 }
553
Andreas Huberdb622222010-09-28 09:16:21 -0700554 mCurrentPageSamples =
555 mCurrentPage.mGranulePosition - mPrevGranulePosition;
556 mFirstPacketInPage = true;
557
558 mPrevGranulePosition = mCurrentPage.mGranulePosition;
559
Andreas Huberee7ff202010-05-07 10:35:13 -0700560 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
Andreas Huberdb622222010-09-28 09:16:21 -0700572 buffer->meta_data()->setInt32(
573 kKeyValidSamples, mCurrentPageSamples);
574 mFirstPacketInPage = false;
575
Andreas Huberee7ff202010-05-07 10:35:13 -0700576 *out = buffer;
577
578 return OK;
579 }
580 }
581 }
582}
583
Andreas Huber5a4001d2010-05-26 12:59:06 -0700584status_t MyVorbisExtractor::init() {
Andreas Huberee7ff202010-05-07 10:35:13 -0700585 mMeta = new MetaData;
586 mMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_VORBIS);
587
588 MediaBuffer *packet;
Andreas Huber5a4001d2010-05-26 12:59:06 -0700589 status_t err;
590 if ((err = readNextPacket(&packet)) != OK) {
591 return err;
592 }
Steve Block3856b092011-10-20 11:56:00 +0100593 ALOGV("read packet of size %d\n", packet->range_length());
Andreas Huber5a4001d2010-05-26 12:59:06 -0700594 err = verifyHeader(packet, 1);
Andreas Huberee7ff202010-05-07 10:35:13 -0700595 packet->release();
596 packet = NULL;
Andreas Huber5a4001d2010-05-26 12:59:06 -0700597 if (err != OK) {
598 return err;
599 }
Andreas Huberee7ff202010-05-07 10:35:13 -0700600
Andreas Huber5a4001d2010-05-26 12:59:06 -0700601 if ((err = readNextPacket(&packet)) != OK) {
602 return err;
603 }
Steve Block3856b092011-10-20 11:56:00 +0100604 ALOGV("read packet of size %d\n", packet->range_length());
Andreas Huber5a4001d2010-05-26 12:59:06 -0700605 err = verifyHeader(packet, 3);
Andreas Huberee7ff202010-05-07 10:35:13 -0700606 packet->release();
607 packet = NULL;
Andreas Huber5a4001d2010-05-26 12:59:06 -0700608 if (err != OK) {
609 return err;
610 }
Andreas Huberee7ff202010-05-07 10:35:13 -0700611
Andreas Huber5a4001d2010-05-26 12:59:06 -0700612 if ((err = readNextPacket(&packet)) != OK) {
613 return err;
614 }
Steve Block3856b092011-10-20 11:56:00 +0100615 ALOGV("read packet of size %d\n", packet->range_length());
Andreas Huber5a4001d2010-05-26 12:59:06 -0700616 err = verifyHeader(packet, 5);
Andreas Huberee7ff202010-05-07 10:35:13 -0700617 packet->release();
618 packet = NULL;
Andreas Huber5a4001d2010-05-26 12:59:06 -0700619 if (err != OK) {
620 return err;
621 }
Andreas Huber96f52cd2010-05-11 10:11:55 -0700622
623 mFirstDataOffset = mOffset + mCurrentPageSize;
Andreas Huber5a4001d2010-05-26 12:59:06 -0700624
Andreas Huber3fd91ba2011-03-02 11:10:10 -0800625 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
Andreas Huber5a4001d2010-05-26 12:59:06 -0700643 return OK;
Andreas Huberee7ff202010-05-07 10:35:13 -0700644}
645
Andreas Huber3fd91ba2011-03-02 11:10:10 -0800646void 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
Andreas Huber5a4001d2010-05-26 12:59:06 -0700685status_t MyVorbisExtractor::verifyHeader(
Andreas Huberee7ff202010-05-07 10:35:13 -0700686 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
Andreas Huber5a4001d2010-05-26 12:59:06 -0700692 if (size < 7 || data[0] != type || memcmp(&data[1], "vorbis", 6)) {
693 return ERROR_MALFORMED;
694 }
Andreas Huberee7ff202010-05-07 10:35:13 -0700695
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
Steve Block3856b092011-10-20 11:56:00 +0100725 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);
Andreas Huberee7ff202010-05-07 10:35:13 -0700729
James Dongc7fc37a2010-11-16 14:04:54 -0800730 off64_t size;
Andreas Huberee7ff202010-05-07 10:35:13 -0700731 if (mSource->getSize(&size) == OK) {
732 uint64_t bps = approxBitrate();
Dongwon Kangf9fb1392011-06-17 22:52:19 +0900733 if (bps != 0) {
734 mMeta->setInt64(kKeyDuration, size * 8000000ll / bps);
735 }
Andreas Huberee7ff202010-05-07 10:35:13 -0700736 }
737 break;
738 }
739
740 case 3:
741 {
Andreas Huber5a4001d2010-05-26 12:59:06 -0700742 if (0 != _vorbis_unpack_comment(&mVc, &bits)) {
743 return ERROR_MALFORMED;
744 }
Andreas Huber4595be92010-05-25 11:20:29 -0700745
746 parseFileMetaData();
Andreas Huberee7ff202010-05-07 10:35:13 -0700747 break;
748 }
749
750 case 5:
751 {
Andreas Huber5a4001d2010-05-26 12:59:06 -0700752 if (0 != _vorbis_unpack_books(&mVi, &bits)) {
753 return ERROR_MALFORMED;
754 }
Andreas Huberee7ff202010-05-07 10:35:13 -0700755
756 mMeta->setData(kKeyVorbisBooks, 0, data, size);
757 break;
758 }
759 }
Andreas Huber5a4001d2010-05-26 12:59:06 -0700760
761 return OK;
Andreas Huberee7ff202010-05-07 10:35:13 -0700762}
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
Andreas Huber4595be92010-05-25 11:20:29 -0700772void MyVorbisExtractor::parseFileMetaData() {
773 mFileMeta = new MetaData;
774 mFileMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_CONTAINER_OGG);
775
Glenn Kasten856990b2011-01-13 11:17:00 -0800776 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);
Steve Blockdf64d152012-01-04 20:05:49 +0000780 //ALOGI("comment #%d: '%s'", i + 1, mVc.user_comments[i]);
Glenn Kasten856990b2011-01-13 11:17:00 -0800781 }
782}
783
784void parseVorbisComment(
785 const sp<MetaData> &fileMeta, const char *comment, size_t commentLength)
786{
Andreas Huber4595be92010-05-25 11:20:29 -0700787 struct {
788 const char *const mTag;
789 uint32_t mKey;
790 } kMap[] = {
791 { "TITLE", kKeyTitle },
792 { "ARTIST", kKeyArtist },
Marco Nelissen11f81092011-01-06 11:12:17 -0800793 { "ALBUMARTIST", kKeyAlbumArtist },
794 { "ALBUM ARTIST", kKeyAlbumArtist },
795 { "COMPILATION", kKeyCompilation },
Andreas Huber4595be92010-05-25 11:20:29 -0700796 { "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 },
Andreas Huber8ae49d82010-09-03 14:09:21 -0700805 { "ANDROID_LOOP", kKeyAutoLoop },
Andreas Huber4595be92010-05-25 11:20:29 -0700806 };
807
Andreas Huber4595be92010-05-25 11:20:29 -0700808 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(
Glenn Kasten856990b2011-01-13 11:17:00 -0800814 fileMeta,
Andreas Huber4595be92010-05-25 11:20:29 -0700815 &comment[tagLen + 1],
Glenn Kasten856990b2011-01-13 11:17:00 -0800816 commentLength - tagLen - 1);
Andreas Huber8ae49d82010-09-03 14:09:21 -0700817 } else if (kMap[j].mKey == kKeyAutoLoop) {
818 if (!strcasecmp(&comment[tagLen + 1], "true")) {
Glenn Kasten856990b2011-01-13 11:17:00 -0800819 fileMeta->setInt32(kKeyAutoLoop, true);
Andreas Huber8ae49d82010-09-03 14:09:21 -0700820 }
Andreas Huber4595be92010-05-25 11:20:29 -0700821 } else {
Glenn Kasten856990b2011-01-13 11:17:00 -0800822 fileMeta->setCString(kMap[j].mKey, &comment[tagLen + 1]);
Andreas Huber4595be92010-05-25 11:20:29 -0700823 }
824 }
825 }
Andreas Huber4595be92010-05-25 11:20:29 -0700826
Andreas Huber4595be92010-05-25 11:20:29 -0700827}
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
Glenn Kasten856990b2011-01-13 11:17:00 -0800894static void extractAlbumArt(
895 const sp<MetaData> &fileMeta, const void *data, size_t size) {
Steve Block3856b092011-10-20 11:56:00 +0100896 ALOGV("extractAlbumArt from '%s'", (const char *)data);
Andreas Huber4595be92010-05-25 11:20:29 -0700897
898 size_t flacSize;
899 uint8_t *flac = DecodeBase64((const char *)data, size, &flacSize);
900
901 if (flac == NULL) {
Steve Block29357bc2012-01-06 19:20:56 +0000902 ALOGE("malformed base64 encoded data.");
Andreas Huber4595be92010-05-25 11:20:29 -0700903 return;
904 }
905
Steve Block3856b092011-10-20 11:56:00 +0100906 ALOGV("got flac of size %d", flacSize);
Andreas Huber4595be92010-05-25 11:20:29 -0700907
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
Steve Block3856b092011-10-20 11:56:00 +0100937 ALOGV("picType = %d, type = '%s'", picType, type);
Andreas Huber4595be92010-05-25 11:20:29 -0700938
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
Steve Block3856b092011-10-20 11:56:00 +0100956 ALOGV("got image data, %d trailing bytes",
Andreas Huber4595be92010-05-25 11:20:29 -0700957 flacSize - 32 - typeLen - descLen - dataLen);
958
Glenn Kasten856990b2011-01-13 11:17:00 -0800959 fileMeta->setData(
Andreas Huber4595be92010-05-25 11:20:29 -0700960 kKeyAlbumArt, 0, &flac[8 + typeLen + 4 + descLen + 20], dataLen);
961
Glenn Kasten856990b2011-01-13 11:17:00 -0800962 fileMeta->setCString(kKeyAlbumArtMIME, type);
Andreas Huber4595be92010-05-25 11:20:29 -0700963
964exit:
965 free(flac);
966 flac = NULL;
967}
968
Andreas Huberee7ff202010-05-07 10:35:13 -0700969////////////////////////////////////////////////////////////////////////////////
970
971OggExtractor::OggExtractor(const sp<DataSource> &source)
972 : mDataSource(source),
973 mInitCheck(NO_INIT),
974 mImpl(NULL) {
975 mImpl = new MyVorbisExtractor(mDataSource);
Andreas Huber5a4001d2010-05-26 12:59:06 -0700976 mInitCheck = mImpl->seekToOffset(0);
Andreas Huberee7ff202010-05-07 10:35:13 -0700977
Andreas Huber5a4001d2010-05-26 12:59:06 -0700978 if (mInitCheck == OK) {
979 mInitCheck = mImpl->init();
980 }
Andreas Huberee7ff202010-05-07 10:35:13 -0700981}
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() {
Andreas Huber4595be92010-05-25 11:20:29 -07001010 return mImpl->getFileMetaData();
Andreas Huberee7ff202010-05-07 10:35:13 -07001011}
1012
1013bool SniffOgg(
Andreas Huber5a1c3522010-08-25 11:09:41 -07001014 const sp<DataSource> &source, String8 *mimeType, float *confidence,
1015 sp<AMessage> *) {
Andreas Huberee7ff202010-05-07 10:35:13 -07001016 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