diff options
Diffstat (limited to 'media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp')
-rw-r--r-- | media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp | 443 |
1 files changed, 301 insertions, 142 deletions
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp index f7aacdd..9f7f09a 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp @@ -21,79 +21,80 @@ #include "NuPlayerDecoderPassThrough.h" +#include "NuPlayerRenderer.h" +#include "NuPlayerSource.h" + #include <media/ICrypto.h> #include <media/stagefright/foundation/ABuffer.h> #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/foundation/AMessage.h> -#include <media/stagefright/MediaDefs.h> #include <media/stagefright/MediaErrors.h> +#include "ATSParser.h" + namespace android { +// TODO optimize buffer size for power consumption +// The offload read buffer size is 32 KB but 24 KB uses less power. +static const size_t kAggregateBufferSizeBytes = 24 * 1024; static const size_t kMaxCachedBytes = 200000; -// The buffers will contain a bit less than kAggregateBufferSizeBytes. -// So we can start off with just enough buffers to keep the cache full. -static const size_t kMaxPendingBuffers = 1 + (kMaxCachedBytes / NuPlayer::kAggregateBufferSizeBytes); NuPlayer::DecoderPassThrough::DecoderPassThrough( - const sp<AMessage> ¬ify) - : Decoder(notify), - mNotify(notify), - mBufferGeneration(0), + const sp<AMessage> ¬ify, + const sp<Source> &source, + const sp<Renderer> &renderer) + : DecoderBase(notify), + mSource(source), + mRenderer(renderer), + mSkipRenderingUntilMediaTimeUs(-1ll), + mPaused(false), mReachedEOS(true), - mPendingBuffersToFill(0), + mPendingAudioErr(OK), mPendingBuffersToDrain(0), mCachedBytes(0), mComponentName("pass through decoder") { - mDecoderLooper = new ALooper; - mDecoderLooper->setName("NuPlayerDecoderPassThrough"); - mDecoderLooper->start(false, false, ANDROID_PRIORITY_AUDIO); + ALOGW_IF(renderer == NULL, "expect a non-NULL renderer"); } NuPlayer::DecoderPassThrough::~DecoderPassThrough() { } -void NuPlayer::DecoderPassThrough::configure(const sp<AMessage> &format) { - sp<AMessage> msg = new AMessage(kWhatConfigure, id()); - msg->setMessage("format", format); - msg->post(); -} - -void NuPlayer::DecoderPassThrough::init() { - mDecoderLooper->registerHandler(this); -} - -void NuPlayer::DecoderPassThrough::signalFlush() { - (new AMessage(kWhatFlush, id()))->post(); -} - -void NuPlayer::DecoderPassThrough::signalResume() { - (new AMessage(kWhatResume, id()))->post(); -} - -void NuPlayer::DecoderPassThrough::initiateShutdown() { - (new AMessage(kWhatShutdown, id()))->post(); -} - -bool NuPlayer::DecoderPassThrough::supportsSeamlessFormatChange( - const sp<AMessage> & /* targetFormat */) const { - return true; +void NuPlayer::DecoderPassThrough::getStats( + int64_t *numFramesTotal, int64_t *numFramesDropped) const { + *numFramesTotal = 0; + *numFramesDropped = 0; } void NuPlayer::DecoderPassThrough::onConfigure(const sp<AMessage> &format) { ALOGV("[%s] onConfigure", mComponentName.c_str()); mCachedBytes = 0; - mPendingBuffersToFill = 0; mPendingBuffersToDrain = 0; mReachedEOS = false; ++mBufferGeneration; - requestMaxBuffers(); + onRequestInputBuffers(); - sp<AMessage> notify = mNotify->dup(); - notify->setInt32("what", kWhatOutputFormatChanged); - notify->setMessage("format", format); - notify->post(); + // The audio sink is already opened before the PassThrough decoder is created. + // Opening again might be relevant if decoder is instantiated after shutdown and + // format is different. + status_t err = mRenderer->openAudioSink( + format, true /* offloadOnly */, false /* hasVideo */, + AUDIO_OUTPUT_FLAG_NONE /* flags */, NULL /* isOffloaded */); + if (err != OK) { + handleError(err); + } +} + +void NuPlayer::DecoderPassThrough::onSetRenderer( + const sp<Renderer> &renderer) { + // renderer can't be changed during offloading + ALOGW_IF(renderer != mRenderer, + "ignoring request to change renderer"); +} + +void NuPlayer::DecoderPassThrough::onGetInputBuffers( + Vector<sp<ABuffer> > * /* dstBuffers */) { + ALOGE("onGetInputBuffers() called unexpectedly"); } bool NuPlayer::DecoderPassThrough::isStaleReply(const sp<AMessage> &msg) { @@ -102,100 +103,301 @@ bool NuPlayer::DecoderPassThrough::isStaleReply(const sp<AMessage> &msg) { return generation != mBufferGeneration; } -bool NuPlayer::DecoderPassThrough::requestABuffer() { - if (mCachedBytes >= kMaxCachedBytes) { - ALOGV("[%s] mCachedBytes = %zu", - mComponentName.c_str(), mCachedBytes); - return false; +bool NuPlayer::DecoderPassThrough::isDoneFetching() const { + ALOGV("[%s] mCachedBytes = %zu, mReachedEOS = %d mPaused = %d", + mComponentName.c_str(), mCachedBytes, mReachedEOS, mPaused); + + return mCachedBytes >= kMaxCachedBytes || mReachedEOS || mPaused; +} + +void NuPlayer::DecoderPassThrough::doRequestBuffers() { + status_t err = OK; + while (!isDoneFetching()) { + sp<AMessage> msg = new AMessage(); + + err = fetchInputData(msg); + if (err != OK) { + break; + } + + onInputBufferFetched(msg); } - if (mReachedEOS) { - ALOGV("[%s] reached EOS", mComponentName.c_str()); - return false; + + if (err == -EWOULDBLOCK + && mSource->feedMoreTSData() == OK) { + scheduleRequestBuffers(); } +} - sp<AMessage> reply = new AMessage(kWhatInputBufferFilled, id()); - reply->setInt32("generation", mBufferGeneration); +status_t NuPlayer::DecoderPassThrough::dequeueAccessUnit(sp<ABuffer> *accessUnit) { + status_t err; + + // Did we save an accessUnit earlier because of a discontinuity? + if (mPendingAudioAccessUnit != NULL) { + *accessUnit = mPendingAudioAccessUnit; + mPendingAudioAccessUnit.clear(); + err = mPendingAudioErr; + ALOGV("feedDecoderInputData() use mPendingAudioAccessUnit"); + } else { + err = mSource->dequeueAccessUnit(true /* audio */, accessUnit); + } - sp<AMessage> notify = mNotify->dup(); - notify->setInt32("what", kWhatFillThisBuffer); - notify->setMessage("reply", reply); - notify->post(); - mPendingBuffersToFill++; - ALOGV("requestABuffer: #ToFill = %zu, #ToDrain = %zu", mPendingBuffersToFill, - mPendingBuffersToDrain); + if (err == INFO_DISCONTINUITY || err == ERROR_END_OF_STREAM) { + if (mAggregateBuffer != NULL) { + // We already have some data so save this for later. + mPendingAudioErr = err; + mPendingAudioAccessUnit = *accessUnit; + (*accessUnit).clear(); + ALOGD("return aggregated buffer and save err(=%d) for later", err); + err = OK; + } + } - return true; + return err; } -void android::NuPlayer::DecoderPassThrough::onInputBufferFilled( +sp<ABuffer> NuPlayer::DecoderPassThrough::aggregateBuffer( + const sp<ABuffer> &accessUnit) { + sp<ABuffer> aggregate; + + if (accessUnit == NULL) { + // accessUnit is saved to mPendingAudioAccessUnit + // return current mAggregateBuffer + aggregate = mAggregateBuffer; + mAggregateBuffer.clear(); + return aggregate; + } + + size_t smallSize = accessUnit->size(); + if ((mAggregateBuffer == NULL) + // Don't bother if only room for a few small buffers. + && (smallSize < (kAggregateBufferSizeBytes / 3))) { + // Create a larger buffer for combining smaller buffers from the extractor. + mAggregateBuffer = new ABuffer(kAggregateBufferSizeBytes); + mAggregateBuffer->setRange(0, 0); // start empty + } + + if (mAggregateBuffer != NULL) { + int64_t timeUs; + int64_t dummy; + bool smallTimestampValid = accessUnit->meta()->findInt64("timeUs", &timeUs); + bool bigTimestampValid = mAggregateBuffer->meta()->findInt64("timeUs", &dummy); + // Will the smaller buffer fit? + size_t bigSize = mAggregateBuffer->size(); + size_t roomLeft = mAggregateBuffer->capacity() - bigSize; + // Should we save this small buffer for the next big buffer? + // If the first small buffer did not have a timestamp then save + // any buffer that does have a timestamp until the next big buffer. + if ((smallSize > roomLeft) + || (!bigTimestampValid && (bigSize > 0) && smallTimestampValid)) { + mPendingAudioErr = OK; + mPendingAudioAccessUnit = accessUnit; + aggregate = mAggregateBuffer; + mAggregateBuffer.clear(); + } else { + // Grab time from first small buffer if available. + if ((bigSize == 0) && smallTimestampValid) { + mAggregateBuffer->meta()->setInt64("timeUs", timeUs); + } + // Append small buffer to the bigger buffer. + memcpy(mAggregateBuffer->base() + bigSize, accessUnit->data(), smallSize); + bigSize += smallSize; + mAggregateBuffer->setRange(0, bigSize); + + ALOGV("feedDecoderInputData() smallSize = %zu, bigSize = %zu, capacity = %zu", + smallSize, bigSize, mAggregateBuffer->capacity()); + } + } else { + // decided not to aggregate + aggregate = accessUnit; + } + + return aggregate; +} + +status_t NuPlayer::DecoderPassThrough::fetchInputData(sp<AMessage> &reply) { + sp<ABuffer> accessUnit; + + do { + status_t err = dequeueAccessUnit(&accessUnit); + + if (err == -EWOULDBLOCK) { + return err; + } else if (err != OK) { + if (err == INFO_DISCONTINUITY) { + int32_t type; + CHECK(accessUnit->meta()->findInt32("discontinuity", &type)); + + bool formatChange = + (type & ATSParser::DISCONTINUITY_AUDIO_FORMAT) != 0; + + bool timeChange = + (type & ATSParser::DISCONTINUITY_TIME) != 0; + + ALOGI("audio discontinuity (formatChange=%d, time=%d)", + formatChange, timeChange); + + if (formatChange || timeChange) { + sp<AMessage> msg = mNotify->dup(); + msg->setInt32("what", kWhatInputDiscontinuity); + // will perform seamless format change, + // only notify NuPlayer to scan sources + msg->setInt32("formatChange", false); + msg->post(); + } + + if (timeChange) { + onFlush(false /* notifyComplete */); + err = OK; + } else if (formatChange) { + // do seamless format change + err = OK; + } else { + // This stream is unaffected by the discontinuity + return -EWOULDBLOCK; + } + } + + reply->setInt32("err", err); + return OK; + } + + accessUnit = aggregateBuffer(accessUnit); + } while (accessUnit == NULL); + +#if 0 + int64_t mediaTimeUs; + CHECK(accessUnit->meta()->findInt64("timeUs", &mediaTimeUs)); + ALOGV("feeding audio input buffer at media time %.2f secs", + mediaTimeUs / 1E6); +#endif + + reply->setBuffer("buffer", accessUnit); + + return OK; +} + +void NuPlayer::DecoderPassThrough::onInputBufferFetched( const sp<AMessage> &msg) { - --mPendingBuffersToFill; if (mReachedEOS) { return; } sp<ABuffer> buffer; - msg->findBuffer("buffer", &buffer); + bool hasBuffer = msg->findBuffer("buffer", &buffer); if (buffer == NULL) { - mReachedEOS = true; + int32_t streamErr = ERROR_END_OF_STREAM; + CHECK(msg->findInt32("err", &streamErr) || !hasBuffer); + if (streamErr == OK) { + return; + } - sp<AMessage> notify = mNotify->dup(); - notify->setInt32("what", kWhatEOS); - notify->setInt32("err", ERROR_END_OF_STREAM); - notify->post(); + mReachedEOS = true; + if (mRenderer != NULL) { + mRenderer->queueEOS(true /* audio */, ERROR_END_OF_STREAM); + } return; } - mCachedBytes += buffer->size(); + sp<AMessage> extra; + if (buffer->meta()->findMessage("extra", &extra) && extra != NULL) { + int64_t resumeAtMediaTimeUs; + if (extra->findInt64( + "resume-at-mediatimeUs", &resumeAtMediaTimeUs)) { + ALOGI("[%s] suppressing rendering until %lld us", + mComponentName.c_str(), (long long)resumeAtMediaTimeUs); + mSkipRenderingUntilMediaTimeUs = resumeAtMediaTimeUs; + } + } + + int32_t bufferSize = buffer->size(); + mCachedBytes += bufferSize; + + if (mSkipRenderingUntilMediaTimeUs >= 0) { + int64_t timeUs = 0; + CHECK(buffer->meta()->findInt64("timeUs", &timeUs)); + + if (timeUs < mSkipRenderingUntilMediaTimeUs) { + ALOGV("[%s] dropping buffer at time %lld as requested.", + mComponentName.c_str(), (long long)timeUs); + + onBufferConsumed(bufferSize); + return; + } + + mSkipRenderingUntilMediaTimeUs = -1; + } + + if (mRenderer == NULL) { + onBufferConsumed(bufferSize); + return; + } sp<AMessage> reply = new AMessage(kWhatBufferConsumed, id()); reply->setInt32("generation", mBufferGeneration); - reply->setInt32("size", buffer->size()); + reply->setInt32("size", bufferSize); + + mRenderer->queueBuffer(true /* audio */, buffer, reply); - sp<AMessage> notify = mNotify->dup(); - notify->setInt32("what", kWhatDrainThisBuffer); - notify->setBuffer("buffer", buffer); - notify->setMessage("reply", reply); - notify->post(); ++mPendingBuffersToDrain; - ALOGV("onInputBufferFilled: #ToFill = %zu, #ToDrain = %zu, cachedBytes = %zu", - mPendingBuffersToFill, mPendingBuffersToDrain, mCachedBytes); + ALOGV("onInputBufferFilled: #ToDrain = %zu, cachedBytes = %zu", + mPendingBuffersToDrain, mCachedBytes); } void NuPlayer::DecoderPassThrough::onBufferConsumed(int32_t size) { --mPendingBuffersToDrain; mCachedBytes -= size; - ALOGV("onBufferConsumed: #ToFill = %zu, #ToDrain = %zu, cachedBytes = %zu", - mPendingBuffersToFill, mPendingBuffersToDrain, mCachedBytes); - requestABuffer(); + ALOGV("onBufferConsumed: #ToDrain = %zu, cachedBytes = %zu", + mPendingBuffersToDrain, mCachedBytes); + onRequestInputBuffers(); +} + +void NuPlayer::DecoderPassThrough::onResume(bool notifyComplete) { + mPaused = false; + + onRequestInputBuffers(); + + if (notifyComplete) { + sp<AMessage> notify = mNotify->dup(); + notify->setInt32("what", kWhatResumeCompleted); + notify->post(); + } } -void NuPlayer::DecoderPassThrough::onFlush() { +void NuPlayer::DecoderPassThrough::onFlush(bool notifyComplete) { ++mBufferGeneration; + mSkipRenderingUntilMediaTimeUs = -1; + mPendingAudioAccessUnit.clear(); + mPendingAudioErr = OK; + mAggregateBuffer.clear(); + + if (mRenderer != NULL) { + mRenderer->flush(true /* audio */, notifyComplete); + mRenderer->signalTimeDiscontinuity(); + } + + if (notifyComplete) { + mPaused = true; + sp<AMessage> notify = mNotify->dup(); + notify->setInt32("what", kWhatFlushCompleted); + notify->post(); + } - sp<AMessage> notify = mNotify->dup(); - notify->setInt32("what", kWhatFlushCompleted); - notify->post(); - mPendingBuffersToFill = 0; mPendingBuffersToDrain = 0; mCachedBytes = 0; mReachedEOS = false; } -void NuPlayer::DecoderPassThrough::requestMaxBuffers() { - for (size_t i = 0; i < kMaxPendingBuffers; i++) { - if (!requestABuffer()) { - break; - } - } -} - -void NuPlayer::DecoderPassThrough::onShutdown() { +void NuPlayer::DecoderPassThrough::onShutdown(bool notifyComplete) { ++mBufferGeneration; + mSkipRenderingUntilMediaTimeUs = -1; + + if (notifyComplete) { + sp<AMessage> notify = mNotify->dup(); + notify->setInt32("what", kWhatShutdownCompleted); + notify->post(); + } - sp<AMessage> notify = mNotify->dup(); - notify->setInt32("what", kWhatShutdownCompleted); - notify->post(); mReachedEOS = true; } @@ -204,31 +406,6 @@ void NuPlayer::DecoderPassThrough::onMessageReceived(const sp<AMessage> &msg) { msg->debugString().c_str()); switch (msg->what()) { - case kWhatConfigure: - { - sp<AMessage> format; - CHECK(msg->findMessage("format", &format)); - onConfigure(format); - break; - } - - case kWhatRequestABuffer: - { - if (!isStaleReply(msg)) { - requestABuffer(); - } - - break; - } - - case kWhatInputBufferFilled: - { - if (!isStaleReply(msg)) { - onInputBufferFilled(msg); - } - break; - } - case kWhatBufferConsumed: { if (!isStaleReply(msg)) { @@ -239,26 +416,8 @@ void NuPlayer::DecoderPassThrough::onMessageReceived(const sp<AMessage> &msg) { break; } - case kWhatFlush: - { - onFlush(); - break; - } - - case kWhatResume: - { - requestMaxBuffers(); - break; - } - - case kWhatShutdown: - { - onShutdown(); - break; - } - default: - TRESPASS(); + DecoderBase::onMessageReceived(msg); break; } } |