summaryrefslogtreecommitdiffstats
path: root/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp')
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp388
1 files changed, 221 insertions, 167 deletions
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index 5d98d98..d521c64 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -56,6 +56,7 @@ NuPlayer::Decoder::Decoder(
mIsVideoAVC(false),
mIsSecure(false),
mFormatChangePending(false),
+ mTimeChangePending(false),
mPaused(true),
mResumePending(false),
mComponentName("decoder") {
@@ -81,25 +82,69 @@ void NuPlayer::Decoder::onMessageReceived(const sp<AMessage> &msg) {
switch (msg->what()) {
case kWhatCodecNotify:
{
- if (!isStaleReply(msg)) {
- int32_t numInput, numOutput;
+ if (mPaused) {
+ break;
+ }
+
+ int32_t cbID;
+ CHECK(msg->findInt32("callbackID", &cbID));
+
+ ALOGV("kWhatCodecNotify: cbID = %d", cbID);
+ switch (cbID) {
+ case MediaCodec::CB_INPUT_AVAILABLE:
+ {
+ int32_t index;
+ CHECK(msg->findInt32("index", &index));
- if (!msg->findInt32("input-buffers", &numInput)) {
- numInput = INT32_MAX;
+ handleAnInputBuffer(index);
+ break;
}
- if (!msg->findInt32("output-buffers", &numOutput)) {
- numOutput = INT32_MAX;
+ case MediaCodec::CB_OUTPUT_AVAILABLE:
+ {
+ int32_t index;
+ size_t offset;
+ size_t size;
+ int64_t timeUs;
+ int32_t flags;
+
+ CHECK(msg->findInt32("index", &index));
+ CHECK(msg->findSize("offset", &offset));
+ CHECK(msg->findSize("size", &size));
+ CHECK(msg->findInt64("timeUs", &timeUs));
+ CHECK(msg->findInt32("flags", &flags));
+
+ handleAnOutputBuffer(index, offset, size, timeUs, flags);
+ break;
}
- if (!mPaused) {
- while (numInput-- > 0 && handleAnInputBuffer()) {}
+ case MediaCodec::CB_OUTPUT_FORMAT_CHANGED:
+ {
+ sp<AMessage> format;
+ CHECK(msg->findMessage("format", &format));
+
+ handleOutputFormatChange(format);
+ break;
+ }
+
+ case MediaCodec::CB_ERROR:
+ {
+ status_t err;
+ CHECK(msg->findInt32("err", &err));
+ ALOGE("Decoder (%s) reported error : 0x%x",
+ mIsAudio ? "audio" : "video", err);
+
+ handleError(err);
+ break;
}
- while (numOutput-- > 0 && handleAnOutputBuffer()) {}
+ default:
+ {
+ TRESPASS();
+ break;
+ }
}
- requestCodecNotification();
break;
}
@@ -121,6 +166,7 @@ void NuPlayer::Decoder::onConfigure(const sp<AMessage> &format) {
CHECK(mCodec == NULL);
mFormatChangePending = false;
+ mTimeChangePending = false;
++mBufferGeneration;
@@ -186,6 +232,9 @@ void NuPlayer::Decoder::onConfigure(const sp<AMessage> &format) {
CHECK_EQ((status_t)OK, mCodec->getOutputFormat(&mOutputFormat));
CHECK_EQ((status_t)OK, mCodec->getInputFormat(&mInputFormat));
+ sp<AMessage> reply = new AMessage(kWhatCodecNotify, this);
+ mCodec->setCallback(reply);
+
err = mCodec->start();
if (err != OK) {
ALOGE("Failed to start %s decoder (err=%d)", mComponentName.c_str(), err);
@@ -195,18 +244,8 @@ void NuPlayer::Decoder::onConfigure(const sp<AMessage> &format) {
return;
}
- // the following should work after start
- CHECK_EQ((status_t)OK, mCodec->getInputBuffers(&mInputBuffers));
releaseAndResetMediaBuffers();
- CHECK_EQ((status_t)OK, mCodec->getOutputBuffers(&mOutputBuffers));
- ALOGV("[%s] got %zu input and %zu output buffers",
- mComponentName.c_str(),
- mInputBuffers.size(),
- mOutputBuffers.size());
- if (mRenderer != NULL) {
- requestCodecNotification();
- }
mPaused = false;
mResumePending = false;
}
@@ -215,16 +254,14 @@ void NuPlayer::Decoder::onSetRenderer(const sp<Renderer> &renderer) {
bool hadNoRenderer = (mRenderer == NULL);
mRenderer = renderer;
if (hadNoRenderer && mRenderer != NULL) {
- requestCodecNotification();
+ // this means that the widevine legacy source is ready
+ onRequestInputBuffers();
}
}
void NuPlayer::Decoder::onGetInputBuffers(
Vector<sp<ABuffer> > *dstBuffers) {
- dstBuffers->clear();
- for (size_t i = 0; i < mInputBuffers.size(); i++) {
- dstBuffers->push(mInputBuffers[i]);
- }
+ CHECK_EQ((status_t)OK, mCodec->getWidevineLegacyBuffers(dstBuffers));
}
void NuPlayer::Decoder::onResume(bool notifyComplete) {
@@ -233,9 +270,10 @@ void NuPlayer::Decoder::onResume(bool notifyComplete) {
if (notifyComplete) {
mResumePending = true;
}
+ mCodec->start();
}
-void NuPlayer::Decoder::onFlush(bool notifyComplete) {
+void NuPlayer::Decoder::doFlush(bool notifyComplete) {
if (mCCDecoder != NULL) {
mCCDecoder->flush();
}
@@ -259,13 +297,23 @@ void NuPlayer::Decoder::onFlush(bool notifyComplete) {
// we attempt to release the buffers even if flush fails.
}
releaseAndResetMediaBuffers();
+ mPaused = true;
+}
- if (notifyComplete) {
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", kWhatFlushCompleted);
- notify->post();
- mPaused = true;
+
+void NuPlayer::Decoder::onFlush() {
+ doFlush(true);
+
+ if (isDiscontinuityPending()) {
+ // This could happen if the client starts seeking/shutdown
+ // after we queued an EOS for discontinuities.
+ // We can consider discontinuity handled.
+ finishHandleDiscontinuity(false /* flushOnTimeChange */);
}
+
+ sp<AMessage> notify = mNotify->dup();
+ notify->setInt32("what", kWhatFlushCompleted);
+ notify->post();
}
void NuPlayer::Decoder::onShutdown(bool notifyComplete) {
@@ -309,16 +357,19 @@ void NuPlayer::Decoder::onShutdown(bool notifyComplete) {
}
void NuPlayer::Decoder::doRequestBuffers() {
- if (mFormatChangePending) {
+ // mRenderer is only NULL if we have a legacy widevine source that
+ // is not yet ready. In this case we must not fetch input.
+ if (isDiscontinuityPending() || mRenderer == NULL) {
return;
}
status_t err = OK;
- while (!mDequeuedInputBuffers.empty()) {
+ while (err == OK && !mDequeuedInputBuffers.empty()) {
size_t bufferIx = *mDequeuedInputBuffers.begin();
sp<AMessage> msg = new AMessage();
msg->setSize("buffer-ix", bufferIx);
err = fetchInputData(msg);
- if (err != OK) {
+ if (err != OK && err != ERROR_END_OF_STREAM) {
+ // if EOS, need to queue EOS buffer
break;
}
mDequeuedInputBuffers.erase(mDequeuedInputBuffers.begin());
@@ -335,34 +386,50 @@ void NuPlayer::Decoder::doRequestBuffers() {
}
}
-bool NuPlayer::Decoder::handleAnInputBuffer() {
- if (mFormatChangePending) {
+void NuPlayer::Decoder::handleError(int32_t err)
+{
+ // We cannot immediately release the codec due to buffers still outstanding
+ // in the renderer. We signal to the player the error so it can shutdown/release the
+ // decoder after flushing and increment the generation to discard unnecessary messages.
+
+ ++mBufferGeneration;
+
+ sp<AMessage> notify = mNotify->dup();
+ notify->setInt32("what", kWhatError);
+ notify->setInt32("err", err);
+ notify->post();
+}
+
+bool NuPlayer::Decoder::handleAnInputBuffer(size_t index) {
+ if (isDiscontinuityPending()) {
return false;
}
- size_t bufferIx = -1;
- status_t res = mCodec->dequeueInputBuffer(&bufferIx);
- ALOGV("[%s] dequeued input: %d",
- mComponentName.c_str(), res == OK ? (int)bufferIx : res);
- if (res != OK) {
- if (res != -EAGAIN) {
- ALOGE("Failed to dequeue input buffer for %s (err=%d)",
- mComponentName.c_str(), res);
- handleError(res);
+
+ sp<ABuffer> buffer;
+ mCodec->getInputBuffer(index, &buffer);
+
+ if (index >= mInputBuffers.size()) {
+ for (size_t i = mInputBuffers.size(); i <= index; ++i) {
+ mInputBuffers.add();
+ mMediaBuffers.add();
+ mInputBufferIsDequeued.add();
+ mMediaBuffers.editItemAt(i) = NULL;
+ mInputBufferIsDequeued.editItemAt(i) = false;
}
- return false;
}
+ mInputBuffers.editItemAt(index) = buffer;
- CHECK_LT(bufferIx, mInputBuffers.size());
+ //CHECK_LT(bufferIx, mInputBuffers.size());
- if (mMediaBuffers[bufferIx] != NULL) {
- mMediaBuffers[bufferIx]->release();
- mMediaBuffers.editItemAt(bufferIx) = NULL;
+ if (mMediaBuffers[index] != NULL) {
+ mMediaBuffers[index]->release();
+ mMediaBuffers.editItemAt(index) = NULL;
}
- mInputBufferIsDequeued.editItemAt(bufferIx) = true;
+ mInputBufferIsDequeued.editItemAt(index) = true;
if (!mCSDsToSubmit.isEmpty()) {
sp<AMessage> msg = new AMessage();
- msg->setSize("buffer-ix", bufferIx);
+ msg->setSize("buffer-ix", index);
sp<ABuffer> buffer = mCSDsToSubmit.itemAt(0);
ALOGI("[%s] resubmitting CSD", mComponentName.c_str());
@@ -380,111 +447,51 @@ bool NuPlayer::Decoder::handleAnInputBuffer() {
mPendingInputMessages.erase(mPendingInputMessages.begin());
}
- if (!mInputBufferIsDequeued.editItemAt(bufferIx)) {
+ if (!mInputBufferIsDequeued.editItemAt(index)) {
return true;
}
- mDequeuedInputBuffers.push_back(bufferIx);
+ mDequeuedInputBuffers.push_back(index);
onRequestInputBuffers();
return true;
}
-bool NuPlayer::Decoder::handleAnOutputBuffer() {
- if (mFormatChangePending) {
- return false;
- }
- size_t bufferIx = -1;
- size_t offset;
- size_t size;
- int64_t timeUs;
- uint32_t flags;
- status_t res = mCodec->dequeueOutputBuffer(
- &bufferIx, &offset, &size, &timeUs, &flags);
-
- if (res != OK) {
- ALOGV("[%s] dequeued output: %d", mComponentName.c_str(), res);
- } else {
- ALOGV("[%s] dequeued output: %d (time=%lld flags=%" PRIu32 ")",
- mComponentName.c_str(), (int)bufferIx, timeUs, flags);
- }
-
- if (res == INFO_OUTPUT_BUFFERS_CHANGED) {
- res = mCodec->getOutputBuffers(&mOutputBuffers);
- if (res != OK) {
- ALOGE("Failed to get output buffers for %s after INFO event (err=%d)",
- mComponentName.c_str(), res);
- handleError(res);
- return false;
- }
- // NuPlayer ignores this
- return true;
- } else if (res == INFO_FORMAT_CHANGED) {
- sp<AMessage> format = new AMessage();
- res = mCodec->getOutputFormat(&format);
- if (res != OK) {
- ALOGE("Failed to get output format for %s after INFO event (err=%d)",
- mComponentName.c_str(), res);
- handleError(res);
- return false;
- }
-
- if (!mIsAudio) {
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", kWhatVideoSizeChanged);
- notify->setMessage("format", format);
- notify->post();
- } else if (mRenderer != NULL) {
- uint32_t flags;
- int64_t durationUs;
- bool hasVideo = (mSource->getFormat(false /* audio */) != NULL);
- if (!hasVideo &&
- mSource->getDuration(&durationUs) == OK &&
- durationUs
- > AUDIO_SINK_MIN_DEEP_BUFFER_DURATION_US) {
- flags = AUDIO_OUTPUT_FLAG_DEEP_BUFFER;
- } else {
- flags = AUDIO_OUTPUT_FLAG_NONE;
- }
+bool NuPlayer::Decoder::handleAnOutputBuffer(
+ size_t index,
+ size_t offset,
+ size_t size,
+ int64_t timeUs,
+ int32_t flags) {
+// CHECK_LT(bufferIx, mOutputBuffers.size());
+ sp<ABuffer> buffer;
+ mCodec->getOutputBuffer(index, &buffer);
- res = mRenderer->openAudioSink(
- format, false /* offloadOnly */, hasVideo, flags, NULL /* isOffloaded */);
- if (res != OK) {
- ALOGE("Failed to open AudioSink on format change for %s (err=%d)",
- mComponentName.c_str(), res);
- handleError(res);
- return false;
- }
+ if (index >= mOutputBuffers.size()) {
+ for (size_t i = mOutputBuffers.size(); i <= index; ++i) {
+ mOutputBuffers.add();
}
- return true;
- } else if (res == INFO_DISCONTINUITY) {
- // nothing to do
- return true;
- } else if (res != OK) {
- if (res != -EAGAIN) {
- ALOGE("Failed to dequeue output buffer for %s (err=%d)",
- mComponentName.c_str(), res);
- handleError(res);
- }
- return false;
}
- CHECK_LT(bufferIx, mOutputBuffers.size());
- sp<ABuffer> buffer = mOutputBuffers[bufferIx];
+ mOutputBuffers.editItemAt(index) = buffer;
+
buffer->setRange(offset, size);
buffer->meta()->clear();
buffer->meta()->setInt64("timeUs", timeUs);
- if (flags & MediaCodec::BUFFER_FLAG_EOS) {
- buffer->meta()->setInt32("eos", true);
- notifyResumeCompleteIfNecessary();
- }
+
+ bool eos = flags & MediaCodec::BUFFER_FLAG_EOS;
// we do not expect CODECCONFIG or SYNCFRAME for decoder
- sp<AMessage> reply = new AMessage(kWhatRenderBuffer, id());
- reply->setSize("buffer-ix", bufferIx);
+ sp<AMessage> reply = new AMessage(kWhatRenderBuffer, this);
+ reply->setSize("buffer-ix", index);
reply->setInt32("generation", mBufferGeneration);
- if (mSkipRenderingUntilMediaTimeUs >= 0) {
+ if (eos) {
+ ALOGI("[%s] saw output EOS", mIsAudio ? "audio" : "video");
+
+ buffer->meta()->setInt32("eos", true);
+ reply->setInt32("eos", true);
+ } else if (mSkipRenderingUntilMediaTimeUs >= 0) {
if (timeUs < mSkipRenderingUntilMediaTimeUs) {
ALOGV("[%s] dropping buffer at time %lld as requested.",
mComponentName.c_str(), (long long)timeUs);
@@ -502,7 +509,7 @@ bool NuPlayer::Decoder::handleAnOutputBuffer() {
if (mRenderer != NULL) {
// send the buffer to renderer.
mRenderer->queueBuffer(mIsAudio, buffer, reply);
- if (flags & MediaCodec::BUFFER_FLAG_EOS) {
+ if (eos && !isDiscontinuityPending()) {
mRenderer->queueEOS(mIsAudio, ERROR_END_OF_STREAM);
}
}
@@ -510,6 +517,29 @@ bool NuPlayer::Decoder::handleAnOutputBuffer() {
return true;
}
+void NuPlayer::Decoder::handleOutputFormatChange(const sp<AMessage> &format) {
+ if (!mIsAudio) {
+ sp<AMessage> notify = mNotify->dup();
+ notify->setInt32("what", kWhatVideoSizeChanged);
+ notify->setMessage("format", format);
+ notify->post();
+ } else if (mRenderer != NULL) {
+ uint32_t flags;
+ int64_t durationUs;
+ bool hasVideo = (mSource->getFormat(false /* audio */) != NULL);
+ if (!hasVideo &&
+ mSource->getDuration(&durationUs) == OK &&
+ durationUs > AUDIO_SINK_MIN_DEEP_BUFFER_DURATION_US) {
+ flags = AUDIO_OUTPUT_FLAG_DEEP_BUFFER;
+ } else {
+ flags = AUDIO_OUTPUT_FLAG_NONE;
+ }
+
+ mRenderer->openAudioSink(
+ format, false /* offloadOnly */, hasVideo, flags, NULL /* isOffloaed */);
+ }
+}
+
void NuPlayer::Decoder::releaseAndResetMediaBuffers() {
for (size_t i = 0; i < mMediaBuffers.size(); i++) {
if (mMediaBuffers[i] != NULL) {
@@ -533,11 +563,8 @@ void NuPlayer::Decoder::releaseAndResetMediaBuffers() {
}
void NuPlayer::Decoder::requestCodecNotification() {
- if (mFormatChangePending) {
- return;
- }
if (mCodec != NULL) {
- sp<AMessage> reply = new AMessage(kWhatCodecNotify, id());
+ sp<AMessage> reply = new AMessage(kWhatCodecNotify, this);
reply->setInt32("generation", mBufferGeneration);
mCodec->requestActivityNotification(reply);
}
@@ -582,39 +609,31 @@ status_t NuPlayer::Decoder::fetchInputData(sp<AMessage> &reply) {
formatChange = !seamlessFormatChange;
}
- if (formatChange || timeChange) {
- sp<AMessage> msg = mNotify->dup();
- msg->setInt32("what", kWhatInputDiscontinuity);
- msg->setInt32("formatChange", formatChange);
- msg->post();
- }
-
+ // For format or time change, return EOS to queue EOS input,
+ // then wait for EOS on output.
if (formatChange /* not seamless */) {
- // must change decoder
- // return EOS and wait to be killed
mFormatChangePending = true;
- return ERROR_END_OF_STREAM;
+ err = ERROR_END_OF_STREAM;
} else if (timeChange) {
- // need to flush
- // TODO: Ideally we shouldn't need a flush upon time
- // discontinuity, flushing will cause loss of frames.
- // We probably should queue a time change marker to the
- // output queue, and handles it in renderer instead.
rememberCodecSpecificData(newFormat);
- onFlush(false /* notifyComplete */);
- err = OK;
+ mTimeChangePending = true;
+ err = ERROR_END_OF_STREAM;
} else if (seamlessFormatChange) {
// reuse existing decoder and don't flush
rememberCodecSpecificData(newFormat);
- err = OK;
+ continue;
} else {
// This stream is unaffected by the discontinuity
return -EWOULDBLOCK;
}
}
+ // reply should only be returned without a buffer set
+ // when there is an error (including EOS)
+ CHECK(err != OK);
+
reply->setInt32("err", err);
- return OK;
+ return ERROR_END_OF_STREAM;
}
if (!mIsAudio) {
@@ -636,7 +655,7 @@ status_t NuPlayer::Decoder::fetchInputData(sp<AMessage> &reply) {
#if 0
int64_t mediaTimeUs;
CHECK(accessUnit->meta()->findInt64("timeUs", &mediaTimeUs));
- ALOGV("feeding %s input buffer at media time %.2f secs",
+ ALOGV("[%s] feeding input buffer at media time %" PRId64,
mIsAudio ? "audio" : "video",
mediaTimeUs / 1E6);
#endif
@@ -696,10 +715,7 @@ bool NuPlayer::Decoder::onInputBufferFetched(const sp<AMessage> &msg) {
int32_t streamErr = ERROR_END_OF_STREAM;
CHECK(msg->findInt32("err", &streamErr) || !hasBuffer);
- if (streamErr == OK) {
- /* buffers are returned to hold on to */
- return true;
- }
+ CHECK(streamErr != OK);
// attempt to queue EOS
status_t err = mCodec->queueInputBuffer(
@@ -781,6 +797,7 @@ void NuPlayer::Decoder::onRenderBuffer(const sp<AMessage> &msg) {
status_t err;
int32_t render;
size_t bufferIx;
+ int32_t eos;
CHECK(msg->findSize("buffer-ix", &bufferIx));
if (!mIsAudio) {
@@ -805,6 +822,43 @@ void NuPlayer::Decoder::onRenderBuffer(const sp<AMessage> &msg) {
mComponentName.c_str(), err);
handleError(err);
}
+ if (msg->findInt32("eos", &eos) && eos
+ && isDiscontinuityPending()) {
+ finishHandleDiscontinuity(true /* flushOnTimeChange */);
+ }
+}
+
+bool NuPlayer::Decoder::isDiscontinuityPending() const {
+ return mFormatChangePending || mTimeChangePending;
+}
+
+void NuPlayer::Decoder::finishHandleDiscontinuity(bool flushOnTimeChange) {
+ ALOGV("finishHandleDiscontinuity: format %d, time %d, flush %d",
+ mFormatChangePending, mTimeChangePending, flushOnTimeChange);
+
+ // If we have format change, pause and wait to be killed;
+ // If we have time change only, flush and restart fetching.
+
+ if (mFormatChangePending) {
+ mPaused = true;
+ } else if (mTimeChangePending) {
+ if (flushOnTimeChange) {
+ doFlush(false /* notifyComplete */);
+ signalResume(false /* notifyComplete */);
+ }
+
+ // restart fetching input
+ scheduleRequestBuffers();
+ }
+
+ // Notify NuPlayer to either shutdown decoder, or rescan sources
+ sp<AMessage> msg = mNotify->dup();
+ msg->setInt32("what", kWhatInputDiscontinuity);
+ msg->setInt32("formatChange", mFormatChangePending);
+ msg->post();
+
+ mFormatChangePending = false;
+ mTimeChangePending = false;
}
bool NuPlayer::Decoder::supportsSeamlessAudioFormatChange(