MatroskaExtractor.cpp revision abd1f4f870925d6776dbe4b930b759a1ab6595ca
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/DataSource.h> 26#include <media/stagefright/MediaBuffer.h> 27#include <media/stagefright/MediaDebug.h> 28#include <media/stagefright/MediaDefs.h> 29#include <media/stagefright/MediaErrors.h> 30#include <media/stagefright/MediaSource.h> 31#include <media/stagefright/MetaData.h> 32#include <utils/String8.h> 33 34namespace android { 35 36struct DataSourceReader : public mkvparser::IMkvReader { 37 DataSourceReader(const sp<DataSource> &source) 38 : mSource(source) { 39 } 40 41 virtual int Read(long long position, long length, unsigned char* buffer) { 42 CHECK(position >= 0); 43 CHECK(length >= 0); 44 45 if (length == 0) { 46 return 0; 47 } 48 49 ssize_t n = mSource->readAt(position, buffer, length); 50 51 if (n <= 0) { 52 return -1; 53 } 54 55 return 0; 56 } 57 58 virtual int Length(long long* total, long long* available) { 59 off_t size; 60 if (mSource->getSize(&size) != OK) { 61 return -1; 62 } 63 64 if (total) { 65 *total = size; 66 } 67 68 if (available) { 69 *available = size; 70 } 71 72 return 0; 73 } 74 75private: 76 sp<DataSource> mSource; 77 78 DataSourceReader(const DataSourceReader &); 79 DataSourceReader &operator=(const DataSourceReader &); 80}; 81 82//////////////////////////////////////////////////////////////////////////////// 83 84#include <ctype.h> 85static void hexdump(const void *_data, size_t size) { 86 const uint8_t *data = (const uint8_t *)_data; 87 size_t offset = 0; 88 while (offset < size) { 89 printf("0x%04x ", offset); 90 91 size_t n = size - offset; 92 if (n > 16) { 93 n = 16; 94 } 95 96 for (size_t i = 0; i < 16; ++i) { 97 if (i == 8) { 98 printf(" "); 99 } 100 101 if (offset + i < size) { 102 printf("%02x ", data[offset + i]); 103 } else { 104 printf(" "); 105 } 106 } 107 108 printf(" "); 109 110 for (size_t i = 0; i < n; ++i) { 111 if (isprint(data[offset + i])) { 112 printf("%c", data[offset + i]); 113 } else { 114 printf("."); 115 } 116 } 117 118 printf("\n"); 119 120 offset += 16; 121 } 122} 123 124struct BlockIterator { 125 BlockIterator(mkvparser::Segment *segment, unsigned long trackNum); 126 127 bool eos() const; 128 129 void advance(); 130 void reset(); 131 void seek(int64_t seekTimeUs); 132 133 const mkvparser::Block *block() const; 134 int64_t blockTimeUs() const; 135 136private: 137 mkvparser::Segment *mSegment; 138 unsigned long mTrackNum; 139 140 mkvparser::Cluster *mCluster; 141 const mkvparser::BlockEntry *mBlockEntry; 142 143 BlockIterator(const BlockIterator &); 144 BlockIterator &operator=(const BlockIterator &); 145}; 146 147struct MatroskaSource : public MediaSource { 148 MatroskaSource( 149 const sp<MatroskaExtractor> &extractor, size_t index); 150 151 virtual status_t start(MetaData *params); 152 virtual status_t stop(); 153 154 virtual sp<MetaData> getFormat(); 155 156 virtual status_t read( 157 MediaBuffer **buffer, const ReadOptions *options); 158 159private: 160 enum Type { 161 AVC, 162 AAC, 163 OTHER 164 }; 165 166 sp<MatroskaExtractor> mExtractor; 167 size_t mTrackIndex; 168 Type mType; 169 BlockIterator mBlockIter; 170 171 status_t advance(); 172 173 MatroskaSource(const MatroskaSource &); 174 MatroskaSource &operator=(const MatroskaSource &); 175}; 176 177MatroskaSource::MatroskaSource( 178 const sp<MatroskaExtractor> &extractor, size_t index) 179 : mExtractor(extractor), 180 mTrackIndex(index), 181 mType(OTHER), 182 mBlockIter(mExtractor->mSegment, 183 mExtractor->mTracks.itemAt(index).mTrackNum) { 184 const char *mime; 185 CHECK(mExtractor->mTracks.itemAt(index).mMeta-> 186 findCString(kKeyMIMEType, &mime)); 187 188 if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) { 189 mType = AVC; 190 } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) { 191 mType = AAC; 192 } 193} 194 195status_t MatroskaSource::start(MetaData *params) { 196 mBlockIter.reset(); 197 198 return OK; 199} 200 201status_t MatroskaSource::stop() { 202 return OK; 203} 204 205sp<MetaData> MatroskaSource::getFormat() { 206 return mExtractor->mTracks.itemAt(mTrackIndex).mMeta; 207} 208 209//////////////////////////////////////////////////////////////////////////////// 210 211BlockIterator::BlockIterator( 212 mkvparser::Segment *segment, unsigned long trackNum) 213 : mSegment(segment), 214 mTrackNum(trackNum), 215 mCluster(NULL), 216 mBlockEntry(NULL) { 217 reset(); 218} 219 220bool BlockIterator::eos() const { 221 return mCluster == NULL || mCluster->EOS(); 222} 223 224void BlockIterator::advance() { 225 while (!eos()) { 226 if (mBlockEntry != NULL) { 227 mBlockEntry = mCluster->GetNext(mBlockEntry); 228 } else if (mCluster != NULL) { 229 mCluster = mSegment->GetNext(mCluster); 230 231 if (eos()) { 232 break; 233 } 234 235 mBlockEntry = mCluster->GetFirst(); 236 } 237 238 if (mBlockEntry != NULL 239 && mBlockEntry->GetBlock()->GetTrackNumber() == mTrackNum) { 240 break; 241 } 242 } 243} 244 245void BlockIterator::reset() { 246 mCluster = mSegment->GetFirst(); 247 mBlockEntry = mCluster->GetFirst(); 248 249 while (!eos() && block()->GetTrackNumber() != mTrackNum) { 250 advance(); 251 } 252} 253 254void BlockIterator::seek(int64_t seekTimeUs) { 255 mCluster = mSegment->GetCluster(seekTimeUs * 1000ll); 256 mBlockEntry = mCluster != NULL ? mCluster->GetFirst() : NULL; 257 258 while (!eos() && block()->GetTrackNumber() != mTrackNum) { 259 advance(); 260 } 261 262 while (!eos() && !mBlockEntry->GetBlock()->IsKey()) { 263 advance(); 264 } 265} 266 267const mkvparser::Block *BlockIterator::block() const { 268 CHECK(!eos()); 269 270 return mBlockEntry->GetBlock(); 271} 272 273int64_t BlockIterator::blockTimeUs() const { 274 return (mBlockEntry->GetBlock()->GetTime(mCluster) + 500ll) / 1000ll; 275} 276 277//////////////////////////////////////////////////////////////////////////////// 278 279status_t MatroskaSource::read( 280 MediaBuffer **out, const ReadOptions *options) { 281 *out = NULL; 282 283 int64_t seekTimeUs; 284 ReadOptions::SeekMode mode; 285 if (options && options->getSeekTo(&seekTimeUs, &mode)) { 286 mBlockIter.seek(seekTimeUs); 287 } 288 289 if (mBlockIter.eos()) { 290 return ERROR_END_OF_STREAM; 291 } 292 293 const mkvparser::Block *block = mBlockIter.block(); 294 size_t size = block->GetSize(); 295 int64_t timeUs = mBlockIter.blockTimeUs(); 296 297 MediaBuffer *buffer = new MediaBuffer(size + 2); 298 buffer->meta_data()->setInt64(kKeyTime, timeUs); 299 300 long res = block->Read( 301 mExtractor->mReader, (unsigned char *)buffer->data() + 2); 302 303 if (res != 0) { 304 return ERROR_END_OF_STREAM; 305 } 306 307 buffer->set_range(2, size); 308 309 if (mType == AVC) { 310 CHECK(size >= 2); 311 312 uint8_t *data = (uint8_t *)buffer->data(); 313 314 unsigned NALsize = data[2] << 8 | data[3]; 315 CHECK_EQ(size, NALsize + 2); 316 317 memcpy(data, "\x00\x00\x00\x01", 4); 318 buffer->set_range(0, size + 2); 319 } else if (mType == AAC) { 320 // There's strange junk at the beginning... 321 322 const uint8_t *data = (const uint8_t *)buffer->data() + 2; 323 size_t offset = 0; 324 while (offset < size && data[offset] != 0x21) { 325 ++offset; 326 } 327 buffer->set_range(2 + offset, size - offset); 328 } 329 330 *out = buffer; 331 332#if 0 333 hexdump((const uint8_t *)buffer->data() + buffer->range_offset(), 334 buffer->range_length()); 335#endif 336 337 mBlockIter.advance(); 338 339 return OK; 340} 341 342//////////////////////////////////////////////////////////////////////////////// 343 344MatroskaExtractor::MatroskaExtractor(const sp<DataSource> &source) 345 : mDataSource(source), 346 mReader(new DataSourceReader(mDataSource)), 347 mSegment(NULL), 348 mExtractedThumbnails(false) { 349 mkvparser::EBMLHeader ebmlHeader; 350 long long pos; 351 if (ebmlHeader.Parse(mReader, pos) < 0) { 352 return; 353 } 354 355 long long ret = 356 mkvparser::Segment::CreateInstance(mReader, pos, mSegment); 357 358 if (ret) { 359 CHECK(mSegment == NULL); 360 return; 361 } 362 363 ret = mSegment->Load(); 364 365 if (ret < 0) { 366 delete mSegment; 367 mSegment = NULL; 368 return; 369 } 370 371 addTracks(); 372} 373 374MatroskaExtractor::~MatroskaExtractor() { 375 delete mSegment; 376 mSegment = NULL; 377 378 delete mReader; 379 mReader = NULL; 380} 381 382size_t MatroskaExtractor::countTracks() { 383 return mTracks.size(); 384} 385 386sp<MediaSource> MatroskaExtractor::getTrack(size_t index) { 387 if (index >= mTracks.size()) { 388 return NULL; 389 } 390 391 return new MatroskaSource(this, index); 392} 393 394sp<MetaData> MatroskaExtractor::getTrackMetaData( 395 size_t index, uint32_t flags) { 396 if (index >= mTracks.size()) { 397 return NULL; 398 } 399 400 if ((flags & kIncludeExtensiveMetaData) && !mExtractedThumbnails) { 401 findThumbnails(); 402 mExtractedThumbnails = true; 403 } 404 405 return mTracks.itemAt(index).mMeta; 406} 407 408static void addESDSFromAudioSpecificInfo( 409 const sp<MetaData> &meta, const void *asi, size_t asiSize) { 410 static const uint8_t kStaticESDS[] = { 411 0x03, 22, 412 0x00, 0x00, // ES_ID 413 0x00, // streamDependenceFlag, URL_Flag, OCRstreamFlag 414 415 0x04, 17, 416 0x40, // Audio ISO/IEC 14496-3 417 0x00, 0x00, 0x00, 0x00, 418 0x00, 0x00, 0x00, 0x00, 419 0x00, 0x00, 0x00, 0x00, 420 421 0x05, 422 // AudioSpecificInfo (with size prefix) follows 423 }; 424 425 CHECK(asiSize < 128); 426 size_t esdsSize = sizeof(kStaticESDS) + asiSize + 1; 427 uint8_t *esds = new uint8_t[esdsSize]; 428 memcpy(esds, kStaticESDS, sizeof(kStaticESDS)); 429 uint8_t *ptr = esds + sizeof(kStaticESDS); 430 *ptr++ = asiSize; 431 memcpy(ptr, asi, asiSize); 432 433 meta->setData(kKeyESDS, 0, esds, esdsSize); 434 435 delete[] esds; 436 esds = NULL; 437} 438 439void addVorbisCodecInfo( 440 const sp<MetaData> &meta, 441 const void *_codecPrivate, size_t codecPrivateSize) { 442 // printf("vorbis private data follows:\n"); 443 // hexdump(_codecPrivate, codecPrivateSize); 444 445 CHECK(codecPrivateSize >= 3); 446 447 const uint8_t *codecPrivate = (const uint8_t *)_codecPrivate; 448 CHECK(codecPrivate[0] == 0x02); 449 450 size_t len1 = codecPrivate[1]; 451 size_t len2 = codecPrivate[2]; 452 453 CHECK(codecPrivateSize > 3 + len1 + len2); 454 455 CHECK(codecPrivate[3] == 0x01); 456 meta->setData(kKeyVorbisInfo, 0, &codecPrivate[3], len1); 457 458 CHECK(codecPrivate[len1 + 3] == 0x03); 459 460 CHECK(codecPrivate[len1 + len2 + 3] == 0x05); 461 meta->setData( 462 kKeyVorbisBooks, 0, &codecPrivate[len1 + len2 + 3], 463 codecPrivateSize - len1 - len2 - 3); 464} 465 466void MatroskaExtractor::addTracks() { 467 const mkvparser::Tracks *tracks = mSegment->GetTracks(); 468 469 for (size_t index = 0; index < tracks->GetTracksCount(); ++index) { 470 const mkvparser::Track *track = tracks->GetTrackByIndex(index); 471 472 const char *const codecID = track->GetCodecId(); 473 LOGV("codec id = %s", codecID); 474 LOGV("codec name = %s", track->GetCodecNameAsUTF8()); 475 476 size_t codecPrivateSize; 477 const unsigned char *codecPrivate = 478 track->GetCodecPrivate(&codecPrivateSize); 479 480 enum { VIDEO_TRACK = 1, AUDIO_TRACK = 2 }; 481 482 sp<MetaData> meta = new MetaData; 483 484 switch (track->GetType()) { 485 case VIDEO_TRACK: 486 { 487 const mkvparser::VideoTrack *vtrack = 488 static_cast<const mkvparser::VideoTrack *>(track); 489 490 if (!strcmp("V_MPEG4/ISO/AVC", codecID)) { 491 meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC); 492 meta->setData(kKeyAVCC, 0, codecPrivate, codecPrivateSize); 493 } else if (!strcmp("V_VP8", codecID)) { 494 meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_VPX); 495 } else { 496 continue; 497 } 498 499 meta->setInt32(kKeyWidth, vtrack->GetWidth()); 500 meta->setInt32(kKeyHeight, vtrack->GetHeight()); 501 break; 502 } 503 504 case AUDIO_TRACK: 505 { 506 const mkvparser::AudioTrack *atrack = 507 static_cast<const mkvparser::AudioTrack *>(track); 508 509 if (!strcmp("A_AAC", codecID)) { 510 meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC); 511 CHECK(codecPrivateSize >= 2); 512 513 addESDSFromAudioSpecificInfo( 514 meta, codecPrivate, codecPrivateSize); 515 } else if (!strcmp("A_VORBIS", codecID)) { 516 meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_VORBIS); 517 518 addVorbisCodecInfo(meta, codecPrivate, codecPrivateSize); 519 } else { 520 continue; 521 } 522 523 meta->setInt32(kKeySampleRate, atrack->GetSamplingRate()); 524 meta->setInt32(kKeyChannelCount, atrack->GetChannels()); 525 break; 526 } 527 528 default: 529 continue; 530 } 531 532 long long durationNs = mSegment->GetDuration(); 533 meta->setInt64(kKeyDuration, (durationNs + 500) / 1000); 534 535 mTracks.push(); 536 TrackInfo *trackInfo = &mTracks.editItemAt(mTracks.size() - 1); 537 trackInfo->mTrackNum = track->GetNumber(); 538 trackInfo->mMeta = meta; 539 } 540} 541 542void MatroskaExtractor::findThumbnails() { 543 for (size_t i = 0; i < mTracks.size(); ++i) { 544 TrackInfo *info = &mTracks.editItemAt(i); 545 546 const char *mime; 547 CHECK(info->mMeta->findCString(kKeyMIMEType, &mime)); 548 549 if (strncasecmp(mime, "video/", 6)) { 550 continue; 551 } 552 553 BlockIterator iter(mSegment, info->mTrackNum); 554 int32_t i = 0; 555 int64_t thumbnailTimeUs = 0; 556 size_t maxBlockSize = 0; 557 while (!iter.eos() && i < 20) { 558 if (iter.block()->IsKey()) { 559 ++i; 560 561 size_t blockSize = iter.block()->GetSize(); 562 if (blockSize > maxBlockSize) { 563 maxBlockSize = blockSize; 564 thumbnailTimeUs = iter.blockTimeUs(); 565 } 566 } 567 iter.advance(); 568 } 569 info->mMeta->setInt64(kKeyThumbnailTime, thumbnailTimeUs); 570 } 571} 572 573sp<MetaData> MatroskaExtractor::getMetaData() { 574 sp<MetaData> meta = new MetaData; 575 meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_CONTAINER_MATROSKA); 576 577 return meta; 578} 579 580bool SniffMatroska( 581 const sp<DataSource> &source, String8 *mimeType, float *confidence) { 582 DataSourceReader reader(source); 583 mkvparser::EBMLHeader ebmlHeader; 584 long long pos; 585 if (ebmlHeader.Parse(&reader, pos) < 0) { 586 return false; 587 } 588 589 mimeType->setTo(MEDIA_MIMETYPE_CONTAINER_MATROSKA); 590 *confidence = 0.6; 591 592 return true; 593} 594 595} // namespace android 596