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