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