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 if (mDataSource != NULL) { 59 mDataSource->close(); 60 } 61} 62 63status_t NuMediaExtractor::setDataSource( 64 const sp<IMediaHTTPService> &httpService, 65 const char *path, 66 const KeyedVector<String8, String8> *headers) { 67 Mutex::Autolock autoLock(mLock); 68 69 if (mImpl != NULL) { 70 return -EINVAL; 71 } 72 73 sp<DataSource> dataSource = 74 DataSource::CreateFromURI(httpService, path, headers); 75 76 if (dataSource == NULL) { 77 return -ENOENT; 78 } 79 80 mIsWidevineExtractor = false; 81 if (!strncasecmp("widevine://", path, 11)) { 82 String8 mimeType; 83 float confidence; 84 sp<AMessage> dummy; 85 bool success = SniffWVM(dataSource, &mimeType, &confidence, &dummy); 86 87 if (!success 88 || strcasecmp( 89 mimeType.string(), MEDIA_MIMETYPE_CONTAINER_WVM)) { 90 return ERROR_UNSUPPORTED; 91 } 92 93 sp<WVMExtractor> extractor = new WVMExtractor(dataSource); 94 extractor->setAdaptiveStreamingMode(true); 95 96 mImpl = extractor; 97 mIsWidevineExtractor = true; 98 } else { 99 mImpl = MediaExtractor::Create(dataSource); 100 } 101 102 if (mImpl == NULL) { 103 return ERROR_UNSUPPORTED; 104 } 105 106 sp<MetaData> fileMeta = mImpl->getMetaData(); 107 const char *containerMime; 108 if (fileMeta != NULL 109 && fileMeta->findCString(kKeyMIMEType, &containerMime) 110 && !strcasecmp(containerMime, "video/wvm")) { 111 // We always want to use "cryptoPluginMode" when using the wvm 112 // extractor. We can tell that it is this extractor by looking 113 // at the container mime type. 114 // The cryptoPluginMode ensures that the extractor will actually 115 // give us data in a call to MediaSource::read(), unlike its 116 // default mode that we used in AwesomePlayer. 117 // TODO: change default mode 118 static_cast<WVMExtractor *>(mImpl.get())->setCryptoPluginMode(true); 119 } else if (mImpl->getDrmFlag()) { 120 // For all other drm content, we don't want to expose decrypted 121 // content to Java application. 122 mImpl.clear(); 123 mImpl = NULL; 124 return ERROR_UNSUPPORTED; 125 } 126 127 status_t err = updateDurationAndBitrate(); 128 if (err == OK) { 129 mDataSource = dataSource; 130 } 131 132 return OK; 133} 134 135status_t NuMediaExtractor::setDataSource(int fd, off64_t offset, off64_t size) { 136 137 ALOGV("setDataSource fd=%d (%s), offset=%lld, length=%lld", 138 fd, nameForFd(fd).c_str(), (long long) offset, (long long) size); 139 140 Mutex::Autolock autoLock(mLock); 141 142 if (mImpl != NULL) { 143 return -EINVAL; 144 } 145 146 sp<FileSource> fileSource = new FileSource(dup(fd), offset, size); 147 148 status_t err = fileSource->initCheck(); 149 if (err != OK) { 150 return err; 151 } 152 153 mImpl = MediaExtractor::Create(fileSource); 154 155 if (mImpl == NULL) { 156 return ERROR_UNSUPPORTED; 157 } 158 159 err = updateDurationAndBitrate(); 160 if (err == OK) { 161 mDataSource = fileSource; 162 } 163 164 return OK; 165} 166 167status_t NuMediaExtractor::setDataSource(const sp<DataSource> &source) { 168 Mutex::Autolock autoLock(mLock); 169 170 if (mImpl != NULL) { 171 return -EINVAL; 172 } 173 174 status_t err = source->initCheck(); 175 if (err != OK) { 176 return err; 177 } 178 179 mImpl = MediaExtractor::Create(source); 180 181 if (mImpl == NULL) { 182 return ERROR_UNSUPPORTED; 183 } 184 185 err = updateDurationAndBitrate(); 186 if (err == OK) { 187 mDataSource = source; 188 } 189 190 return err; 191} 192 193status_t NuMediaExtractor::updateDurationAndBitrate() { 194 if (mImpl->countTracks() > kMaxTrackCount) { 195 return ERROR_UNSUPPORTED; 196 } 197 198 mTotalBitrate = 0ll; 199 mDurationUs = -1ll; 200 201 for (size_t i = 0; i < mImpl->countTracks(); ++i) { 202 sp<MetaData> meta = mImpl->getTrackMetaData(i); 203 if (meta == NULL) { 204 ALOGW("no metadata for track %zu", i); 205 continue; 206 } 207 208 int32_t bitrate; 209 if (!meta->findInt32(kKeyBitRate, &bitrate)) { 210 const char *mime; 211 CHECK(meta->findCString(kKeyMIMEType, &mime)); 212 ALOGV("track of type '%s' does not publish bitrate", mime); 213 214 mTotalBitrate = -1ll; 215 } else if (mTotalBitrate >= 0ll) { 216 mTotalBitrate += bitrate; 217 } 218 219 int64_t durationUs; 220 if (meta->findInt64(kKeyDuration, &durationUs) 221 && durationUs > mDurationUs) { 222 mDurationUs = durationUs; 223 } 224 } 225 return OK; 226} 227 228size_t NuMediaExtractor::countTracks() const { 229 Mutex::Autolock autoLock(mLock); 230 231 return mImpl == NULL ? 0 : mImpl->countTracks(); 232} 233 234status_t NuMediaExtractor::getTrackFormat( 235 size_t index, sp<AMessage> *format, uint32_t flags) const { 236 Mutex::Autolock autoLock(mLock); 237 238 *format = NULL; 239 240 if (mImpl == NULL) { 241 return -EINVAL; 242 } 243 244 if (index >= mImpl->countTracks()) { 245 return -ERANGE; 246 } 247 248 sp<MetaData> meta = mImpl->getTrackMetaData(index, flags); 249 // Extractors either support trackID-s or not, so either all tracks have trackIDs or none. 250 // Generate trackID if missing. 251 int32_t trackID; 252 if (meta != NULL && !meta->findInt32(kKeyTrackID, &trackID)) { 253 meta->setInt32(kKeyTrackID, (int32_t)index + 1); 254 } 255 return convertMetaDataToMessage(meta, format); 256} 257 258status_t NuMediaExtractor::getFileFormat(sp<AMessage> *format) const { 259 Mutex::Autolock autoLock(mLock); 260 261 *format = NULL; 262 263 if (mImpl == NULL) { 264 return -EINVAL; 265 } 266 267 sp<MetaData> meta = mImpl->getMetaData(); 268 269 const char *mime; 270 CHECK(meta->findCString(kKeyMIMEType, &mime)); 271 *format = new AMessage(); 272 (*format)->setString("mime", mime); 273 274 uint32_t type; 275 const void *pssh; 276 size_t psshsize; 277 if (meta->findData(kKeyPssh, &type, &pssh, &psshsize)) { 278 sp<ABuffer> buf = new ABuffer(psshsize); 279 memcpy(buf->data(), pssh, psshsize); 280 (*format)->setBuffer("pssh", buf); 281 } 282 283 return OK; 284} 285 286status_t NuMediaExtractor::selectTrack(size_t index) { 287 Mutex::Autolock autoLock(mLock); 288 289 if (mImpl == NULL) { 290 return -EINVAL; 291 } 292 293 if (index >= mImpl->countTracks()) { 294 return -ERANGE; 295 } 296 297 for (size_t i = 0; i < mSelectedTracks.size(); ++i) { 298 TrackInfo *info = &mSelectedTracks.editItemAt(i); 299 300 if (info->mTrackIndex == index) { 301 // This track has already been selected. 302 return OK; 303 } 304 } 305 306 sp<IMediaSource> source = mImpl->getTrack(index); 307 308 if (source == nullptr) { 309 return ERROR_MALFORMED; 310 } 311 312 status_t ret = source->start(); 313 if (ret != OK) { 314 return ret; 315 } 316 317 mSelectedTracks.push(); 318 TrackInfo *info = &mSelectedTracks.editItemAt(mSelectedTracks.size() - 1); 319 320 info->mSource = source; 321 info->mTrackIndex = index; 322 info->mFinalResult = OK; 323 info->mSample = NULL; 324 info->mSampleTimeUs = -1ll; 325 info->mTrackFlags = 0; 326 327 const char *mime; 328 CHECK(source->getFormat()->findCString(kKeyMIMEType, &mime)); 329 330 if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_VORBIS)) { 331 info->mTrackFlags |= kIsVorbis; 332 } 333 334 return OK; 335} 336 337status_t NuMediaExtractor::unselectTrack(size_t index) { 338 Mutex::Autolock autoLock(mLock); 339 340 if (mImpl == NULL) { 341 return -EINVAL; 342 } 343 344 if (index >= mImpl->countTracks()) { 345 return -ERANGE; 346 } 347 348 size_t i; 349 for (i = 0; i < mSelectedTracks.size(); ++i) { 350 TrackInfo *info = &mSelectedTracks.editItemAt(i); 351 352 if (info->mTrackIndex == index) { 353 break; 354 } 355 } 356 357 if (i == mSelectedTracks.size()) { 358 // Not selected. 359 return OK; 360 } 361 362 TrackInfo *info = &mSelectedTracks.editItemAt(i); 363 364 if (info->mSample != NULL) { 365 info->mSample->release(); 366 info->mSample = NULL; 367 368 info->mSampleTimeUs = -1ll; 369 } 370 371 CHECK_EQ((status_t)OK, info->mSource->stop()); 372 373 mSelectedTracks.removeAt(i); 374 375 return OK; 376} 377 378void NuMediaExtractor::releaseTrackSamples() { 379 for (size_t i = 0; i < mSelectedTracks.size(); ++i) { 380 TrackInfo *info = &mSelectedTracks.editItemAt(i); 381 382 if (info->mSample != NULL) { 383 info->mSample->release(); 384 info->mSample = NULL; 385 386 info->mSampleTimeUs = -1ll; 387 } 388 } 389} 390 391ssize_t NuMediaExtractor::fetchTrackSamples( 392 int64_t seekTimeUs, MediaSource::ReadOptions::SeekMode mode) { 393 TrackInfo *minInfo = NULL; 394 ssize_t minIndex = -1; 395 396 for (size_t i = 0; i < mSelectedTracks.size(); ++i) { 397 TrackInfo *info = &mSelectedTracks.editItemAt(i); 398 399 if (seekTimeUs >= 0ll) { 400 info->mFinalResult = OK; 401 402 if (info->mSample != NULL) { 403 info->mSample->release(); 404 info->mSample = NULL; 405 info->mSampleTimeUs = -1ll; 406 } 407 } else if (info->mFinalResult != OK) { 408 continue; 409 } 410 411 if (info->mSample == NULL) { 412 MediaSource::ReadOptions options; 413 if (seekTimeUs >= 0ll) { 414 options.setSeekTo(seekTimeUs, mode); 415 } 416 status_t err = info->mSource->read(&info->mSample, &options); 417 418 if (err != OK) { 419 CHECK(info->mSample == NULL); 420 421 info->mFinalResult = err; 422 423 if (info->mFinalResult != ERROR_END_OF_STREAM) { 424 ALOGW("read on track %zu failed with error %d", 425 info->mTrackIndex, err); 426 } 427 428 info->mSampleTimeUs = -1ll; 429 continue; 430 } else { 431 CHECK(info->mSample != NULL); 432 CHECK(info->mSample->meta_data()->findInt64( 433 kKeyTime, &info->mSampleTimeUs)); 434 } 435 } 436 437 if (minInfo == NULL || info->mSampleTimeUs < minInfo->mSampleTimeUs) { 438 minInfo = info; 439 minIndex = i; 440 } 441 } 442 443 return minIndex; 444} 445 446status_t NuMediaExtractor::seekTo( 447 int64_t timeUs, MediaSource::ReadOptions::SeekMode mode) { 448 Mutex::Autolock autoLock(mLock); 449 450 ssize_t minIndex = fetchTrackSamples(timeUs, mode); 451 452 if (minIndex < 0) { 453 return ERROR_END_OF_STREAM; 454 } 455 456 return OK; 457} 458 459status_t NuMediaExtractor::advance() { 460 Mutex::Autolock autoLock(mLock); 461 462 ssize_t minIndex = fetchTrackSamples(); 463 464 if (minIndex < 0) { 465 return ERROR_END_OF_STREAM; 466 } 467 468 TrackInfo *info = &mSelectedTracks.editItemAt(minIndex); 469 470 info->mSample->release(); 471 info->mSample = NULL; 472 info->mSampleTimeUs = -1ll; 473 474 return OK; 475} 476 477status_t NuMediaExtractor::appendVorbisNumPageSamples(TrackInfo *info, const sp<ABuffer> &buffer) { 478 int32_t numPageSamples; 479 if (!info->mSample->meta_data()->findInt32( 480 kKeyValidSamples, &numPageSamples)) { 481 numPageSamples = -1; 482 } 483 484 memcpy((uint8_t *)buffer->data() + info->mSample->range_length(), 485 &numPageSamples, 486 sizeof(numPageSamples)); 487 488 uint32_t type; 489 const void *data; 490 size_t size, size2; 491 if (info->mSample->meta_data()->findData(kKeyEncryptedSizes, &type, &data, &size)) { 492 // Signal numPageSamples (a plain int32_t) is appended at the end, 493 // i.e. sizeof(numPageSamples) plain bytes + 0 encrypted bytes 494 if (SIZE_MAX - size < sizeof(int32_t)) { 495 return -ENOMEM; 496 } 497 498 size_t newSize = size + sizeof(int32_t); 499 sp<ABuffer> abuf = new ABuffer(newSize); 500 uint8_t *adata = static_cast<uint8_t *>(abuf->data()); 501 if (adata == NULL) { 502 return -ENOMEM; 503 } 504 505 // append 0 to encrypted sizes 506 int32_t zero = 0; 507 memcpy(adata, data, size); 508 memcpy(adata + size, &zero, sizeof(zero)); 509 info->mSample->meta_data()->setData(kKeyEncryptedSizes, type, adata, newSize); 510 511 if (info->mSample->meta_data()->findData(kKeyPlainSizes, &type, &data, &size2)) { 512 if (size2 != size) { 513 return ERROR_MALFORMED; 514 } 515 memcpy(adata, data, size); 516 } else { 517 // if sample meta data does not include plain size array, assume filled with zeros, 518 // i.e. entire buffer is encrypted 519 memset(adata, 0, size); 520 } 521 // append sizeof(numPageSamples) to plain sizes. 522 int32_t int32Size = sizeof(numPageSamples); 523 memcpy(adata + size, &int32Size, sizeof(int32Size)); 524 info->mSample->meta_data()->setData(kKeyPlainSizes, type, adata, newSize); 525 } 526 527 return OK; 528} 529 530status_t NuMediaExtractor::readSampleData(const sp<ABuffer> &buffer) { 531 Mutex::Autolock autoLock(mLock); 532 533 ssize_t minIndex = fetchTrackSamples(); 534 535 if (minIndex < 0) { 536 return ERROR_END_OF_STREAM; 537 } 538 539 TrackInfo *info = &mSelectedTracks.editItemAt(minIndex); 540 541 size_t sampleSize = info->mSample->range_length(); 542 543 if (info->mTrackFlags & kIsVorbis) { 544 // Each sample's data is suffixed by the number of page samples 545 // or -1 if not available. 546 sampleSize += sizeof(int32_t); 547 } 548 549 if (buffer->capacity() < sampleSize) { 550 return -ENOMEM; 551 } 552 553 const uint8_t *src = 554 (const uint8_t *)info->mSample->data() 555 + info->mSample->range_offset(); 556 557 memcpy((uint8_t *)buffer->data(), src, info->mSample->range_length()); 558 559 status_t err = OK; 560 if (info->mTrackFlags & kIsVorbis) { 561 err = appendVorbisNumPageSamples(info, buffer); 562 } 563 564 if (err == OK) { 565 buffer->setRange(0, sampleSize); 566 } 567 568 return err; 569} 570 571status_t NuMediaExtractor::getSampleTrackIndex(size_t *trackIndex) { 572 Mutex::Autolock autoLock(mLock); 573 574 ssize_t minIndex = fetchTrackSamples(); 575 576 if (minIndex < 0) { 577 return ERROR_END_OF_STREAM; 578 } 579 580 TrackInfo *info = &mSelectedTracks.editItemAt(minIndex); 581 *trackIndex = info->mTrackIndex; 582 583 return OK; 584} 585 586status_t NuMediaExtractor::getSampleTime(int64_t *sampleTimeUs) { 587 Mutex::Autolock autoLock(mLock); 588 589 ssize_t minIndex = fetchTrackSamples(); 590 591 if (minIndex < 0) { 592 return ERROR_END_OF_STREAM; 593 } 594 595 TrackInfo *info = &mSelectedTracks.editItemAt(minIndex); 596 *sampleTimeUs = info->mSampleTimeUs; 597 598 return OK; 599} 600 601status_t NuMediaExtractor::getSampleMeta(sp<MetaData> *sampleMeta) { 602 Mutex::Autolock autoLock(mLock); 603 604 *sampleMeta = NULL; 605 606 ssize_t minIndex = fetchTrackSamples(); 607 608 if (minIndex < 0) { 609 return ERROR_END_OF_STREAM; 610 } 611 612 TrackInfo *info = &mSelectedTracks.editItemAt(minIndex); 613 *sampleMeta = info->mSample->meta_data(); 614 615 return OK; 616} 617 618bool NuMediaExtractor::getTotalBitrate(int64_t *bitrate) const { 619 if (mTotalBitrate >= 0) { 620 *bitrate = mTotalBitrate; 621 return true; 622 } 623 624 off64_t size; 625 if (mDurationUs > 0 && mDataSource->getSize(&size) == OK) { 626 *bitrate = size * 8000000ll / mDurationUs; // in bits/sec 627 return true; 628 } 629 630 return false; 631} 632 633// Returns true iff cached duration is available/applicable. 634bool NuMediaExtractor::getCachedDuration( 635 int64_t *durationUs, bool *eos) const { 636 Mutex::Autolock autoLock(mLock); 637 638 int64_t bitrate; 639 if (mIsWidevineExtractor) { 640 sp<WVMExtractor> wvmExtractor = 641 static_cast<WVMExtractor *>(mImpl.get()); 642 643 status_t finalStatus; 644 *durationUs = wvmExtractor->getCachedDurationUs(&finalStatus); 645 *eos = (finalStatus != OK); 646 return true; 647 } else if ((mDataSource->flags() & DataSource::kIsCachingDataSource) 648 && getTotalBitrate(&bitrate)) { 649 sp<NuCachedSource2> cachedSource = 650 static_cast<NuCachedSource2 *>(mDataSource.get()); 651 652 status_t finalStatus; 653 size_t cachedDataRemaining = 654 cachedSource->approxDataRemaining(&finalStatus); 655 656 *durationUs = cachedDataRemaining * 8000000ll / bitrate; 657 *eos = (finalStatus != OK); 658 return true; 659 } 660 661 return false; 662} 663 664} // namespace android 665