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