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