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 26 #include <media/DataSource.h> 27 #include <media/MediaExtractor.h> 28 #include <media/MediaSource.h> 29 #include <media/stagefright/foundation/ABuffer.h> 30 #include <media/stagefright/foundation/ADebug.h> 31 #include <media/stagefright/foundation/AMessage.h> 32 #include <media/stagefright/DataSourceFactory.h> 33 #include <media/stagefright/FileSource.h> 34 #include <media/stagefright/MediaBuffer.h> 35 #include <media/stagefright/MediaDefs.h> 36 #include <media/stagefright/MediaErrors.h> 37 #include <media/stagefright/MediaExtractorFactory.h> 38 #include <media/stagefright/MetaData.h> 39 #include <media/stagefright/Utils.h> 40 41 namespace android { 42 43 NuMediaExtractor::Sample::Sample() 44 : mBuffer(NULL), 45 mSampleTimeUs(-1ll) { 46 } 47 48 NuMediaExtractor::Sample::Sample(MediaBufferBase *buffer, int64_t timeUs) 49 : mBuffer(buffer), 50 mSampleTimeUs(timeUs) { 51 } 52 53 NuMediaExtractor::NuMediaExtractor() 54 : mTotalBitrate(-1ll), 55 mDurationUs(-1ll) { 56 } 57 58 NuMediaExtractor::~NuMediaExtractor() { 59 releaseAllTrackSamples(); 60 61 for (size_t i = 0; i < mSelectedTracks.size(); ++i) { 62 TrackInfo *info = &mSelectedTracks.editItemAt(i); 63 64 status_t err = info->mSource->stop(); 65 ALOGE_IF(err != OK, "error %d stopping track %zu", err, i); 66 } 67 68 mSelectedTracks.clear(); 69 if (mDataSource != NULL) { 70 mDataSource->close(); 71 } 72 } 73 74 status_t NuMediaExtractor::setDataSource( 75 const sp<MediaHTTPService> &httpService, 76 const char *path, 77 const KeyedVector<String8, String8> *headers) { 78 Mutex::Autolock autoLock(mLock); 79 80 if (mImpl != NULL || path == NULL) { 81 return -EINVAL; 82 } 83 84 sp<DataSource> dataSource = 85 DataSourceFactory::CreateFromURI(httpService, path, headers); 86 87 if (dataSource == NULL) { 88 return -ENOENT; 89 } 90 91 mImpl = MediaExtractorFactory::Create(dataSource); 92 93 if (mImpl == NULL) { 94 return ERROR_UNSUPPORTED; 95 } 96 97 if (!mCasToken.empty()) { 98 mImpl->setMediaCas(mCasToken); 99 } 100 101 status_t err = updateDurationAndBitrate(); 102 if (err == OK) { 103 mDataSource = dataSource; 104 } 105 106 return OK; 107 } 108 109 status_t NuMediaExtractor::setDataSource(int fd, off64_t offset, off64_t size) { 110 111 ALOGV("setDataSource fd=%d (%s), offset=%lld, length=%lld", 112 fd, nameForFd(fd).c_str(), (long long) offset, (long long) size); 113 114 Mutex::Autolock autoLock(mLock); 115 116 if (mImpl != NULL) { 117 return -EINVAL; 118 } 119 120 sp<FileSource> fileSource = new FileSource(dup(fd), offset, size); 121 122 status_t err = fileSource->initCheck(); 123 if (err != OK) { 124 return err; 125 } 126 127 mImpl = MediaExtractorFactory::Create(fileSource); 128 129 if (mImpl == NULL) { 130 return ERROR_UNSUPPORTED; 131 } 132 133 if (!mCasToken.empty()) { 134 mImpl->setMediaCas(mCasToken); 135 } 136 137 err = updateDurationAndBitrate(); 138 if (err == OK) { 139 mDataSource = fileSource; 140 } 141 142 return OK; 143 } 144 145 status_t NuMediaExtractor::setDataSource(const sp<DataSource> &source) { 146 Mutex::Autolock autoLock(mLock); 147 148 if (mImpl != NULL) { 149 return -EINVAL; 150 } 151 152 status_t err = source->initCheck(); 153 if (err != OK) { 154 return err; 155 } 156 157 mImpl = MediaExtractorFactory::Create(source); 158 159 if (mImpl == NULL) { 160 return ERROR_UNSUPPORTED; 161 } 162 163 if (!mCasToken.empty()) { 164 mImpl->setMediaCas(mCasToken); 165 } 166 167 err = updateDurationAndBitrate(); 168 if (err == OK) { 169 mDataSource = source; 170 } 171 172 return err; 173 } 174 175 static String8 arrayToString(const std::vector<uint8_t> &array) { 176 String8 result; 177 for (size_t i = 0; i < array.size(); i++) { 178 result.appendFormat("%02x ", array[i]); 179 } 180 if (result.isEmpty()) { 181 result.append("(null)"); 182 } 183 return result; 184 } 185 186 status_t NuMediaExtractor::setMediaCas(const HInterfaceToken &casToken) { 187 ALOGV("setMediaCas: casToken={%s}", arrayToString(casToken).c_str()); 188 189 Mutex::Autolock autoLock(mLock); 190 191 if (casToken.empty()) { 192 return BAD_VALUE; 193 } 194 195 mCasToken = casToken; 196 197 if (mImpl != NULL) { 198 mImpl->setMediaCas(casToken); 199 status_t err = updateDurationAndBitrate(); 200 if (err != OK) { 201 return err; 202 } 203 } 204 205 return OK; 206 } 207 208 void NuMediaExtractor::disconnect() { 209 if (mDataSource != NULL) { 210 // disconnect data source 211 if (mDataSource->flags() & DataSource::kIsCachingDataSource) { 212 static_cast<NuCachedSource2 *>(mDataSource.get())->disconnect(); 213 } 214 } 215 } 216 217 status_t NuMediaExtractor::updateDurationAndBitrate() { 218 if (mImpl->countTracks() > kMaxTrackCount) { 219 return ERROR_UNSUPPORTED; 220 } 221 222 mTotalBitrate = 0ll; 223 mDurationUs = -1ll; 224 225 for (size_t i = 0; i < mImpl->countTracks(); ++i) { 226 sp<MetaData> meta = mImpl->getTrackMetaData(i); 227 if (meta == NULL) { 228 ALOGW("no metadata for track %zu", i); 229 continue; 230 } 231 232 int32_t bitrate; 233 if (!meta->findInt32(kKeyBitRate, &bitrate)) { 234 const char *mime; 235 CHECK(meta->findCString(kKeyMIMEType, &mime)); 236 ALOGV("track of type '%s' does not publish bitrate", mime); 237 238 mTotalBitrate = -1ll; 239 } else if (mTotalBitrate >= 0ll) { 240 mTotalBitrate += bitrate; 241 } 242 243 int64_t durationUs; 244 if (meta->findInt64(kKeyDuration, &durationUs) 245 && durationUs > mDurationUs) { 246 mDurationUs = durationUs; 247 } 248 } 249 return OK; 250 } 251 252 size_t NuMediaExtractor::countTracks() const { 253 Mutex::Autolock autoLock(mLock); 254 255 return mImpl == NULL ? 0 : mImpl->countTracks(); 256 } 257 258 status_t NuMediaExtractor::getTrackFormat( 259 size_t index, sp<AMessage> *format, uint32_t flags) const { 260 Mutex::Autolock autoLock(mLock); 261 262 *format = NULL; 263 264 if (mImpl == NULL) { 265 return -EINVAL; 266 } 267 268 if (index >= mImpl->countTracks()) { 269 return -ERANGE; 270 } 271 272 sp<MetaData> meta = mImpl->getTrackMetaData(index, flags); 273 // Extractors either support trackID-s or not, so either all tracks have trackIDs or none. 274 // Generate trackID if missing. 275 int32_t trackID; 276 if (meta != NULL && !meta->findInt32(kKeyTrackID, &trackID)) { 277 meta->setInt32(kKeyTrackID, (int32_t)index + 1); 278 } 279 return convertMetaDataToMessage(meta, format); 280 } 281 282 status_t NuMediaExtractor::getFileFormat(sp<AMessage> *format) const { 283 Mutex::Autolock autoLock(mLock); 284 285 *format = NULL; 286 287 if (mImpl == NULL) { 288 return -EINVAL; 289 } 290 291 sp<MetaData> meta = mImpl->getMetaData(); 292 293 const char *mime; 294 CHECK(meta->findCString(kKeyMIMEType, &mime)); 295 *format = new AMessage(); 296 (*format)->setString("mime", mime); 297 298 uint32_t type; 299 const void *pssh; 300 size_t psshsize; 301 if (meta->findData(kKeyPssh, &type, &pssh, &psshsize)) { 302 sp<ABuffer> buf = new ABuffer(psshsize); 303 memcpy(buf->data(), pssh, psshsize); 304 (*format)->setBuffer("pssh", buf); 305 } 306 307 return OK; 308 } 309 310 status_t NuMediaExtractor::getExifOffsetSize(off64_t *offset, size_t *size) const { 311 Mutex::Autolock autoLock(mLock); 312 313 if (mImpl == NULL) { 314 return -EINVAL; 315 } 316 317 sp<MetaData> meta = mImpl->getMetaData(); 318 319 int64_t exifOffset, exifSize; 320 if (meta->findInt64(kKeyExifOffset, &exifOffset) 321 && meta->findInt64(kKeyExifSize, &exifSize)) { 322 *offset = (off64_t) exifOffset; 323 *size = (size_t) exifSize; 324 325 return OK; 326 } 327 return ERROR_UNSUPPORTED; 328 } 329 330 status_t NuMediaExtractor::selectTrack(size_t index, 331 int64_t startTimeUs, MediaSource::ReadOptions::SeekMode mode) { 332 Mutex::Autolock autoLock(mLock); 333 334 if (mImpl == NULL) { 335 return -EINVAL; 336 } 337 338 if (index >= mImpl->countTracks()) { 339 return -ERANGE; 340 } 341 342 for (size_t i = 0; i < mSelectedTracks.size(); ++i) { 343 TrackInfo *info = &mSelectedTracks.editItemAt(i); 344 345 if (info->mTrackIndex == index) { 346 // This track has already been selected. 347 return OK; 348 } 349 } 350 351 sp<IMediaSource> source = mImpl->getTrack(index); 352 353 if (source == nullptr) { 354 ALOGE("track %zu is empty", index); 355 return ERROR_MALFORMED; 356 } 357 358 status_t ret = source->start(); 359 if (ret != OK) { 360 ALOGE("track %zu failed to start", index); 361 return ret; 362 } 363 364 sp<MetaData> meta = source->getFormat(); 365 if (meta == NULL) { 366 ALOGE("track %zu has no meta data", index); 367 return ERROR_MALFORMED; 368 } 369 370 const char *mime; 371 if (!meta->findCString(kKeyMIMEType, &mime)) { 372 ALOGE("track %zu has no mime type in meta data", index); 373 return ERROR_MALFORMED; 374 } 375 ALOGV("selectTrack, track[%zu]: %s", index, mime); 376 377 mSelectedTracks.push(); 378 TrackInfo *info = &mSelectedTracks.editItemAt(mSelectedTracks.size() - 1); 379 380 info->mSource = source; 381 info->mTrackIndex = index; 382 if (!strncasecmp(mime, "audio/", 6)) { 383 info->mTrackType = MEDIA_TRACK_TYPE_AUDIO; 384 info->mMaxFetchCount = 64; 385 } else if (!strncasecmp(mime, "video/", 6)) { 386 info->mTrackType = MEDIA_TRACK_TYPE_VIDEO; 387 info->mMaxFetchCount = 8; 388 } else { 389 info->mTrackType = MEDIA_TRACK_TYPE_UNKNOWN; 390 info->mMaxFetchCount = 1; 391 } 392 info->mFinalResult = OK; 393 releaseTrackSamples(info); 394 info->mTrackFlags = 0; 395 396 if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_VORBIS)) { 397 info->mTrackFlags |= kIsVorbis; 398 } 399 400 if (startTimeUs >= 0) { 401 fetchTrackSamples(info, startTimeUs, mode); 402 } 403 404 return OK; 405 } 406 407 status_t NuMediaExtractor::unselectTrack(size_t index) { 408 Mutex::Autolock autoLock(mLock); 409 410 if (mImpl == NULL) { 411 return -EINVAL; 412 } 413 414 if (index >= mImpl->countTracks()) { 415 return -ERANGE; 416 } 417 418 size_t i; 419 for (i = 0; i < mSelectedTracks.size(); ++i) { 420 TrackInfo *info = &mSelectedTracks.editItemAt(i); 421 422 if (info->mTrackIndex == index) { 423 break; 424 } 425 } 426 427 if (i == mSelectedTracks.size()) { 428 // Not selected. 429 return OK; 430 } 431 432 TrackInfo *info = &mSelectedTracks.editItemAt(i); 433 434 releaseTrackSamples(info); 435 436 CHECK_EQ((status_t)OK, info->mSource->stop()); 437 438 mSelectedTracks.removeAt(i); 439 440 return OK; 441 } 442 443 void NuMediaExtractor::releaseOneSample(TrackInfo *info) { 444 if (info == NULL || info->mSamples.empty()) { 445 return; 446 } 447 448 auto it = info->mSamples.begin(); 449 if (it->mBuffer != NULL) { 450 it->mBuffer->release(); 451 } 452 info->mSamples.erase(it); 453 } 454 455 void NuMediaExtractor::releaseTrackSamples(TrackInfo *info) { 456 if (info == NULL) { 457 return; 458 } 459 460 auto it = info->mSamples.begin(); 461 while (it != info->mSamples.end()) { 462 if (it->mBuffer != NULL) { 463 it->mBuffer->release(); 464 } 465 it = info->mSamples.erase(it); 466 } 467 } 468 469 void NuMediaExtractor::releaseAllTrackSamples() { 470 for (size_t i = 0; i < mSelectedTracks.size(); ++i) { 471 releaseTrackSamples(&mSelectedTracks.editItemAt(i)); 472 } 473 } 474 475 ssize_t NuMediaExtractor::fetchAllTrackSamples( 476 int64_t seekTimeUs, MediaSource::ReadOptions::SeekMode mode) { 477 TrackInfo *minInfo = NULL; 478 ssize_t minIndex = ERROR_END_OF_STREAM; 479 480 for (size_t i = 0; i < mSelectedTracks.size(); ++i) { 481 TrackInfo *info = &mSelectedTracks.editItemAt(i); 482 fetchTrackSamples(info, seekTimeUs, mode); 483 484 status_t err = info->mFinalResult; 485 if (err != OK && err != ERROR_END_OF_STREAM) { 486 return err; 487 } 488 489 if (info->mSamples.empty()) { 490 continue; 491 } 492 493 if (minInfo == NULL) { 494 minInfo = info; 495 minIndex = i; 496 } else { 497 auto it = info->mSamples.begin(); 498 auto itMin = minInfo->mSamples.begin(); 499 if (it->mSampleTimeUs < itMin->mSampleTimeUs) { 500 minInfo = info; 501 minIndex = i; 502 } 503 } 504 } 505 506 return minIndex; 507 } 508 509 void NuMediaExtractor::fetchTrackSamples(TrackInfo *info, 510 int64_t seekTimeUs, MediaSource::ReadOptions::SeekMode mode) { 511 if (info == NULL) { 512 return; 513 } 514 515 MediaSource::ReadOptions options; 516 if (seekTimeUs >= 0ll) { 517 options.setSeekTo(seekTimeUs, mode); 518 info->mFinalResult = OK; 519 releaseTrackSamples(info); 520 } else if (info->mFinalResult != OK || !info->mSamples.empty()) { 521 return; 522 } 523 524 status_t err = OK; 525 Vector<MediaBufferBase *> mediaBuffers; 526 if (info->mSource->supportReadMultiple()) { 527 options.setNonBlocking(); 528 err = info->mSource->readMultiple(&mediaBuffers, info->mMaxFetchCount, &options); 529 } else { 530 MediaBufferBase *mbuf = NULL; 531 err = info->mSource->read(&mbuf, &options); 532 if (err == OK && mbuf != NULL) { 533 mediaBuffers.push_back(mbuf); 534 } 535 } 536 537 info->mFinalResult = err; 538 if (err != OK && err != ERROR_END_OF_STREAM) { 539 ALOGW("read on track %zu failed with error %d", info->mTrackIndex, err); 540 size_t count = mediaBuffers.size(); 541 for (size_t id = 0; id < count; ++id) { 542 MediaBufferBase *mbuf = mediaBuffers[id]; 543 if (mbuf != NULL) { 544 mbuf->release(); 545 } 546 } 547 return; 548 } 549 550 size_t count = mediaBuffers.size(); 551 bool releaseRemaining = false; 552 for (size_t id = 0; id < count; ++id) { 553 int64_t timeUs; 554 MediaBufferBase *mbuf = mediaBuffers[id]; 555 if (mbuf == NULL) { 556 continue; 557 } 558 if (releaseRemaining) { 559 mbuf->release(); 560 continue; 561 } 562 if (mbuf->meta_data().findInt64(kKeyTime, &timeUs)) { 563 info->mSamples.emplace_back(mbuf, timeUs); 564 } else { 565 mbuf->meta_data().dumpToLog(); 566 info->mFinalResult = ERROR_MALFORMED; 567 mbuf->release(); 568 releaseRemaining = true; 569 } 570 } 571 } 572 573 status_t NuMediaExtractor::seekTo( 574 int64_t timeUs, MediaSource::ReadOptions::SeekMode mode) { 575 Mutex::Autolock autoLock(mLock); 576 577 ssize_t minIndex = fetchAllTrackSamples(timeUs, mode); 578 579 if (minIndex < 0) { 580 return ERROR_END_OF_STREAM; 581 } 582 583 return OK; 584 } 585 586 status_t NuMediaExtractor::advance() { 587 Mutex::Autolock autoLock(mLock); 588 589 ssize_t minIndex = fetchAllTrackSamples(); 590 591 if (minIndex < 0) { 592 return ERROR_END_OF_STREAM; 593 } 594 595 TrackInfo *info = &mSelectedTracks.editItemAt(minIndex); 596 597 releaseOneSample(info); 598 599 return OK; 600 } 601 602 status_t NuMediaExtractor::appendVorbisNumPageSamples( 603 MediaBufferBase *mbuf, const sp<ABuffer> &buffer) { 604 int32_t numPageSamples; 605 if (!mbuf->meta_data().findInt32( 606 kKeyValidSamples, &numPageSamples)) { 607 numPageSamples = -1; 608 } 609 610 memcpy((uint8_t *)buffer->data() + mbuf->range_length(), 611 &numPageSamples, 612 sizeof(numPageSamples)); 613 614 uint32_t type; 615 const void *data; 616 size_t size, size2; 617 if (mbuf->meta_data().findData(kKeyEncryptedSizes, &type, &data, &size)) { 618 // Signal numPageSamples (a plain int32_t) is appended at the end, 619 // i.e. sizeof(numPageSamples) plain bytes + 0 encrypted bytes 620 if (SIZE_MAX - size < sizeof(int32_t)) { 621 return -ENOMEM; 622 } 623 624 size_t newSize = size + sizeof(int32_t); 625 sp<ABuffer> abuf = new ABuffer(newSize); 626 uint8_t *adata = static_cast<uint8_t *>(abuf->data()); 627 if (adata == NULL) { 628 return -ENOMEM; 629 } 630 631 // append 0 to encrypted sizes 632 int32_t zero = 0; 633 memcpy(adata, data, size); 634 memcpy(adata + size, &zero, sizeof(zero)); 635 mbuf->meta_data().setData(kKeyEncryptedSizes, type, adata, newSize); 636 637 if (mbuf->meta_data().findData(kKeyPlainSizes, &type, &data, &size2)) { 638 if (size2 != size) { 639 return ERROR_MALFORMED; 640 } 641 memcpy(adata, data, size); 642 } else { 643 // if sample meta data does not include plain size array, assume filled with zeros, 644 // i.e. entire buffer is encrypted 645 memset(adata, 0, size); 646 } 647 // append sizeof(numPageSamples) to plain sizes. 648 int32_t int32Size = sizeof(numPageSamples); 649 memcpy(adata + size, &int32Size, sizeof(int32Size)); 650 mbuf->meta_data().setData(kKeyPlainSizes, type, adata, newSize); 651 } 652 653 return OK; 654 } 655 656 status_t NuMediaExtractor::readSampleData(const sp<ABuffer> &buffer) { 657 Mutex::Autolock autoLock(mLock); 658 659 ssize_t minIndex = fetchAllTrackSamples(); 660 661 if (minIndex < 0) { 662 return ERROR_END_OF_STREAM; 663 } 664 665 TrackInfo *info = &mSelectedTracks.editItemAt(minIndex); 666 667 auto it = info->mSamples.begin(); 668 size_t sampleSize = it->mBuffer->range_length(); 669 670 if (info->mTrackFlags & kIsVorbis) { 671 // Each sample's data is suffixed by the number of page samples 672 // or -1 if not available. 673 sampleSize += sizeof(int32_t); 674 } 675 676 if (buffer->capacity() < sampleSize) { 677 return -ENOMEM; 678 } 679 680 const uint8_t *src = 681 (const uint8_t *)it->mBuffer->data() 682 + it->mBuffer->range_offset(); 683 684 memcpy((uint8_t *)buffer->data(), src, it->mBuffer->range_length()); 685 686 status_t err = OK; 687 if (info->mTrackFlags & kIsVorbis) { 688 err = appendVorbisNumPageSamples(it->mBuffer, buffer); 689 } 690 691 if (err == OK) { 692 buffer->setRange(0, sampleSize); 693 } 694 695 return err; 696 } 697 698 status_t NuMediaExtractor::getSampleSize(size_t *sampleSize) { 699 Mutex::Autolock autoLock(mLock); 700 701 ssize_t minIndex = fetchAllTrackSamples(); 702 703 if (minIndex < 0) { 704 return ERROR_END_OF_STREAM; 705 } 706 707 TrackInfo *info = &mSelectedTracks.editItemAt(minIndex); 708 auto it = info->mSamples.begin(); 709 *sampleSize = it->mBuffer->range_length(); 710 711 if (info->mTrackFlags & kIsVorbis) { 712 // Each sample's data is suffixed by the number of page samples 713 // or -1 if not available. 714 *sampleSize += sizeof(int32_t); 715 } 716 717 return OK; 718 } 719 720 status_t NuMediaExtractor::getSampleTrackIndex(size_t *trackIndex) { 721 Mutex::Autolock autoLock(mLock); 722 723 ssize_t minIndex = fetchAllTrackSamples(); 724 725 if (minIndex < 0) { 726 return ERROR_END_OF_STREAM; 727 } 728 729 TrackInfo *info = &mSelectedTracks.editItemAt(minIndex); 730 *trackIndex = info->mTrackIndex; 731 732 return OK; 733 } 734 735 status_t NuMediaExtractor::getSampleTime(int64_t *sampleTimeUs) { 736 Mutex::Autolock autoLock(mLock); 737 738 ssize_t minIndex = fetchAllTrackSamples(); 739 740 if (minIndex < 0) { 741 return ERROR_END_OF_STREAM; 742 } 743 744 TrackInfo *info = &mSelectedTracks.editItemAt(minIndex); 745 *sampleTimeUs = info->mSamples.begin()->mSampleTimeUs; 746 747 return OK; 748 } 749 750 status_t NuMediaExtractor::getSampleMeta(sp<MetaData> *sampleMeta) { 751 Mutex::Autolock autoLock(mLock); 752 753 *sampleMeta = NULL; 754 755 ssize_t minIndex = fetchAllTrackSamples(); 756 757 if (minIndex < 0) { 758 status_t err = minIndex; 759 return err; 760 } 761 762 TrackInfo *info = &mSelectedTracks.editItemAt(minIndex); 763 *sampleMeta = new MetaData(info->mSamples.begin()->mBuffer->meta_data()); 764 765 return OK; 766 } 767 768 status_t NuMediaExtractor::getMetrics(Parcel *reply) { 769 status_t status = mImpl->getMetrics(reply); 770 return status; 771 } 772 773 bool NuMediaExtractor::getTotalBitrate(int64_t *bitrate) const { 774 if (mTotalBitrate > 0) { 775 *bitrate = mTotalBitrate; 776 return true; 777 } 778 779 off64_t size; 780 if (mDurationUs > 0 && mDataSource->getSize(&size) == OK) { 781 *bitrate = size * 8000000ll / mDurationUs; // in bits/sec 782 return true; 783 } 784 785 return false; 786 } 787 788 // Returns true iff cached duration is available/applicable. 789 bool NuMediaExtractor::getCachedDuration( 790 int64_t *durationUs, bool *eos) const { 791 Mutex::Autolock autoLock(mLock); 792 793 int64_t bitrate; 794 if ((mDataSource->flags() & DataSource::kIsCachingDataSource) 795 && getTotalBitrate(&bitrate)) { 796 sp<NuCachedSource2> cachedSource = 797 static_cast<NuCachedSource2 *>(mDataSource.get()); 798 799 status_t finalStatus; 800 size_t cachedDataRemaining = 801 cachedSource->approxDataRemaining(&finalStatus); 802 803 *durationUs = cachedDataRemaining * 8000000ll / bitrate; 804 *eos = (finalStatus != OK); 805 return true; 806 } 807 808 return false; 809 } 810 811 } // namespace android 812