| /* |
| * Copyright 2012, The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| //#define LOG_NDEBUG 0 |
| #define LOG_TAG "NuMediaExtractor" |
| #include <utils/Log.h> |
| |
| #include <media/stagefright/NuMediaExtractor.h> |
| |
| #include <media/esds/ESDS.h> |
| |
| #include <datasource/DataSourceFactory.h> |
| #include <datasource/FileSource.h> |
| #include <media/DataSource.h> |
| #include <media/stagefright/MediaSource.h> |
| #include <media/stagefright/foundation/ABuffer.h> |
| #include <media/stagefright/foundation/ADebug.h> |
| #include <media/stagefright/foundation/AMessage.h> |
| #include <media/stagefright/MediaBuffer.h> |
| #include <media/stagefright/MediaDefs.h> |
| #include <media/stagefright/MediaErrors.h> |
| #include <media/stagefright/MediaExtractor.h> |
| #include <media/stagefright/MediaExtractorFactory.h> |
| #include <media/stagefright/MetaData.h> |
| #include <media/stagefright/Utils.h> |
| #include <media/stagefright/FoundationUtils.h> |
| |
| namespace android { |
| |
| NuMediaExtractor::Sample::Sample() |
| : mBuffer(NULL), |
| mSampleTimeUs(-1LL) { |
| } |
| |
| NuMediaExtractor::Sample::Sample(MediaBufferBase *buffer, int64_t timeUs) |
| : mBuffer(buffer), |
| mSampleTimeUs(timeUs) { |
| } |
| |
| NuMediaExtractor::NuMediaExtractor(EntryPoint entryPoint) |
| : mEntryPoint(entryPoint), |
| mTotalBitrate(-1LL), |
| mDurationUs(-1LL) { |
| } |
| |
| NuMediaExtractor::~NuMediaExtractor() { |
| releaseAllTrackSamples(); |
| |
| for (size_t i = 0; i < mSelectedTracks.size(); ++i) { |
| TrackInfo *info = &mSelectedTracks.editItemAt(i); |
| |
| status_t err = info->mSource->stop(); |
| ALOGE_IF(err != OK, "error %d stopping track %zu", err, i); |
| } |
| |
| mSelectedTracks.clear(); |
| if (mDataSource != NULL) { |
| mDataSource->close(); |
| } |
| } |
| |
| status_t NuMediaExtractor::initMediaExtractor(const sp<DataSource>& dataSource) { |
| status_t err = OK; |
| |
| mImpl = MediaExtractorFactory::Create(dataSource); |
| if (mImpl == NULL) { |
| ALOGE("%s: failed to create MediaExtractor", __FUNCTION__); |
| return ERROR_UNSUPPORTED; |
| } |
| |
| setEntryPointToRemoteMediaExtractor(); |
| |
| if (!mCasToken.empty()) { |
| err = mImpl->setMediaCas(mCasToken); |
| if (err != OK) { |
| ALOGE("%s: failed to setMediaCas (%d)", __FUNCTION__, err); |
| return err; |
| } |
| } |
| |
| // Get the name of the implementation. |
| mName = mImpl->name(); |
| |
| // Update the duration and bitrate |
| err = updateDurationAndBitrate(); |
| if (err == OK) { |
| mDataSource = dataSource; |
| } |
| |
| return OK; |
| } |
| |
| status_t NuMediaExtractor::setDataSource( |
| const sp<MediaHTTPService> &httpService, |
| const char *path, |
| const KeyedVector<String8, String8> *headers) { |
| Mutex::Autolock autoLock(mLock); |
| |
| if (mImpl != NULL || path == NULL) { |
| return -EINVAL; |
| } |
| |
| sp<DataSource> dataSource = |
| DataSourceFactory::getInstance()->CreateFromURI(httpService, path, headers); |
| |
| if (dataSource == NULL) { |
| return -ENOENT; |
| } |
| |
| // Initialize MediaExtractor using the data source |
| return initMediaExtractor(dataSource); |
| } |
| |
| status_t NuMediaExtractor::setDataSource(int fd, off64_t offset, off64_t size) { |
| |
| ALOGV("setDataSource fd=%d (%s), offset=%lld, length=%lld", |
| fd, nameForFd(fd).c_str(), (long long) offset, (long long) size); |
| |
| Mutex::Autolock autoLock(mLock); |
| |
| if (mImpl != NULL) { |
| return -EINVAL; |
| } |
| |
| sp<FileSource> fileSource = new FileSource(dup(fd), offset, size); |
| |
| status_t err = fileSource->initCheck(); |
| if (err != OK) { |
| return err; |
| } |
| |
| // Initialize MediaExtractor using the file source |
| return initMediaExtractor(fileSource); |
| } |
| |
| status_t NuMediaExtractor::setDataSource(const sp<DataSource> &source) { |
| Mutex::Autolock autoLock(mLock); |
| |
| if (mImpl != NULL) { |
| return -EINVAL; |
| } |
| |
| status_t err = source->initCheck(); |
| if (err != OK) { |
| return err; |
| } |
| |
| // Initialize MediaExtractor using the given data source |
| return initMediaExtractor(source); |
| } |
| |
| const char* NuMediaExtractor::getName() const { |
| Mutex::Autolock autoLock(mLock); |
| return mImpl == nullptr ? nullptr : mName.c_str(); |
| } |
| |
| static String8 arrayToString(const std::vector<uint8_t> &array) { |
| String8 result; |
| for (size_t i = 0; i < array.size(); i++) { |
| result.appendFormat("%02x ", array[i]); |
| } |
| if (result.empty()) { |
| result.append("(null)"); |
| } |
| return result; |
| } |
| |
| status_t NuMediaExtractor::setMediaCas(const HInterfaceToken &casToken) { |
| ALOGV("setMediaCas: casToken={%s}", arrayToString(casToken).c_str()); |
| |
| Mutex::Autolock autoLock(mLock); |
| |
| if (casToken.empty()) { |
| return BAD_VALUE; |
| } |
| |
| mCasToken = casToken; |
| |
| if (mImpl != NULL) { |
| status_t err = mImpl->setMediaCas(casToken); |
| if (err != OK) { |
| ALOGE("%s: failed to setMediaCas (%d)", __FUNCTION__, err); |
| return err; |
| } |
| err = updateDurationAndBitrate(); |
| if (err != OK) { |
| return err; |
| } |
| } |
| |
| return OK; |
| } |
| |
| status_t NuMediaExtractor::updateDurationAndBitrate() { |
| if (mImpl->countTracks() > kMaxTrackCount) { |
| return ERROR_UNSUPPORTED; |
| } |
| |
| mTotalBitrate = 0LL; |
| mDurationUs = -1LL; |
| |
| for (size_t i = 0; i < mImpl->countTracks(); ++i) { |
| sp<MetaData> meta = mImpl->getTrackMetaData(i); |
| if (meta == NULL) { |
| ALOGW("no metadata for track %zu", i); |
| continue; |
| } |
| |
| int32_t bitrate; |
| if (!meta->findInt32(kKeyBitRate, &bitrate)) { |
| const char *mime; |
| CHECK(meta->findCString(kKeyMIMEType, &mime)); |
| ALOGV("track of type '%s' does not publish bitrate", mime); |
| |
| mTotalBitrate = -1LL; |
| } else if (mTotalBitrate >= 0LL) { |
| mTotalBitrate += bitrate; |
| } |
| |
| int64_t durationUs; |
| if (meta->findInt64(kKeyDuration, &durationUs) |
| && durationUs > mDurationUs) { |
| mDurationUs = durationUs; |
| } |
| } |
| return OK; |
| } |
| |
| size_t NuMediaExtractor::countTracks() const { |
| Mutex::Autolock autoLock(mLock); |
| |
| return mImpl == NULL ? 0 : mImpl->countTracks(); |
| } |
| |
| status_t NuMediaExtractor::getTrackFormat( |
| size_t index, sp<AMessage> *format, uint32_t flags) const { |
| Mutex::Autolock autoLock(mLock); |
| |
| *format = NULL; |
| |
| if (mImpl == NULL) { |
| return -EINVAL; |
| } |
| |
| if (index >= mImpl->countTracks()) { |
| return -ERANGE; |
| } |
| |
| sp<MetaData> meta = mImpl->getTrackMetaData(index, flags); |
| // Extractors either support trackID-s or not, so either all tracks have trackIDs or none. |
| // Generate trackID if missing. |
| int32_t trackID; |
| if (meta != NULL && !meta->findInt32(kKeyTrackID, &trackID)) { |
| meta->setInt32(kKeyTrackID, (int32_t)index + 1); |
| } |
| return convertMetaDataToMessage(meta, format); |
| } |
| |
| status_t NuMediaExtractor::getFileFormat(sp<AMessage> *format) const { |
| Mutex::Autolock autoLock(mLock); |
| |
| *format = NULL; |
| |
| if (mImpl == NULL) { |
| return -EINVAL; |
| } |
| |
| sp<MetaData> meta = mImpl->getMetaData(); |
| |
| if (meta == nullptr) { |
| //extractor did not publish file metadata |
| return -EINVAL; |
| } |
| |
| const char *mime; |
| if (!meta->findCString(kKeyMIMEType, &mime)) { |
| // no mime type maps to invalid |
| return -EINVAL; |
| } |
| *format = new AMessage(); |
| (*format)->setString("mime", mime); |
| |
| uint32_t type; |
| const void *pssh; |
| size_t psshsize; |
| if (meta->findData(kKeyPssh, &type, &pssh, &psshsize)) { |
| sp<ABuffer> buf = new ABuffer(psshsize); |
| if (buf->data() == nullptr) { |
| return -ENOMEM; |
| } |
| memcpy(buf->data(), pssh, psshsize); |
| (*format)->setBuffer("pssh", buf); |
| } |
| |
| // Copy over the slow-motion related metadata |
| const void *slomoMarkers; |
| size_t slomoMarkersSize; |
| if (meta->findData(kKeySlowMotionMarkers, &type, &slomoMarkers, &slomoMarkersSize) |
| && slomoMarkersSize > 0) { |
| sp<ABuffer> buf = new ABuffer(slomoMarkersSize); |
| if (buf->data() == nullptr) { |
| return -ENOMEM; |
| } |
| memcpy(buf->data(), slomoMarkers, slomoMarkersSize); |
| (*format)->setBuffer("slow-motion-markers", buf); |
| } |
| |
| int32_t temporalLayerCount; |
| if (meta->findInt32(kKeyTemporalLayerCount, &temporalLayerCount) |
| && temporalLayerCount > 0) { |
| (*format)->setInt32("temporal-layer-count", temporalLayerCount); |
| } |
| |
| float captureFps; |
| if (meta->findFloat(kKeyCaptureFramerate, &captureFps) && captureFps > 0.0f) { |
| (*format)->setFloat("capture-rate", captureFps); |
| } |
| |
| return OK; |
| } |
| |
| status_t NuMediaExtractor::getExifOffsetSize(off64_t *offset, size_t *size) const { |
| Mutex::Autolock autoLock(mLock); |
| |
| if (mImpl == NULL) { |
| return -EINVAL; |
| } |
| |
| sp<MetaData> meta = mImpl->getMetaData(); |
| |
| if (meta == nullptr) { |
| //extractor did not publish file metadata |
| return -EINVAL; |
| } |
| |
| int64_t exifOffset, exifSize; |
| if (meta->findInt64(kKeyExifOffset, &exifOffset) |
| && meta->findInt64(kKeyExifSize, &exifSize)) { |
| *offset = (off64_t) exifOffset; |
| *size = (size_t) exifSize; |
| |
| return OK; |
| } |
| return ERROR_UNSUPPORTED; |
| } |
| |
| status_t NuMediaExtractor::selectTrack(size_t index, |
| int64_t startTimeUs, MediaSource::ReadOptions::SeekMode mode) { |
| Mutex::Autolock autoLock(mLock); |
| |
| if (mImpl == NULL) { |
| return -EINVAL; |
| } |
| |
| if (index >= mImpl->countTracks()) { |
| return -ERANGE; |
| } |
| |
| for (size_t i = 0; i < mSelectedTracks.size(); ++i) { |
| TrackInfo *info = &mSelectedTracks.editItemAt(i); |
| |
| if (info->mTrackIndex == index) { |
| // This track has already been selected. |
| return OK; |
| } |
| } |
| |
| sp<IMediaSource> source = mImpl->getTrack(index); |
| |
| if (source == nullptr) { |
| ALOGE("track %zu is empty", index); |
| return ERROR_MALFORMED; |
| } |
| |
| status_t ret = source->start(); |
| if (ret != OK) { |
| ALOGE("track %zu failed to start", index); |
| return ret; |
| } |
| |
| sp<MetaData> meta = source->getFormat(); |
| if (meta == NULL) { |
| ALOGE("track %zu has no meta data", index); |
| return ERROR_MALFORMED; |
| } |
| |
| const char *mime; |
| if (!meta->findCString(kKeyMIMEType, &mime)) { |
| ALOGE("track %zu has no mime type in meta data", index); |
| return ERROR_MALFORMED; |
| } |
| ALOGV("selectTrack, track[%zu]: %s", index, mime); |
| |
| mSelectedTracks.push(); |
| TrackInfo *info = &mSelectedTracks.editItemAt(mSelectedTracks.size() - 1); |
| |
| info->mSource = source; |
| info->mTrackIndex = index; |
| if (!strncasecmp(mime, "audio/", 6)) { |
| info->mTrackType = MEDIA_TRACK_TYPE_AUDIO; |
| info->mMaxFetchCount = 64; |
| } else if (!strncasecmp(mime, "video/", 6)) { |
| info->mTrackType = MEDIA_TRACK_TYPE_VIDEO; |
| info->mMaxFetchCount = 8; |
| } else { |
| info->mTrackType = MEDIA_TRACK_TYPE_UNKNOWN; |
| info->mMaxFetchCount = 1; |
| } |
| info->mFinalResult = OK; |
| releaseTrackSamples(info); |
| info->mTrackFlags = 0; |
| |
| if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_VORBIS)) { |
| info->mTrackFlags |= kIsVorbis; |
| } |
| |
| if (startTimeUs >= 0) { |
| fetchTrackSamples(info, startTimeUs, mode); |
| } |
| |
| return OK; |
| } |
| |
| status_t NuMediaExtractor::unselectTrack(size_t index) { |
| Mutex::Autolock autoLock(mLock); |
| |
| if (mImpl == NULL) { |
| return -EINVAL; |
| } |
| |
| if (index >= mImpl->countTracks()) { |
| return -ERANGE; |
| } |
| |
| size_t i; |
| for (i = 0; i < mSelectedTracks.size(); ++i) { |
| TrackInfo *info = &mSelectedTracks.editItemAt(i); |
| |
| if (info->mTrackIndex == index) { |
| break; |
| } |
| } |
| |
| if (i == mSelectedTracks.size()) { |
| // Not selected. |
| return OK; |
| } |
| |
| TrackInfo *info = &mSelectedTracks.editItemAt(i); |
| |
| releaseTrackSamples(info); |
| |
| CHECK_EQ((status_t)OK, info->mSource->stop()); |
| |
| mSelectedTracks.removeAt(i); |
| |
| return OK; |
| } |
| |
| void NuMediaExtractor::releaseTrackSamples(TrackInfo *info) { |
| if (info == NULL) { |
| return; |
| } |
| |
| auto it = info->mSamples.begin(); |
| while (it != info->mSamples.end()) { |
| if (it->mBuffer != NULL) { |
| it->mBuffer->release(); |
| } |
| it = info->mSamples.erase(it); |
| } |
| } |
| |
| void NuMediaExtractor::releaseAllTrackSamples() { |
| for (size_t i = 0; i < mSelectedTracks.size(); ++i) { |
| releaseTrackSamples(&mSelectedTracks.editItemAt(i)); |
| } |
| } |
| |
| void NuMediaExtractor::setEntryPointToRemoteMediaExtractor() { |
| if (mImpl == NULL) { |
| return; |
| } |
| status_t err = mImpl->setEntryPoint(mEntryPoint); |
| if (err != OK) { |
| ALOGW("Failed to set entry point with error %d.", err); |
| } |
| } |
| |
| ssize_t NuMediaExtractor::fetchAllTrackSamples( |
| int64_t seekTimeUs, MediaSource::ReadOptions::SeekMode mode) { |
| TrackInfo *minInfo = NULL; |
| ssize_t minIndex = ERROR_END_OF_STREAM; |
| |
| for (size_t i = 0; i < mSelectedTracks.size(); ++i) { |
| TrackInfo *info = &mSelectedTracks.editItemAt(i); |
| fetchTrackSamples(info, seekTimeUs, mode); |
| |
| status_t err = info->mFinalResult; |
| if (err != OK && err != ERROR_END_OF_STREAM && info->mSamples.empty()) { |
| return err; |
| } |
| |
| if (info->mSamples.empty()) { |
| continue; |
| } |
| |
| if (minInfo == NULL) { |
| minInfo = info; |
| minIndex = i; |
| } else { |
| auto it = info->mSamples.begin(); |
| auto itMin = minInfo->mSamples.begin(); |
| if (it->mSampleTimeUs < itMin->mSampleTimeUs) { |
| minInfo = info; |
| minIndex = i; |
| } |
| } |
| } |
| |
| return minIndex; |
| } |
| |
| void NuMediaExtractor::fetchTrackSamples(TrackInfo *info, |
| int64_t seekTimeUs, MediaSource::ReadOptions::SeekMode mode) { |
| if (info == NULL) { |
| return; |
| } |
| |
| MediaSource::ReadOptions options; |
| if (seekTimeUs >= 0LL) { |
| options.setSeekTo(seekTimeUs, mode); |
| info->mFinalResult = OK; |
| releaseTrackSamples(info); |
| } else if (info->mFinalResult != OK || !info->mSamples.empty()) { |
| return; |
| } |
| |
| status_t err = OK; |
| Vector<MediaBufferBase *> mediaBuffers; |
| if (info->mSource->supportReadMultiple()) { |
| options.setNonBlocking(); |
| err = info->mSource->readMultiple(&mediaBuffers, info->mMaxFetchCount, &options); |
| } else { |
| MediaBufferBase *mbuf = NULL; |
| err = info->mSource->read(&mbuf, &options); |
| if (err == OK && mbuf != NULL) { |
| mediaBuffers.push_back(mbuf); |
| } |
| } |
| |
| info->mFinalResult = err; |
| if (err != OK && err != ERROR_END_OF_STREAM) { |
| ALOGW("read on track %zu failed with error %d", info->mTrackIndex, err); |
| } |
| |
| size_t count = mediaBuffers.size(); |
| bool releaseRemaining = false; |
| for (size_t id = 0; id < count; ++id) { |
| int64_t timeUs; |
| MediaBufferBase *mbuf = mediaBuffers[id]; |
| if (mbuf == NULL) { |
| continue; |
| } |
| if (releaseRemaining) { |
| mbuf->release(); |
| continue; |
| } |
| if (mbuf->meta_data().findInt64(kKeyTime, &timeUs)) { |
| info->mSamples.emplace_back(mbuf, timeUs); |
| } else { |
| mbuf->meta_data().dumpToLog(); |
| info->mFinalResult = ERROR_MALFORMED; |
| mbuf->release(); |
| releaseRemaining = true; |
| } |
| } |
| } |
| |
| status_t NuMediaExtractor::seekTo( |
| int64_t timeUs, MediaSource::ReadOptions::SeekMode mode) { |
| Mutex::Autolock autoLock(mLock); |
| |
| ssize_t minIndex = fetchAllTrackSamples(timeUs, mode); |
| |
| if (minIndex < 0) { |
| return ERROR_END_OF_STREAM; |
| } |
| |
| return OK; |
| } |
| |
| status_t NuMediaExtractor::advance() { |
| Mutex::Autolock autoLock(mLock); |
| |
| ssize_t minIndex = fetchAllTrackSamples(); |
| |
| if (minIndex < 0) { |
| return ERROR_END_OF_STREAM; |
| } |
| |
| TrackInfo *info = &mSelectedTracks.editItemAt(minIndex); |
| |
| if (info == NULL || info->mSamples.empty()) { |
| return ERROR_END_OF_STREAM; |
| } |
| |
| auto it = info->mSamples.begin(); |
| if (it->mBuffer != NULL) { |
| it->mBuffer->release(); |
| } |
| info->mSamples.erase(it); |
| |
| if (info->mSamples.empty()) { |
| minIndex = fetchAllTrackSamples(); |
| if (minIndex < 0) { |
| return ERROR_END_OF_STREAM; |
| } |
| info = &mSelectedTracks.editItemAt(minIndex); |
| if (info == NULL || info->mSamples.empty()) { |
| return ERROR_END_OF_STREAM; |
| } |
| } |
| return OK; |
| } |
| |
| status_t NuMediaExtractor::appendVorbisNumPageSamples( |
| MediaBufferBase *mbuf, const sp<ABuffer> &buffer) { |
| int32_t numPageSamples; |
| if (!mbuf->meta_data().findInt32( |
| kKeyValidSamples, &numPageSamples)) { |
| numPageSamples = -1; |
| } |
| |
| // caller has verified there is sufficient space |
| // insert, including accounting for the space used. |
| memcpy((uint8_t *)buffer->data() + mbuf->range_length(), |
| &numPageSamples, |
| sizeof(numPageSamples)); |
| buffer->setRange(buffer->offset(), buffer->size() + sizeof(numPageSamples)); |
| |
| uint32_t type; |
| const void *data; |
| size_t size, size2; |
| if (mbuf->meta_data().findData(kKeyEncryptedSizes, &type, &data, &size)) { |
| // Signal numPageSamples (a plain int32_t) is appended at the end, |
| // i.e. sizeof(numPageSamples) plain bytes + 0 encrypted bytes |
| if (SIZE_MAX - size < sizeof(int32_t)) { |
| return -ENOMEM; |
| } |
| |
| size_t newSize = size + sizeof(int32_t); |
| sp<ABuffer> abuf = new ABuffer(newSize); |
| uint8_t *adata = static_cast<uint8_t *>(abuf->data()); |
| if (adata == NULL) { |
| return -ENOMEM; |
| } |
| |
| // append 0 to encrypted sizes |
| int32_t zero = 0; |
| memcpy(adata, data, size); |
| memcpy(adata + size, &zero, sizeof(zero)); |
| mbuf->meta_data().setData(kKeyEncryptedSizes, type, adata, newSize); |
| |
| if (mbuf->meta_data().findData(kKeyPlainSizes, &type, &data, &size2)) { |
| if (size2 != size) { |
| return ERROR_MALFORMED; |
| } |
| memcpy(adata, data, size); |
| } else { |
| // if sample meta data does not include plain size array, assume filled with zeros, |
| // i.e. entire buffer is encrypted |
| memset(adata, 0, size); |
| } |
| // append sizeof(numPageSamples) to plain sizes. |
| int32_t int32Size = sizeof(numPageSamples); |
| memcpy(adata + size, &int32Size, sizeof(int32Size)); |
| mbuf->meta_data().setData(kKeyPlainSizes, type, adata, newSize); |
| } |
| |
| return OK; |
| } |
| |
| status_t NuMediaExtractor::readSampleData(const sp<ABuffer> &buffer) { |
| Mutex::Autolock autoLock(mLock); |
| |
| ssize_t minIndex = fetchAllTrackSamples(); |
| |
| buffer->setRange(0, 0); // start with an empty buffer |
| |
| if (minIndex < 0) { |
| return ERROR_END_OF_STREAM; |
| } |
| |
| TrackInfo *info = &mSelectedTracks.editItemAt(minIndex); |
| |
| auto it = info->mSamples.begin(); |
| size_t sampleSize = it->mBuffer->range_length(); |
| |
| if (info->mTrackFlags & kIsVorbis) { |
| // Each sample's data is suffixed by the number of page samples |
| // or -1 if not available. |
| sampleSize += sizeof(int32_t); |
| } |
| |
| // capacity() is ok since we cleared out the buffer |
| if (buffer->capacity() < sampleSize) { |
| return -ENOMEM; |
| } |
| |
| const size_t srclen = it->mBuffer->range_length(); |
| const uint8_t *src = |
| (const uint8_t *)it->mBuffer->data() |
| + it->mBuffer->range_offset(); |
| |
| memcpy((uint8_t *)buffer->data(), src, srclen); |
| buffer->setRange(0, srclen); |
| |
| status_t err = OK; |
| if (info->mTrackFlags & kIsVorbis) { |
| // adjusts range when it inserts the extra bits |
| err = appendVorbisNumPageSamples(it->mBuffer, buffer); |
| } |
| |
| return err; |
| } |
| |
| status_t NuMediaExtractor::getSampleSize(size_t *sampleSize) { |
| Mutex::Autolock autoLock(mLock); |
| |
| ssize_t minIndex = fetchAllTrackSamples(); |
| |
| if (minIndex < 0) { |
| return ERROR_END_OF_STREAM; |
| } |
| |
| TrackInfo *info = &mSelectedTracks.editItemAt(minIndex); |
| auto it = info->mSamples.begin(); |
| *sampleSize = it->mBuffer->range_length(); |
| |
| if (info->mTrackFlags & kIsVorbis) { |
| // Each sample's data is suffixed by the number of page samples |
| // or -1 if not available. |
| *sampleSize += sizeof(int32_t); |
| } |
| |
| return OK; |
| } |
| |
| status_t NuMediaExtractor::getSampleTrackIndex(size_t *trackIndex) { |
| Mutex::Autolock autoLock(mLock); |
| |
| ssize_t minIndex = fetchAllTrackSamples(); |
| |
| if (minIndex < 0) { |
| return ERROR_END_OF_STREAM; |
| } |
| |
| TrackInfo *info = &mSelectedTracks.editItemAt(minIndex); |
| *trackIndex = info->mTrackIndex; |
| |
| return OK; |
| } |
| |
| status_t NuMediaExtractor::getSampleTime(int64_t *sampleTimeUs) { |
| Mutex::Autolock autoLock(mLock); |
| |
| ssize_t minIndex = fetchAllTrackSamples(); |
| |
| if (minIndex < 0) { |
| return ERROR_END_OF_STREAM; |
| } |
| |
| TrackInfo *info = &mSelectedTracks.editItemAt(minIndex); |
| *sampleTimeUs = info->mSamples.begin()->mSampleTimeUs; |
| |
| return OK; |
| } |
| |
| status_t NuMediaExtractor::getSampleMeta(sp<MetaData> *sampleMeta) { |
| Mutex::Autolock autoLock(mLock); |
| |
| *sampleMeta = NULL; |
| |
| ssize_t minIndex = fetchAllTrackSamples(); |
| |
| if (minIndex < 0) { |
| status_t err = minIndex; |
| return err; |
| } |
| |
| TrackInfo *info = &mSelectedTracks.editItemAt(minIndex); |
| *sampleMeta = new MetaData(info->mSamples.begin()->mBuffer->meta_data()); |
| |
| return OK; |
| } |
| |
| status_t NuMediaExtractor::getMetrics(Parcel *reply) { |
| if (mImpl == NULL) { |
| return -EINVAL; |
| } |
| status_t status = mImpl->getMetrics(reply); |
| return status; |
| } |
| |
| bool NuMediaExtractor::getTotalBitrate(int64_t *bitrate) const { |
| if (mTotalBitrate > 0) { |
| *bitrate = mTotalBitrate; |
| return true; |
| } |
| |
| off64_t size; |
| if (mDurationUs > 0 && mDataSource->getSize(&size) == OK) { |
| *bitrate = size * 8000000LL / mDurationUs; // in bits/sec |
| return true; |
| } |
| |
| return false; |
| } |
| |
| // Returns true iff cached duration is available/applicable. |
| bool NuMediaExtractor::getCachedDuration( |
| int64_t *durationUs, bool *eos) const { |
| Mutex::Autolock autoLock(mLock); |
| |
| off64_t cachedDataRemaining = -1; |
| status_t finalStatus = mDataSource->getAvailableSize(-1, &cachedDataRemaining); |
| |
| int64_t bitrate; |
| if (cachedDataRemaining >= 0 |
| && getTotalBitrate(&bitrate)) { |
| *durationUs = cachedDataRemaining * 8000000ll / bitrate; |
| *eos = (finalStatus != OK); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| // Return OK if we have received an audio presentation info. |
| // Return ERROR_END_OF_STREAM if no tracks are available. |
| // Return ERROR_UNSUPPORTED if the track has no audio presentation. |
| // Return INVALID_OPERATION if audio presentation metadata version does not match. |
| status_t NuMediaExtractor::getAudioPresentations( |
| size_t trackIndex, AudioPresentationCollection *presentations) { |
| Mutex::Autolock autoLock(mLock); |
| ssize_t minIndex = fetchAllTrackSamples(); |
| if (minIndex < 0) { |
| return ERROR_END_OF_STREAM; |
| } |
| for (size_t i = 0; i < mSelectedTracks.size(); ++i) { |
| TrackInfo *info = &mSelectedTracks.editItemAt(i); |
| |
| if (info->mTrackIndex == trackIndex) { |
| sp<MetaData> meta = new MetaData(info->mSamples.begin()->mBuffer->meta_data()); |
| |
| uint32_t type; |
| const void *data; |
| size_t size; |
| if (meta != NULL && meta->findData(kKeyAudioPresentationInfo, &type, &data, &size)) { |
| std::istringstream inStream(std::string(static_cast<const char*>(data), size)); |
| return deserializeAudioPresentations(&inStream, presentations); |
| } |
| ALOGV("Track %zu does not contain any audio presentation", trackIndex); |
| return ERROR_UNSUPPORTED; |
| } |
| } |
| ALOGV("Source does not contain any audio presentation"); |
| return ERROR_UNSUPPORTED; |
| } |
| |
| status_t NuMediaExtractor::setLogSessionId(const String8& logSessionId) { |
| if (mImpl == nullptr) { |
| return ERROR_UNSUPPORTED; |
| } |
| status_t status = mImpl->setLogSessionId(logSessionId); |
| if (status != OK) { |
| ALOGW("Failed to set log session id: %d.", status); |
| } |
| return status; |
| } |
| |
| } // namespace android |