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::selectTrack(size_t index) {
    232     Mutex::Autolock autoLock(mLock);
    233 
    234     if (mImpl == NULL) {
    235         return -EINVAL;
    236     }
    237 
    238     if (index >= mImpl->countTracks()) {
    239         return -ERANGE;
    240     }
    241 
    242     for (size_t i = 0; i < mSelectedTracks.size(); ++i) {
    243         TrackInfo *info = &mSelectedTracks.editItemAt(i);
    244 
    245         if (info->mTrackIndex == index) {
    246             // This track has already been selected.
    247             return OK;
    248         }
    249     }
    250 
    251     sp<MediaSource> source = mImpl->getTrack(index);
    252 
    253     CHECK_EQ((status_t)OK, source->start());
    254 
    255     mSelectedTracks.push();
    256     TrackInfo *info = &mSelectedTracks.editItemAt(mSelectedTracks.size() - 1);
    257 
    258     info->mSource = source;
    259     info->mTrackIndex = index;
    260     info->mFinalResult = OK;
    261     info->mSample = NULL;
    262     info->mSampleTimeUs = -1ll;
    263     info->mTrackFlags = 0;
    264 
    265     const char *mime;
    266     CHECK(source->getFormat()->findCString(kKeyMIMEType, &mime));
    267 
    268     if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_VORBIS)) {
    269         info->mTrackFlags |= kIsVorbis;
    270     }
    271 
    272     return OK;
    273 }
    274 
    275 status_t NuMediaExtractor::unselectTrack(size_t index) {
    276     Mutex::Autolock autoLock(mLock);
    277 
    278     if (mImpl == NULL) {
    279         return -EINVAL;
    280     }
    281 
    282     if (index >= mImpl->countTracks()) {
    283         return -ERANGE;
    284     }
    285 
    286     size_t i;
    287     for (i = 0; i < mSelectedTracks.size(); ++i) {
    288         TrackInfo *info = &mSelectedTracks.editItemAt(i);
    289 
    290         if (info->mTrackIndex == index) {
    291             break;
    292         }
    293     }
    294 
    295     if (i == mSelectedTracks.size()) {
    296         // Not selected.
    297         return OK;
    298     }
    299 
    300     TrackInfo *info = &mSelectedTracks.editItemAt(i);
    301 
    302     if (info->mSample != NULL) {
    303         info->mSample->release();
    304         info->mSample = NULL;
    305 
    306         info->mSampleTimeUs = -1ll;
    307     }
    308 
    309     CHECK_EQ((status_t)OK, info->mSource->stop());
    310 
    311     mSelectedTracks.removeAt(i);
    312 
    313     return OK;
    314 }
    315 
    316 void NuMediaExtractor::releaseTrackSamples() {
    317     for (size_t i = 0; i < mSelectedTracks.size(); ++i) {
    318         TrackInfo *info = &mSelectedTracks.editItemAt(i);
    319 
    320         if (info->mSample != NULL) {
    321             info->mSample->release();
    322             info->mSample = NULL;
    323 
    324             info->mSampleTimeUs = -1ll;
    325         }
    326     }
    327 }
    328 
    329 ssize_t NuMediaExtractor::fetchTrackSamples(
    330         int64_t seekTimeUs, MediaSource::ReadOptions::SeekMode mode) {
    331     TrackInfo *minInfo = NULL;
    332     ssize_t minIndex = -1;
    333 
    334     for (size_t i = 0; i < mSelectedTracks.size(); ++i) {
    335         TrackInfo *info = &mSelectedTracks.editItemAt(i);
    336 
    337         if (seekTimeUs >= 0ll) {
    338             info->mFinalResult = OK;
    339 
    340             if (info->mSample != NULL) {
    341                 info->mSample->release();
    342                 info->mSample = NULL;
    343                 info->mSampleTimeUs = -1ll;
    344             }
    345         } else if (info->mFinalResult != OK) {
    346             continue;
    347         }
    348 
    349         if (info->mSample == NULL) {
    350             MediaSource::ReadOptions options;
    351             if (seekTimeUs >= 0ll) {
    352                 options.setSeekTo(seekTimeUs, mode);
    353             }
    354             status_t err = info->mSource->read(&info->mSample, &options);
    355 
    356             if (err != OK) {
    357                 CHECK(info->mSample == NULL);
    358 
    359                 info->mFinalResult = err;
    360 
    361                 if (info->mFinalResult != ERROR_END_OF_STREAM) {
    362                     ALOGW("read on track %d failed with error %d",
    363                           info->mTrackIndex, err);
    364                 }
    365 
    366                 info->mSampleTimeUs = -1ll;
    367                 continue;
    368             } else {
    369                 CHECK(info->mSample != NULL);
    370                 CHECK(info->mSample->meta_data()->findInt64(
    371                             kKeyTime, &info->mSampleTimeUs));
    372             }
    373         }
    374 
    375         if (minInfo == NULL  || info->mSampleTimeUs < minInfo->mSampleTimeUs) {
    376             minInfo = info;
    377             minIndex = i;
    378         }
    379     }
    380 
    381     return minIndex;
    382 }
    383 
    384 status_t NuMediaExtractor::seekTo(
    385         int64_t timeUs, MediaSource::ReadOptions::SeekMode mode) {
    386     Mutex::Autolock autoLock(mLock);
    387 
    388     ssize_t minIndex = fetchTrackSamples(timeUs, mode);
    389 
    390     if (minIndex < 0) {
    391         return ERROR_END_OF_STREAM;
    392     }
    393 
    394     return OK;
    395 }
    396 
    397 status_t NuMediaExtractor::advance() {
    398     Mutex::Autolock autoLock(mLock);
    399 
    400     ssize_t minIndex = fetchTrackSamples();
    401 
    402     if (minIndex < 0) {
    403         return ERROR_END_OF_STREAM;
    404     }
    405 
    406     TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
    407 
    408     info->mSample->release();
    409     info->mSample = NULL;
    410     info->mSampleTimeUs = -1ll;
    411 
    412     return OK;
    413 }
    414 
    415 status_t NuMediaExtractor::readSampleData(const sp<ABuffer> &buffer) {
    416     Mutex::Autolock autoLock(mLock);
    417 
    418     ssize_t minIndex = fetchTrackSamples();
    419 
    420     if (minIndex < 0) {
    421         return ERROR_END_OF_STREAM;
    422     }
    423 
    424     TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
    425 
    426     size_t sampleSize = info->mSample->range_length();
    427 
    428     if (info->mTrackFlags & kIsVorbis) {
    429         // Each sample's data is suffixed by the number of page samples
    430         // or -1 if not available.
    431         sampleSize += sizeof(int32_t);
    432     }
    433 
    434     if (buffer->capacity() < sampleSize) {
    435         return -ENOMEM;
    436     }
    437 
    438     const uint8_t *src =
    439         (const uint8_t *)info->mSample->data()
    440             + info->mSample->range_offset();
    441 
    442     memcpy((uint8_t *)buffer->data(), src, info->mSample->range_length());
    443 
    444     if (info->mTrackFlags & kIsVorbis) {
    445         int32_t numPageSamples;
    446         if (!info->mSample->meta_data()->findInt32(
    447                     kKeyValidSamples, &numPageSamples)) {
    448             numPageSamples = -1;
    449         }
    450 
    451         memcpy((uint8_t *)buffer->data() + info->mSample->range_length(),
    452                &numPageSamples,
    453                sizeof(numPageSamples));
    454     }
    455 
    456     buffer->setRange(0, sampleSize);
    457 
    458     return OK;
    459 }
    460 
    461 status_t NuMediaExtractor::getSampleTrackIndex(size_t *trackIndex) {
    462     Mutex::Autolock autoLock(mLock);
    463 
    464     ssize_t minIndex = fetchTrackSamples();
    465 
    466     if (minIndex < 0) {
    467         return ERROR_END_OF_STREAM;
    468     }
    469 
    470     TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
    471     *trackIndex = info->mTrackIndex;
    472 
    473     return OK;
    474 }
    475 
    476 status_t NuMediaExtractor::getSampleTime(int64_t *sampleTimeUs) {
    477     Mutex::Autolock autoLock(mLock);
    478 
    479     ssize_t minIndex = fetchTrackSamples();
    480 
    481     if (minIndex < 0) {
    482         return ERROR_END_OF_STREAM;
    483     }
    484 
    485     TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
    486     *sampleTimeUs = info->mSampleTimeUs;
    487 
    488     return OK;
    489 }
    490 
    491 status_t NuMediaExtractor::getSampleMeta(sp<MetaData> *sampleMeta) {
    492     Mutex::Autolock autoLock(mLock);
    493 
    494     *sampleMeta = NULL;
    495 
    496     ssize_t minIndex = fetchTrackSamples();
    497 
    498     if (minIndex < 0) {
    499         return ERROR_END_OF_STREAM;
    500     }
    501 
    502     TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
    503     *sampleMeta = info->mSample->meta_data();
    504 
    505     return OK;
    506 }
    507 
    508 bool NuMediaExtractor::getTotalBitrate(int64_t *bitrate) const {
    509     if (mTotalBitrate >= 0) {
    510         *bitrate = mTotalBitrate;
    511         return true;
    512     }
    513 
    514     off64_t size;
    515     if (mDurationUs >= 0 && mDataSource->getSize(&size) == OK) {
    516         *bitrate = size * 8000000ll / mDurationUs;  // in bits/sec
    517         return true;
    518     }
    519 
    520     return false;
    521 }
    522 
    523 // Returns true iff cached duration is available/applicable.
    524 bool NuMediaExtractor::getCachedDuration(
    525         int64_t *durationUs, bool *eos) const {
    526     Mutex::Autolock autoLock(mLock);
    527 
    528     int64_t bitrate;
    529     if (mIsWidevineExtractor) {
    530         sp<WVMExtractor> wvmExtractor =
    531             static_cast<WVMExtractor *>(mImpl.get());
    532 
    533         status_t finalStatus;
    534         *durationUs = wvmExtractor->getCachedDurationUs(&finalStatus);
    535         *eos = (finalStatus != OK);
    536         return true;
    537     } else if ((mDataSource->flags() & DataSource::kIsCachingDataSource)
    538             && getTotalBitrate(&bitrate)) {
    539         sp<NuCachedSource2> cachedSource =
    540             static_cast<NuCachedSource2 *>(mDataSource.get());
    541 
    542         status_t finalStatus;
    543         size_t cachedDataRemaining =
    544             cachedSource->approxDataRemaining(&finalStatus);
    545 
    546         *durationUs = cachedDataRemaining * 8000000ll / bitrate;
    547         *eos = (finalStatus != OK);
    548         return true;
    549     }
    550 
    551     return false;
    552 }
    553 
    554 }  // namespace android
    555