MatroskaExtractor.cpp revision 093437c388e5dff6903a3d43f2ca9f8a1ba4744a
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 MatroskaSource : public MediaSource { 125 MatroskaSource( 126 const sp<MatroskaExtractor> &extractor, size_t index); 127 128 virtual status_t start(MetaData *params); 129 virtual status_t stop(); 130 131 virtual sp<MetaData> getFormat(); 132 133 virtual status_t read( 134 MediaBuffer **buffer, const ReadOptions *options); 135 136private: 137 enum Type { 138 AVC, 139 AAC, 140 OTHER 141 }; 142 143 sp<MatroskaExtractor> mExtractor; 144 size_t mTrackIndex; 145 unsigned long mTrackNum; 146 Type mType; 147 mkvparser::Cluster *mCluster; 148 const mkvparser::BlockEntry *mBlockEntry; 149 150 status_t advance(); 151 152 MatroskaSource(const MatroskaSource &); 153 MatroskaSource &operator=(const MatroskaSource &); 154}; 155 156MatroskaSource::MatroskaSource( 157 const sp<MatroskaExtractor> &extractor, size_t index) 158 : mExtractor(extractor), 159 mTrackIndex(index), 160 mType(OTHER), 161 mCluster(NULL), 162 mBlockEntry(NULL) { 163 mTrackNum = mExtractor->mTracks.itemAt(index).mTrackNum; 164 165 const char *mime; 166 CHECK(mExtractor->mTracks.itemAt(index).mMeta-> 167 findCString(kKeyMIMEType, &mime)); 168 169 if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) { 170 mType = AVC; 171 } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) { 172 mType = AAC; 173 } 174} 175 176status_t MatroskaSource::start(MetaData *params) { 177 mCluster = NULL; 178 mBlockEntry = NULL; 179 180 return OK; 181} 182 183status_t MatroskaSource::stop() { 184 return OK; 185} 186 187sp<MetaData> MatroskaSource::getFormat() { 188 return mExtractor->mTracks.itemAt(mTrackIndex).mMeta; 189} 190 191status_t MatroskaSource::advance() { 192 for (;;) { 193 if (mBlockEntry == NULL || mBlockEntry->EOS()) { 194 if (mCluster == NULL) { 195 mCluster = mExtractor->mSegment->GetFirst(); 196 } else { 197 mCluster = mExtractor->mSegment->GetNext(mCluster); 198 } 199 if (mCluster == NULL || mCluster->EOS()) { 200 return ERROR_END_OF_STREAM; 201 } 202 mBlockEntry = mCluster->GetFirst(); 203 } 204 205 if (mBlockEntry->GetBlock()->GetTrackNumber() != mTrackNum) { 206 mBlockEntry = mCluster->GetNext(mBlockEntry); 207 continue; 208 } 209 210 break; 211 } 212 213 return OK; 214} 215 216status_t MatroskaSource::read( 217 MediaBuffer **out, const ReadOptions *options) { 218 *out = NULL; 219 220 int64_t seekTimeUs; 221 if (options && options->getSeekTo(&seekTimeUs)) { 222 mBlockEntry = NULL; 223 mCluster = mExtractor->mSegment->GetCluster(seekTimeUs * 1000ll); 224 225 status_t err; 226 while ((err = advance()) == OK && !mBlockEntry->GetBlock()->IsKey()) { 227 mBlockEntry = mCluster->GetNext(mBlockEntry); 228 } 229 230 if (err != OK) { 231 return ERROR_END_OF_STREAM; 232 } 233 } 234 235 if (advance() != OK) { 236 return ERROR_END_OF_STREAM; 237 } 238 239 const mkvparser::Block *block = mBlockEntry->GetBlock(); 240 size_t size = block->GetSize(); 241 long long timeNs = block->GetTime(mCluster); 242 243 MediaBuffer *buffer = new MediaBuffer(size + 2); 244 buffer->meta_data()->setInt64(kKeyTime, (timeNs + 500) / 1000); 245 246 long res = block->Read( 247 mExtractor->mReader, (unsigned char *)buffer->data() + 2); 248 249 if (res != 0) { 250 return ERROR_END_OF_STREAM; 251 } 252 253 buffer->set_range(2, size); 254 255 if (mType == AVC) { 256 CHECK(size >= 2); 257 258 uint8_t *data = (uint8_t *)buffer->data(); 259 260 unsigned NALsize = data[2] << 8 | data[3]; 261 CHECK_EQ(size, NALsize + 2); 262 263 memcpy(data, "\x00\x00\x00\x01", 4); 264 buffer->set_range(0, size + 2); 265 } else if (mType == AAC) { 266 // There's strange junk at the beginning... 267 268 const uint8_t *data = (const uint8_t *)buffer->data() + 2; 269 size_t offset = 0; 270 while (offset < size && data[offset] != 0x21) { 271 ++offset; 272 } 273 buffer->set_range(2 + offset, size - offset); 274 } 275 276 *out = buffer; 277 278#if 0 279 hexdump((const uint8_t *)buffer->data() + buffer->range_offset(), 280 buffer->range_length()); 281#endif 282 283 mBlockEntry = mCluster->GetNext(mBlockEntry); 284 285 return OK; 286} 287 288//////////////////////////////////////////////////////////////////////////////// 289 290MatroskaExtractor::MatroskaExtractor(const sp<DataSource> &source) 291 : mDataSource(source), 292 mReader(new DataSourceReader(mDataSource)), 293 mSegment(NULL) { 294 mkvparser::EBMLHeader ebmlHeader; 295 long long pos; 296 if (ebmlHeader.Parse(mReader, pos) < 0) { 297 return; 298 } 299 300 long long ret = 301 mkvparser::Segment::CreateInstance(mReader, pos, mSegment); 302 303 if (ret) { 304 CHECK(mSegment == NULL); 305 return; 306 } 307 308 ret = mSegment->Load(); 309 310 if (ret < 0) { 311 delete mSegment; 312 mSegment = NULL; 313 return; 314 } 315 316 addTracks(); 317} 318 319MatroskaExtractor::~MatroskaExtractor() { 320 delete mSegment; 321 mSegment = NULL; 322 323 delete mReader; 324 mReader = NULL; 325} 326 327size_t MatroskaExtractor::countTracks() { 328 return mTracks.size(); 329} 330 331sp<MediaSource> MatroskaExtractor::getTrack(size_t index) { 332 if (index >= mTracks.size()) { 333 return NULL; 334 } 335 336 return new MatroskaSource(this, index); 337} 338 339sp<MetaData> MatroskaExtractor::getTrackMetaData( 340 size_t index, uint32_t flags) { 341 if (index >= mTracks.size()) { 342 return NULL; 343 } 344 345 return mTracks.itemAt(index).mMeta; 346} 347 348static void addESDSFromAudioSpecificInfo( 349 const sp<MetaData> &meta, const void *asi, size_t asiSize) { 350 static const uint8_t kStaticESDS[] = { 351 0x03, 22, 352 0x00, 0x00, // ES_ID 353 0x00, // streamDependenceFlag, URL_Flag, OCRstreamFlag 354 355 0x04, 17, 356 0x40, // Audio ISO/IEC 14496-3 357 0x00, 0x00, 0x00, 0x00, 358 0x00, 0x00, 0x00, 0x00, 359 0x00, 0x00, 0x00, 0x00, 360 361 0x05, 362 // AudioSpecificInfo (with size prefix) follows 363 }; 364 365 CHECK(asiSize < 128); 366 size_t esdsSize = sizeof(kStaticESDS) + asiSize + 1; 367 uint8_t *esds = new uint8_t[esdsSize]; 368 memcpy(esds, kStaticESDS, sizeof(kStaticESDS)); 369 uint8_t *ptr = esds + sizeof(kStaticESDS); 370 *ptr++ = asiSize; 371 memcpy(ptr, asi, asiSize); 372 373 meta->setData(kKeyESDS, 0, esds, esdsSize); 374 375 delete[] esds; 376 esds = NULL; 377} 378 379void addVorbisCodecInfo( 380 const sp<MetaData> &meta, 381 const void *_codecPrivate, size_t codecPrivateSize) { 382 // printf("vorbis private data follows:\n"); 383 // hexdump(_codecPrivate, codecPrivateSize); 384 385 CHECK(codecPrivateSize >= 3); 386 387 const uint8_t *codecPrivate = (const uint8_t *)_codecPrivate; 388 CHECK(codecPrivate[0] == 0x02); 389 390 size_t len1 = codecPrivate[1]; 391 size_t len2 = codecPrivate[2]; 392 393 CHECK(codecPrivateSize > 3 + len1 + len2); 394 395 CHECK(codecPrivate[3] == 0x01); 396 meta->setData(kKeyVorbisInfo, 0, &codecPrivate[3], len1); 397 398 CHECK(codecPrivate[len1 + 3] == 0x03); 399 400 CHECK(codecPrivate[len1 + len2 + 3] == 0x05); 401 meta->setData( 402 kKeyVorbisBooks, 0, &codecPrivate[len1 + len2 + 3], 403 codecPrivateSize - len1 - len2 - 3); 404} 405 406void MatroskaExtractor::addTracks() { 407 const mkvparser::Tracks *tracks = mSegment->GetTracks(); 408 409 for (size_t index = 0; index < tracks->GetTracksCount(); ++index) { 410 const mkvparser::Track *track = tracks->GetTrackByIndex(index); 411 412 const char *const codecID = track->GetCodecId(); 413 LOGV("codec id = %s", codecID); 414 LOGV("codec name = %s", track->GetCodecNameAsUTF8()); 415 416 size_t codecPrivateSize; 417 const unsigned char *codecPrivate = 418 track->GetCodecPrivate(&codecPrivateSize); 419 420 enum { VIDEO_TRACK = 1, AUDIO_TRACK = 2 }; 421 422 sp<MetaData> meta = new MetaData; 423 424 switch (track->GetType()) { 425 case VIDEO_TRACK: 426 { 427 const mkvparser::VideoTrack *vtrack = 428 static_cast<const mkvparser::VideoTrack *>(track); 429 430 if (!strcmp("V_MPEG4/ISO/AVC", codecID)) { 431 meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC); 432 meta->setData(kKeyAVCC, 0, codecPrivate, codecPrivateSize); 433 } else if (!strcmp("V_VP8", codecID)) { 434 meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_VPX); 435 } else { 436 continue; 437 } 438 439 meta->setInt32(kKeyWidth, vtrack->GetWidth()); 440 meta->setInt32(kKeyHeight, vtrack->GetHeight()); 441 break; 442 } 443 444 case AUDIO_TRACK: 445 { 446 const mkvparser::AudioTrack *atrack = 447 static_cast<const mkvparser::AudioTrack *>(track); 448 449 if (!strcmp("A_AAC", codecID)) { 450 meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC); 451 CHECK(codecPrivateSize >= 2); 452 453 addESDSFromAudioSpecificInfo( 454 meta, codecPrivate, codecPrivateSize); 455 } else if (!strcmp("A_VORBIS", codecID)) { 456 meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_VORBIS); 457 458 addVorbisCodecInfo(meta, codecPrivate, codecPrivateSize); 459 } else { 460 continue; 461 } 462 463 meta->setInt32(kKeySampleRate, atrack->GetSamplingRate()); 464 meta->setInt32(kKeyChannelCount, atrack->GetChannels()); 465 break; 466 } 467 468 default: 469 continue; 470 } 471 472 long long durationNs = mSegment->GetDuration(); 473 meta->setInt64(kKeyDuration, (durationNs + 500) / 1000); 474 475 mTracks.push(); 476 TrackInfo *trackInfo = &mTracks.editItemAt(mTracks.size() - 1); 477 trackInfo->mTrackNum = track->GetNumber(); 478 trackInfo->mMeta = meta; 479 } 480} 481 482sp<MetaData> MatroskaExtractor::getMetaData() { 483 sp<MetaData> meta = new MetaData; 484 meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_CONTAINER_MATROSKA); 485 486 return meta; 487} 488 489bool SniffMatroska( 490 const sp<DataSource> &source, String8 *mimeType, float *confidence) { 491 DataSourceReader reader(source); 492 mkvparser::EBMLHeader ebmlHeader; 493 long long pos; 494 if (ebmlHeader.Parse(&reader, pos) < 0) { 495 return false; 496 } 497 498 mimeType->setTo(MEDIA_MIMETYPE_CONTAINER_MATROSKA); 499 *confidence = 0.6; 500 501 return true; 502} 503 504} // namespace android 505