Home | History | Annotate | Download | only in libstagefright
      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