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