MatroskaExtractor.cpp revision 8c32b164d00d3e4d73764d06956331f09693ef43
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 "MatroskaExtractor" 19#include <utils/Log.h> 20 21#include "MatroskaExtractor.h" 22 23#include "mkvparser.hpp" 24 25#include <media/stagefright/foundation/ADebug.h> 26#include <media/stagefright/foundation/hexdump.h> 27#include <media/stagefright/DataSource.h> 28#include <media/stagefright/MediaBuffer.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 36namespace android { 37 38struct DataSourceReader : public mkvparser::IMkvReader { 39 DataSourceReader(const sp<DataSource> &source) 40 : mSource(source) { 41 } 42 43 virtual int Read(long long position, long length, unsigned char* buffer) { 44 CHECK(position >= 0); 45 CHECK(length >= 0); 46 47 if (length == 0) { 48 return 0; 49 } 50 51 ssize_t n = mSource->readAt(position, buffer, length); 52 53 if (n <= 0) { 54 return -1; 55 } 56 57 return 0; 58 } 59 60 virtual int Length(long long* total, long long* available) { 61 off64_t size; 62 if (mSource->getSize(&size) != OK) { 63 *total = -1; 64 *available = (long long)((1ull << 63) - 1); 65 66 return 0; 67 } 68 69 if (total) { 70 *total = size; 71 } 72 73 if (available) { 74 *available = size; 75 } 76 77 return 0; 78 } 79 80private: 81 sp<DataSource> mSource; 82 83 DataSourceReader(const DataSourceReader &); 84 DataSourceReader &operator=(const DataSourceReader &); 85}; 86 87//////////////////////////////////////////////////////////////////////////////// 88 89struct BlockIterator { 90 BlockIterator(MatroskaExtractor *extractor, unsigned long trackNum); 91 92 bool eos() const; 93 94 void advance(); 95 void reset(); 96 void seek(int64_t seekTimeUs); 97 98 const mkvparser::Block *block() const; 99 int64_t blockTimeUs() const; 100 101private: 102 MatroskaExtractor *mExtractor; 103 unsigned long mTrackNum; 104 105 const mkvparser::Cluster *mCluster; 106 const mkvparser::BlockEntry *mBlockEntry; 107 long mBlockEntryIndex; 108 109 void advance_l(); 110 111 BlockIterator(const BlockIterator &); 112 BlockIterator &operator=(const BlockIterator &); 113}; 114 115struct MatroskaSource : public MediaSource { 116 MatroskaSource( 117 const sp<MatroskaExtractor> &extractor, size_t index); 118 119 virtual status_t start(MetaData *params); 120 virtual status_t stop(); 121 122 virtual sp<MetaData> getFormat(); 123 124 virtual status_t read( 125 MediaBuffer **buffer, const ReadOptions *options); 126 127protected: 128 virtual ~MatroskaSource(); 129 130private: 131 enum Type { 132 AVC, 133 AAC, 134 OTHER 135 }; 136 137 sp<MatroskaExtractor> mExtractor; 138 size_t mTrackIndex; 139 Type mType; 140 BlockIterator mBlockIter; 141 size_t mNALSizeLen; // for type AVC 142 143 List<MediaBuffer *> mPendingFrames; 144 145 status_t advance(); 146 147 status_t readBlock(); 148 void clearPendingFrames(); 149 150 MatroskaSource(const MatroskaSource &); 151 MatroskaSource &operator=(const MatroskaSource &); 152}; 153 154MatroskaSource::MatroskaSource( 155 const sp<MatroskaExtractor> &extractor, size_t index) 156 : mExtractor(extractor), 157 mTrackIndex(index), 158 mType(OTHER), 159 mBlockIter(mExtractor.get(), 160 mExtractor->mTracks.itemAt(index).mTrackNum), 161 mNALSizeLen(0) { 162 sp<MetaData> meta = mExtractor->mTracks.itemAt(index).mMeta; 163 164 const char *mime; 165 CHECK(meta->findCString(kKeyMIMEType, &mime)); 166 167 if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) { 168 mType = AVC; 169 170 uint32_t dummy; 171 const uint8_t *avcc; 172 size_t avccSize; 173 CHECK(meta->findData( 174 kKeyAVCC, &dummy, (const void **)&avcc, &avccSize)); 175 176 CHECK_GE(avccSize, 5u); 177 178 mNALSizeLen = 1 + (avcc[4] & 3); 179 LOGV("mNALSizeLen = %d", mNALSizeLen); 180 } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) { 181 mType = AAC; 182 } 183} 184 185MatroskaSource::~MatroskaSource() { 186 clearPendingFrames(); 187} 188 189status_t MatroskaSource::start(MetaData *params) { 190 mBlockIter.reset(); 191 192 return OK; 193} 194 195status_t MatroskaSource::stop() { 196 clearPendingFrames(); 197 198 return OK; 199} 200 201sp<MetaData> MatroskaSource::getFormat() { 202 return mExtractor->mTracks.itemAt(mTrackIndex).mMeta; 203} 204 205//////////////////////////////////////////////////////////////////////////////// 206 207BlockIterator::BlockIterator( 208 MatroskaExtractor *extractor, unsigned long trackNum) 209 : mExtractor(extractor), 210 mTrackNum(trackNum), 211 mCluster(NULL), 212 mBlockEntry(NULL), 213 mBlockEntryIndex(0) { 214 reset(); 215} 216 217bool BlockIterator::eos() const { 218 return mCluster == NULL || mCluster->EOS(); 219} 220 221void BlockIterator::advance() { 222 Mutex::Autolock autoLock(mExtractor->mLock); 223 advance_l(); 224} 225 226void BlockIterator::advance_l() { 227 for (;;) { 228 long res = mCluster->GetEntry(mBlockEntryIndex, mBlockEntry); 229 LOGV("GetEntry returned %ld", res); 230 231 long long pos; 232 long len; 233 if (res < 0) { 234 // Need to parse this cluster some more 235 236 CHECK_EQ(res, mkvparser::E_BUFFER_NOT_FULL); 237 238 res = mCluster->Parse(pos, len); 239 LOGV("Parse returned %ld", res); 240 241 if (res < 0) { 242 // I/O error 243 244 LOGE("Cluster::Parse returned result %ld", res); 245 246 mCluster = NULL; 247 break; 248 } 249 250 continue; 251 } else if (res == 0) { 252 // We're done with this cluster 253 254 const mkvparser::Cluster *nextCluster; 255 res = mExtractor->mSegment->ParseNext( 256 mCluster, nextCluster, pos, len); 257 LOGV("ParseNext returned %ld", res); 258 259 if (res > 0) { 260 // EOF 261 262 mCluster = NULL; 263 break; 264 } 265 266 CHECK_EQ(res, 0); 267 CHECK(nextCluster != NULL); 268 CHECK(!nextCluster->EOS()); 269 270 mCluster = nextCluster; 271 272 res = mCluster->Parse(pos, len); 273 LOGV("Parse (2) returned %ld", res); 274 CHECK_GE(res, 0); 275 276 mBlockEntryIndex = 0; 277 continue; 278 } 279 280 CHECK(mBlockEntry != NULL); 281 CHECK(mBlockEntry->GetBlock() != NULL); 282 ++mBlockEntryIndex; 283 284 if (mBlockEntry->GetBlock()->GetTrackNumber() == mTrackNum) { 285 break; 286 } 287 } 288} 289 290void BlockIterator::reset() { 291 Mutex::Autolock autoLock(mExtractor->mLock); 292 293 mCluster = mExtractor->mSegment->GetFirst(); 294 mBlockEntry = NULL; 295 mBlockEntryIndex = 0; 296 297 do { 298 advance_l(); 299 } while (!eos() && block()->GetTrackNumber() != mTrackNum); 300} 301 302void BlockIterator::seek(int64_t seekTimeUs) { 303 Mutex::Autolock autoLock(mExtractor->mLock); 304 305 mCluster = mExtractor->mSegment->FindCluster(seekTimeUs * 1000ll); 306 mBlockEntry = NULL; 307 mBlockEntryIndex = 0; 308 309 do { 310 advance_l(); 311 } 312 while (!eos() && block()->GetTrackNumber() != mTrackNum); 313 314 while (!eos() && !mBlockEntry->GetBlock()->IsKey()) { 315 advance_l(); 316 } 317} 318 319const mkvparser::Block *BlockIterator::block() const { 320 CHECK(!eos()); 321 322 return mBlockEntry->GetBlock(); 323} 324 325int64_t BlockIterator::blockTimeUs() const { 326 return (mBlockEntry->GetBlock()->GetTime(mCluster) + 500ll) / 1000ll; 327} 328 329//////////////////////////////////////////////////////////////////////////////// 330 331static unsigned U24_AT(const uint8_t *ptr) { 332 return ptr[0] << 16 | ptr[1] << 8 | ptr[2]; 333} 334 335static size_t clz(uint8_t x) { 336 size_t numLeadingZeroes = 0; 337 338 while (!(x & 0x80)) { 339 ++numLeadingZeroes; 340 x = x << 1; 341 } 342 343 return numLeadingZeroes; 344} 345 346void MatroskaSource::clearPendingFrames() { 347 while (!mPendingFrames.empty()) { 348 MediaBuffer *frame = *mPendingFrames.begin(); 349 mPendingFrames.erase(mPendingFrames.begin()); 350 351 frame->release(); 352 frame = NULL; 353 } 354} 355 356status_t MatroskaSource::readBlock() { 357 CHECK(mPendingFrames.empty()); 358 359 if (mBlockIter.eos()) { 360 return ERROR_END_OF_STREAM; 361 } 362 363 const mkvparser::Block *block = mBlockIter.block(); 364 365 int64_t timeUs = mBlockIter.blockTimeUs(); 366 367 for (int i = 0; i < block->GetFrameCount(); ++i) { 368 const mkvparser::Block::Frame &frame = block->GetFrame(i); 369 370 MediaBuffer *mbuf = new MediaBuffer(frame.len); 371 mbuf->meta_data()->setInt64(kKeyTime, timeUs); 372 mbuf->meta_data()->setInt32(kKeyIsSyncFrame, block->IsKey()); 373 374 long n = frame.Read(mExtractor->mReader, (unsigned char *)mbuf->data()); 375 if (n != 0) { 376 mPendingFrames.clear(); 377 378 mBlockIter.advance(); 379 return ERROR_IO; 380 } 381 382 mPendingFrames.push_back(mbuf); 383 } 384 385 mBlockIter.advance(); 386 387 return OK; 388} 389 390status_t MatroskaSource::read( 391 MediaBuffer **out, const ReadOptions *options) { 392 *out = NULL; 393 394 int64_t seekTimeUs; 395 ReadOptions::SeekMode mode; 396 if (options && options->getSeekTo(&seekTimeUs, &mode) 397 && !mExtractor->isLiveStreaming()) { 398 clearPendingFrames(); 399 mBlockIter.seek(seekTimeUs); 400 } 401 402again: 403 while (mPendingFrames.empty()) { 404 status_t err = readBlock(); 405 406 if (err != OK) { 407 clearPendingFrames(); 408 409 return err; 410 } 411 } 412 413 MediaBuffer *frame = *mPendingFrames.begin(); 414 mPendingFrames.erase(mPendingFrames.begin()); 415 416 size_t size = frame->range_length(); 417 418 if (mType != AVC) { 419 *out = frame; 420 421 return OK; 422 } 423 424 if (size < mNALSizeLen) { 425 frame->release(); 426 frame = NULL; 427 428 return ERROR_MALFORMED; 429 } 430 431 // In the case of AVC content, each NAL unit is prefixed by 432 // mNALSizeLen bytes of length. We want to prefix the data with 433 // a four-byte 0x00000001 startcode instead of the length prefix. 434 // mNALSizeLen ranges from 1 through 4 bytes, so add an extra 435 // 3 bytes of padding to the buffer start. 436 static const size_t kPadding = 3; 437 438 MediaBuffer *buffer = new MediaBuffer(size + kPadding); 439 440 int64_t timeUs; 441 CHECK(frame->meta_data()->findInt64(kKeyTime, &timeUs)); 442 int32_t isSync; 443 CHECK(frame->meta_data()->findInt32(kKeyIsSyncFrame, &isSync)); 444 445 buffer->meta_data()->setInt64(kKeyTime, timeUs); 446 buffer->meta_data()->setInt32(kKeyIsSyncFrame, isSync); 447 448 memcpy((uint8_t *)buffer->data() + kPadding, 449 (const uint8_t *)frame->data() + frame->range_offset(), 450 size); 451 452 buffer->set_range(kPadding, size); 453 454 frame->release(); 455 frame = NULL; 456 457 uint8_t *data = (uint8_t *)buffer->data(); 458 459 size_t NALsize; 460 switch (mNALSizeLen) { 461 case 1: NALsize = data[kPadding]; break; 462 case 2: NALsize = U16_AT(&data[kPadding]); break; 463 case 3: NALsize = U24_AT(&data[kPadding]); break; 464 case 4: NALsize = U32_AT(&data[kPadding]); break; 465 default: 466 TRESPASS(); 467 } 468 469 if (size < NALsize + mNALSizeLen) { 470 buffer->release(); 471 buffer = NULL; 472 473 return ERROR_MALFORMED; 474 } 475 476 if (size > NALsize + mNALSizeLen) { 477 LOGW("discarding %d bytes of data.", size - NALsize - mNALSizeLen); 478 } 479 480 // actual data starts at &data[kPadding + mNALSizeLen] 481 482 memcpy(&data[mNALSizeLen - 1], "\x00\x00\x00\x01", 4); 483 buffer->set_range(mNALSizeLen - 1, NALsize + 4); 484 485 *out = buffer; 486 487 return OK; 488} 489 490//////////////////////////////////////////////////////////////////////////////// 491 492MatroskaExtractor::MatroskaExtractor(const sp<DataSource> &source) 493 : mDataSource(source), 494 mReader(new DataSourceReader(mDataSource)), 495 mSegment(NULL), 496 mExtractedThumbnails(false), 497 mIsWebm(false) { 498 off64_t size; 499 mIsLiveStreaming = 500 (mDataSource->flags() 501 & (DataSource::kWantsPrefetching 502 | DataSource::kIsCachingDataSource)) 503 && mDataSource->getSize(&size) != OK; 504 505 mkvparser::EBMLHeader ebmlHeader; 506 long long pos; 507 if (ebmlHeader.Parse(mReader, pos) < 0) { 508 return; 509 } 510 511 if (ebmlHeader.m_docType && !strcmp("webm", ebmlHeader.m_docType)) { 512 mIsWebm = true; 513 } 514 515 long long ret = 516 mkvparser::Segment::CreateInstance(mReader, pos, mSegment); 517 518 if (ret) { 519 CHECK(mSegment == NULL); 520 return; 521 } 522 523 if (isLiveStreaming()) { 524 ret = mSegment->ParseHeaders(); 525 CHECK_EQ(ret, 0); 526 527 long len; 528 ret = mSegment->LoadCluster(pos, len); 529 CHECK_EQ(ret, 0); 530 } else { 531 ret = mSegment->Load(); 532 } 533 534 if (ret < 0) { 535 delete mSegment; 536 mSegment = NULL; 537 return; 538 } 539 540 addTracks(); 541} 542 543MatroskaExtractor::~MatroskaExtractor() { 544 delete mSegment; 545 mSegment = NULL; 546 547 delete mReader; 548 mReader = NULL; 549} 550 551size_t MatroskaExtractor::countTracks() { 552 return mTracks.size(); 553} 554 555sp<MediaSource> MatroskaExtractor::getTrack(size_t index) { 556 if (index >= mTracks.size()) { 557 return NULL; 558 } 559 560 return new MatroskaSource(this, index); 561} 562 563sp<MetaData> MatroskaExtractor::getTrackMetaData( 564 size_t index, uint32_t flags) { 565 if (index >= mTracks.size()) { 566 return NULL; 567 } 568 569 if ((flags & kIncludeExtensiveMetaData) && !mExtractedThumbnails 570 && !isLiveStreaming()) { 571 findThumbnails(); 572 mExtractedThumbnails = true; 573 } 574 575 return mTracks.itemAt(index).mMeta; 576} 577 578bool MatroskaExtractor::isLiveStreaming() const { 579 return mIsLiveStreaming; 580} 581 582static void addESDSFromAudioSpecificInfo( 583 const sp<MetaData> &meta, const void *asi, size_t asiSize) { 584 static const uint8_t kStaticESDS[] = { 585 0x03, 22, 586 0x00, 0x00, // ES_ID 587 0x00, // streamDependenceFlag, URL_Flag, OCRstreamFlag 588 589 0x04, 17, 590 0x40, // Audio ISO/IEC 14496-3 591 0x00, 0x00, 0x00, 0x00, 592 0x00, 0x00, 0x00, 0x00, 593 0x00, 0x00, 0x00, 0x00, 594 595 0x05, 596 // AudioSpecificInfo (with size prefix) follows 597 }; 598 599 // Make sure all sizes can be coded in a single byte. 600 CHECK(asiSize + 22 - 2 < 128); 601 size_t esdsSize = sizeof(kStaticESDS) + asiSize + 1; 602 uint8_t *esds = new uint8_t[esdsSize]; 603 memcpy(esds, kStaticESDS, sizeof(kStaticESDS)); 604 uint8_t *ptr = esds + sizeof(kStaticESDS); 605 *ptr++ = asiSize; 606 memcpy(ptr, asi, asiSize); 607 608 // Increment by codecPrivateSize less 2 bytes that are accounted for 609 // already in lengths of 22/17 610 esds[1] += asiSize - 2; 611 esds[6] += asiSize - 2; 612 613 meta->setData(kKeyESDS, 0, esds, esdsSize); 614 615 delete[] esds; 616 esds = NULL; 617} 618 619void addVorbisCodecInfo( 620 const sp<MetaData> &meta, 621 const void *_codecPrivate, size_t codecPrivateSize) { 622 // printf("vorbis private data follows:\n"); 623 // hexdump(_codecPrivate, codecPrivateSize); 624 625 CHECK(codecPrivateSize >= 3); 626 627 const uint8_t *codecPrivate = (const uint8_t *)_codecPrivate; 628 CHECK(codecPrivate[0] == 0x02); 629 630 size_t len1 = codecPrivate[1]; 631 size_t len2 = codecPrivate[2]; 632 633 CHECK(codecPrivateSize > 3 + len1 + len2); 634 635 CHECK(codecPrivate[3] == 0x01); 636 meta->setData(kKeyVorbisInfo, 0, &codecPrivate[3], len1); 637 638 CHECK(codecPrivate[len1 + 3] == 0x03); 639 640 CHECK(codecPrivate[len1 + len2 + 3] == 0x05); 641 meta->setData( 642 kKeyVorbisBooks, 0, &codecPrivate[len1 + len2 + 3], 643 codecPrivateSize - len1 - len2 - 3); 644} 645 646void MatroskaExtractor::addTracks() { 647 const mkvparser::Tracks *tracks = mSegment->GetTracks(); 648 649 for (size_t index = 0; index < tracks->GetTracksCount(); ++index) { 650 const mkvparser::Track *track = tracks->GetTrackByIndex(index); 651 652 if (track == NULL) { 653 // Apparently this is currently valid (if unexpected) behaviour 654 // of the mkv parser lib. 655 continue; 656 } 657 658 const char *const codecID = track->GetCodecId(); 659 LOGV("codec id = %s", codecID); 660 LOGV("codec name = %s", track->GetCodecNameAsUTF8()); 661 662 size_t codecPrivateSize; 663 const unsigned char *codecPrivate = 664 track->GetCodecPrivate(codecPrivateSize); 665 666 enum { VIDEO_TRACK = 1, AUDIO_TRACK = 2 }; 667 668 sp<MetaData> meta = new MetaData; 669 670 switch (track->GetType()) { 671 case VIDEO_TRACK: 672 { 673 const mkvparser::VideoTrack *vtrack = 674 static_cast<const mkvparser::VideoTrack *>(track); 675 676 if (!strcmp("V_MPEG4/ISO/AVC", codecID)) { 677 meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC); 678 meta->setData(kKeyAVCC, 0, codecPrivate, codecPrivateSize); 679 } else if (!strcmp("V_VP8", codecID)) { 680 meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_VPX); 681 } else { 682 continue; 683 } 684 685 meta->setInt32(kKeyWidth, vtrack->GetWidth()); 686 meta->setInt32(kKeyHeight, vtrack->GetHeight()); 687 break; 688 } 689 690 case AUDIO_TRACK: 691 { 692 const mkvparser::AudioTrack *atrack = 693 static_cast<const mkvparser::AudioTrack *>(track); 694 695 if (!strcmp("A_AAC", codecID)) { 696 meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC); 697 CHECK(codecPrivateSize >= 2); 698 699 addESDSFromAudioSpecificInfo( 700 meta, codecPrivate, codecPrivateSize); 701 } else if (!strcmp("A_VORBIS", codecID)) { 702 meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_VORBIS); 703 704 addVorbisCodecInfo(meta, codecPrivate, codecPrivateSize); 705 } else { 706 continue; 707 } 708 709 meta->setInt32(kKeySampleRate, atrack->GetSamplingRate()); 710 meta->setInt32(kKeyChannelCount, atrack->GetChannels()); 711 break; 712 } 713 714 default: 715 continue; 716 } 717 718 long long durationNs = mSegment->GetDuration(); 719 meta->setInt64(kKeyDuration, (durationNs + 500) / 1000); 720 721 mTracks.push(); 722 TrackInfo *trackInfo = &mTracks.editItemAt(mTracks.size() - 1); 723 trackInfo->mTrackNum = track->GetNumber(); 724 trackInfo->mMeta = meta; 725 } 726} 727 728void MatroskaExtractor::findThumbnails() { 729 for (size_t i = 0; i < mTracks.size(); ++i) { 730 TrackInfo *info = &mTracks.editItemAt(i); 731 732 const char *mime; 733 CHECK(info->mMeta->findCString(kKeyMIMEType, &mime)); 734 735 if (strncasecmp(mime, "video/", 6)) { 736 continue; 737 } 738 739 BlockIterator iter(this, info->mTrackNum); 740 int32_t i = 0; 741 int64_t thumbnailTimeUs = 0; 742 size_t maxBlockSize = 0; 743 while (!iter.eos() && i < 20) { 744 if (iter.block()->IsKey()) { 745 ++i; 746 747 size_t blockSize = 0; 748 for (int i = 0; i < iter.block()->GetFrameCount(); ++i) { 749 blockSize += iter.block()->GetFrame(i).len; 750 } 751 752 if (blockSize > maxBlockSize) { 753 maxBlockSize = blockSize; 754 thumbnailTimeUs = iter.blockTimeUs(); 755 } 756 } 757 iter.advance(); 758 } 759 info->mMeta->setInt64(kKeyThumbnailTime, thumbnailTimeUs); 760 } 761} 762 763sp<MetaData> MatroskaExtractor::getMetaData() { 764 sp<MetaData> meta = new MetaData; 765 766 meta->setCString( 767 kKeyMIMEType, 768 mIsWebm ? "video/webm" : MEDIA_MIMETYPE_CONTAINER_MATROSKA); 769 770 return meta; 771} 772 773uint32_t MatroskaExtractor::flags() const { 774 uint32_t x = CAN_PAUSE; 775 if (!isLiveStreaming()) { 776 x |= CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD | CAN_SEEK; 777 } 778 779 return x; 780} 781 782bool SniffMatroska( 783 const sp<DataSource> &source, String8 *mimeType, float *confidence, 784 sp<AMessage> *) { 785 DataSourceReader reader(source); 786 mkvparser::EBMLHeader ebmlHeader; 787 long long pos; 788 if (ebmlHeader.Parse(&reader, pos) < 0) { 789 return false; 790 } 791 792 mimeType->setTo(MEDIA_MIMETYPE_CONTAINER_MATROSKA); 793 *confidence = 0.6; 794 795 return true; 796} 797 798} // namespace android 799