Merge "NuMediaExtractor: support track switch"
diff --git a/media/libstagefright/NuMediaExtractor.cpp b/media/libstagefright/NuMediaExtractor.cpp
index 25656c3..648aa77 100644
--- a/media/libstagefright/NuMediaExtractor.cpp
+++ b/media/libstagefright/NuMediaExtractor.cpp
@@ -40,13 +40,23 @@
namespace android {
+NuMediaExtractor::Sample::Sample()
+ : mBuffer(NULL),
+ mSampleTimeUs(-1ll) {
+}
+
+NuMediaExtractor::Sample::Sample(MediaBuffer *buffer, int64_t timeUs)
+ : mBuffer(buffer),
+ mSampleTimeUs(timeUs) {
+}
+
NuMediaExtractor::NuMediaExtractor()
: mTotalBitrate(-1ll),
mDurationUs(-1ll) {
}
NuMediaExtractor::~NuMediaExtractor() {
- releaseTrackSamples();
+ releaseAllTrackSamples();
for (size_t i = 0; i < mSelectedTracks.size(); ++i) {
TrackInfo *info = &mSelectedTracks.editItemAt(i);
@@ -288,7 +298,8 @@
return OK;
}
-status_t NuMediaExtractor::selectTrack(size_t index) {
+status_t NuMediaExtractor::selectTrack(size_t index,
+ int64_t startTimeUs, MediaSource::ReadOptions::SeekMode mode) {
Mutex::Autolock autoLock(mLock);
if (mImpl == NULL) {
@@ -311,31 +322,56 @@
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;
- info->mSample = NULL;
- info->mSampleTimeUs = -1ll;
+ releaseTrackSamples(info);
info->mTrackFlags = 0;
- const char *mime;
- CHECK(source->getFormat()->findCString(kKeyMIMEType, &mime));
-
if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_VORBIS)) {
info->mTrackFlags |= kIsVorbis;
}
+ if (startTimeUs >= 0) {
+ fetchTrackSamples(info, startTimeUs, mode);
+ }
+
return OK;
}
@@ -366,12 +402,7 @@
TrackInfo *info = &mSelectedTracks.editItemAt(i);
- if (info->mSample != NULL) {
- info->mSample->release();
- info->mSample = NULL;
-
- info->mSampleTimeUs = -1ll;
- }
+ releaseTrackSamples(info);
CHECK_EQ((status_t)OK, info->mSource->stop());
@@ -380,79 +411,136 @@
return OK;
}
-void NuMediaExtractor::releaseTrackSamples() {
- for (size_t i = 0; i < mSelectedTracks.size(); ++i) {
- TrackInfo *info = &mSelectedTracks.editItemAt(i);
+void NuMediaExtractor::releaseOneSample(TrackInfo *info) {
+ if (info == NULL || info->mSamples.empty()) {
+ return;
+ }
- if (info->mSample != NULL) {
- info->mSample->release();
- info->mSample = NULL;
+ auto it = info->mSamples.begin();
+ if (it->mBuffer != NULL) {
+ it->mBuffer->release();
+ }
+ info->mSamples.erase(it);
+}
- info->mSampleTimeUs = -1ll;
+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);
}
}
-ssize_t NuMediaExtractor::fetchTrackSamples(
+void NuMediaExtractor::releaseAllTrackSamples() {
+ for (size_t i = 0; i < mSelectedTracks.size(); ++i) {
+ releaseTrackSamples(&mSelectedTracks.editItemAt(i));
+ }
+}
+
+ssize_t NuMediaExtractor::fetchAllTrackSamples(
int64_t seekTimeUs, MediaSource::ReadOptions::SeekMode mode) {
TrackInfo *minInfo = NULL;
ssize_t minIndex = -1;
for (size_t i = 0; i < mSelectedTracks.size(); ++i) {
TrackInfo *info = &mSelectedTracks.editItemAt(i);
+ fetchTrackSamples(info, seekTimeUs, mode);
- if (seekTimeUs >= 0ll) {
- info->mFinalResult = OK;
-
- if (info->mSample != NULL) {
- info->mSample->release();
- info->mSample = NULL;
- info->mSampleTimeUs = -1ll;
- }
- } else if (info->mFinalResult != OK) {
+ if (info->mSamples.empty()) {
continue;
}
- if (info->mSample == NULL) {
- MediaSource::ReadOptions options;
- if (seekTimeUs >= 0ll) {
- options.setSeekTo(seekTimeUs, mode);
- }
- status_t err = info->mSource->read(&info->mSample, &options);
-
- if (err != OK) {
- CHECK(info->mSample == NULL);
-
- info->mFinalResult = err;
-
- if (info->mFinalResult != ERROR_END_OF_STREAM) {
- ALOGW("read on track %zu failed with error %d",
- info->mTrackIndex, err);
- }
-
- info->mSampleTimeUs = -1ll;
- continue;
- } else {
- CHECK(info->mSample != NULL);
- CHECK(info->mSample->meta_data()->findInt64(
- kKeyTime, &info->mSampleTimeUs));
- }
- }
-
- if (minInfo == NULL || info->mSampleTimeUs < minInfo->mSampleTimeUs) {
+ 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<MediaBuffer *> mediaBuffers;
+ if (info->mSource->supportReadMultiple()) {
+ options.setNonBlocking();
+ err = info->mSource->readMultiple(&mediaBuffers, info->mMaxFetchCount, &options);
+ } else {
+ MediaBuffer *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();
+ for (size_t id = 0; id < count; ++id) {
+ MediaBuffer *mbuf = mediaBuffers[id];
+ if (mbuf != NULL) {
+ mbuf->release();
+ }
+ }
+ return;
+ }
+
+ size_t count = mediaBuffers.size();
+ bool releaseRemaining = false;
+ for (size_t id = 0; id < count; ++id) {
+ int64_t timeUs;
+ MediaBuffer *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 = fetchTrackSamples(timeUs, mode);
+ ssize_t minIndex = fetchAllTrackSamples(timeUs, mode);
if (minIndex < 0) {
return ERROR_END_OF_STREAM;
@@ -464,7 +552,7 @@
status_t NuMediaExtractor::advance() {
Mutex::Autolock autoLock(mLock);
- ssize_t minIndex = fetchTrackSamples();
+ ssize_t minIndex = fetchAllTrackSamples();
if (minIndex < 0) {
return ERROR_END_OF_STREAM;
@@ -472,28 +560,26 @@
TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
- info->mSample->release();
- info->mSample = NULL;
- info->mSampleTimeUs = -1ll;
+ releaseOneSample(info);
return OK;
}
-status_t NuMediaExtractor::appendVorbisNumPageSamples(TrackInfo *info, const sp<ABuffer> &buffer) {
+status_t NuMediaExtractor::appendVorbisNumPageSamples(MediaBuffer *mbuf, const sp<ABuffer> &buffer) {
int32_t numPageSamples;
- if (!info->mSample->meta_data()->findInt32(
+ if (!mbuf->meta_data()->findInt32(
kKeyValidSamples, &numPageSamples)) {
numPageSamples = -1;
}
- memcpy((uint8_t *)buffer->data() + info->mSample->range_length(),
+ memcpy((uint8_t *)buffer->data() + mbuf->range_length(),
&numPageSamples,
sizeof(numPageSamples));
uint32_t type;
const void *data;
size_t size, size2;
- if (info->mSample->meta_data()->findData(kKeyEncryptedSizes, &type, &data, &size)) {
+ 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)) {
@@ -511,9 +597,9 @@
int32_t zero = 0;
memcpy(adata, data, size);
memcpy(adata + size, &zero, sizeof(zero));
- info->mSample->meta_data()->setData(kKeyEncryptedSizes, type, adata, newSize);
+ mbuf->meta_data()->setData(kKeyEncryptedSizes, type, adata, newSize);
- if (info->mSample->meta_data()->findData(kKeyPlainSizes, &type, &data, &size2)) {
+ if (mbuf->meta_data()->findData(kKeyPlainSizes, &type, &data, &size2)) {
if (size2 != size) {
return ERROR_MALFORMED;
}
@@ -526,7 +612,7 @@
// append sizeof(numPageSamples) to plain sizes.
int32_t int32Size = sizeof(numPageSamples);
memcpy(adata + size, &int32Size, sizeof(int32Size));
- info->mSample->meta_data()->setData(kKeyPlainSizes, type, adata, newSize);
+ mbuf->meta_data()->setData(kKeyPlainSizes, type, adata, newSize);
}
return OK;
@@ -535,7 +621,7 @@
status_t NuMediaExtractor::readSampleData(const sp<ABuffer> &buffer) {
Mutex::Autolock autoLock(mLock);
- ssize_t minIndex = fetchTrackSamples();
+ ssize_t minIndex = fetchAllTrackSamples();
if (minIndex < 0) {
return ERROR_END_OF_STREAM;
@@ -543,7 +629,8 @@
TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
- size_t sampleSize = info->mSample->range_length();
+ 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
@@ -556,14 +643,14 @@
}
const uint8_t *src =
- (const uint8_t *)info->mSample->data()
- + info->mSample->range_offset();
+ (const uint8_t *)it->mBuffer->data()
+ + it->mBuffer->range_offset();
- memcpy((uint8_t *)buffer->data(), src, info->mSample->range_length());
+ memcpy((uint8_t *)buffer->data(), src, it->mBuffer->range_length());
status_t err = OK;
if (info->mTrackFlags & kIsVorbis) {
- err = appendVorbisNumPageSamples(info, buffer);
+ err = appendVorbisNumPageSamples(it->mBuffer, buffer);
}
if (err == OK) {
@@ -576,7 +663,7 @@
status_t NuMediaExtractor::getSampleTrackIndex(size_t *trackIndex) {
Mutex::Autolock autoLock(mLock);
- ssize_t minIndex = fetchTrackSamples();
+ ssize_t minIndex = fetchAllTrackSamples();
if (minIndex < 0) {
return ERROR_END_OF_STREAM;
@@ -591,14 +678,14 @@
status_t NuMediaExtractor::getSampleTime(int64_t *sampleTimeUs) {
Mutex::Autolock autoLock(mLock);
- ssize_t minIndex = fetchTrackSamples();
+ ssize_t minIndex = fetchAllTrackSamples();
if (minIndex < 0) {
return ERROR_END_OF_STREAM;
}
TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
- *sampleTimeUs = info->mSampleTimeUs;
+ *sampleTimeUs = info->mSamples.begin()->mSampleTimeUs;
return OK;
}
@@ -608,14 +695,14 @@
*sampleMeta = NULL;
- ssize_t minIndex = fetchTrackSamples();
+ ssize_t minIndex = fetchAllTrackSamples();
if (minIndex < 0) {
return ERROR_END_OF_STREAM;
}
TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
- *sampleMeta = info->mSample->meta_data();
+ *sampleMeta = info->mSamples.begin()->mBuffer->meta_data();
return OK;
}
diff --git a/media/libstagefright/include/media/stagefright/NuMediaExtractor.h b/media/libstagefright/include/media/stagefright/NuMediaExtractor.h
index 2902682..5af0745 100644
--- a/media/libstagefright/include/media/stagefright/NuMediaExtractor.h
+++ b/media/libstagefright/include/media/stagefright/NuMediaExtractor.h
@@ -17,6 +17,8 @@
#ifndef NU_MEDIA_EXTRACTOR_H_
#define NU_MEDIA_EXTRACTOR_H_
+#include <list>
+#include <media/mediaplayer.h>
#include <media/stagefright/foundation/ABase.h>
#include <media/IMediaExtractor.h>
#include <media/MediaSource.h>
@@ -67,7 +69,9 @@
status_t getFileFormat(sp<AMessage> *format) const;
- status_t selectTrack(size_t index);
+ status_t selectTrack(size_t index, int64_t startTimeUs = -1ll,
+ MediaSource::ReadOptions::SeekMode mode =
+ MediaSource::ReadOptions::SEEK_CLOSEST_SYNC);
status_t unselectTrack(size_t index);
status_t seekTo(
@@ -75,8 +79,12 @@
MediaSource::ReadOptions::SeekMode mode =
MediaSource::ReadOptions::SEEK_CLOSEST_SYNC);
+ // Each selected track has a read pointer.
+ // advance() advances the read pointer with the lowest timestamp.
status_t advance();
+ // readSampleData() reads the sample with the lowest timestamp.
status_t readSampleData(const sp<ABuffer> &buffer);
+
status_t getSampleTrackIndex(size_t *trackIndex);
status_t getSampleTime(int64_t *sampleTimeUs);
status_t getSampleMeta(sp<MetaData> *sampleMeta);
@@ -96,12 +104,20 @@
kMaxTrackCount = 16384,
};
+ struct Sample {
+ Sample();
+ Sample(MediaBuffer *buffer, int64_t timeUs);
+ MediaBuffer *mBuffer;
+ int64_t mSampleTimeUs;
+ };
+
struct TrackInfo {
sp<IMediaSource> mSource;
size_t mTrackIndex;
+ media_track_type mTrackType;
+ size_t mMaxFetchCount;
status_t mFinalResult;
- MediaBuffer *mSample;
- int64_t mSampleTimeUs;
+ std::list<Sample> mSamples;
uint32_t mTrackFlags; // bitmask of "TrackFlags"
};
@@ -117,16 +133,23 @@
int64_t mTotalBitrate; // in bits/sec
int64_t mDurationUs;
- ssize_t fetchTrackSamples(
+ ssize_t fetchAllTrackSamples(
+ int64_t seekTimeUs = -1ll,
+ MediaSource::ReadOptions::SeekMode mode =
+ MediaSource::ReadOptions::SEEK_CLOSEST_SYNC);
+ void fetchTrackSamples(
+ TrackInfo *info,
int64_t seekTimeUs = -1ll,
MediaSource::ReadOptions::SeekMode mode =
MediaSource::ReadOptions::SEEK_CLOSEST_SYNC);
- void releaseTrackSamples();
+ void releaseOneSample(TrackInfo *info);
+ void releaseTrackSamples(TrackInfo *info);
+ void releaseAllTrackSamples();
bool getTotalBitrate(int64_t *bitRate) const;
status_t updateDurationAndBitrate();
- status_t appendVorbisNumPageSamples(TrackInfo *info, const sp<ABuffer> &buffer);
+ status_t appendVorbisNumPageSamples(MediaBuffer *mbuf, const sp<ABuffer> &buffer);
DISALLOW_EVIL_CONSTRUCTORS(NuMediaExtractor);
};