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/ABuffer.h>
25 #include <media/stagefright/foundation/ADebug.h>
26 #include <media/stagefright/DataSource.h>
27 #include <media/stagefright/MediaBuffer.h>
28 #include <media/stagefright/MediaBufferGroup.h>
29 #include <media/stagefright/MediaDefs.h>
30 #include <media/stagefright/MediaErrors.h>
31 #include <media/stagefright/MediaSource.h>
32 #include <media/stagefright/MetaData.h>
33 #include <media/stagefright/Utils.h>
34 #include <utils/String8.h>
35
36 extern "C" {
37 #include <Tremolo/codec_internal.h>
38
39 int _vorbis_unpack_books(vorbis_info *vi,oggpack_buffer *opb);
40 int _vorbis_unpack_info(vorbis_info *vi,oggpack_buffer *opb);
41 int _vorbis_unpack_comment(vorbis_comment *vc,oggpack_buffer *opb);
42 long vorbis_packet_blocksize(vorbis_info *vi,ogg_packet *op);
43 }
44
45 namespace android {
46
47 struct OggSource : public MediaSource {
48 OggSource(const sp<OggExtractor> &extractor);
49
50 virtual sp<MetaData> getFormat();
51
52 virtual status_t start(MetaData *params = NULL);
53 virtual status_t stop();
54
55 virtual status_t read(
56 MediaBuffer **buffer, const ReadOptions *options = NULL);
57
58 protected:
59 virtual ~OggSource();
60
61 private:
62 sp<OggExtractor> mExtractor;
63 bool mStarted;
64
65 OggSource(const OggSource &);
66 OggSource &operator=(const OggSource &);
67 };
68
69 struct MyOggExtractor {
70 MyOggExtractor(
71 const sp<DataSource> &source,
72 const char *mimeType,
73 size_t numHeaders,
74 int64_t seekPreRollUs);
75 virtual ~MyOggExtractor();
76
77 sp<MetaData> getFormat() const;
78
79 // Returns an approximate bitrate in bits per second.
80 virtual uint64_t approxBitrate() const = 0;
81
82 status_t seekToTime(int64_t timeUs);
83 status_t seekToOffset(off64_t offset);
84 virtual status_t readNextPacket(MediaBuffer **buffer) = 0;
85
86 status_t init();
87
getFileMetaDataandroid::MyOggExtractor88 sp<MetaData> getFileMetaData() { return mFileMeta; }
89
90 protected:
91 struct Page {
92 uint64_t mGranulePosition;
93 int32_t mPrevPacketSize;
94 uint64_t mPrevPacketPos;
95 uint32_t mSerialNo;
96 uint32_t mPageNo;
97 uint8_t mFlags;
98 uint8_t mNumSegments;
99 uint8_t mLace[255];
100 };
101
102 struct TOCEntry {
103 off64_t mPageOffset;
104 int64_t mTimeUs;
105 };
106
107 sp<DataSource> mSource;
108 off64_t mOffset;
109 Page mCurrentPage;
110 uint64_t mCurGranulePosition;
111 uint64_t mPrevGranulePosition;
112 size_t mCurrentPageSize;
113 bool mFirstPacketInPage;
114 uint64_t mCurrentPageSamples;
115 size_t mNextLaceIndex;
116
117 const char *mMimeType;
118 size_t mNumHeaders;
119 int64_t mSeekPreRollUs;
120
121 off64_t mFirstDataOffset;
122
123 vorbis_info mVi;
124 vorbis_comment mVc;
125
126 sp<MetaData> mMeta;
127 sp<MetaData> mFileMeta;
128
129 Vector<TOCEntry> mTableOfContents;
130
131 ssize_t readPage(off64_t offset, Page *page);
132 status_t findNextPage(off64_t startOffset, off64_t *pageOffset);
133
134 virtual int64_t getTimeUsOfGranule(uint64_t granulePos) const = 0;
135
136 // Extract codec format, metadata tags, and various codec specific data;
137 // the format and CSD's are required to setup the decoders for the enclosed media content.
138 //
139 // Valid values for `type` are:
140 // 1 - bitstream identification header
141 // 3 - comment header
142 // 5 - codec setup header (Vorbis only)
143 virtual status_t verifyHeader(MediaBuffer *buffer, uint8_t type) = 0;
144
145 // Read the next ogg packet from the underlying data source; optionally
146 // calculate the timestamp for the output packet whilst pretending
147 // that we are parsing an Ogg Vorbis stream.
148 //
149 // *buffer is NULL'ed out immediately upon entry, and if successful a new buffer is allocated;
150 // clients are responsible for releasing the original buffer.
151 status_t _readNextPacket(MediaBuffer **buffer, bool calcVorbisTimestamp);
152
153 int32_t getPacketBlockSize(MediaBuffer *buffer);
154
155 void parseFileMetaData();
156
157 status_t findPrevGranulePosition(off64_t pageOffset, uint64_t *granulePos);
158
159 void buildTableOfContents();
160
161 MyOggExtractor(const MyOggExtractor &);
162 MyOggExtractor &operator=(const MyOggExtractor &);
163 };
164
165 struct MyVorbisExtractor : public MyOggExtractor {
MyVorbisExtractorandroid::MyVorbisExtractor166 MyVorbisExtractor(const sp<DataSource> &source)
167 : MyOggExtractor(source,
168 MEDIA_MIMETYPE_AUDIO_VORBIS,
169 /* numHeaders */ 3,
170 /* seekPreRollUs */ 0) {
171 }
172
173 virtual uint64_t approxBitrate() const;
174
readNextPacketandroid::MyVorbisExtractor175 virtual status_t readNextPacket(MediaBuffer **buffer) {
176 return _readNextPacket(buffer, /* calcVorbisTimestamp = */ true);
177 }
178
179 protected:
getTimeUsOfGranuleandroid::MyVorbisExtractor180 virtual int64_t getTimeUsOfGranule(uint64_t granulePos) const {
181 return granulePos * 1000000ll / mVi.rate;
182 }
183
184 virtual status_t verifyHeader(MediaBuffer *buffer, uint8_t type);
185 };
186
187 struct MyOpusExtractor : public MyOggExtractor {
188 static const int32_t kOpusSampleRate = 48000;
189 static const int64_t kOpusSeekPreRollUs = 80000; // 80 ms
190
MyOpusExtractorandroid::MyOpusExtractor191 MyOpusExtractor(const sp<DataSource> &source)
192 : MyOggExtractor(source, MEDIA_MIMETYPE_AUDIO_OPUS, /*numHeaders*/ 2, kOpusSeekPreRollUs),
193 mChannelCount(0),
194 mCodecDelay(0),
195 mStartGranulePosition(-1) {
196 }
197
approxBitrateandroid::MyOpusExtractor198 virtual uint64_t approxBitrate() const {
199 return 0;
200 }
201
202 virtual status_t readNextPacket(MediaBuffer **buffer);
203
204 protected:
205 virtual int64_t getTimeUsOfGranule(uint64_t granulePos) const;
206 virtual status_t verifyHeader(MediaBuffer *buffer, uint8_t type);
207
208 private:
209 status_t verifyOpusHeader(MediaBuffer *buffer);
210 status_t verifyOpusComments(MediaBuffer *buffer);
211 uint32_t getNumSamplesInPacket(MediaBuffer *buffer) const;
212
213 uint8_t mChannelCount;
214 uint16_t mCodecDelay;
215 int64_t mStartGranulePosition;
216 };
217
218 static void extractAlbumArt(
219 const sp<MetaData> &fileMeta, const void *data, size_t size);
220
221 ////////////////////////////////////////////////////////////////////////////////
222
OggSource(const sp<OggExtractor> & extractor)223 OggSource::OggSource(const sp<OggExtractor> &extractor)
224 : mExtractor(extractor),
225 mStarted(false) {
226 }
227
~OggSource()228 OggSource::~OggSource() {
229 if (mStarted) {
230 stop();
231 }
232 }
233
getFormat()234 sp<MetaData> OggSource::getFormat() {
235 return mExtractor->mImpl->getFormat();
236 }
237
start(MetaData *)238 status_t OggSource::start(MetaData * /* params */) {
239 if (mStarted) {
240 return INVALID_OPERATION;
241 }
242
243 mStarted = true;
244
245 return OK;
246 }
247
stop()248 status_t OggSource::stop() {
249 mStarted = false;
250
251 return OK;
252 }
253
read(MediaBuffer ** out,const ReadOptions * options)254 status_t OggSource::read(
255 MediaBuffer **out, const ReadOptions *options) {
256 *out = NULL;
257
258 int64_t seekTimeUs;
259 ReadOptions::SeekMode mode;
260 if (options && options->getSeekTo(&seekTimeUs, &mode)) {
261 status_t err = mExtractor->mImpl->seekToTime(seekTimeUs);
262 if (err != OK) {
263 return err;
264 }
265 }
266
267 MediaBuffer *packet;
268 status_t err = mExtractor->mImpl->readNextPacket(&packet);
269
270 if (err != OK) {
271 return err;
272 }
273
274 #if 0
275 int64_t timeUs;
276 if (packet->meta_data()->findInt64(kKeyTime, &timeUs)) {
277 ALOGI("found time = %lld us", timeUs);
278 } else {
279 ALOGI("NO time");
280 }
281 #endif
282
283 packet->meta_data()->setInt32(kKeyIsSyncFrame, 1);
284
285 *out = packet;
286
287 return OK;
288 }
289
290 ////////////////////////////////////////////////////////////////////////////////
291
MyOggExtractor(const sp<DataSource> & source,const char * mimeType,size_t numHeaders,int64_t seekPreRollUs)292 MyOggExtractor::MyOggExtractor(
293 const sp<DataSource> &source,
294 const char *mimeType,
295 size_t numHeaders,
296 int64_t seekPreRollUs)
297 : mSource(source),
298 mOffset(0),
299 mCurGranulePosition(0),
300 mPrevGranulePosition(0),
301 mCurrentPageSize(0),
302 mFirstPacketInPage(true),
303 mCurrentPageSamples(0),
304 mNextLaceIndex(0),
305 mMimeType(mimeType),
306 mNumHeaders(numHeaders),
307 mSeekPreRollUs(seekPreRollUs),
308 mFirstDataOffset(-1) {
309 mCurrentPage.mNumSegments = 0;
310
311 vorbis_info_init(&mVi);
312 vorbis_comment_init(&mVc);
313 }
314
~MyOggExtractor()315 MyOggExtractor::~MyOggExtractor() {
316 vorbis_comment_clear(&mVc);
317 vorbis_info_clear(&mVi);
318 }
319
getFormat() const320 sp<MetaData> MyOggExtractor::getFormat() const {
321 return mMeta;
322 }
323
findNextPage(off64_t startOffset,off64_t * pageOffset)324 status_t MyOggExtractor::findNextPage(
325 off64_t startOffset, off64_t *pageOffset) {
326 *pageOffset = startOffset;
327
328 for (;;) {
329 char signature[4];
330 ssize_t n = mSource->readAt(*pageOffset, &signature, 4);
331
332 if (n < 4) {
333 *pageOffset = 0;
334
335 return (n < 0) ? n : (status_t)ERROR_END_OF_STREAM;
336 }
337
338 if (!memcmp(signature, "OggS", 4)) {
339 if (*pageOffset > startOffset) {
340 ALOGV("skipped %lld bytes of junk to reach next frame",
341 (long long)(*pageOffset - startOffset));
342 }
343
344 return OK;
345 }
346
347 ++*pageOffset;
348 }
349 }
350
351 // Given the offset of the "current" page, find the page immediately preceding
352 // it (if any) and return its granule position.
353 // To do this we back up from the "current" page's offset until we find any
354 // page preceding it and then scan forward to just before the current page.
findPrevGranulePosition(off64_t pageOffset,uint64_t * granulePos)355 status_t MyOggExtractor::findPrevGranulePosition(
356 off64_t pageOffset, uint64_t *granulePos) {
357 *granulePos = 0;
358
359 off64_t prevPageOffset = 0;
360 off64_t prevGuess = pageOffset;
361 for (;;) {
362 if (prevGuess >= 5000) {
363 prevGuess -= 5000;
364 } else {
365 prevGuess = 0;
366 }
367
368 ALOGV("backing up %lld bytes", (long long)(pageOffset - prevGuess));
369
370 status_t err = findNextPage(prevGuess, &prevPageOffset);
371 if (err == ERROR_END_OF_STREAM) {
372 // We are at the last page and didn't back off enough;
373 // back off 5000 bytes more and try again.
374 continue;
375 } else if (err != OK) {
376 return err;
377 }
378
379 if (prevPageOffset < pageOffset || prevGuess == 0) {
380 break;
381 }
382 }
383
384 if (prevPageOffset == pageOffset) {
385 // We did not find a page preceding this one.
386 return UNKNOWN_ERROR;
387 }
388
389 ALOGV("prevPageOffset at %lld, pageOffset at %lld",
390 (long long)prevPageOffset, (long long)pageOffset);
391
392 for (;;) {
393 Page prevPage;
394 ssize_t n = readPage(prevPageOffset, &prevPage);
395
396 if (n <= 0) {
397 return (status_t)n;
398 }
399
400 prevPageOffset += n;
401
402 if (prevPageOffset == pageOffset) {
403 *granulePos = prevPage.mGranulePosition;
404 return OK;
405 }
406 }
407 }
408
seekToTime(int64_t timeUs)409 status_t MyOggExtractor::seekToTime(int64_t timeUs) {
410 timeUs -= mSeekPreRollUs;
411 if (timeUs < 0) {
412 timeUs = 0;
413 }
414
415 if (mTableOfContents.isEmpty()) {
416 // Perform approximate seeking based on avg. bitrate.
417 uint64_t bps = approxBitrate();
418 if (bps <= 0) {
419 return INVALID_OPERATION;
420 }
421
422 off64_t pos = timeUs * bps / 8000000ll;
423
424 ALOGV("seeking to offset %lld", (long long)pos);
425 return seekToOffset(pos);
426 }
427
428 size_t left = 0;
429 size_t right_plus_one = mTableOfContents.size();
430 while (left < right_plus_one) {
431 size_t center = left + (right_plus_one - left) / 2;
432
433 const TOCEntry &entry = mTableOfContents.itemAt(center);
434
435 if (timeUs < entry.mTimeUs) {
436 right_plus_one = center;
437 } else if (timeUs > entry.mTimeUs) {
438 left = center + 1;
439 } else {
440 left = center;
441 break;
442 }
443 }
444
445 if (left == mTableOfContents.size()) {
446 --left;
447 }
448
449 const TOCEntry &entry = mTableOfContents.itemAt(left);
450
451 ALOGV("seeking to entry %zu / %zu at offset %lld",
452 left, mTableOfContents.size(), (long long)entry.mPageOffset);
453
454 return seekToOffset(entry.mPageOffset);
455 }
456
seekToOffset(off64_t offset)457 status_t MyOggExtractor::seekToOffset(off64_t offset) {
458 if (mFirstDataOffset >= 0 && offset < mFirstDataOffset) {
459 // Once we know where the actual audio data starts (past the headers)
460 // don't ever seek to anywhere before that.
461 offset = mFirstDataOffset;
462 }
463
464 off64_t pageOffset;
465 status_t err = findNextPage(offset, &pageOffset);
466
467 if (err != OK) {
468 return err;
469 }
470
471 // We found the page we wanted to seek to, but we'll also need
472 // the page preceding it to determine how many valid samples are on
473 // this page.
474 findPrevGranulePosition(pageOffset, &mPrevGranulePosition);
475
476 mOffset = pageOffset;
477
478 mCurrentPageSize = 0;
479 mFirstPacketInPage = true;
480 mCurrentPageSamples = 0;
481 mCurrentPage.mNumSegments = 0;
482 mCurrentPage.mPrevPacketSize = -1;
483 mNextLaceIndex = 0;
484
485 // XXX what if new page continues packet from last???
486
487 return OK;
488 }
489
readPage(off64_t offset,Page * page)490 ssize_t MyOggExtractor::readPage(off64_t offset, Page *page) {
491 uint8_t header[27];
492 ssize_t n;
493 if ((n = mSource->readAt(offset, header, sizeof(header)))
494 < (ssize_t)sizeof(header)) {
495 ALOGV("failed to read %zu bytes at offset %#016llx, got %zd bytes",
496 sizeof(header), (long long)offset, n);
497
498 if (n < 0) {
499 return n;
500 } else if (n == 0) {
501 return ERROR_END_OF_STREAM;
502 } else {
503 return ERROR_IO;
504 }
505 }
506
507 if (memcmp(header, "OggS", 4)) {
508 return ERROR_MALFORMED;
509 }
510
511 if (header[4] != 0) {
512 // Wrong version.
513
514 return ERROR_UNSUPPORTED;
515 }
516
517 page->mFlags = header[5];
518
519 if (page->mFlags & ~7) {
520 // Only bits 0-2 are defined in version 0.
521 return ERROR_MALFORMED;
522 }
523
524 page->mGranulePosition = U64LE_AT(&header[6]);
525
526 #if 0
527 printf("granulePosition = %llu (0x%llx)\n",
528 page->mGranulePosition, page->mGranulePosition);
529 #endif
530
531 page->mSerialNo = U32LE_AT(&header[14]);
532 page->mPageNo = U32LE_AT(&header[18]);
533
534 page->mNumSegments = header[26];
535 if (mSource->readAt(
536 offset + sizeof(header), page->mLace, page->mNumSegments)
537 < (ssize_t)page->mNumSegments) {
538 return ERROR_IO;
539 }
540
541 size_t totalSize = 0;;
542 for (size_t i = 0; i < page->mNumSegments; ++i) {
543 totalSize += page->mLace[i];
544 }
545
546 #if 0
547 String8 tmp;
548 for (size_t i = 0; i < page->mNumSegments; ++i) {
549 char x[32];
550 sprintf(x, "%s%u", i > 0 ? ", " : "", (unsigned)page->mLace[i]);
551
552 tmp.append(x);
553 }
554
555 ALOGV("%c %s", page->mFlags & 1 ? '+' : ' ', tmp.string());
556 #endif
557
558 return sizeof(header) + page->mNumSegments + totalSize;
559 }
560
readNextPacket(MediaBuffer ** out)561 status_t MyOpusExtractor::readNextPacket(MediaBuffer **out) {
562 if (mOffset <= mFirstDataOffset && mStartGranulePosition < 0) {
563 // The first sample might not start at time 0; find out where by subtracting
564 // the number of samples on the first page from the granule position
565 // (position of last complete sample) of the first page. This happens
566 // the first time before we attempt to read a packet from the first page.
567 MediaBuffer *mBuf;
568 uint32_t numSamples = 0;
569 uint64_t curGranulePosition = 0;
570 while (true) {
571 status_t err = _readNextPacket(&mBuf, /* calcVorbisTimestamp = */false);
572 if (err != OK && err != ERROR_END_OF_STREAM) {
573 return err;
574 }
575 // First two pages are header pages.
576 if (err == ERROR_END_OF_STREAM || mCurrentPage.mPageNo > 2) {
577 break;
578 }
579 curGranulePosition = mCurrentPage.mGranulePosition;
580 numSamples += getNumSamplesInPacket(mBuf);
581 mBuf->release();
582 mBuf = NULL;
583 }
584
585 if (curGranulePosition > numSamples) {
586 mStartGranulePosition = curGranulePosition - numSamples;
587 } else {
588 mStartGranulePosition = 0;
589 }
590 seekToOffset(0);
591 }
592
593 status_t err = _readNextPacket(out, /* calcVorbisTimestamp = */false);
594 if (err != OK) {
595 return err;
596 }
597
598 int32_t currentPageSamples;
599 // Calculate timestamps by accumulating durations starting from the first sample of a page;
600 // We assume that we only seek to page boundaries.
601 if ((*out)->meta_data()->findInt32(kKeyValidSamples, ¤tPageSamples)) {
602 // first packet in page
603 if (mOffset == mFirstDataOffset) {
604 currentPageSamples -= mStartGranulePosition;
605 (*out)->meta_data()->setInt32(kKeyValidSamples, currentPageSamples);
606 }
607 mCurGranulePosition = mCurrentPage.mGranulePosition - currentPageSamples;
608 }
609
610 int64_t timeUs = getTimeUsOfGranule(mCurGranulePosition);
611 (*out)->meta_data()->setInt64(kKeyTime, timeUs);
612
613 uint32_t frames = getNumSamplesInPacket(*out);
614 mCurGranulePosition += frames;
615 return OK;
616 }
617
getNumSamplesInPacket(MediaBuffer * buffer) const618 uint32_t MyOpusExtractor::getNumSamplesInPacket(MediaBuffer *buffer) const {
619 if (buffer == NULL || buffer->range_length() < 1) {
620 return 0;
621 }
622
623 uint8_t *data = (uint8_t *)buffer->data() + buffer->range_offset();
624 uint8_t toc = data[0];
625 uint8_t config = (toc >> 3) & 0x1f;
626 uint32_t frameSizesUs[] = {
627 10000, 20000, 40000, 60000, // 0...3
628 10000, 20000, 40000, 60000, // 4...7
629 10000, 20000, 40000, 60000, // 8...11
630 10000, 20000, // 12...13
631 10000, 20000, // 14...15
632 2500, 5000, 10000, 20000, // 16...19
633 2500, 5000, 10000, 20000, // 20...23
634 2500, 5000, 10000, 20000, // 24...27
635 2500, 5000, 10000, 20000 // 28...31
636 };
637 uint32_t frameSizeUs = frameSizesUs[config];
638
639 uint32_t numFrames;
640 uint8_t c = toc & 3;
641 switch (c) {
642 case 0:
643 numFrames = 1;
644 break;
645 case 1:
646 case 2:
647 numFrames = 2;
648 break;
649 case 3:
650 if (buffer->range_length() < 3) {
651 numFrames = 0;
652 } else {
653 numFrames = data[2] & 0x3f;
654 }
655 break;
656 default:
657 TRESPASS();
658 }
659
660 uint32_t numSamples = frameSizeUs * numFrames * kOpusSampleRate / 1000000;
661 return numSamples;
662 }
663
_readNextPacket(MediaBuffer ** out,bool calcVorbisTimestamp)664 status_t MyOggExtractor::_readNextPacket(MediaBuffer **out, bool calcVorbisTimestamp) {
665 *out = NULL;
666
667 MediaBuffer *buffer = NULL;
668 int64_t timeUs = -1;
669
670 for (;;) {
671 size_t i;
672 size_t packetSize = 0;
673 bool gotFullPacket = false;
674 for (i = mNextLaceIndex; i < mCurrentPage.mNumSegments; ++i) {
675 uint8_t lace = mCurrentPage.mLace[i];
676
677 packetSize += lace;
678
679 if (lace < 255) {
680 gotFullPacket = true;
681 ++i;
682 break;
683 }
684 }
685
686 if (mNextLaceIndex < mCurrentPage.mNumSegments) {
687 off64_t dataOffset = mOffset + 27 + mCurrentPage.mNumSegments;
688 for (size_t j = 0; j < mNextLaceIndex; ++j) {
689 dataOffset += mCurrentPage.mLace[j];
690 }
691
692 size_t fullSize = packetSize;
693 if (buffer != NULL) {
694 fullSize += buffer->range_length();
695 }
696 MediaBuffer *tmp = new MediaBuffer(fullSize);
697 if (buffer != NULL) {
698 memcpy(tmp->data(), buffer->data(), buffer->range_length());
699 tmp->set_range(0, buffer->range_length());
700 buffer->release();
701 } else {
702 tmp->set_range(0, 0);
703 }
704 buffer = tmp;
705
706 ssize_t n = mSource->readAt(
707 dataOffset,
708 (uint8_t *)buffer->data() + buffer->range_length(),
709 packetSize);
710
711 if (n < (ssize_t)packetSize) {
712 ALOGV("failed to read %zu bytes at %#016llx, got %zd bytes",
713 packetSize, (long long)dataOffset, n);
714 return ERROR_IO;
715 }
716
717 buffer->set_range(0, fullSize);
718
719 mNextLaceIndex = i;
720
721 if (gotFullPacket) {
722 // We've just read the entire packet.
723
724 if (mFirstPacketInPage) {
725 buffer->meta_data()->setInt32(
726 kKeyValidSamples, mCurrentPageSamples);
727 mFirstPacketInPage = false;
728 }
729
730 if (calcVorbisTimestamp) {
731 int32_t curBlockSize = getPacketBlockSize(buffer);
732 if (mCurrentPage.mPrevPacketSize < 0) {
733 mCurrentPage.mPrevPacketSize = curBlockSize;
734 mCurrentPage.mPrevPacketPos =
735 mCurrentPage.mGranulePosition - mCurrentPageSamples;
736 timeUs = mCurrentPage.mPrevPacketPos * 1000000ll / mVi.rate;
737 } else {
738 // The effective block size is the average of the two overlapped blocks
739 int32_t actualBlockSize =
740 (curBlockSize + mCurrentPage.mPrevPacketSize) / 2;
741 timeUs = mCurrentPage.mPrevPacketPos * 1000000ll / mVi.rate;
742 // The actual size output by the decoder will be half the effective
743 // size, due to the overlap
744 mCurrentPage.mPrevPacketPos += actualBlockSize / 2;
745 mCurrentPage.mPrevPacketSize = curBlockSize;
746 }
747 buffer->meta_data()->setInt64(kKeyTime, timeUs);
748 }
749 *out = buffer;
750
751 return OK;
752 }
753
754 // fall through, the buffer now contains the start of the packet.
755 }
756
757 CHECK_EQ(mNextLaceIndex, mCurrentPage.mNumSegments);
758
759 mOffset += mCurrentPageSize;
760 ssize_t n = readPage(mOffset, &mCurrentPage);
761
762 if (n <= 0) {
763 if (buffer) {
764 buffer->release();
765 buffer = NULL;
766 }
767
768 ALOGV("readPage returned %zd", n);
769
770 return n < 0 ? n : (status_t)ERROR_END_OF_STREAM;
771 }
772
773 mCurrentPageSamples =
774 mCurrentPage.mGranulePosition - mPrevGranulePosition;
775 mFirstPacketInPage = true;
776
777 mPrevGranulePosition = mCurrentPage.mGranulePosition;
778
779 mCurrentPageSize = n;
780 mNextLaceIndex = 0;
781
782 if (buffer != NULL) {
783 if ((mCurrentPage.mFlags & 1) == 0) {
784 // This page does not continue the packet, i.e. the packet
785 // is already complete.
786
787 if (timeUs >= 0) {
788 buffer->meta_data()->setInt64(kKeyTime, timeUs);
789 }
790
791 buffer->meta_data()->setInt32(
792 kKeyValidSamples, mCurrentPageSamples);
793 mFirstPacketInPage = false;
794
795 *out = buffer;
796
797 return OK;
798 }
799 }
800 }
801 }
802
init()803 status_t MyOggExtractor::init() {
804 mMeta = new MetaData;
805 mMeta->setCString(kKeyMIMEType, mMimeType);
806
807 status_t err;
808 MediaBuffer *packet;
809 for (size_t i = 0; i < mNumHeaders; ++i) {
810 // ignore timestamp for configuration packets
811 if ((err = _readNextPacket(&packet, /* calcVorbisTimestamp = */ false)) != OK) {
812 return err;
813 }
814 ALOGV("read packet of size %zu\n", packet->range_length());
815 err = verifyHeader(packet, /* type = */ i * 2 + 1);
816 packet->release();
817 packet = NULL;
818 if (err != OK) {
819 return err;
820 }
821 }
822
823 mFirstDataOffset = mOffset + mCurrentPageSize;
824
825 off64_t size;
826 uint64_t lastGranulePosition;
827 if (!(mSource->flags() & DataSource::kIsCachingDataSource)
828 && mSource->getSize(&size) == OK
829 && findPrevGranulePosition(size, &lastGranulePosition) == OK) {
830 // Let's assume it's cheap to seek to the end.
831 // The granule position of the final page in the stream will
832 // give us the exact duration of the content, something that
833 // we can only approximate using avg. bitrate if seeking to
834 // the end is too expensive or impossible (live streaming).
835
836 int64_t durationUs = getTimeUsOfGranule(lastGranulePosition);
837
838 mMeta->setInt64(kKeyDuration, durationUs);
839
840 buildTableOfContents();
841 }
842
843 return OK;
844 }
845
buildTableOfContents()846 void MyOggExtractor::buildTableOfContents() {
847 off64_t offset = mFirstDataOffset;
848 Page page;
849 ssize_t pageSize;
850 while ((pageSize = readPage(offset, &page)) > 0) {
851 mTableOfContents.push();
852
853 TOCEntry &entry =
854 mTableOfContents.editItemAt(mTableOfContents.size() - 1);
855
856 entry.mPageOffset = offset;
857 entry.mTimeUs = getTimeUsOfGranule(page.mGranulePosition);
858
859 offset += (size_t)pageSize;
860 }
861
862 // Limit the maximum amount of RAM we spend on the table of contents,
863 // if necessary thin out the table evenly to trim it down to maximum
864 // size.
865
866 static const size_t kMaxTOCSize = 8192;
867 static const size_t kMaxNumTOCEntries = kMaxTOCSize / sizeof(TOCEntry);
868
869 size_t numerator = mTableOfContents.size();
870
871 if (numerator > kMaxNumTOCEntries) {
872 size_t denom = numerator - kMaxNumTOCEntries;
873
874 size_t accum = 0;
875 for (ssize_t i = mTableOfContents.size() - 1; i >= 0; --i) {
876 accum += denom;
877 if (accum >= numerator) {
878 mTableOfContents.removeAt(i);
879 accum -= numerator;
880 }
881 }
882 }
883 }
884
getPacketBlockSize(MediaBuffer * buffer)885 int32_t MyOggExtractor::getPacketBlockSize(MediaBuffer *buffer) {
886 const uint8_t *data =
887 (const uint8_t *)buffer->data() + buffer->range_offset();
888
889 size_t size = buffer->range_length();
890
891 ogg_buffer buf;
892 buf.data = (uint8_t *)data;
893 buf.size = size;
894 buf.refcount = 1;
895 buf.ptr.owner = NULL;
896
897 ogg_reference ref;
898 ref.buffer = &buf;
899 ref.begin = 0;
900 ref.length = size;
901 ref.next = NULL;
902
903 ogg_packet pack;
904 pack.packet = &ref;
905 pack.bytes = ref.length;
906 pack.b_o_s = 0;
907 pack.e_o_s = 0;
908 pack.granulepos = 0;
909 pack.packetno = 0;
910
911 return vorbis_packet_blocksize(&mVi, &pack);
912 }
913
getTimeUsOfGranule(uint64_t granulePos) const914 int64_t MyOpusExtractor::getTimeUsOfGranule(uint64_t granulePos) const {
915 uint64_t pcmSamplePosition = 0;
916 if (granulePos > mCodecDelay) {
917 pcmSamplePosition = granulePos - mCodecDelay;
918 }
919 return pcmSamplePosition * 1000000ll / kOpusSampleRate;
920 }
921
verifyHeader(MediaBuffer * buffer,uint8_t type)922 status_t MyOpusExtractor::verifyHeader(MediaBuffer *buffer, uint8_t type) {
923 switch (type) {
924 // there are actually no header types defined in the Opus spec; we choose 1 and 3 to mean
925 // header and comments such that we can share code with MyVorbisExtractor.
926 case 1:
927 return verifyOpusHeader(buffer);
928 case 3:
929 return verifyOpusComments(buffer);
930 default:
931 return INVALID_OPERATION;
932 }
933 }
934
verifyOpusHeader(MediaBuffer * buffer)935 status_t MyOpusExtractor::verifyOpusHeader(MediaBuffer *buffer) {
936 const size_t kOpusHeaderSize = 19;
937 const uint8_t *data =
938 (const uint8_t *)buffer->data() + buffer->range_offset();
939
940 size_t size = buffer->range_length();
941
942 if (size < kOpusHeaderSize
943 || memcmp(data, "OpusHead", 8)
944 || /* version = */ data[8] != 1) {
945 return ERROR_MALFORMED;
946 }
947
948 mChannelCount = data[9];
949 mCodecDelay = U16LE_AT(&data[10]);
950
951 mMeta->setData(kKeyOpusHeader, 0, data, size);
952 mMeta->setInt32(kKeySampleRate, kOpusSampleRate);
953 mMeta->setInt32(kKeyChannelCount, mChannelCount);
954 mMeta->setInt64(kKeyOpusSeekPreRoll /* ns */, kOpusSeekPreRollUs * 1000 /* = 80 ms*/);
955 mMeta->setInt64(kKeyOpusCodecDelay /* ns */,
956 mCodecDelay /* sample/s */ * 1000000000 / kOpusSampleRate);
957
958 return OK;
959 }
960
verifyOpusComments(MediaBuffer * buffer)961 status_t MyOpusExtractor::verifyOpusComments(MediaBuffer *buffer) {
962 // add artificial framing bit so we can reuse _vorbis_unpack_comment
963 int32_t commentSize = buffer->range_length() + 1;
964 sp<ABuffer> aBuf = new ABuffer(commentSize);
965 if (aBuf->capacity() <= buffer->range_length()) {
966 return ERROR_MALFORMED;
967 }
968
969 uint8_t* commentData = aBuf->data();
970 memcpy(commentData,
971 (uint8_t *)buffer->data() + buffer->range_offset(),
972 buffer->range_length());
973
974 ogg_buffer buf;
975 buf.data = commentData;
976 buf.size = commentSize;
977 buf.refcount = 1;
978 buf.ptr.owner = NULL;
979
980 ogg_reference ref;
981 ref.buffer = &buf;
982 ref.begin = 0;
983 ref.length = commentSize;
984 ref.next = NULL;
985
986 oggpack_buffer bits;
987 oggpack_readinit(&bits, &ref);
988
989 // skip 'OpusTags'
990 const char *OpusTags = "OpusTags";
991 const int32_t headerLen = strlen(OpusTags);
992 int32_t framingBitOffset = headerLen;
993 for (int i = 0; i < headerLen; ++i) {
994 char chr = oggpack_read(&bits, 8);
995 if (chr != OpusTags[i]) {
996 return ERROR_MALFORMED;
997 }
998 }
999
1000 int32_t vendorLen = oggpack_read(&bits, 32);
1001 framingBitOffset += 4;
1002 if (vendorLen < 0 || vendorLen > commentSize - 8) {
1003 return ERROR_MALFORMED;
1004 }
1005 // skip vendor string
1006 framingBitOffset += vendorLen;
1007 for (int i = 0; i < vendorLen; ++i) {
1008 oggpack_read(&bits, 8);
1009 }
1010
1011 int32_t n = oggpack_read(&bits, 32);
1012 framingBitOffset += 4;
1013 if (n < 0 || n > ((commentSize - oggpack_bytes(&bits)) >> 2)) {
1014 return ERROR_MALFORMED;
1015 }
1016 for (int i = 0; i < n; ++i) {
1017 int32_t len = oggpack_read(&bits, 32);
1018 framingBitOffset += 4;
1019 if (len < 0 || len > (commentSize - oggpack_bytes(&bits))) {
1020 return ERROR_MALFORMED;
1021 }
1022 framingBitOffset += len;
1023 for (int j = 0; j < len; ++j) {
1024 oggpack_read(&bits, 8);
1025 }
1026 }
1027 if (framingBitOffset < 0 || framingBitOffset >= commentSize) {
1028 return ERROR_MALFORMED;
1029 }
1030 commentData[framingBitOffset] = 1;
1031
1032 buf.data = commentData + headerLen;
1033 buf.size = commentSize - headerLen;
1034 buf.refcount = 1;
1035 buf.ptr.owner = NULL;
1036
1037 ref.buffer = &buf;
1038 ref.begin = 0;
1039 ref.length = commentSize - headerLen;
1040 ref.next = NULL;
1041
1042 oggpack_readinit(&bits, &ref);
1043 int err = _vorbis_unpack_comment(&mVc, &bits);
1044 if (0 != err) {
1045 return ERROR_MALFORMED;
1046 }
1047
1048 parseFileMetaData();
1049 return OK;
1050 }
1051
verifyHeader(MediaBuffer * buffer,uint8_t type)1052 status_t MyVorbisExtractor::verifyHeader(
1053 MediaBuffer *buffer, uint8_t type) {
1054 const uint8_t *data =
1055 (const uint8_t *)buffer->data() + buffer->range_offset();
1056
1057 size_t size = buffer->range_length();
1058
1059 if (size < 7 || data[0] != type || memcmp(&data[1], "vorbis", 6)) {
1060 return ERROR_MALFORMED;
1061 }
1062
1063 ogg_buffer buf;
1064 buf.data = (uint8_t *)data;
1065 buf.size = size;
1066 buf.refcount = 1;
1067 buf.ptr.owner = NULL;
1068
1069 ogg_reference ref;
1070 ref.buffer = &buf;
1071 ref.begin = 0;
1072 ref.length = size;
1073 ref.next = NULL;
1074
1075 oggpack_buffer bits;
1076 oggpack_readinit(&bits, &ref);
1077
1078 if (oggpack_read(&bits, 8) != type) {
1079 return ERROR_MALFORMED;
1080 }
1081 for (size_t i = 0; i < 6; ++i) {
1082 oggpack_read(&bits, 8); // skip 'vorbis'
1083 }
1084
1085 switch (type) {
1086 case 1:
1087 {
1088 if (0 != _vorbis_unpack_info(&mVi, &bits)) {
1089 return ERROR_MALFORMED;
1090 }
1091
1092 mMeta->setData(kKeyVorbisInfo, 0, data, size);
1093 mMeta->setInt32(kKeySampleRate, mVi.rate);
1094 mMeta->setInt32(kKeyChannelCount, mVi.channels);
1095
1096 ALOGV("lower-bitrate = %ld", mVi.bitrate_lower);
1097 ALOGV("upper-bitrate = %ld", mVi.bitrate_upper);
1098 ALOGV("nominal-bitrate = %ld", mVi.bitrate_nominal);
1099 ALOGV("window-bitrate = %ld", mVi.bitrate_window);
1100 ALOGV("blocksizes: %d/%d",
1101 vorbis_info_blocksize(&mVi, 0),
1102 vorbis_info_blocksize(&mVi, 1)
1103 );
1104
1105 off64_t size;
1106 if (mSource->getSize(&size) == OK) {
1107 uint64_t bps = approxBitrate();
1108 if (bps != 0) {
1109 mMeta->setInt64(kKeyDuration, size * 8000000ll / bps);
1110 }
1111 }
1112 break;
1113 }
1114
1115 case 3:
1116 {
1117 if (0 != _vorbis_unpack_comment(&mVc, &bits)) {
1118 return ERROR_MALFORMED;
1119 }
1120
1121 parseFileMetaData();
1122 break;
1123 }
1124
1125 case 5:
1126 {
1127 if (0 != _vorbis_unpack_books(&mVi, &bits)) {
1128 return ERROR_MALFORMED;
1129 }
1130
1131 mMeta->setData(kKeyVorbisBooks, 0, data, size);
1132 break;
1133 }
1134 }
1135
1136 return OK;
1137 }
1138
approxBitrate() const1139 uint64_t MyVorbisExtractor::approxBitrate() const {
1140 if (mVi.bitrate_nominal != 0) {
1141 return mVi.bitrate_nominal;
1142 }
1143
1144 return (mVi.bitrate_lower + mVi.bitrate_upper) / 2;
1145 }
1146
parseFileMetaData()1147 void MyOggExtractor::parseFileMetaData() {
1148 mFileMeta = new MetaData;
1149 mFileMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_CONTAINER_OGG);
1150
1151 for (int i = 0; i < mVc.comments; ++i) {
1152 const char *comment = mVc.user_comments[i];
1153 size_t commentLength = mVc.comment_lengths[i];
1154 parseVorbisComment(mFileMeta, comment, commentLength);
1155 //ALOGI("comment #%d: '%s'", i + 1, mVc.user_comments[i]);
1156 }
1157 }
1158
parseVorbisComment(const sp<MetaData> & fileMeta,const char * comment,size_t commentLength)1159 void parseVorbisComment(
1160 const sp<MetaData> &fileMeta, const char *comment, size_t commentLength)
1161 {
1162 struct {
1163 const char *const mTag;
1164 uint32_t mKey;
1165 } kMap[] = {
1166 { "TITLE", kKeyTitle },
1167 { "ARTIST", kKeyArtist },
1168 { "ALBUMARTIST", kKeyAlbumArtist },
1169 { "ALBUM ARTIST", kKeyAlbumArtist },
1170 { "COMPILATION", kKeyCompilation },
1171 { "ALBUM", kKeyAlbum },
1172 { "COMPOSER", kKeyComposer },
1173 { "GENRE", kKeyGenre },
1174 { "AUTHOR", kKeyAuthor },
1175 { "TRACKNUMBER", kKeyCDTrackNumber },
1176 { "DISCNUMBER", kKeyDiscNumber },
1177 { "DATE", kKeyDate },
1178 { "YEAR", kKeyYear },
1179 { "LYRICIST", kKeyWriter },
1180 { "METADATA_BLOCK_PICTURE", kKeyAlbumArt },
1181 { "ANDROID_LOOP", kKeyAutoLoop },
1182 };
1183
1184 for (size_t j = 0; j < sizeof(kMap) / sizeof(kMap[0]); ++j) {
1185 size_t tagLen = strlen(kMap[j].mTag);
1186 if (!strncasecmp(kMap[j].mTag, comment, tagLen)
1187 && comment[tagLen] == '=') {
1188 if (kMap[j].mKey == kKeyAlbumArt) {
1189 extractAlbumArt(
1190 fileMeta,
1191 &comment[tagLen + 1],
1192 commentLength - tagLen - 1);
1193 } else if (kMap[j].mKey == kKeyAutoLoop) {
1194 if (!strcasecmp(&comment[tagLen + 1], "true")) {
1195 fileMeta->setInt32(kKeyAutoLoop, true);
1196 }
1197 } else {
1198 fileMeta->setCString(kMap[j].mKey, &comment[tagLen + 1]);
1199 }
1200 }
1201 }
1202
1203 }
1204
1205 // The returned buffer should be free()d.
DecodeBase64(const char * s,size_t size,size_t * outSize)1206 static uint8_t *DecodeBase64(const char *s, size_t size, size_t *outSize) {
1207 *outSize = 0;
1208
1209 if ((size % 4) != 0) {
1210 return NULL;
1211 }
1212
1213 size_t n = size;
1214 size_t padding = 0;
1215 if (n >= 1 && s[n - 1] == '=') {
1216 padding = 1;
1217
1218 if (n >= 2 && s[n - 2] == '=') {
1219 padding = 2;
1220 }
1221 }
1222
1223 size_t outLen = 3 * size / 4 - padding;
1224
1225 *outSize = outLen;
1226
1227 void *buffer = malloc(outLen);
1228
1229 uint8_t *out = (uint8_t *)buffer;
1230 size_t j = 0;
1231 uint32_t accum = 0;
1232 for (size_t i = 0; i < n; ++i) {
1233 char c = s[i];
1234 unsigned value;
1235 if (c >= 'A' && c <= 'Z') {
1236 value = c - 'A';
1237 } else if (c >= 'a' && c <= 'z') {
1238 value = 26 + c - 'a';
1239 } else if (c >= '0' && c <= '9') {
1240 value = 52 + c - '0';
1241 } else if (c == '+') {
1242 value = 62;
1243 } else if (c == '/') {
1244 value = 63;
1245 } else if (c != '=') {
1246 return NULL;
1247 } else {
1248 if (i < n - padding) {
1249 return NULL;
1250 }
1251
1252 value = 0;
1253 }
1254
1255 accum = (accum << 6) | value;
1256
1257 if (((i + 1) % 4) == 0) {
1258 out[j++] = (accum >> 16);
1259
1260 if (j < outLen) { out[j++] = (accum >> 8) & 0xff; }
1261 if (j < outLen) { out[j++] = accum & 0xff; }
1262
1263 accum = 0;
1264 }
1265 }
1266
1267 return (uint8_t *)buffer;
1268 }
1269
extractAlbumArt(const sp<MetaData> & fileMeta,const void * data,size_t size)1270 static void extractAlbumArt(
1271 const sp<MetaData> &fileMeta, const void *data, size_t size) {
1272 ALOGV("extractAlbumArt from '%s'", (const char *)data);
1273
1274 size_t flacSize;
1275 uint8_t *flac = DecodeBase64((const char *)data, size, &flacSize);
1276
1277 if (flac == NULL) {
1278 ALOGE("malformed base64 encoded data.");
1279 return;
1280 }
1281
1282 ALOGV("got flac of size %zu", flacSize);
1283
1284 uint32_t picType;
1285 uint32_t typeLen;
1286 uint32_t descLen;
1287 uint32_t dataLen;
1288 char type[128];
1289
1290 if (flacSize < 8) {
1291 goto exit;
1292 }
1293
1294 picType = U32_AT(flac);
1295
1296 if (picType != 3) {
1297 // This is not a front cover.
1298 goto exit;
1299 }
1300
1301 typeLen = U32_AT(&flac[4]);
1302 if (typeLen > sizeof(type) - 1) {
1303 goto exit;
1304 }
1305
1306 // we've already checked above that flacSize >= 8
1307 if (flacSize - 8 < typeLen) {
1308 goto exit;
1309 }
1310
1311 memcpy(type, &flac[8], typeLen);
1312 type[typeLen] = '\0';
1313
1314 ALOGV("picType = %d, type = '%s'", picType, type);
1315
1316 if (!strcmp(type, "-->")) {
1317 // This is not inline cover art, but an external url instead.
1318 goto exit;
1319 }
1320
1321 descLen = U32_AT(&flac[8 + typeLen]);
1322
1323 if (flacSize < 32 ||
1324 flacSize - 32 < typeLen ||
1325 flacSize - 32 - typeLen < descLen) {
1326 goto exit;
1327 }
1328
1329 dataLen = U32_AT(&flac[8 + typeLen + 4 + descLen + 16]);
1330
1331
1332 // we've already checked above that (flacSize - 32 - typeLen - descLen) >= 0
1333 if (flacSize - 32 - typeLen - descLen < dataLen) {
1334 goto exit;
1335 }
1336
1337 ALOGV("got image data, %zu trailing bytes",
1338 flacSize - 32 - typeLen - descLen - dataLen);
1339
1340 fileMeta->setData(
1341 kKeyAlbumArt, 0, &flac[8 + typeLen + 4 + descLen + 20], dataLen);
1342
1343 fileMeta->setCString(kKeyAlbumArtMIME, type);
1344
1345 exit:
1346 free(flac);
1347 flac = NULL;
1348 }
1349
1350 ////////////////////////////////////////////////////////////////////////////////
1351
OggExtractor(const sp<DataSource> & source)1352 OggExtractor::OggExtractor(const sp<DataSource> &source)
1353 : mDataSource(source),
1354 mInitCheck(NO_INIT),
1355 mImpl(NULL) {
1356 for (int i = 0; i < 2; ++i) {
1357 if (mImpl != NULL) {
1358 delete mImpl;
1359 }
1360 if (i == 0) {
1361 mImpl = new MyVorbisExtractor(mDataSource);
1362 } else {
1363 mImpl = new MyOpusExtractor(mDataSource);
1364 }
1365 mInitCheck = mImpl->seekToOffset(0);
1366
1367 if (mInitCheck == OK) {
1368 mInitCheck = mImpl->init();
1369 if (mInitCheck == OK) {
1370 break;
1371 }
1372 }
1373 }
1374 }
1375
~OggExtractor()1376 OggExtractor::~OggExtractor() {
1377 delete mImpl;
1378 mImpl = NULL;
1379 }
1380
countTracks()1381 size_t OggExtractor::countTracks() {
1382 return mInitCheck != OK ? 0 : 1;
1383 }
1384
getTrack(size_t index)1385 sp<MediaSource> OggExtractor::getTrack(size_t index) {
1386 if (index >= 1) {
1387 return NULL;
1388 }
1389
1390 return new OggSource(this);
1391 }
1392
getTrackMetaData(size_t index,uint32_t)1393 sp<MetaData> OggExtractor::getTrackMetaData(
1394 size_t index, uint32_t /* flags */) {
1395 if (index >= 1) {
1396 return NULL;
1397 }
1398
1399 return mImpl->getFormat();
1400 }
1401
getMetaData()1402 sp<MetaData> OggExtractor::getMetaData() {
1403 return mImpl->getFileMetaData();
1404 }
1405
SniffOgg(const sp<DataSource> & source,String8 * mimeType,float * confidence,sp<AMessage> *)1406 bool SniffOgg(
1407 const sp<DataSource> &source, String8 *mimeType, float *confidence,
1408 sp<AMessage> *) {
1409 char tmp[4];
1410 if (source->readAt(0, tmp, 4) < 4 || memcmp(tmp, "OggS", 4)) {
1411 return false;
1412 }
1413
1414 mimeType->setTo(MEDIA_MIMETYPE_CONTAINER_OGG);
1415 *confidence = 0.2f;
1416
1417 return true;
1418 }
1419
1420 } // namespace android
1421