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