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