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 }
     59 
     60 status_t NuMediaExtractor::setDataSource(
     61         const char *path, const KeyedVector<String8, String8> *headers) {
     62     Mutex::Autolock autoLock(mLock);
     63 
     64     if (mImpl != NULL) {
     65         return -EINVAL;
     66     }
     67 
     68     sp<DataSource> dataSource =
     69         DataSource::CreateFromURI(path, headers);
     70 
     71     if (dataSource == NULL) {
     72         return -ENOENT;
     73     }
     74 
     75     mIsWidevineExtractor = false;
     76     if (!strncasecmp("widevine://", path, 11)) {
     77         String8 mimeType;
     78         float confidence;
     79         sp<AMessage> dummy;
     80         bool success = SniffWVM(dataSource, &mimeType, &confidence, &dummy);
     81 
     82         if (!success
     83                 || strcasecmp(
     84                     mimeType.string(), MEDIA_MIMETYPE_CONTAINER_WVM)) {
     85             return ERROR_UNSUPPORTED;
     86         }
     87 
     88         sp<WVMExtractor> extractor = new WVMExtractor(dataSource);
     89         extractor->setAdaptiveStreamingMode(true);
     90 
     91         mImpl = extractor;
     92         mIsWidevineExtractor = true;
     93     } else {
     94         mImpl = MediaExtractor::Create(dataSource);
     95     }
     96 
     97     if (mImpl == NULL) {
     98         return ERROR_UNSUPPORTED;
     99     }
    100 
    101     sp<MetaData> fileMeta = mImpl->getMetaData();
    102     const char *containerMime;
    103     if (fileMeta != NULL
    104             && fileMeta->findCString(kKeyMIMEType, &containerMime)
    105             && !strcasecmp(containerMime, "video/wvm")) {
    106         // We always want to use "cryptoPluginMode" when using the wvm
    107         // extractor. We can tell that it is this extractor by looking
    108         // at the container mime type.
    109         // The cryptoPluginMode ensures that the extractor will actually
    110         // give us data in a call to MediaSource::read(), unlike its
    111         // default mode that we use from AwesomePlayer.
    112         static_cast<WVMExtractor *>(mImpl.get())->setCryptoPluginMode(true);
    113     } else if (mImpl->getDrmFlag()) {
    114         // For all other drm content, we don't want to expose decrypted
    115         // content to Java application.
    116         mImpl.clear();
    117         mImpl = NULL;
    118         return ERROR_UNSUPPORTED;
    119     }
    120 
    121     mDataSource = dataSource;
    122 
    123     updateDurationAndBitrate();
    124 
    125     return OK;
    126 }
    127 
    128 status_t NuMediaExtractor::setDataSource(int fd, off64_t offset, off64_t size) {
    129 
    130     Mutex::Autolock autoLock(mLock);
    131 
    132     if (mImpl != NULL) {
    133         return -EINVAL;
    134     }
    135 
    136     sp<FileSource> fileSource = new FileSource(dup(fd), offset, size);
    137 
    138     status_t err = fileSource->initCheck();
    139     if (err != OK) {
    140         return err;
    141     }
    142 
    143     mImpl = MediaExtractor::Create(fileSource);
    144 
    145     if (mImpl == NULL) {
    146         return ERROR_UNSUPPORTED;
    147     }
    148 
    149     mDataSource = fileSource;
    150 
    151     updateDurationAndBitrate();
    152 
    153     return OK;
    154 }
    155 
    156 status_t NuMediaExtractor::setDataSource(const sp<DataSource> &source) {
    157     Mutex::Autolock autoLock(mLock);
    158 
    159     if (mImpl != NULL) {
    160         return -EINVAL;
    161     }
    162 
    163     status_t err = source->initCheck();
    164     if (err != OK) {
    165         return err;
    166     }
    167 
    168     mImpl = MediaExtractor::Create(source);
    169 
    170     if (mImpl == NULL) {
    171         return ERROR_UNSUPPORTED;
    172     }
    173 
    174     mDataSource = source;
    175 
    176     updateDurationAndBitrate();
    177 
    178     return OK;
    179 }
    180 
    181 void NuMediaExtractor::updateDurationAndBitrate() {
    182     mTotalBitrate = 0ll;
    183     mDurationUs = -1ll;
    184 
    185     for (size_t i = 0; i < mImpl->countTracks(); ++i) {
    186         sp<MetaData> meta = mImpl->getTrackMetaData(i);
    187 
    188         int32_t bitrate;
    189         if (!meta->findInt32(kKeyBitRate, &bitrate)) {
    190             const char *mime;
    191             CHECK(meta->findCString(kKeyMIMEType, &mime));
    192             ALOGV("track of type '%s' does not publish bitrate", mime);
    193 
    194             mTotalBitrate = -1ll;
    195         } else if (mTotalBitrate >= 0ll) {
    196             mTotalBitrate += bitrate;
    197         }
    198 
    199         int64_t durationUs;
    200         if (meta->findInt64(kKeyDuration, &durationUs)
    201                 && durationUs > mDurationUs) {
    202             mDurationUs = durationUs;
    203         }
    204     }
    205 }
    206 
    207 size_t NuMediaExtractor::countTracks() const {
    208     Mutex::Autolock autoLock(mLock);
    209 
    210     return mImpl == NULL ? 0 : mImpl->countTracks();
    211 }
    212 
    213 status_t NuMediaExtractor::getTrackFormat(
    214         size_t index, sp<AMessage> *format) const {
    215     Mutex::Autolock autoLock(mLock);
    216 
    217     *format = NULL;
    218 
    219     if (mImpl == NULL) {
    220         return -EINVAL;
    221     }
    222 
    223     if (index >= mImpl->countTracks()) {
    224         return -ERANGE;
    225     }
    226 
    227     sp<MetaData> meta = mImpl->getTrackMetaData(index);
    228     return convertMetaDataToMessage(meta, format);
    229 }
    230 
    231 status_t NuMediaExtractor::getFileFormat(sp<AMessage> *format) const {
    232     Mutex::Autolock autoLock(mLock);
    233 
    234     *format = NULL;
    235 
    236     if (mImpl == NULL) {
    237         return -EINVAL;
    238     }
    239 
    240     sp<MetaData> meta = mImpl->getMetaData();
    241 
    242     const char *mime;
    243     CHECK(meta->findCString(kKeyMIMEType, &mime));
    244     *format = new AMessage();
    245     (*format)->setString("mime", mime);
    246 
    247     uint32_t type;
    248     const void *pssh;
    249     size_t psshsize;
    250     if (meta->findData(kKeyPssh, &type, &pssh, &psshsize)) {
    251         sp<ABuffer> buf = new ABuffer(psshsize);
    252         memcpy(buf->data(), pssh, psshsize);
    253         (*format)->setBuffer("pssh", buf);
    254     }
    255 
    256     return OK;
    257 }
    258 
    259 status_t NuMediaExtractor::selectTrack(size_t index) {
    260     Mutex::Autolock autoLock(mLock);
    261 
    262     if (mImpl == NULL) {
    263         return -EINVAL;
    264     }
    265 
    266     if (index >= mImpl->countTracks()) {
    267         return -ERANGE;
    268     }
    269 
    270     for (size_t i = 0; i < mSelectedTracks.size(); ++i) {
    271         TrackInfo *info = &mSelectedTracks.editItemAt(i);
    272 
    273         if (info->mTrackIndex == index) {
    274             // This track has already been selected.
    275             return OK;
    276         }
    277     }
    278 
    279     sp<MediaSource> source = mImpl->getTrack(index);
    280 
    281     CHECK_EQ((status_t)OK, source->start());
    282 
    283     mSelectedTracks.push();
    284     TrackInfo *info = &mSelectedTracks.editItemAt(mSelectedTracks.size() - 1);
    285 
    286     info->mSource = source;
    287     info->mTrackIndex = index;
    288     info->mFinalResult = OK;
    289     info->mSample = NULL;
    290     info->mSampleTimeUs = -1ll;
    291     info->mTrackFlags = 0;
    292 
    293     const char *mime;
    294     CHECK(source->getFormat()->findCString(kKeyMIMEType, &mime));
    295 
    296     if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_VORBIS)) {
    297         info->mTrackFlags |= kIsVorbis;
    298     }
    299 
    300     return OK;
    301 }
    302 
    303 status_t NuMediaExtractor::unselectTrack(size_t index) {
    304     Mutex::Autolock autoLock(mLock);
    305 
    306     if (mImpl == NULL) {
    307         return -EINVAL;
    308     }
    309 
    310     if (index >= mImpl->countTracks()) {
    311         return -ERANGE;
    312     }
    313 
    314     size_t i;
    315     for (i = 0; i < mSelectedTracks.size(); ++i) {
    316         TrackInfo *info = &mSelectedTracks.editItemAt(i);
    317 
    318         if (info->mTrackIndex == index) {
    319             break;
    320         }
    321     }
    322 
    323     if (i == mSelectedTracks.size()) {
    324         // Not selected.
    325         return OK;
    326     }
    327 
    328     TrackInfo *info = &mSelectedTracks.editItemAt(i);
    329 
    330     if (info->mSample != NULL) {
    331         info->mSample->release();
    332         info->mSample = NULL;
    333 
    334         info->mSampleTimeUs = -1ll;
    335     }
    336 
    337     CHECK_EQ((status_t)OK, info->mSource->stop());
    338 
    339     mSelectedTracks.removeAt(i);
    340 
    341     return OK;
    342 }
    343 
    344 void NuMediaExtractor::releaseTrackSamples() {
    345     for (size_t i = 0; i < mSelectedTracks.size(); ++i) {
    346         TrackInfo *info = &mSelectedTracks.editItemAt(i);
    347 
    348         if (info->mSample != NULL) {
    349             info->mSample->release();
    350             info->mSample = NULL;
    351 
    352             info->mSampleTimeUs = -1ll;
    353         }
    354     }
    355 }
    356 
    357 ssize_t NuMediaExtractor::fetchTrackSamples(
    358         int64_t seekTimeUs, MediaSource::ReadOptions::SeekMode mode) {
    359     TrackInfo *minInfo = NULL;
    360     ssize_t minIndex = -1;
    361 
    362     for (size_t i = 0; i < mSelectedTracks.size(); ++i) {
    363         TrackInfo *info = &mSelectedTracks.editItemAt(i);
    364 
    365         if (seekTimeUs >= 0ll) {
    366             info->mFinalResult = OK;
    367 
    368             if (info->mSample != NULL) {
    369                 info->mSample->release();
    370                 info->mSample = NULL;
    371                 info->mSampleTimeUs = -1ll;
    372             }
    373         } else if (info->mFinalResult != OK) {
    374             continue;
    375         }
    376 
    377         if (info->mSample == NULL) {
    378             MediaSource::ReadOptions options;
    379             if (seekTimeUs >= 0ll) {
    380                 options.setSeekTo(seekTimeUs, mode);
    381             }
    382             status_t err = info->mSource->read(&info->mSample, &options);
    383 
    384             if (err != OK) {
    385                 CHECK(info->mSample == NULL);
    386 
    387                 info->mFinalResult = err;
    388 
    389                 if (info->mFinalResult != ERROR_END_OF_STREAM) {
    390                     ALOGW("read on track %d failed with error %d",
    391                           info->mTrackIndex, err);
    392                 }
    393 
    394                 info->mSampleTimeUs = -1ll;
    395                 continue;
    396             } else {
    397                 CHECK(info->mSample != NULL);
    398                 CHECK(info->mSample->meta_data()->findInt64(
    399                             kKeyTime, &info->mSampleTimeUs));
    400             }
    401         }
    402 
    403         if (minInfo == NULL  || info->mSampleTimeUs < minInfo->mSampleTimeUs) {
    404             minInfo = info;
    405             minIndex = i;
    406         }
    407     }
    408 
    409     return minIndex;
    410 }
    411 
    412 status_t NuMediaExtractor::seekTo(
    413         int64_t timeUs, MediaSource::ReadOptions::SeekMode mode) {
    414     Mutex::Autolock autoLock(mLock);
    415 
    416     ssize_t minIndex = fetchTrackSamples(timeUs, mode);
    417 
    418     if (minIndex < 0) {
    419         return ERROR_END_OF_STREAM;
    420     }
    421 
    422     return OK;
    423 }
    424 
    425 status_t NuMediaExtractor::advance() {
    426     Mutex::Autolock autoLock(mLock);
    427 
    428     ssize_t minIndex = fetchTrackSamples();
    429 
    430     if (minIndex < 0) {
    431         return ERROR_END_OF_STREAM;
    432     }
    433 
    434     TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
    435 
    436     info->mSample->release();
    437     info->mSample = NULL;
    438     info->mSampleTimeUs = -1ll;
    439 
    440     return OK;
    441 }
    442 
    443 status_t NuMediaExtractor::readSampleData(const sp<ABuffer> &buffer) {
    444     Mutex::Autolock autoLock(mLock);
    445 
    446     ssize_t minIndex = fetchTrackSamples();
    447 
    448     if (minIndex < 0) {
    449         return ERROR_END_OF_STREAM;
    450     }
    451 
    452     TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
    453 
    454     size_t sampleSize = info->mSample->range_length();
    455 
    456     if (info->mTrackFlags & kIsVorbis) {
    457         // Each sample's data is suffixed by the number of page samples
    458         // or -1 if not available.
    459         sampleSize += sizeof(int32_t);
    460     }
    461 
    462     if (buffer->capacity() < sampleSize) {
    463         return -ENOMEM;
    464     }
    465 
    466     const uint8_t *src =
    467         (const uint8_t *)info->mSample->data()
    468             + info->mSample->range_offset();
    469 
    470     memcpy((uint8_t *)buffer->data(), src, info->mSample->range_length());
    471 
    472     if (info->mTrackFlags & kIsVorbis) {
    473         int32_t numPageSamples;
    474         if (!info->mSample->meta_data()->findInt32(
    475                     kKeyValidSamples, &numPageSamples)) {
    476             numPageSamples = -1;
    477         }
    478 
    479         memcpy((uint8_t *)buffer->data() + info->mSample->range_length(),
    480                &numPageSamples,
    481                sizeof(numPageSamples));
    482     }
    483 
    484     buffer->setRange(0, sampleSize);
    485 
    486     return OK;
    487 }
    488 
    489 status_t NuMediaExtractor::getSampleTrackIndex(size_t *trackIndex) {
    490     Mutex::Autolock autoLock(mLock);
    491 
    492     ssize_t minIndex = fetchTrackSamples();
    493 
    494     if (minIndex < 0) {
    495         return ERROR_END_OF_STREAM;
    496     }
    497 
    498     TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
    499     *trackIndex = info->mTrackIndex;
    500 
    501     return OK;
    502 }
    503 
    504 status_t NuMediaExtractor::getSampleTime(int64_t *sampleTimeUs) {
    505     Mutex::Autolock autoLock(mLock);
    506 
    507     ssize_t minIndex = fetchTrackSamples();
    508 
    509     if (minIndex < 0) {
    510         return ERROR_END_OF_STREAM;
    511     }
    512 
    513     TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
    514     *sampleTimeUs = info->mSampleTimeUs;
    515 
    516     return OK;
    517 }
    518 
    519 status_t NuMediaExtractor::getSampleMeta(sp<MetaData> *sampleMeta) {
    520     Mutex::Autolock autoLock(mLock);
    521 
    522     *sampleMeta = NULL;
    523 
    524     ssize_t minIndex = fetchTrackSamples();
    525 
    526     if (minIndex < 0) {
    527         return ERROR_END_OF_STREAM;
    528     }
    529 
    530     TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
    531     *sampleMeta = info->mSample->meta_data();
    532 
    533     return OK;
    534 }
    535 
    536 bool NuMediaExtractor::getTotalBitrate(int64_t *bitrate) const {
    537     if (mTotalBitrate >= 0) {
    538         *bitrate = mTotalBitrate;
    539         return true;
    540     }
    541 
    542     off64_t size;
    543     if (mDurationUs >= 0 && mDataSource->getSize(&size) == OK) {
    544         *bitrate = size * 8000000ll / mDurationUs;  // in bits/sec
    545         return true;
    546     }
    547 
    548     return false;
    549 }
    550 
    551 // Returns true iff cached duration is available/applicable.
    552 bool NuMediaExtractor::getCachedDuration(
    553         int64_t *durationUs, bool *eos) const {
    554     Mutex::Autolock autoLock(mLock);
    555 
    556     int64_t bitrate;
    557     if (mIsWidevineExtractor) {
    558         sp<WVMExtractor> wvmExtractor =
    559             static_cast<WVMExtractor *>(mImpl.get());
    560 
    561         status_t finalStatus;
    562         *durationUs = wvmExtractor->getCachedDurationUs(&finalStatus);
    563         *eos = (finalStatus != OK);
    564         return true;
    565     } else if ((mDataSource->flags() & DataSource::kIsCachingDataSource)
    566             && getTotalBitrate(&bitrate)) {
    567         sp<NuCachedSource2> cachedSource =
    568             static_cast<NuCachedSource2 *>(mDataSource.get());
    569 
    570         status_t finalStatus;
    571         size_t cachedDataRemaining =
    572             cachedSource->approxDataRemaining(&finalStatus);
    573 
    574         *durationUs = cachedDataRemaining * 8000000ll / bitrate;
    575         *eos = (finalStatus != OK);
    576         return true;
    577     }
    578 
    579     return false;
    580 }
    581 
    582 }  // namespace android
    583