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