am 8138e841: Merge "Support finer seek control on MediaSources." into gingerbread

Merge commit '8138e841a5adb3c5629e263f5a8cb2e936d234ce' into gingerbread-plus-aosp

* commit '8138e841a5adb3c5629e263f5a8cb2e936d234ce':
  Support finer seek control on MediaSources.
This commit is contained in:
Andreas Huber
2010-07-21 10:39:00 -07:00
committed by Android Git Automerger
31 changed files with 459 additions and 67 deletions

View File

@@ -62,14 +62,21 @@ struct MediaSource : public RefBase {
// a) not request a seek // a) not request a seek
// b) not be late, i.e. lateness_us = 0 // b) not be late, i.e. lateness_us = 0
struct ReadOptions { struct ReadOptions {
enum SeekMode {
SEEK_PREVIOUS_SYNC,
SEEK_NEXT_SYNC,
SEEK_CLOSEST_SYNC,
SEEK_CLOSEST,
};
ReadOptions(); ReadOptions();
// Reset everything back to defaults. // Reset everything back to defaults.
void reset(); void reset();
void setSeekTo(int64_t time_us); void setSeekTo(int64_t time_us, SeekMode mode = SEEK_CLOSEST_SYNC);
void clearSeekTo(); void clearSeekTo();
bool getSeekTo(int64_t *time_us) const; bool getSeekTo(int64_t *time_us, SeekMode *mode) const;
void setLateBy(int64_t lateness_us); void setLateBy(int64_t lateness_us);
int64_t getLateBy() const; int64_t getLateBy() const;
@@ -81,6 +88,7 @@ struct MediaSource : public RefBase {
uint32_t mOptions; uint32_t mOptions;
int64_t mSeekTimeUs; int64_t mSeekTimeUs;
SeekMode mSeekMode;
int64_t mLatenessUs; int64_t mLatenessUs;
}; };

View File

@@ -46,6 +46,7 @@ enum {
kKeyIsSyncFrame = 'sync', // int32_t (bool) kKeyIsSyncFrame = 'sync', // int32_t (bool)
kKeyIsCodecConfig = 'conf', // int32_t (bool) kKeyIsCodecConfig = 'conf', // int32_t (bool)
kKeyTime = 'time', // int64_t (usecs) kKeyTime = 'time', // int64_t (usecs)
kKeyTargetTime = 'tarT', // int64_t (usecs)
kKeyDuration = 'dura', // int64_t (usecs) kKeyDuration = 'dura', // int64_t (usecs)
kKeyColorFormat = 'colf', kKeyColorFormat = 'colf',
kKeyPlatformPrivate = 'priv', // pointer kKeyPlatformPrivate = 'priv', // pointer

View File

@@ -141,6 +141,8 @@ private:
bool mNoMoreOutputData; bool mNoMoreOutputData;
bool mOutputPortSettingsHaveChanged; bool mOutputPortSettingsHaveChanged;
int64_t mSeekTimeUs; int64_t mSeekTimeUs;
ReadOptions::SeekMode mSeekMode;
int64_t mTargetTimeUs;
MediaBuffer *mLeftOverBuffer; MediaBuffer *mLeftOverBuffer;

View File

@@ -212,7 +212,8 @@ status_t AMRSource::read(
*out = NULL; *out = NULL;
int64_t seekTimeUs; int64_t seekTimeUs;
if (options && options->getSeekTo(&seekTimeUs)) { ReadOptions::SeekMode mode;
if (options && options->getSeekTo(&seekTimeUs, &mode)) {
int64_t seekFrame = seekTimeUs / 20000ll; // 20ms per frame. int64_t seekFrame = seekTimeUs / 20000ll; // 20ms per frame.
mCurrentTimeUs = seekFrame * 20000ll; mCurrentTimeUs = seekFrame * 20000ll;
mOffset = seekFrame * mFrameSize + (mIsWide ? 9 : 6); mOffset = seekFrame * mFrameSize + (mIsWide ? 9 : 6);

View File

@@ -914,7 +914,8 @@ void AwesomePlayer::onVideoEvent() {
if (mSeeking) { if (mSeeking) {
LOGV("seeking to %lld us (%.2f secs)", mSeekTimeUs, mSeekTimeUs / 1E6); LOGV("seeking to %lld us (%.2f secs)", mSeekTimeUs, mSeekTimeUs / 1E6);
options.setSeekTo(mSeekTimeUs); options.setSeekTo(
mSeekTimeUs, MediaSource::ReadOptions::SEEK_CLOSEST_SYNC);
} }
for (;;) { for (;;) {
status_t err = mVideoSource->read(&mVideoBuffer, &options); status_t err = mVideoSource->read(&mVideoBuffer, &options);

View File

@@ -267,7 +267,8 @@ status_t CameraSource::read(
*buffer = NULL; *buffer = NULL;
int64_t seekTimeUs; int64_t seekTimeUs;
if (options && options->getSeekTo(&seekTimeUs)) { ReadOptions::SeekMode mode;
if (options && options->getSeekTo(&seekTimeUs, &mode)) {
return ERROR_UNSUPPORTED; return ERROR_UNSUPPORTED;
} }

View File

@@ -112,7 +112,8 @@ status_t JPEGSource::read(
*out = NULL; *out = NULL;
int64_t seekTimeUs; int64_t seekTimeUs;
if (options != NULL && options->getSeekTo(&seekTimeUs)) { ReadOptions::SeekMode mode;
if (options != NULL && options->getSeekTo(&seekTimeUs, &mode)) {
return UNKNOWN_ERROR; return UNKNOWN_ERROR;
} }

View File

@@ -586,7 +586,8 @@ status_t MP3Source::read(
*out = NULL; *out = NULL;
int64_t seekTimeUs; int64_t seekTimeUs;
if (options != NULL && options->getSeekTo(&seekTimeUs)) { ReadOptions::SeekMode mode;
if (options != NULL && options->getSeekTo(&seekTimeUs, &mode)) {
int32_t bitrate; int32_t bitrate;
if (!mMeta->findInt32(kKeyBitRate, &bitrate)) { if (!mMeta->findInt32(kKeyBitRate, &bitrate)) {
// bitrate is in bits/sec. // bitrate is in bits/sec.

View File

@@ -1468,12 +1468,45 @@ status_t MPEG4Source::read(
*out = NULL; *out = NULL;
int64_t targetSampleTimeUs = -1;
int64_t seekTimeUs; int64_t seekTimeUs;
if (options && options->getSeekTo(&seekTimeUs)) { ReadOptions::SeekMode mode;
if (options && options->getSeekTo(&seekTimeUs, &mode)) {
uint32_t findFlags = 0;
switch (mode) {
case ReadOptions::SEEK_PREVIOUS_SYNC:
findFlags = SampleTable::kFlagBefore;
break;
case ReadOptions::SEEK_NEXT_SYNC:
findFlags = SampleTable::kFlagAfter;
break;
case ReadOptions::SEEK_CLOSEST_SYNC:
case ReadOptions::SEEK_CLOSEST:
findFlags = SampleTable::kFlagClosest;
break;
default:
CHECK(!"Should not be here.");
break;
}
uint32_t sampleIndex; uint32_t sampleIndex;
status_t err = mSampleTable->findClosestSample( status_t err = mSampleTable->findSampleAtTime(
seekTimeUs * mTimescale / 1000000, seekTimeUs * mTimescale / 1000000,
&sampleIndex, SampleTable::kSyncSample_Flag); &sampleIndex, findFlags);
if (mode == ReadOptions::SEEK_CLOSEST) {
// We found the closest sample already, now we want the sync
// sample preceding it (or the sample itself of course), even
// if the subsequent sync sample is closer.
findFlags = SampleTable::kFlagBefore;
}
uint32_t syncSampleIndex;
if (err == OK) {
err = mSampleTable->findSyncSampleNear(
sampleIndex, &syncSampleIndex, findFlags);
}
if (err != OK) { if (err != OK) {
if (err == ERROR_OUT_OF_RANGE) { if (err == ERROR_OUT_OF_RANGE) {
@@ -1487,7 +1520,27 @@ status_t MPEG4Source::read(
return err; return err;
} }
mCurrentSampleIndex = sampleIndex; uint32_t sampleTime;
CHECK_EQ(OK, mSampleTable->getMetaDataForSample(
sampleIndex, NULL, NULL, &sampleTime));
if (mode == ReadOptions::SEEK_CLOSEST) {
targetSampleTimeUs = (sampleTime * 1000000ll) / mTimescale;
}
#if 0
uint32_t syncSampleTime;
CHECK_EQ(OK, mSampleTable->getMetaDataForSample(
syncSampleIndex, NULL, NULL, &syncSampleTime));
LOGI("seek to time %lld us => sample at time %lld us, "
"sync sample at time %lld us",
seekTimeUs,
sampleTime * 1000000ll / mTimescale,
syncSampleTime * 1000000ll / mTimescale);
#endif
mCurrentSampleIndex = syncSampleIndex;
if (mBuffer != NULL) { if (mBuffer != NULL) {
mBuffer->release(); mBuffer->release();
mBuffer = NULL; mBuffer = NULL;
@@ -1536,6 +1589,12 @@ status_t MPEG4Source::read(
mBuffer->meta_data()->clear(); mBuffer->meta_data()->clear();
mBuffer->meta_data()->setInt64( mBuffer->meta_data()->setInt64(
kKeyTime, ((int64_t)dts * 1000000) / mTimescale); kKeyTime, ((int64_t)dts * 1000000) / mTimescale);
if (targetSampleTimeUs >= 0) {
mBuffer->meta_data()->setInt64(
kKeyTargetTime, targetSampleTimeUs);
}
++mCurrentSampleIndex; ++mCurrentSampleIndex;
} }
@@ -1632,6 +1691,12 @@ status_t MPEG4Source::read(
mBuffer->meta_data()->clear(); mBuffer->meta_data()->clear();
mBuffer->meta_data()->setInt64( mBuffer->meta_data()->setInt64(
kKeyTime, ((int64_t)dts * 1000000) / mTimescale); kKeyTime, ((int64_t)dts * 1000000) / mTimescale);
if (targetSampleTimeUs >= 0) {
mBuffer->meta_data()->setInt64(
kKeyTargetTime, targetSampleTimeUs);
}
++mCurrentSampleIndex; ++mCurrentSampleIndex;
*out = mBuffer; *out = mBuffer;

View File

@@ -34,18 +34,22 @@ void MediaSource::ReadOptions::reset() {
mLatenessUs = 0; mLatenessUs = 0;
} }
void MediaSource::ReadOptions::setSeekTo(int64_t time_us) { void MediaSource::ReadOptions::setSeekTo(int64_t time_us, SeekMode mode) {
mOptions |= kSeekTo_Option; mOptions |= kSeekTo_Option;
mSeekTimeUs = time_us; mSeekTimeUs = time_us;
mSeekMode = mode;
} }
void MediaSource::ReadOptions::clearSeekTo() { void MediaSource::ReadOptions::clearSeekTo() {
mOptions &= ~kSeekTo_Option; mOptions &= ~kSeekTo_Option;
mSeekTimeUs = 0; mSeekTimeUs = 0;
mSeekMode = SEEK_CLOSEST_SYNC;
} }
bool MediaSource::ReadOptions::getSeekTo(int64_t *time_us) const { bool MediaSource::ReadOptions::getSeekTo(
int64_t *time_us, SeekMode *mode) const {
*time_us = mSeekTimeUs; *time_us = mSeekTimeUs;
*mode = mSeekMode;
return (mOptions & kSeekTo_Option) != 0; return (mOptions & kSeekTo_Option) != 0;
} }

View File

@@ -1272,6 +1272,8 @@ OMXCodec::OMXCodec(
mNoMoreOutputData(false), mNoMoreOutputData(false),
mOutputPortSettingsHaveChanged(false), mOutputPortSettingsHaveChanged(false),
mSeekTimeUs(-1), mSeekTimeUs(-1),
mSeekMode(ReadOptions::SEEK_CLOSEST_SYNC),
mTargetTimeUs(-1),
mLeftOverBuffer(NULL), mLeftOverBuffer(NULL),
mPaused(false) { mPaused(false) {
mPortStatus[kPortIndexInput] = ENABLED; mPortStatus[kPortIndexInput] = ENABLED;
@@ -1640,13 +1642,33 @@ void OMXCodec::on_message(const omx_message &msg) {
kKeyBufferID, kKeyBufferID,
msg.u.extended_buffer_data.buffer); msg.u.extended_buffer_data.buffer);
mFilledBuffers.push_back(i);
mBufferFilled.signal();
if (msg.u.extended_buffer_data.flags & OMX_BUFFERFLAG_EOS) { if (msg.u.extended_buffer_data.flags & OMX_BUFFERFLAG_EOS) {
CODEC_LOGV("No more output data."); CODEC_LOGV("No more output data.");
mNoMoreOutputData = true; mNoMoreOutputData = true;
} }
if (mTargetTimeUs >= 0) {
CHECK(msg.u.extended_buffer_data.timestamp <= mTargetTimeUs);
if (msg.u.extended_buffer_data.timestamp < mTargetTimeUs) {
CODEC_LOGV(
"skipping output buffer at timestamp %lld us",
msg.u.extended_buffer_data.timestamp);
fillOutputBuffer(info);
break;
}
CODEC_LOGV(
"returning output buffer at target timestamp "
"%lld us",
msg.u.extended_buffer_data.timestamp);
mTargetTimeUs = -1;
}
mFilledBuffers.push_back(i);
mBufferFilled.signal();
} }
break; break;
@@ -2185,12 +2207,24 @@ void OMXCodec::drainInputBuffer(BufferInfo *info) {
} }
MediaSource::ReadOptions options; MediaSource::ReadOptions options;
options.setSeekTo(mSeekTimeUs); options.setSeekTo(mSeekTimeUs, mSeekMode);
mSeekTimeUs = -1; mSeekTimeUs = -1;
mSeekMode = ReadOptions::SEEK_CLOSEST_SYNC;
mBufferFilled.signal(); mBufferFilled.signal();
err = mSource->read(&srcBuffer, &options); err = mSource->read(&srcBuffer, &options);
if (err == OK) {
int64_t targetTimeUs;
if (srcBuffer->meta_data()->findInt64(
kKeyTargetTime, &targetTimeUs)
&& targetTimeUs >= 0) {
mTargetTimeUs = targetTimeUs;
} else {
mTargetTimeUs = -1;
}
}
} else if (mLeftOverBuffer) { } else if (mLeftOverBuffer) {
srcBuffer = mLeftOverBuffer; srcBuffer = mLeftOverBuffer;
mLeftOverBuffer = NULL; mLeftOverBuffer = NULL;
@@ -2239,7 +2273,7 @@ void OMXCodec::drainInputBuffer(BufferInfo *info) {
int64_t lastBufferTimeUs; int64_t lastBufferTimeUs;
CHECK(srcBuffer->meta_data()->findInt64(kKeyTime, &lastBufferTimeUs)); CHECK(srcBuffer->meta_data()->findInt64(kKeyTime, &lastBufferTimeUs));
CHECK(timestampUs >= 0); CHECK(lastBufferTimeUs >= 0);
if (offset == 0) { if (offset == 0) {
timestampUs = lastBufferTimeUs; timestampUs = lastBufferTimeUs;
@@ -2696,6 +2730,8 @@ status_t OMXCodec::start(MetaData *meta) {
mNoMoreOutputData = false; mNoMoreOutputData = false;
mOutputPortSettingsHaveChanged = false; mOutputPortSettingsHaveChanged = false;
mSeekTimeUs = -1; mSeekTimeUs = -1;
mSeekMode = ReadOptions::SEEK_CLOSEST_SYNC;
mTargetTimeUs = -1;
mFilledBuffers.clear(); mFilledBuffers.clear();
mPaused = false; mPaused = false;
@@ -2790,7 +2826,8 @@ status_t OMXCodec::read(
bool seeking = false; bool seeking = false;
int64_t seekTimeUs; int64_t seekTimeUs;
if (options && options->getSeekTo(&seekTimeUs)) { ReadOptions::SeekMode seekMode;
if (options && options->getSeekTo(&seekTimeUs, &seekMode)) {
seeking = true; seeking = true;
} }
@@ -2800,6 +2837,7 @@ status_t OMXCodec::read(
if (seeking) { if (seeking) {
CHECK(seekTimeUs >= 0); CHECK(seekTimeUs >= 0);
mSeekTimeUs = seekTimeUs; mSeekTimeUs = seekTimeUs;
mSeekMode = seekMode;
// There's no reason to trigger the code below, there's // There's no reason to trigger the code below, there's
// nothing to flush yet. // nothing to flush yet.
@@ -2823,6 +2861,7 @@ status_t OMXCodec::read(
CHECK(seekTimeUs >= 0); CHECK(seekTimeUs >= 0);
mSeekTimeUs = seekTimeUs; mSeekTimeUs = seekTimeUs;
mSeekMode = seekMode;
mFilledBuffers.clear(); mFilledBuffers.clear();

View File

@@ -155,7 +155,8 @@ status_t OggSource::read(
*out = NULL; *out = NULL;
int64_t seekTimeUs; int64_t seekTimeUs;
if (options && options->getSeekTo(&seekTimeUs)) { ReadOptions::SeekMode mode;
if (options && options->getSeekTo(&seekTimeUs, &mode)) {
off_t pos = seekTimeUs * mExtractor->mImpl->approxBitrate() / 8000000ll; off_t pos = seekTimeUs * mExtractor->mImpl->approxBitrate() / 8000000ll;
LOGI("seeking to offset %ld", pos); LOGI("seeking to offset %ld", pos);

View File

@@ -314,8 +314,10 @@ uint32_t abs_difference(uint32_t time1, uint32_t time2) {
return time1 > time2 ? time1 - time2 : time2 - time1; return time1 > time2 ? time1 - time2 : time2 - time1;
} }
status_t SampleTable::findClosestSample( status_t SampleTable::findSampleAtTime(
uint32_t req_time, uint32_t *sample_index, uint32_t flags) { uint32_t req_time, uint32_t *sample_index, uint32_t flags) {
*sample_index = 0;
Mutex::Autolock autoLock(mLock); Mutex::Autolock autoLock(mLock);
uint32_t cur_sample = 0; uint32_t cur_sample = 0;
@@ -330,16 +332,37 @@ status_t SampleTable::findClosestSample(
uint32_t time1 = time + j * delta; uint32_t time1 = time + j * delta;
uint32_t time2 = time1 + delta; uint32_t time2 = time1 + delta;
uint32_t sampleTime;
if (i+1 == mTimeToSampleCount if (i+1 == mTimeToSampleCount
|| (abs_difference(req_time, time1) || (abs_difference(req_time, time1)
< abs_difference(req_time, time2))) { < abs_difference(req_time, time2))) {
*sample_index = cur_sample + j; *sample_index = cur_sample + j;
sampleTime = time1;
} else { } else {
*sample_index = cur_sample + j + 1; *sample_index = cur_sample + j + 1;
sampleTime = time2;
} }
if (flags & kSyncSample_Flag) { switch (flags) {
return findClosestSyncSample_l(*sample_index, sample_index); case kFlagBefore:
{
if (sampleTime > req_time && *sample_index > 0) {
--*sample_index;
}
break;
}
case kFlagAfter:
{
if (sampleTime < req_time
&& *sample_index + 1 < mNumSampleSizes) {
++*sample_index;
}
break;
}
default:
break;
} }
return OK; return OK;
@@ -352,8 +375,10 @@ status_t SampleTable::findClosestSample(
return ERROR_OUT_OF_RANGE; return ERROR_OUT_OF_RANGE;
} }
status_t SampleTable::findClosestSyncSample_l( status_t SampleTable::findSyncSampleNear(
uint32_t start_sample_index, uint32_t *sample_index) { uint32_t start_sample_index, uint32_t *sample_index, uint32_t flags) {
Mutex::Autolock autoLock(mLock);
*sample_index = 0; *sample_index = 0;
if (mSyncSampleOffset < 0) { if (mSyncSampleOffset < 0) {
@@ -362,29 +387,124 @@ status_t SampleTable::findClosestSyncSample_l(
return OK; return OK;
} }
uint32_t x; if (mNumSyncSamples == 0) {
uint32_t left = 0; *sample_index = 0;
uint32_t right = mNumSyncSamples; return OK;
while (left < right) { }
uint32_t mid = (left + right) / 2;
uint32_t left = 0;
while (left < mNumSyncSamples) {
uint32_t x;
if (mDataSource->readAt( if (mDataSource->readAt(
mSyncSampleOffset + 8 + mid * 4, &x, 4) != 4) { mSyncSampleOffset + 8 + left * 4, &x, 4) != 4) {
return ERROR_IO; return ERROR_IO;
} }
x = ntohl(x); x = ntohl(x);
--x;
if (x < (start_sample_index + 1)) { if (x >= start_sample_index) {
left = mid + 1;
} else if (x > (start_sample_index + 1)) {
right = mid;
} else {
break; break;
} }
++left;
}
--left;
uint32_t x;
if (mDataSource->readAt(
mSyncSampleOffset + 8 + left * 4, &x, 4) != 4) {
return ERROR_IO;
}
x = ntohl(x);
--x;
if (left + 1 < mNumSyncSamples) {
uint32_t y;
if (mDataSource->readAt(
mSyncSampleOffset + 8 + (left + 1) * 4, &y, 4) != 4) {
return ERROR_IO;
}
y = ntohl(y);
--y;
// our sample lies between sync samples x and y.
status_t err = mSampleIterator->seekTo(start_sample_index);
if (err != OK) {
return err;
}
uint32_t sample_time = mSampleIterator->getSampleTime();
err = mSampleIterator->seekTo(x);
if (err != OK) {
return err;
}
uint32_t x_time = mSampleIterator->getSampleTime();
err = mSampleIterator->seekTo(y);
if (err != OK) {
return err;
}
uint32_t y_time = mSampleIterator->getSampleTime();
if (abs_difference(x_time, sample_time)
> abs_difference(y_time, sample_time)) {
// Pick the sync sample closest (timewise) to the start-sample.
x = y;
++left;
}
} }
*sample_index = x - 1; switch (flags) {
case kFlagBefore:
{
if (x > start_sample_index) {
CHECK(left > 0);
if (mDataSource->readAt(
mSyncSampleOffset + 8 + (left - 1) * 4, &x, 4) != 4) {
return ERROR_IO;
}
x = ntohl(x);
--x;
CHECK(x <= start_sample_index);
}
break;
}
case kFlagAfter:
{
if (x < start_sample_index) {
if (left + 1 >= mNumSyncSamples) {
return ERROR_OUT_OF_RANGE;
}
if (mDataSource->readAt(
mSyncSampleOffset + 8 + (left + 1) * 4, &x, 4) != 4) {
return ERROR_IO;
}
x = ntohl(x);
--x;
CHECK(x >= start_sample_index);
}
break;
}
default:
break;
}
*sample_index = x;
return OK; return OK;
} }

View File

@@ -93,7 +93,8 @@ status_t ShoutcastSource::read(
*out = NULL; *out = NULL;
int64_t seekTimeUs; int64_t seekTimeUs;
if (options && options->getSeekTo(&seekTimeUs)) { ReadOptions::SeekMode mode;
if (options && options->getSeekTo(&seekTimeUs, &mode)) {
return ERROR_UNSUPPORTED; return ERROR_UNSUPPORTED;
} }

View File

@@ -282,7 +282,8 @@ status_t WAVSource::read(
*out = NULL; *out = NULL;
int64_t seekTimeUs; int64_t seekTimeUs;
if (options != NULL && options->getSeekTo(&seekTimeUs)) { ReadOptions::SeekMode mode;
if (options != NULL && options->getSeekTo(&seekTimeUs, &mode)) {
int64_t pos = (seekTimeUs * mSampleRate) / 1000000 * mNumChannels * 2; int64_t pos = (seekTimeUs * mSampleRate) / 1000000 * mNumChannels * 2;
if (pos > mSize) { if (pos > mSize) {
pos = mSize; pos = mSize;

View File

@@ -159,7 +159,8 @@ status_t AACDecoder::read(
*out = NULL; *out = NULL;
int64_t seekTimeUs; int64_t seekTimeUs;
if (options && options->getSeekTo(&seekTimeUs)) { ReadOptions::SeekMode mode;
if (options && options->getSeekTo(&seekTimeUs, &mode)) {
CHECK(seekTimeUs >= 0); CHECK(seekTimeUs >= 0);
mNumSamplesOutput = 0; mNumSamplesOutput = 0;

View File

@@ -202,7 +202,8 @@ status_t AACEncoder::read(
*out = NULL; *out = NULL;
int64_t seekTimeUs; int64_t seekTimeUs;
CHECK(options == NULL || !options->getSeekTo(&seekTimeUs)); ReadOptions::SeekMode mode;
CHECK(options == NULL || !options->getSeekTo(&seekTimeUs, &mode));
MediaBuffer *buffer; MediaBuffer *buffer;
CHECK_EQ(mBufferGroup->acquire_buffer(&buffer), OK); CHECK_EQ(mBufferGroup->acquire_buffer(&buffer), OK);

View File

@@ -117,7 +117,8 @@ status_t AMRNBDecoder::read(
*out = NULL; *out = NULL;
int64_t seekTimeUs; int64_t seekTimeUs;
if (options && options->getSeekTo(&seekTimeUs)) { ReadOptions::SeekMode mode;
if (options && options->getSeekTo(&seekTimeUs, &mode)) {
CHECK(seekTimeUs >= 0); CHECK(seekTimeUs >= 0);
mNumSamplesOutput = 0; mNumSamplesOutput = 0;

View File

@@ -145,7 +145,8 @@ status_t AMRNBEncoder::read(
*out = NULL; *out = NULL;
int64_t seekTimeUs; int64_t seekTimeUs;
CHECK(options == NULL || !options->getSeekTo(&seekTimeUs)); ReadOptions::SeekMode mode;
CHECK(options == NULL || !options->getSeekTo(&seekTimeUs, &mode));
while (mNumInputSamples < kNumSamplesPerFrame) { while (mNumInputSamples < kNumSamplesPerFrame) {
if (mInputBuffer == NULL) { if (mInputBuffer == NULL) {

View File

@@ -135,7 +135,8 @@ status_t AMRWBDecoder::read(
*out = NULL; *out = NULL;
int64_t seekTimeUs; int64_t seekTimeUs;
if (options && options->getSeekTo(&seekTimeUs)) { ReadOptions::SeekMode seekMode;
if (options && options->getSeekTo(&seekTimeUs, &seekMode)) {
CHECK(seekTimeUs >= 0); CHECK(seekTimeUs >= 0);
mNumSamplesOutput = 0; mNumSamplesOutput = 0;

View File

@@ -196,7 +196,8 @@ status_t AMRWBEncoder::read(
*out = NULL; *out = NULL;
int64_t seekTimeUs; int64_t seekTimeUs;
CHECK(options == NULL || !options->getSeekTo(&seekTimeUs)); ReadOptions::SeekMode mode;
CHECK(options == NULL || !options->getSeekTo(&seekTimeUs, &mode));
while (mNumInputSamples < kNumSamplesPerFrame) { while (mNumInputSamples < kNumSamplesPerFrame) {
if (mInputBuffer == NULL) { if (mInputBuffer == NULL) {

View File

@@ -51,7 +51,9 @@ AVCDecoder::AVCDecoder(const sp<MediaSource> &source)
mInputBuffer(NULL), mInputBuffer(NULL),
mAnchorTimeUs(0), mAnchorTimeUs(0),
mNumSamplesOutput(0), mNumSamplesOutput(0),
mPendingSeekTimeUs(-1) { mPendingSeekTimeUs(-1),
mPendingSeekMode(MediaSource::ReadOptions::SEEK_CLOSEST_SYNC),
mTargetTimeUs(-1) {
memset(mHandle, 0, sizeof(tagAVCHandle)); memset(mHandle, 0, sizeof(tagAVCHandle));
mHandle->AVCObject = NULL; mHandle->AVCObject = NULL;
mHandle->userData = this; mHandle->userData = this;
@@ -161,6 +163,8 @@ status_t AVCDecoder::start(MetaData *) {
mAnchorTimeUs = 0; mAnchorTimeUs = 0;
mNumSamplesOutput = 0; mNumSamplesOutput = 0;
mPendingSeekTimeUs = -1; mPendingSeekTimeUs = -1;
mPendingSeekMode = ReadOptions::SEEK_CLOSEST_SYNC;
mTargetTimeUs = -1;
mStarted = true; mStarted = true;
return OK; return OK;
@@ -229,11 +233,13 @@ status_t AVCDecoder::read(
*out = NULL; *out = NULL;
int64_t seekTimeUs; int64_t seekTimeUs;
if (options && options->getSeekTo(&seekTimeUs)) { ReadOptions::SeekMode mode;
if (options && options->getSeekTo(&seekTimeUs, &mode)) {
LOGV("seek requested to %lld us (%.2f secs)", seekTimeUs, seekTimeUs / 1E6); LOGV("seek requested to %lld us (%.2f secs)", seekTimeUs, seekTimeUs / 1E6);
CHECK(seekTimeUs >= 0); CHECK(seekTimeUs >= 0);
mPendingSeekTimeUs = seekTimeUs; mPendingSeekTimeUs = seekTimeUs;
mPendingSeekMode = mode;
if (mInputBuffer) { if (mInputBuffer) {
mInputBuffer->release(); mInputBuffer->release();
@@ -246,6 +252,8 @@ status_t AVCDecoder::read(
if (mInputBuffer == NULL) { if (mInputBuffer == NULL) {
LOGV("fetching new input buffer."); LOGV("fetching new input buffer.");
bool seeking = false;
if (!mCodecSpecificData.isEmpty()) { if (!mCodecSpecificData.isEmpty()) {
mInputBuffer = mCodecSpecificData.editItemAt(0); mInputBuffer = mCodecSpecificData.editItemAt(0);
mCodecSpecificData.removeAt(0); mCodecSpecificData.removeAt(0);
@@ -258,7 +266,9 @@ status_t AVCDecoder::read(
ReadOptions seekOptions; ReadOptions seekOptions;
if (mPendingSeekTimeUs >= 0) { if (mPendingSeekTimeUs >= 0) {
seekOptions.setSeekTo(mPendingSeekTimeUs); seeking = true;
seekOptions.setSeekTo(mPendingSeekTimeUs, mPendingSeekMode);
mPendingSeekTimeUs = -1; mPendingSeekTimeUs = -1;
} }
status_t err = mSource->read(&mInputBuffer, &seekOptions); status_t err = mSource->read(&mInputBuffer, &seekOptions);
@@ -276,6 +286,16 @@ status_t AVCDecoder::read(
mInputBuffer = NULL; mInputBuffer = NULL;
} }
} }
if (seeking) {
int64_t targetTimeUs;
if (mInputBuffer->meta_data()->findInt64(kKeyTargetTime, &targetTimeUs)
&& targetTimeUs >= 0) {
mTargetTimeUs = targetTimeUs;
} else {
mTargetTimeUs = -1;
}
}
} }
const uint8_t *fragPtr; const uint8_t *fragPtr;
@@ -394,9 +414,35 @@ status_t AVCDecoder::read(
CHECK(index >= 0); CHECK(index >= 0);
CHECK(index < (int32_t)mFrames.size()); CHECK(index < (int32_t)mFrames.size());
*out = mFrames.editItemAt(index); MediaBuffer *mbuf = mFrames.editItemAt(index);
(*out)->set_range(0, (*out)->size());
(*out)->add_ref(); bool skipFrame = false;
if (mTargetTimeUs >= 0) {
int64_t timeUs;
CHECK(mbuf->meta_data()->findInt64(kKeyTime, &timeUs));
CHECK(timeUs <= mTargetTimeUs);
if (timeUs < mTargetTimeUs) {
// We're still waiting for the frame with the matching
// timestamp and we won't return the current one.
skipFrame = true;
LOGV("skipping frame at %lld us", timeUs);
} else {
LOGV("found target frame at %lld us", timeUs);
mTargetTimeUs = -1;
}
}
if (!skipFrame) {
*out = mbuf;
(*out)->set_range(0, (*out)->size());
(*out)->add_ref();
} else {
*out = new MediaBuffer(0);
}
// Do _not_ release input buffer yet. // Do _not_ release input buffer yet.

View File

@@ -37,7 +37,8 @@ M4vH263Decoder::M4vH263Decoder(const sp<MediaSource> &source)
mStarted(false), mStarted(false),
mHandle(new tagvideoDecControls), mHandle(new tagvideoDecControls),
mInputBuffer(NULL), mInputBuffer(NULL),
mNumSamplesOutput(0) { mNumSamplesOutput(0),
mTargetTimeUs(-1) {
LOGV("M4vH263Decoder"); LOGV("M4vH263Decoder");
memset(mHandle, 0, sizeof(tagvideoDecControls)); memset(mHandle, 0, sizeof(tagvideoDecControls));
@@ -146,6 +147,7 @@ status_t M4vH263Decoder::start(MetaData *) {
mSource->start(); mSource->start();
mNumSamplesOutput = 0; mNumSamplesOutput = 0;
mTargetTimeUs = -1;
mStarted = true; mStarted = true;
return OK; return OK;
@@ -175,8 +177,11 @@ status_t M4vH263Decoder::read(
MediaBuffer **out, const ReadOptions *options) { MediaBuffer **out, const ReadOptions *options) {
*out = NULL; *out = NULL;
bool seeking = false;
int64_t seekTimeUs; int64_t seekTimeUs;
if (options && options->getSeekTo(&seekTimeUs)) { ReadOptions::SeekMode mode;
if (options && options->getSeekTo(&seekTimeUs, &mode)) {
seeking = true;
CHECK_EQ(PVResetVideoDecoder(mHandle), PV_TRUE); CHECK_EQ(PVResetVideoDecoder(mHandle), PV_TRUE);
} }
@@ -186,6 +191,16 @@ status_t M4vH263Decoder::read(
return err; return err;
} }
if (seeking) {
int64_t targetTimeUs;
if (inputBuffer->meta_data()->findInt64(kKeyTargetTime, &targetTimeUs)
&& targetTimeUs >= 0) {
mTargetTimeUs = targetTimeUs;
} else {
mTargetTimeUs = -1;
}
}
uint8_t *bitstream = uint8_t *bitstream =
(uint8_t *) inputBuffer->data() + inputBuffer->range_offset(); (uint8_t *) inputBuffer->data() + inputBuffer->range_offset();
@@ -221,17 +236,40 @@ status_t M4vH263Decoder::read(
return INFO_FORMAT_CHANGED; return INFO_FORMAT_CHANGED;
} }
*out = mFrames[mNumSamplesOutput & 0x01];
(*out)->add_ref();
int64_t timeUs; int64_t timeUs;
CHECK(inputBuffer->meta_data()->findInt64(kKeyTime, &timeUs)); CHECK(inputBuffer->meta_data()->findInt64(kKeyTime, &timeUs));
(*out)->meta_data()->setInt64(kKeyTime, timeUs);
++mNumSamplesOutput;
inputBuffer->release(); inputBuffer->release();
inputBuffer = NULL; inputBuffer = NULL;
bool skipFrame = false;
if (mTargetTimeUs >= 0) {
CHECK(timeUs <= mTargetTimeUs);
if (timeUs < mTargetTimeUs) {
// We're still waiting for the frame with the matching
// timestamp and we won't return the current one.
skipFrame = true;
LOGV("skipping frame at %lld us", timeUs);
} else {
LOGV("found target frame at %lld us", timeUs);
mTargetTimeUs = -1;
}
}
if (skipFrame) {
*out = new MediaBuffer(0);
} else {
*out = mFrames[mNumSamplesOutput & 0x01];
(*out)->add_ref();
(*out)->meta_data()->setInt64(kKeyTime, timeUs);
}
++mNumSamplesOutput;
return OK; return OK;
} }

View File

@@ -122,7 +122,8 @@ status_t MP3Decoder::read(
*out = NULL; *out = NULL;
int64_t seekTimeUs; int64_t seekTimeUs;
if (options && options->getSeekTo(&seekTimeUs)) { ReadOptions::SeekMode mode;
if (options && options->getSeekTo(&seekTimeUs, &mode)) {
CHECK(seekTimeUs >= 0); CHECK(seekTimeUs >= 0);
mNumFramesOutput = 0; mNumFramesOutput = 0;

View File

@@ -39,7 +39,8 @@ VPXDecoder::VPXDecoder(const sp<MediaSource> &source)
mStarted(false), mStarted(false),
mBufferSize(0), mBufferSize(0),
mCtx(NULL), mCtx(NULL),
mBufferGroup(NULL) { mBufferGroup(NULL),
mTargetTimeUs(-1) {
sp<MetaData> inputFormat = source->getFormat(); sp<MetaData> inputFormat = source->getFormat();
const char *mime; const char *mime;
CHECK(inputFormat->findCString(kKeyMIMEType, &mime)); CHECK(inputFormat->findCString(kKeyMIMEType, &mime));
@@ -94,6 +95,8 @@ status_t VPXDecoder::start(MetaData *) {
mBufferGroup->add_buffer(new MediaBuffer(mBufferSize)); mBufferGroup->add_buffer(new MediaBuffer(mBufferSize));
mBufferGroup->add_buffer(new MediaBuffer(mBufferSize)); mBufferGroup->add_buffer(new MediaBuffer(mBufferSize));
mTargetTimeUs = -1;
mStarted = true; mStarted = true;
return OK; return OK;
@@ -126,6 +129,13 @@ status_t VPXDecoder::read(
MediaBuffer **out, const ReadOptions *options) { MediaBuffer **out, const ReadOptions *options) {
*out = NULL; *out = NULL;
bool seeking = false;
int64_t seekTimeUs;
ReadOptions::SeekMode seekMode;
if (options && options->getSeekTo(&seekTimeUs, &seekMode)) {
seeking = true;
}
MediaBuffer *input; MediaBuffer *input;
status_t err = mSource->read(&input, options); status_t err = mSource->read(&input, options);
@@ -135,6 +145,16 @@ status_t VPXDecoder::read(
LOGV("read %d bytes from source\n", input->range_length()); LOGV("read %d bytes from source\n", input->range_length());
if (seeking) {
int64_t targetTimeUs;
if (input->meta_data()->findInt64(kKeyTargetTime, &targetTimeUs)
&& targetTimeUs >= 0) {
mTargetTimeUs = targetTimeUs;
} else {
mTargetTimeUs = -1;
}
}
if (vpx_codec_decode( if (vpx_codec_decode(
(vpx_codec_ctx_t *)mCtx, (vpx_codec_ctx_t *)mCtx,
(uint8_t *)input->data() + input->range_offset(), (uint8_t *)input->data() + input->range_offset(),
@@ -156,6 +176,29 @@ status_t VPXDecoder::read(
input->release(); input->release();
input = NULL; input = NULL;
bool skipFrame = false;
if (mTargetTimeUs >= 0) {
CHECK(timeUs <= mTargetTimeUs);
if (timeUs < mTargetTimeUs) {
// We're still waiting for the frame with the matching
// timestamp and we won't return the current one.
skipFrame = true;
LOGV("skipping frame at %lld us", timeUs);
} else {
LOGV("found target frame at %lld us", timeUs);
mTargetTimeUs = -1;
}
}
if (skipFrame) {
*out = new MediaBuffer(0);
return OK;
}
vpx_codec_iter_t iter = NULL; vpx_codec_iter_t iter = NULL;
vpx_image_t *img = vpx_codec_get_frame((vpx_codec_ctx_t *)mCtx, &iter); vpx_image_t *img = vpx_codec_get_frame((vpx_codec_ctx_t *)mCtx, &iter);

View File

@@ -200,7 +200,8 @@ status_t VorbisDecoder::read(
*out = NULL; *out = NULL;
int64_t seekTimeUs; int64_t seekTimeUs;
if (options && options->getSeekTo(&seekTimeUs)) { ReadOptions::SeekMode mode;
if (options && options->getSeekTo(&seekTimeUs, &mode)) {
CHECK(seekTimeUs >= 0); CHECK(seekTimeUs >= 0);
mNumFramesOutput = 0; mNumFramesOutput = 0;

View File

@@ -58,6 +58,9 @@ private:
int64_t mAnchorTimeUs; int64_t mAnchorTimeUs;
int64_t mNumSamplesOutput; int64_t mNumSamplesOutput;
int64_t mPendingSeekTimeUs; int64_t mPendingSeekTimeUs;
MediaSource::ReadOptions::SeekMode mPendingSeekMode;
int64_t mTargetTimeUs;
void addCodecSpecificData(const uint8_t *data, size_t size); void addCodecSpecificData(const uint8_t *data, size_t size);

View File

@@ -54,6 +54,7 @@ private:
MediaBuffer *mInputBuffer; MediaBuffer *mInputBuffer;
int64_t mNumSamplesOutput; int64_t mNumSamplesOutput;
int64_t mTargetTimeUs;
void allocateFrames(int32_t width, int32_t height); void allocateFrames(int32_t width, int32_t height);
void releaseFrames(); void releaseFrames();

View File

@@ -63,11 +63,17 @@ public:
uint32_t *decodingTime); uint32_t *decodingTime);
enum { enum {
kSyncSample_Flag = 1 kFlagBefore,
kFlagAfter,
kFlagClosest
}; };
status_t findClosestSample( status_t findSampleAtTime(
uint32_t req_time, uint32_t *sample_index, uint32_t flags); uint32_t req_time, uint32_t *sample_index, uint32_t flags);
status_t findSyncSampleNear(
uint32_t start_sample_index, uint32_t *sample_index,
uint32_t flags);
status_t findThumbnailSample(uint32_t *sample_index); status_t findThumbnailSample(uint32_t *sample_index);
protected: protected:
@@ -111,9 +117,6 @@ private:
friend struct SampleIterator; friend struct SampleIterator;
status_t findClosestSyncSample_l(
uint32_t start_sample_index, uint32_t *sample_index);
status_t getSampleSize_l(uint32_t sample_index, size_t *sample_size); status_t getSampleSize_l(uint32_t sample_index, size_t *sample_size);
SampleTable(const SampleTable &); SampleTable(const SampleTable &);

View File

@@ -48,6 +48,8 @@ private:
void *mCtx; void *mCtx;
MediaBufferGroup *mBufferGroup; MediaBufferGroup *mBufferGroup;
int64_t mTargetTimeUs;
sp<MetaData> mFormat; sp<MetaData> mFormat;
VPXDecoder(const VPXDecoder &); VPXDecoder(const VPXDecoder &);

View File

@@ -281,7 +281,8 @@ status_t MatroskaSource::read(
*out = NULL; *out = NULL;
int64_t seekTimeUs; int64_t seekTimeUs;
if (options && options->getSeekTo(&seekTimeUs)) { ReadOptions::SeekMode mode;
if (options && options->getSeekTo(&seekTimeUs, &mode)) {
mBlockIter.seek(seekTimeUs); mBlockIter.seek(seekTimeUs);
} }