NuMediaExtractor.cpp revision 94c1c8c99b70f2fdab87e131812eb253271e5500
1/* 2 * Copyright 2012, 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 "NuMediaExtractor" 19#include <utils/Log.h> 20 21#include <media/stagefright/NuMediaExtractor.h> 22 23#include "include/ESDS.h" 24#include "include/NuCachedSource2.h" 25#include "include/WVMExtractor.h" 26 27#include <media/stagefright/foundation/ABuffer.h> 28#include <media/stagefright/foundation/ADebug.h> 29#include <media/stagefright/foundation/AMessage.h> 30#include <media/stagefright/DataSource.h> 31#include <media/stagefright/FileSource.h> 32#include <media/stagefright/MediaBuffer.h> 33#include <media/stagefright/MediaDefs.h> 34#include <media/stagefright/MediaErrors.h> 35#include <media/stagefright/MediaExtractor.h> 36#include <media/stagefright/MediaSource.h> 37#include <media/stagefright/MetaData.h> 38#include <media/stagefright/Utils.h> 39 40namespace android { 41 42NuMediaExtractor::NuMediaExtractor() 43 : mIsWidevineExtractor(false), 44 mTotalBitrate(-1ll), 45 mDurationUs(-1ll) { 46} 47 48NuMediaExtractor::~NuMediaExtractor() { 49 releaseTrackSamples(); 50 51 for (size_t i = 0; i < mSelectedTracks.size(); ++i) { 52 TrackInfo *info = &mSelectedTracks.editItemAt(i); 53 54 CHECK_EQ((status_t)OK, info->mSource->stop()); 55 } 56 57 mSelectedTracks.clear(); 58} 59 60status_t NuMediaExtractor::setDataSource( 61 const char *path, const KeyedVector<String8, String8> *headers) { 62 Mutex::Autolock autoLock(mLock); 63 64 if (mImpl != NULL) { 65 return -EINVAL; 66 } 67 68 sp<DataSource> dataSource = 69 DataSource::CreateFromURI(path, headers); 70 71 if (dataSource == NULL) { 72 return -ENOENT; 73 } 74 75 mIsWidevineExtractor = false; 76 if (!strncasecmp("widevine://", path, 11)) { 77 String8 mimeType; 78 float confidence; 79 sp<AMessage> dummy; 80 bool success = SniffWVM(dataSource, &mimeType, &confidence, &dummy); 81 82 if (!success 83 || strcasecmp( 84 mimeType.string(), MEDIA_MIMETYPE_CONTAINER_WVM)) { 85 return ERROR_UNSUPPORTED; 86 } 87 88 sp<WVMExtractor> extractor = new WVMExtractor(dataSource); 89 extractor->setAdaptiveStreamingMode(true); 90 91 mImpl = extractor; 92 mIsWidevineExtractor = true; 93 } else { 94 mImpl = MediaExtractor::Create(dataSource); 95 } 96 97 if (mImpl == NULL) { 98 return ERROR_UNSUPPORTED; 99 } 100 101 sp<MetaData> fileMeta = mImpl->getMetaData(); 102 const char *containerMime; 103 if (fileMeta != NULL 104 && fileMeta->findCString(kKeyMIMEType, &containerMime) 105 && !strcasecmp(containerMime, "video/wvm")) { 106 // We always want to use "cryptoPluginMode" when using the wvm 107 // extractor. We can tell that it is this extractor by looking 108 // at the container mime type. 109 // The cryptoPluginMode ensures that the extractor will actually 110 // give us data in a call to MediaSource::read(), unlike its 111 // default mode that we use from AwesomePlayer. 112 static_cast<WVMExtractor *>(mImpl.get())->setCryptoPluginMode(true); 113 } 114 115 mDataSource = dataSource; 116 117 updateDurationAndBitrate(); 118 119 return OK; 120} 121 122status_t NuMediaExtractor::setDataSource(int fd, off64_t offset, off64_t size) { 123 Mutex::Autolock autoLock(mLock); 124 125 if (mImpl != NULL) { 126 return -EINVAL; 127 } 128 129 sp<FileSource> fileSource = new FileSource(dup(fd), offset, size); 130 131 status_t err = fileSource->initCheck(); 132 if (err != OK) { 133 return err; 134 } 135 136 mImpl = MediaExtractor::Create(fileSource); 137 138 if (mImpl == NULL) { 139 return ERROR_UNSUPPORTED; 140 } 141 142 mDataSource = fileSource; 143 144 updateDurationAndBitrate(); 145 146 return OK; 147} 148 149void NuMediaExtractor::updateDurationAndBitrate() { 150 mTotalBitrate = 0ll; 151 mDurationUs = -1ll; 152 153 for (size_t i = 0; i < mImpl->countTracks(); ++i) { 154 sp<MetaData> meta = mImpl->getTrackMetaData(i); 155 156 int32_t bitrate; 157 if (!meta->findInt32(kKeyBitRate, &bitrate)) { 158 const char *mime; 159 CHECK(meta->findCString(kKeyMIMEType, &mime)); 160 ALOGV("track of type '%s' does not publish bitrate", mime); 161 162 mTotalBitrate = -1ll; 163 } else if (mTotalBitrate >= 0ll) { 164 mTotalBitrate += bitrate; 165 } 166 167 int64_t durationUs; 168 if (meta->findInt64(kKeyDuration, &durationUs) 169 && durationUs > mDurationUs) { 170 mDurationUs = durationUs; 171 } 172 } 173} 174 175size_t NuMediaExtractor::countTracks() const { 176 Mutex::Autolock autoLock(mLock); 177 178 return mImpl == NULL ? 0 : mImpl->countTracks(); 179} 180 181status_t NuMediaExtractor::getTrackFormat( 182 size_t index, sp<AMessage> *format) const { 183 Mutex::Autolock autoLock(mLock); 184 185 *format = NULL; 186 187 if (mImpl == NULL) { 188 return -EINVAL; 189 } 190 191 if (index >= mImpl->countTracks()) { 192 return -ERANGE; 193 } 194 195 sp<MetaData> meta = mImpl->getTrackMetaData(index); 196 197 const char *mime; 198 CHECK(meta->findCString(kKeyMIMEType, &mime)); 199 200 sp<AMessage> msg = new AMessage; 201 msg->setString("mime", mime); 202 203 int64_t durationUs; 204 if (meta->findInt64(kKeyDuration, &durationUs)) { 205 msg->setInt64("durationUs", durationUs); 206 } 207 208 if (!strncasecmp("video/", mime, 6)) { 209 int32_t width, height; 210 CHECK(meta->findInt32(kKeyWidth, &width)); 211 CHECK(meta->findInt32(kKeyHeight, &height)); 212 213 msg->setInt32("width", width); 214 msg->setInt32("height", height); 215 } else if (!strncasecmp("audio/", mime, 6)) { 216 int32_t numChannels, sampleRate; 217 CHECK(meta->findInt32(kKeyChannelCount, &numChannels)); 218 CHECK(meta->findInt32(kKeySampleRate, &sampleRate)); 219 220 msg->setInt32("channel-count", numChannels); 221 msg->setInt32("sample-rate", sampleRate); 222 223 int32_t isADTS; 224 if (meta->findInt32(kKeyIsADTS, &isADTS)) { 225 msg->setInt32("is-adts", true); 226 } 227 } 228 229 int32_t maxInputSize; 230 if (meta->findInt32(kKeyMaxInputSize, &maxInputSize)) { 231 msg->setInt32("max-input-size", maxInputSize); 232 } 233 234 uint32_t type; 235 const void *data; 236 size_t size; 237 if (meta->findData(kKeyAVCC, &type, &data, &size)) { 238 // Parse the AVCDecoderConfigurationRecord 239 240 const uint8_t *ptr = (const uint8_t *)data; 241 242 CHECK(size >= 7); 243 CHECK_EQ((unsigned)ptr[0], 1u); // configurationVersion == 1 244 uint8_t profile = ptr[1]; 245 uint8_t level = ptr[3]; 246 247 // There is decodable content out there that fails the following 248 // assertion, let's be lenient for now... 249 // CHECK((ptr[4] >> 2) == 0x3f); // reserved 250 251 size_t lengthSize = 1 + (ptr[4] & 3); 252 253 // commented out check below as H264_QVGA_500_NO_AUDIO.3gp 254 // violates it... 255 // CHECK((ptr[5] >> 5) == 7); // reserved 256 257 size_t numSeqParameterSets = ptr[5] & 31; 258 259 ptr += 6; 260 size -= 6; 261 262 sp<ABuffer> buffer = new ABuffer(1024); 263 buffer->setRange(0, 0); 264 265 for (size_t i = 0; i < numSeqParameterSets; ++i) { 266 CHECK(size >= 2); 267 size_t length = U16_AT(ptr); 268 269 ptr += 2; 270 size -= 2; 271 272 CHECK(size >= length); 273 274 memcpy(buffer->data() + buffer->size(), "\x00\x00\x00\x01", 4); 275 memcpy(buffer->data() + buffer->size() + 4, ptr, length); 276 buffer->setRange(0, buffer->size() + 4 + length); 277 278 ptr += length; 279 size -= length; 280 } 281 282 buffer->meta()->setInt32("csd", true); 283 buffer->meta()->setInt64("timeUs", 0); 284 285 msg->setBuffer("csd-0", buffer); 286 287 buffer = new ABuffer(1024); 288 buffer->setRange(0, 0); 289 290 CHECK(size >= 1); 291 size_t numPictureParameterSets = *ptr; 292 ++ptr; 293 --size; 294 295 for (size_t i = 0; i < numPictureParameterSets; ++i) { 296 CHECK(size >= 2); 297 size_t length = U16_AT(ptr); 298 299 ptr += 2; 300 size -= 2; 301 302 CHECK(size >= length); 303 304 memcpy(buffer->data() + buffer->size(), "\x00\x00\x00\x01", 4); 305 memcpy(buffer->data() + buffer->size() + 4, ptr, length); 306 buffer->setRange(0, buffer->size() + 4 + length); 307 308 ptr += length; 309 size -= length; 310 } 311 312 buffer->meta()->setInt32("csd", true); 313 buffer->meta()->setInt64("timeUs", 0); 314 msg->setBuffer("csd-1", buffer); 315 } else if (meta->findData(kKeyESDS, &type, &data, &size)) { 316 ESDS esds((const char *)data, size); 317 CHECK_EQ(esds.InitCheck(), (status_t)OK); 318 319 const void *codec_specific_data; 320 size_t codec_specific_data_size; 321 esds.getCodecSpecificInfo( 322 &codec_specific_data, &codec_specific_data_size); 323 324 sp<ABuffer> buffer = new ABuffer(codec_specific_data_size); 325 326 memcpy(buffer->data(), codec_specific_data, 327 codec_specific_data_size); 328 329 buffer->meta()->setInt32("csd", true); 330 buffer->meta()->setInt64("timeUs", 0); 331 msg->setBuffer("csd-0", buffer); 332 } else if (meta->findData(kKeyVorbisInfo, &type, &data, &size)) { 333 sp<ABuffer> buffer = new ABuffer(size); 334 memcpy(buffer->data(), data, size); 335 336 buffer->meta()->setInt32("csd", true); 337 buffer->meta()->setInt64("timeUs", 0); 338 msg->setBuffer("csd-0", buffer); 339 340 if (!meta->findData(kKeyVorbisBooks, &type, &data, &size)) { 341 return -EINVAL; 342 } 343 344 buffer = new ABuffer(size); 345 memcpy(buffer->data(), data, size); 346 347 buffer->meta()->setInt32("csd", true); 348 buffer->meta()->setInt64("timeUs", 0); 349 msg->setBuffer("csd-1", buffer); 350 } 351 352 *format = msg; 353 354 return OK; 355} 356 357status_t NuMediaExtractor::selectTrack(size_t index) { 358 Mutex::Autolock autoLock(mLock); 359 360 if (mImpl == NULL) { 361 return -EINVAL; 362 } 363 364 if (index >= mImpl->countTracks()) { 365 return -ERANGE; 366 } 367 368 for (size_t i = 0; i < mSelectedTracks.size(); ++i) { 369 TrackInfo *info = &mSelectedTracks.editItemAt(i); 370 371 if (info->mTrackIndex == index) { 372 // This track has already been selected. 373 return OK; 374 } 375 } 376 377 sp<MediaSource> source = mImpl->getTrack(index); 378 379 CHECK_EQ((status_t)OK, source->start()); 380 381 mSelectedTracks.push(); 382 TrackInfo *info = &mSelectedTracks.editItemAt(mSelectedTracks.size() - 1); 383 384 info->mSource = source; 385 info->mTrackIndex = index; 386 info->mFinalResult = OK; 387 info->mSample = NULL; 388 info->mSampleTimeUs = -1ll; 389 info->mTrackFlags = 0; 390 391 const char *mime; 392 CHECK(source->getFormat()->findCString(kKeyMIMEType, &mime)); 393 394 if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_VORBIS)) { 395 info->mTrackFlags |= kIsVorbis; 396 } 397 398 return OK; 399} 400 401status_t NuMediaExtractor::unselectTrack(size_t index) { 402 Mutex::Autolock autoLock(mLock); 403 404 if (mImpl == NULL) { 405 return -EINVAL; 406 } 407 408 if (index >= mImpl->countTracks()) { 409 return -ERANGE; 410 } 411 412 size_t i; 413 for (i = 0; i < mSelectedTracks.size(); ++i) { 414 TrackInfo *info = &mSelectedTracks.editItemAt(i); 415 416 if (info->mTrackIndex == index) { 417 break; 418 } 419 } 420 421 if (i == mSelectedTracks.size()) { 422 // Not selected. 423 return OK; 424 } 425 426 TrackInfo *info = &mSelectedTracks.editItemAt(i); 427 428 if (info->mSample != NULL) { 429 info->mSample->release(); 430 info->mSample = NULL; 431 432 info->mSampleTimeUs = -1ll; 433 } 434 435 CHECK_EQ((status_t)OK, info->mSource->stop()); 436 437 mSelectedTracks.removeAt(i); 438 439 return OK; 440} 441 442void NuMediaExtractor::releaseTrackSamples() { 443 for (size_t i = 0; i < mSelectedTracks.size(); ++i) { 444 TrackInfo *info = &mSelectedTracks.editItemAt(i); 445 446 if (info->mSample != NULL) { 447 info->mSample->release(); 448 info->mSample = NULL; 449 450 info->mSampleTimeUs = -1ll; 451 } 452 } 453} 454 455ssize_t NuMediaExtractor::fetchTrackSamples( 456 int64_t seekTimeUs, MediaSource::ReadOptions::SeekMode mode) { 457 TrackInfo *minInfo = NULL; 458 ssize_t minIndex = -1; 459 460 for (size_t i = 0; i < mSelectedTracks.size(); ++i) { 461 TrackInfo *info = &mSelectedTracks.editItemAt(i); 462 463 if (seekTimeUs >= 0ll) { 464 info->mFinalResult = OK; 465 466 if (info->mSample != NULL) { 467 info->mSample->release(); 468 info->mSample = NULL; 469 info->mSampleTimeUs = -1ll; 470 } 471 } else if (info->mFinalResult != OK) { 472 continue; 473 } 474 475 if (info->mSample == NULL) { 476 MediaSource::ReadOptions options; 477 if (seekTimeUs >= 0ll) { 478 options.setSeekTo(seekTimeUs, mode); 479 } 480 status_t err = info->mSource->read(&info->mSample, &options); 481 482 if (err != OK) { 483 CHECK(info->mSample == NULL); 484 485 info->mFinalResult = err; 486 info->mSampleTimeUs = -1ll; 487 continue; 488 } else { 489 CHECK(info->mSample != NULL); 490 CHECK(info->mSample->meta_data()->findInt64( 491 kKeyTime, &info->mSampleTimeUs)); 492 } 493 } 494 495 if (minInfo == NULL || info->mSampleTimeUs < minInfo->mSampleTimeUs) { 496 minInfo = info; 497 minIndex = i; 498 } 499 } 500 501 return minIndex; 502} 503 504status_t NuMediaExtractor::seekTo( 505 int64_t timeUs, MediaSource::ReadOptions::SeekMode mode) { 506 Mutex::Autolock autoLock(mLock); 507 508 ssize_t minIndex = fetchTrackSamples(timeUs, mode); 509 510 if (minIndex < 0) { 511 return ERROR_END_OF_STREAM; 512 } 513 514 return OK; 515} 516 517status_t NuMediaExtractor::advance() { 518 Mutex::Autolock autoLock(mLock); 519 520 ssize_t minIndex = fetchTrackSamples(); 521 522 if (minIndex < 0) { 523 return ERROR_END_OF_STREAM; 524 } 525 526 TrackInfo *info = &mSelectedTracks.editItemAt(minIndex); 527 528 info->mSample->release(); 529 info->mSample = NULL; 530 info->mSampleTimeUs = -1ll; 531 532 return OK; 533} 534 535status_t NuMediaExtractor::readSampleData(const sp<ABuffer> &buffer) { 536 Mutex::Autolock autoLock(mLock); 537 538 ssize_t minIndex = fetchTrackSamples(); 539 540 if (minIndex < 0) { 541 return ERROR_END_OF_STREAM; 542 } 543 544 TrackInfo *info = &mSelectedTracks.editItemAt(minIndex); 545 546 size_t sampleSize = info->mSample->range_length(); 547 548 if (info->mTrackFlags & kIsVorbis) { 549 // Each sample's data is suffixed by the number of page samples 550 // or -1 if not available. 551 sampleSize += sizeof(int32_t); 552 } 553 554 if (buffer->capacity() < sampleSize) { 555 return -ENOMEM; 556 } 557 558 const uint8_t *src = 559 (const uint8_t *)info->mSample->data() 560 + info->mSample->range_offset(); 561 562 memcpy((uint8_t *)buffer->data(), src, info->mSample->range_length()); 563 564 if (info->mTrackFlags & kIsVorbis) { 565 int32_t numPageSamples; 566 if (!info->mSample->meta_data()->findInt32( 567 kKeyValidSamples, &numPageSamples)) { 568 numPageSamples = -1; 569 } 570 571 memcpy((uint8_t *)buffer->data() + info->mSample->range_length(), 572 &numPageSamples, 573 sizeof(numPageSamples)); 574 } 575 576 buffer->setRange(0, sampleSize); 577 578 return OK; 579} 580 581status_t NuMediaExtractor::getSampleTrackIndex(size_t *trackIndex) { 582 Mutex::Autolock autoLock(mLock); 583 584 ssize_t minIndex = fetchTrackSamples(); 585 586 if (minIndex < 0) { 587 return ERROR_END_OF_STREAM; 588 } 589 590 TrackInfo *info = &mSelectedTracks.editItemAt(minIndex); 591 *trackIndex = info->mTrackIndex; 592 593 return OK; 594} 595 596status_t NuMediaExtractor::getSampleTime(int64_t *sampleTimeUs) { 597 Mutex::Autolock autoLock(mLock); 598 599 ssize_t minIndex = fetchTrackSamples(); 600 601 if (minIndex < 0) { 602 return ERROR_END_OF_STREAM; 603 } 604 605 TrackInfo *info = &mSelectedTracks.editItemAt(minIndex); 606 *sampleTimeUs = info->mSampleTimeUs; 607 608 return OK; 609} 610 611status_t NuMediaExtractor::getSampleMeta(sp<MetaData> *sampleMeta) { 612 Mutex::Autolock autoLock(mLock); 613 614 *sampleMeta = NULL; 615 616 ssize_t minIndex = fetchTrackSamples(); 617 618 if (minIndex < 0) { 619 return ERROR_END_OF_STREAM; 620 } 621 622 TrackInfo *info = &mSelectedTracks.editItemAt(minIndex); 623 *sampleMeta = info->mSample->meta_data(); 624 625 return OK; 626} 627 628bool NuMediaExtractor::getTotalBitrate(int64_t *bitrate) const { 629 if (mTotalBitrate >= 0) { 630 *bitrate = mTotalBitrate; 631 return true; 632 } 633 634 off64_t size; 635 if (mDurationUs >= 0 && mDataSource->getSize(&size) == OK) { 636 *bitrate = size * 8000000ll / mDurationUs; // in bits/sec 637 return true; 638 } 639 640 return false; 641} 642 643// Returns true iff cached duration is available/applicable. 644bool NuMediaExtractor::getCachedDuration( 645 int64_t *durationUs, bool *eos) const { 646 Mutex::Autolock autoLock(mLock); 647 648 int64_t bitrate; 649 if (mIsWidevineExtractor) { 650 sp<WVMExtractor> wvmExtractor = 651 static_cast<WVMExtractor *>(mImpl.get()); 652 653 status_t finalStatus; 654 *durationUs = wvmExtractor->getCachedDurationUs(&finalStatus); 655 *eos = (finalStatus != OK); 656 return true; 657 } else if ((mDataSource->flags() & DataSource::kIsCachingDataSource) 658 && getTotalBitrate(&bitrate)) { 659 sp<NuCachedSource2> cachedSource = 660 static_cast<NuCachedSource2 *>(mDataSource.get()); 661 662 status_t finalStatus; 663 size_t cachedDataRemaining = 664 cachedSource->approxDataRemaining(&finalStatus); 665 666 *durationUs = cachedDataRemaining * 8000000ll / bitrate; 667 *eos = (finalStatus != OK); 668 return true; 669 } 670 671 return false; 672} 673 674} // namespace android 675