summaryrefslogtreecommitdiffstats
path: root/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
diff options
context:
space:
mode:
authorChong Zhang <chz@google.com>2014-11-12 16:41:05 -0800
committerChong Zhang <chz@google.com>2014-11-20 18:14:31 -0800
commit7137ec7e005a5a6e3c0edb91cfacf16a31f4bf6a (patch)
treedf4513073e85173034aca859ff220d8908969636 /media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
parentf4ea51fda9396d7ee851b1d4d220384210ea2336 (diff)
downloadframeworks_av-7137ec7e005a5a6e3c0edb91cfacf16a31f4bf6a.zip
frameworks_av-7137ec7e005a5a6e3c0edb91cfacf16a31f4bf6a.tar.gz
frameworks_av-7137ec7e005a5a6e3c0edb91cfacf16a31f4bf6a.tar.bz2
tunnel NuPlayer source and decoder input
Bug: 18342383 Change-Id: Ieff1cd3bad2b39d46f127ddd5d5139b919992461
Diffstat (limited to 'media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp')
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp1163
1 files changed, 434 insertions, 729 deletions
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index e695c43..0439a9a 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 The Android Open Source Project
+ * Copyright 2014 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.
@@ -19,13 +19,12 @@
#include <utils/Log.h>
#include <inttypes.h>
+#include "NuPlayerCCDecoder.h"
#include "NuPlayerDecoder.h"
-
#include "NuPlayerRenderer.h"
#include "NuPlayerSource.h"
#include <media/ICrypto.h>
-#include <media/stagefright/foundation/ABitReader.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
@@ -34,76 +33,103 @@
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
+#include "avc_utils.h"
+#include "ATSParser.h"
+
namespace android {
NuPlayer::Decoder::Decoder(
const sp<AMessage> &notify,
const sp<Source> &source,
const sp<Renderer> &renderer,
- const sp<NativeWindowWrapper> &nativeWindow)
+ const sp<NativeWindowWrapper> &nativeWindow,
+ const sp<CCDecoder> &ccDecoder)
: mNotify(notify),
mNativeWindow(nativeWindow),
mSource(source),
mRenderer(renderer),
+ mCCDecoder(ccDecoder),
mSkipRenderingUntilMediaTimeUs(-1ll),
+ mNumFramesTotal(0ll),
+ mNumFramesDropped(0ll),
+ mIsAudio(true),
+ mIsVideoAVC(false),
+ mIsSecure(false),
+ mFormatChangePending(false),
mBufferGeneration(0),
mPaused(true),
mComponentName("decoder") {
- // Every decoder has its own looper because MediaCodec operations
- // are blocking, but NuPlayer needs asynchronous operations.
- mDecoderLooper = new ALooper;
- mDecoderLooper->setName("NPDecoder");
- mDecoderLooper->start(false, false, ANDROID_PRIORITY_AUDIO);
-
mCodecLooper = new ALooper;
mCodecLooper->setName("NPDecoder-CL");
mCodecLooper->start(false, false, ANDROID_PRIORITY_AUDIO);
}
NuPlayer::Decoder::~Decoder() {
- mDecoderLooper->unregisterHandler(id());
- mDecoderLooper->stop();
-
releaseAndResetMediaBuffers();
}
-static
-status_t PostAndAwaitResponse(
- const sp<AMessage> &msg, sp<AMessage> *response) {
- status_t err = msg->postAndAwaitResponse(response);
+void NuPlayer::Decoder::getStats(
+ int64_t *numFramesTotal,
+ int64_t *numFramesDropped) const {
+ *numFramesTotal = mNumFramesTotal;
+ *numFramesDropped = mNumFramesDropped;
+}
- if (err != OK) {
- return err;
- }
+void NuPlayer::Decoder::onMessageReceived(const sp<AMessage> &msg) {
+ ALOGV("[%s] onMessage: %s", mComponentName.c_str(), msg->debugString().c_str());
- if (!(*response)->findInt32("err", &err)) {
- err = OK;
- }
+ switch (msg->what()) {
+ case kWhatCodecNotify:
+ {
+ if (!isStaleReply(msg)) {
+ int32_t numInput, numOutput;
- return err;
-}
+ if (!msg->findInt32("input-buffers", &numInput)) {
+ numInput = INT32_MAX;
+ }
-void NuPlayer::Decoder::rememberCodecSpecificData(const sp<AMessage> &format) {
- mCSDsForCurrentFormat.clear();
- for (int32_t i = 0; ; ++i) {
- AString tag = "csd-";
- tag.append(i);
- sp<ABuffer> buffer;
- if (!format->findBuffer(tag.c_str(), &buffer)) {
+ if (!msg->findInt32("output-buffers", &numOutput)) {
+ numOutput = INT32_MAX;
+ }
+
+ if (!mPaused) {
+ while (numInput-- > 0 && handleAnInputBuffer()) {}
+ }
+
+ while (numOutput-- > 0 && handleAnOutputBuffer()) {}
+ }
+
+ requestCodecNotification();
break;
}
- mCSDsForCurrentFormat.push(buffer);
+
+ case kWhatRenderBuffer:
+ {
+ if (!isStaleReply(msg)) {
+ onRenderBuffer(msg);
+ }
+ break;
+ }
+
+ default:
+ DecoderBase::onMessageReceived(msg);
+ break;
}
}
void NuPlayer::Decoder::onConfigure(const sp<AMessage> &format) {
CHECK(mCodec == NULL);
+ mFormatChangePending = false;
+
++mBufferGeneration;
AString mime;
CHECK(format->findString("mime", &mime));
+ mIsAudio = !strncasecmp("audio/", mime.c_str(), 6);
+ mIsVideoAVC = !strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime.c_str());
+
sp<Surface> surface = NULL;
if (mNativeWindow != NULL) {
surface = mNativeWindow->getSurfaceTextureClient();
@@ -131,6 +157,7 @@ void NuPlayer::Decoder::onConfigure(const sp<AMessage> &format) {
handleError(UNKNOWN_ERROR);
return;
}
+ mIsSecure = secure;
mCodec->getName(&mComponentName);
@@ -183,69 +210,120 @@ void NuPlayer::Decoder::onConfigure(const sp<AMessage> &format) {
mPaused = false;
}
-void NuPlayer::Decoder::releaseAndResetMediaBuffers() {
- for (size_t i = 0; i < mMediaBuffers.size(); i++) {
- if (mMediaBuffers[i] != NULL) {
- mMediaBuffers[i]->release();
- mMediaBuffers.editItemAt(i) = NULL;
- }
- }
- mMediaBuffers.resize(mInputBuffers.size());
- for (size_t i = 0; i < mMediaBuffers.size(); i++) {
- mMediaBuffers.editItemAt(i) = NULL;
+void NuPlayer::Decoder::onSetRenderer(const sp<Renderer> &renderer) {
+ bool hadNoRenderer = (mRenderer == NULL);
+ mRenderer = renderer;
+ if (hadNoRenderer && mRenderer != NULL) {
+ requestCodecNotification();
}
- mInputBufferIsDequeued.clear();
- mInputBufferIsDequeued.resize(mInputBuffers.size());
- for (size_t i = 0; i < mInputBufferIsDequeued.size(); i++) {
- mInputBufferIsDequeued.editItemAt(i) = false;
+}
+
+void NuPlayer::Decoder::onGetInputBuffers(
+ Vector<sp<ABuffer> > *dstBuffers) {
+ dstBuffers->clear();
+ for (size_t i = 0; i < mInputBuffers.size(); i++) {
+ dstBuffers->push(mInputBuffers[i]);
}
+}
- mPendingInputMessages.clear();
- mSkipRenderingUntilMediaTimeUs = -1;
+void NuPlayer::Decoder::onResume() {
+ mPaused = false;
}
-void NuPlayer::Decoder::requestCodecNotification() {
+void NuPlayer::Decoder::onFlush(bool notifyComplete) {
+ if (mCCDecoder != NULL) {
+ mCCDecoder->flush();
+ }
+
+ if (mRenderer != NULL) {
+ mRenderer->flush(mIsAudio, notifyComplete);
+ mRenderer->signalTimeDiscontinuity();
+ }
+
+ status_t err = OK;
if (mCodec != NULL) {
- sp<AMessage> reply = new AMessage(kWhatCodecNotify, id());
- reply->setInt32("generation", mBufferGeneration);
- mCodec->requestActivityNotification(reply);
+ err = mCodec->flush();
+ mCSDsToSubmit = mCSDsForCurrentFormat; // copy operator
+ ++mBufferGeneration;
}
-}
-bool NuPlayer::Decoder::isStaleReply(const sp<AMessage> &msg) {
- int32_t generation;
- CHECK(msg->findInt32("generation", &generation));
- return generation != mBufferGeneration;
-}
+ if (err != OK) {
+ ALOGE("failed to flush %s (err=%d)", mComponentName.c_str(), err);
+ handleError(err);
+ // finish with posting kWhatFlushCompleted.
+ // we attempt to release the buffers even if flush fails.
+ }
+ releaseAndResetMediaBuffers();
-void NuPlayer::Decoder::init() {
- mDecoderLooper->registerHandler(this);
+ if (notifyComplete) {
+ sp<AMessage> notify = mNotify->dup();
+ notify->setInt32("what", kWhatFlushCompleted);
+ notify->post();
+ mPaused = true;
+ }
}
-void NuPlayer::Decoder::configure(const sp<AMessage> &format) {
- sp<AMessage> msg = new AMessage(kWhatConfigure, id());
- msg->setMessage("format", format);
- msg->post();
-}
+void NuPlayer::Decoder::onShutdown(bool notifyComplete) {
+ status_t err = OK;
+ if (mCodec != NULL) {
+ err = mCodec->release();
+ mCodec = NULL;
+ ++mBufferGeneration;
-void NuPlayer::Decoder::setRenderer(const sp<Renderer> &renderer) {
- sp<AMessage> msg = new AMessage(kWhatSetRenderer, id());
- msg->setObject("renderer", renderer);
- msg->post();
-}
+ if (mNativeWindow != NULL) {
+ // reconnect to surface as MediaCodec disconnected from it
+ status_t error =
+ native_window_api_connect(
+ mNativeWindow->getNativeWindow().get(),
+ NATIVE_WINDOW_API_MEDIA);
+ ALOGW_IF(error != NO_ERROR,
+ "[%s] failed to connect to native window, error=%d",
+ mComponentName.c_str(), error);
+ }
+ mComponentName = "decoder";
+ }
+
+ releaseAndResetMediaBuffers();
-void NuPlayer::Decoder::signalUpdateFormat(const sp<AMessage> &format) {
- sp<AMessage> msg = new AMessage(kWhatUpdateFormat, id());
- msg->setMessage("format", format);
- msg->post();
+ if (err != OK) {
+ ALOGE("failed to release %s (err=%d)", mComponentName.c_str(), err);
+ handleError(err);
+ // finish with posting kWhatShutdownCompleted.
+ }
+
+ if (notifyComplete) {
+ sp<AMessage> notify = mNotify->dup();
+ notify->setInt32("what", kWhatShutdownCompleted);
+ notify->post();
+ mPaused = true;
+ }
}
-status_t NuPlayer::Decoder::getInputBuffers(Vector<sp<ABuffer> > *buffers) const {
- sp<AMessage> msg = new AMessage(kWhatGetInputBuffers, id());
- msg->setPointer("buffers", buffers);
+void NuPlayer::Decoder::doRequestBuffers() {
+ if (mFormatChangePending) {
+ return;
+ }
+ status_t err = OK;
+ while (!mDequeuedInputBuffers.empty()) {
+ size_t bufferIx = *mDequeuedInputBuffers.begin();
+ sp<AMessage> msg = new AMessage();
+ msg->setSize("buffer-ix", bufferIx);
+ err = fetchInputData(msg);
+ if (err != OK) {
+ break;
+ }
+ mDequeuedInputBuffers.erase(mDequeuedInputBuffers.begin());
+
+ if (!mPendingInputMessages.empty()
+ || !onInputBufferFetched(msg)) {
+ mPendingInputMessages.push_back(msg);
+ }
+ }
- sp<AMessage> response;
- return PostAndAwaitResponse(msg, &response);
+ if (err == -EWOULDBLOCK
+ && mSource->feedMoreTSData() == OK) {
+ scheduleRequestBuffers();
+ }
}
void NuPlayer::Decoder::handleError(int32_t err)
@@ -263,6 +341,9 @@ void NuPlayer::Decoder::handleError(int32_t err)
}
bool NuPlayer::Decoder::handleAnInputBuffer() {
+ if (mFormatChangePending) {
+ return false;
+ }
size_t bufferIx = -1;
status_t res = mCodec->dequeueInputBuffer(&bufferIx);
ALOGV("[%s] dequeued input: %d",
@@ -284,22 +365,21 @@ bool NuPlayer::Decoder::handleAnInputBuffer() {
}
mInputBufferIsDequeued.editItemAt(bufferIx) = true;
- sp<AMessage> reply = new AMessage(kWhatInputBufferFilled, id());
- reply->setSize("buffer-ix", bufferIx);
- reply->setInt32("generation", mBufferGeneration);
-
if (!mCSDsToSubmit.isEmpty()) {
+ sp<AMessage> msg = new AMessage();
+ msg->setSize("buffer-ix", bufferIx);
+
sp<ABuffer> buffer = mCSDsToSubmit.itemAt(0);
ALOGI("[%s] resubmitting CSD", mComponentName.c_str());
- reply->setBuffer("buffer", buffer);
+ msg->setBuffer("buffer", buffer);
mCSDsToSubmit.removeAt(0);
- CHECK(onInputBufferFilled(reply));
+ CHECK(onInputBufferFetched(msg));
return true;
}
while (!mPendingInputMessages.empty()) {
sp<AMessage> msg = *mPendingInputMessages.begin();
- if (!onInputBufferFilled(msg)) {
+ if (!onInputBufferFetched(msg)) {
break;
}
mPendingInputMessages.erase(mPendingInputMessages.begin());
@@ -309,142 +389,16 @@ bool NuPlayer::Decoder::handleAnInputBuffer() {
return true;
}
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", kWhatFillThisBuffer);
- notify->setBuffer("buffer", mInputBuffers[bufferIx]);
- notify->setMessage("reply", reply);
- notify->post();
- return true;
-}
-
-bool android::NuPlayer::Decoder::onInputBufferFilled(const sp<AMessage> &msg) {
- size_t bufferIx;
- CHECK(msg->findSize("buffer-ix", &bufferIx));
- CHECK_LT(bufferIx, mInputBuffers.size());
- sp<ABuffer> codecBuffer = mInputBuffers[bufferIx];
-
- sp<ABuffer> buffer;
- bool hasBuffer = msg->findBuffer("buffer", &buffer);
+ mDequeuedInputBuffers.push_back(bufferIx);
- // handle widevine classic source - that fills an arbitrary input buffer
- MediaBuffer *mediaBuffer = NULL;
- if (hasBuffer) {
- mediaBuffer = (MediaBuffer *)(buffer->getMediaBufferBase());
- if (mediaBuffer != NULL) {
- // likely filled another buffer than we requested: adjust buffer index
- size_t ix;
- for (ix = 0; ix < mInputBuffers.size(); ix++) {
- const sp<ABuffer> &buf = mInputBuffers[ix];
- if (buf->data() == mediaBuffer->data()) {
- // all input buffers are dequeued on start, hence the check
- if (!mInputBufferIsDequeued[ix]) {
- ALOGV("[%s] received MediaBuffer for #%zu instead of #%zu",
- mComponentName.c_str(), ix, bufferIx);
- mediaBuffer->release();
- return false;
- }
-
- // TRICKY: need buffer for the metadata, so instead, set
- // codecBuffer to the same (though incorrect) buffer to
- // avoid a memcpy into the codecBuffer
- codecBuffer = buffer;
- codecBuffer->setRange(
- mediaBuffer->range_offset(),
- mediaBuffer->range_length());
- bufferIx = ix;
- break;
- }
- }
- CHECK(ix < mInputBuffers.size());
- }
- }
-
- if (buffer == NULL /* includes !hasBuffer */) {
- 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;
- }
-
- // attempt to queue EOS
- status_t err = mCodec->queueInputBuffer(
- bufferIx,
- 0,
- 0,
- 0,
- MediaCodec::BUFFER_FLAG_EOS);
- if (err == OK) {
- mInputBufferIsDequeued.editItemAt(bufferIx) = false;
- } else if (streamErr == ERROR_END_OF_STREAM) {
- streamErr = err;
- // err will not be ERROR_END_OF_STREAM
- }
-
- if (streamErr != ERROR_END_OF_STREAM) {
- ALOGE("Stream error for %s (err=%d), EOS %s queued",
- mComponentName.c_str(),
- streamErr,
- err == OK ? "successfully" : "unsuccessfully");
- handleError(streamErr);
- }
- } else {
- 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;
- }
- }
-
- int64_t timeUs = 0;
- uint32_t flags = 0;
- CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
-
- int32_t eos, csd;
- // we do not expect SYNCFRAME for decoder
- if (buffer->meta()->findInt32("eos", &eos) && eos) {
- flags |= MediaCodec::BUFFER_FLAG_EOS;
- } else if (buffer->meta()->findInt32("csd", &csd) && csd) {
- flags |= MediaCodec::BUFFER_FLAG_CODECCONFIG;
- }
-
- // copy into codec buffer
- if (buffer != codecBuffer) {
- CHECK_LE(buffer->size(), codecBuffer->capacity());
- codecBuffer->setRange(0, buffer->size());
- memcpy(codecBuffer->data(), buffer->data(), buffer->size());
- }
-
- status_t err = mCodec->queueInputBuffer(
- bufferIx,
- codecBuffer->offset(),
- codecBuffer->size(),
- timeUs,
- flags);
- if (err != OK) {
- if (mediaBuffer != NULL) {
- mediaBuffer->release();
- }
- ALOGE("Failed to queue input buffer for %s (err=%d)",
- mComponentName.c_str(), err);
- handleError(err);
- } else {
- mInputBufferIsDequeued.editItemAt(bufferIx) = false;
- if (mediaBuffer != NULL) {
- CHECK(mMediaBuffers[bufferIx] == NULL);
- mMediaBuffers.editItemAt(bufferIx) = mediaBuffer;
- }
- }
- }
+ onRequestInputBuffers();
return true;
}
bool NuPlayer::Decoder::handleAnOutputBuffer() {
+ if (mFormatChangePending) {
+ return false;
+ }
size_t bufferIx = -1;
size_t offset;
size_t size;
@@ -480,7 +434,7 @@ bool NuPlayer::Decoder::handleAnOutputBuffer() {
return false;
}
- if (isVideo()) {
+ if (!mIsAudio) {
sp<AMessage> notify = mNotify->dup();
notify->setInt32("what", kWhatVideoSizeChanged);
notify->setMessage("format", format);
@@ -542,247 +496,314 @@ bool NuPlayer::Decoder::handleAnOutputBuffer() {
if (mRenderer != NULL) {
// send the buffer to renderer.
- mRenderer->queueBuffer(!isVideo(), buffer, reply);
+ mRenderer->queueBuffer(mIsAudio, buffer, reply);
if (flags & MediaCodec::BUFFER_FLAG_EOS) {
- mRenderer->queueEOS(!isVideo(), ERROR_END_OF_STREAM);
+ mRenderer->queueEOS(mIsAudio, ERROR_END_OF_STREAM);
}
}
return true;
}
-void NuPlayer::Decoder::onRenderBuffer(const sp<AMessage> &msg) {
- status_t err;
- int32_t render;
- size_t bufferIx;
- CHECK(msg->findSize("buffer-ix", &bufferIx));
-
- if (isVideo()) {
- int64_t timeUs;
- sp<ABuffer> buffer = mOutputBuffers[bufferIx];
- buffer->meta()->findInt64("timeUs", &timeUs);
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", kWhatRenderBufferTime);
- notify->setInt64("timeUs", timeUs);
- notify->post();
+void NuPlayer::Decoder::releaseAndResetMediaBuffers() {
+ for (size_t i = 0; i < mMediaBuffers.size(); i++) {
+ if (mMediaBuffers[i] != NULL) {
+ mMediaBuffers[i]->release();
+ mMediaBuffers.editItemAt(i) = NULL;
+ }
}
-
- if (msg->findInt32("render", &render) && render) {
- int64_t timestampNs;
- CHECK(msg->findInt64("timestampNs", &timestampNs));
- err = mCodec->renderOutputBufferAndRelease(bufferIx, timestampNs);
- } else {
- err = mCodec->releaseOutputBuffer(bufferIx);
+ mMediaBuffers.resize(mInputBuffers.size());
+ for (size_t i = 0; i < mMediaBuffers.size(); i++) {
+ mMediaBuffers.editItemAt(i) = NULL;
}
- if (err != OK) {
- ALOGE("failed to release output buffer for %s (err=%d)",
- mComponentName.c_str(), err);
- handleError(err);
+ mInputBufferIsDequeued.clear();
+ mInputBufferIsDequeued.resize(mInputBuffers.size());
+ for (size_t i = 0; i < mInputBufferIsDequeued.size(); i++) {
+ mInputBufferIsDequeued.editItemAt(i) = false;
}
+
+ mPendingInputMessages.clear();
+ mDequeuedInputBuffers.clear();
+ mSkipRenderingUntilMediaTimeUs = -1;
}
-void NuPlayer::Decoder::onFlush() {
- if (mRenderer != NULL) {
- mRenderer->flush(!isVideo());
+void NuPlayer::Decoder::requestCodecNotification() {
+ if (mFormatChangePending) {
+ return;
}
-
- status_t err = OK;
if (mCodec != NULL) {
- err = mCodec->flush();
- mCSDsToSubmit = mCSDsForCurrentFormat; // copy operator
- ++mBufferGeneration;
- }
-
- if (err != OK) {
- ALOGE("failed to flush %s (err=%d)", mComponentName.c_str(), err);
- handleError(err);
- // finish with posting kWhatFlushCompleted.
- // we attempt to release the buffers even if flush fails.
+ sp<AMessage> reply = new AMessage(kWhatCodecNotify, id());
+ reply->setInt32("generation", mBufferGeneration);
+ mCodec->requestActivityNotification(reply);
}
- releaseAndResetMediaBuffers();
-
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", kWhatFlushCompleted);
- notify->post();
- mPaused = true;
}
-void NuPlayer::Decoder::onResume() {
- mPaused = false;
+bool NuPlayer::Decoder::isStaleReply(const sp<AMessage> &msg) {
+ int32_t generation;
+ CHECK(msg->findInt32("generation", &generation));
+ return generation != mBufferGeneration;
}
-void NuPlayer::Decoder::onShutdown() {
- status_t err = OK;
- if (mCodec != NULL) {
- err = mCodec->release();
- mCodec = NULL;
- ++mBufferGeneration;
-
- if (mNativeWindow != NULL) {
- // reconnect to surface as MediaCodec disconnected from it
- status_t error =
- native_window_api_connect(
- mNativeWindow->getNativeWindow().get(),
- NATIVE_WINDOW_API_MEDIA);
- ALOGW_IF(error != NO_ERROR,
- "[%s] failed to connect to native window, error=%d",
- mComponentName.c_str(), error);
- }
- mComponentName = "decoder";
- }
-
- releaseAndResetMediaBuffers();
-
- if (err != OK) {
- ALOGE("failed to release %s (err=%d)", mComponentName.c_str(), err);
- handleError(err);
- // finish with posting kWhatShutdownCompleted.
- }
+status_t NuPlayer::Decoder::fetchInputData(sp<AMessage> &reply) {
+ sp<ABuffer> accessUnit;
+ bool dropAccessUnit;
+ do {
+ status_t err = mSource->dequeueAccessUnit(mIsAudio, &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 =
+ (mIsAudio &&
+ (type & ATSParser::DISCONTINUITY_AUDIO_FORMAT))
+ || (!mIsAudio &&
+ (type & ATSParser::DISCONTINUITY_VIDEO_FORMAT));
+
+ bool timeChange = (type & ATSParser::DISCONTINUITY_TIME) != 0;
+
+ ALOGI("%s discontinuity (format=%d, time=%d)",
+ mIsAudio ? "audio" : "video", formatChange, timeChange);
+
+ bool seamlessFormatChange = false;
+ sp<AMessage> newFormat = mSource->getFormat(mIsAudio);
+ if (formatChange) {
+ seamlessFormatChange =
+ supportsSeamlessFormatChange(newFormat);
+ // treat seamless format change separately
+ formatChange = !seamlessFormatChange;
+ }
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", kWhatShutdownCompleted);
- notify->post();
- mPaused = true;
-}
+ if (formatChange || timeChange) {
+ sp<AMessage> msg = mNotify->dup();
+ msg->setInt32("what", kWhatInputDiscontinuity);
+ msg->setInt32("formatChange", formatChange);
+ msg->post();
+ }
-void NuPlayer::Decoder::onMessageReceived(const sp<AMessage> &msg) {
- ALOGV("[%s] onMessage: %s", mComponentName.c_str(), msg->debugString().c_str());
+ if (formatChange /* not seamless */) {
+ // must change decoder
+ // return EOS and wait to be killed
+ mFormatChangePending = true;
+ return 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;
+ } else if (seamlessFormatChange) {
+ // reuse existing decoder and don't flush
+ rememberCodecSpecificData(newFormat);
+ err = OK;
+ } else {
+ // This stream is unaffected by the discontinuity
+ return -EWOULDBLOCK;
+ }
+ }
- switch (msg->what()) {
- case kWhatConfigure:
- {
- sp<AMessage> format;
- CHECK(msg->findMessage("format", &format));
- onConfigure(format);
- break;
+ reply->setInt32("err", err);
+ return OK;
}
- case kWhatSetRenderer:
- {
- bool hadNoRenderer = (mRenderer == NULL);
- sp<RefBase> obj;
- CHECK(msg->findObject("renderer", &obj));
- mRenderer = static_cast<Renderer *>(obj.get());
- if (hadNoRenderer && mRenderer != NULL) {
- requestCodecNotification();
- }
- break;
+ if (!mIsAudio) {
+ ++mNumFramesTotal;
}
- case kWhatUpdateFormat:
- {
- sp<AMessage> format;
- CHECK(msg->findMessage("format", &format));
- rememberCodecSpecificData(format);
- break;
+ dropAccessUnit = false;
+ if (!mIsAudio
+ && !mIsSecure
+ && mRenderer->getVideoLateByUs() > 100000ll
+ && mIsVideoAVC
+ && !IsAVCReferenceFrame(accessUnit)) {
+ dropAccessUnit = true;
+ ++mNumFramesDropped;
}
+ } while (dropAccessUnit);
- case kWhatGetInputBuffers:
- {
- uint32_t replyID;
- CHECK(msg->senderAwaitsResponse(&replyID));
+ // ALOGV("returned a valid buffer of %s data", mIsAudio ? "mIsAudio" : "video");
+#if 0
+ int64_t mediaTimeUs;
+ CHECK(accessUnit->meta()->findInt64("timeUs", &mediaTimeUs));
+ ALOGV("feeding %s input buffer at media time %.2f secs",
+ mIsAudio ? "audio" : "video",
+ mediaTimeUs / 1E6);
+#endif
- Vector<sp<ABuffer> > *dstBuffers;
- CHECK(msg->findPointer("buffers", (void **)&dstBuffers));
+ if (mCCDecoder != NULL) {
+ mCCDecoder->decode(accessUnit);
+ }
- dstBuffers->clear();
- for (size_t i = 0; i < mInputBuffers.size(); i++) {
- dstBuffers->push(mInputBuffers[i]);
- }
+ reply->setBuffer("buffer", accessUnit);
- (new AMessage)->postReply(replyID);
- break;
- }
+ return OK;
+}
- case kWhatCodecNotify:
- {
- if (!isStaleReply(msg)) {
- int32_t numInput, numOutput;
+bool NuPlayer::Decoder::onInputBufferFetched(const sp<AMessage> &msg) {
+ size_t bufferIx;
+ CHECK(msg->findSize("buffer-ix", &bufferIx));
+ CHECK_LT(bufferIx, mInputBuffers.size());
+ sp<ABuffer> codecBuffer = mInputBuffers[bufferIx];
- if (!msg->findInt32("input-buffers", &numInput)) {
- numInput = INT32_MAX;
- }
+ sp<ABuffer> buffer;
+ bool hasBuffer = msg->findBuffer("buffer", &buffer);
- if (!msg->findInt32("output-buffers", &numOutput)) {
- numOutput = INT32_MAX;
- }
+ // handle widevine classic source - that fills an arbitrary input buffer
+ MediaBuffer *mediaBuffer = NULL;
+ if (hasBuffer) {
+ mediaBuffer = (MediaBuffer *)(buffer->getMediaBufferBase());
+ if (mediaBuffer != NULL) {
+ // likely filled another buffer than we requested: adjust buffer index
+ size_t ix;
+ for (ix = 0; ix < mInputBuffers.size(); ix++) {
+ const sp<ABuffer> &buf = mInputBuffers[ix];
+ if (buf->data() == mediaBuffer->data()) {
+ // all input buffers are dequeued on start, hence the check
+ if (!mInputBufferIsDequeued[ix]) {
+ ALOGV("[%s] received MediaBuffer for #%zu instead of #%zu",
+ mComponentName.c_str(), ix, bufferIx);
+ mediaBuffer->release();
+ return false;
+ }
- if (!mPaused) {
- while (numInput-- > 0 && handleAnInputBuffer()) {}
+ // TRICKY: need buffer for the metadata, so instead, set
+ // codecBuffer to the same (though incorrect) buffer to
+ // avoid a memcpy into the codecBuffer
+ codecBuffer = buffer;
+ codecBuffer->setRange(
+ mediaBuffer->range_offset(),
+ mediaBuffer->range_length());
+ bufferIx = ix;
+ break;
}
-
- while (numOutput-- > 0 && handleAnOutputBuffer()) {}
}
-
- requestCodecNotification();
- break;
+ CHECK(ix < mInputBuffers.size());
}
+ }
- case kWhatInputBufferFilled:
- {
- if (!isStaleReply(msg)) {
- if (!mPendingInputMessages.empty()
- || !onInputBufferFilled(msg)) {
- mPendingInputMessages.push_back(msg);
- }
- }
+ if (buffer == NULL /* includes !hasBuffer */) {
+ int32_t streamErr = ERROR_END_OF_STREAM;
+ CHECK(msg->findInt32("err", &streamErr) || !hasBuffer);
- break;
+ if (streamErr == OK) {
+ /* buffers are returned to hold on to */
+ return true;
}
- case kWhatRenderBuffer:
- {
- if (!isStaleReply(msg)) {
- onRenderBuffer(msg);
- }
- break;
+ // attempt to queue EOS
+ status_t err = mCodec->queueInputBuffer(
+ bufferIx,
+ 0,
+ 0,
+ 0,
+ MediaCodec::BUFFER_FLAG_EOS);
+ if (err == OK) {
+ mInputBufferIsDequeued.editItemAt(bufferIx) = false;
+ } else if (streamErr == ERROR_END_OF_STREAM) {
+ streamErr = err;
+ // err will not be ERROR_END_OF_STREAM
}
- case kWhatFlush:
- {
- sp<AMessage> format;
- if (msg->findMessage("new-format", &format)) {
- rememberCodecSpecificData(format);
+ if (streamErr != ERROR_END_OF_STREAM) {
+ ALOGE("Stream error for %s (err=%d), EOS %s queued",
+ mComponentName.c_str(),
+ streamErr,
+ err == OK ? "successfully" : "unsuccessfully");
+ handleError(streamErr);
+ }
+ } else {
+ 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;
}
- onFlush();
- break;
}
- case kWhatResume:
- {
- onResume();
- break;
+ int64_t timeUs = 0;
+ uint32_t flags = 0;
+ CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
+
+ int32_t eos, csd;
+ // we do not expect SYNCFRAME for decoder
+ if (buffer->meta()->findInt32("eos", &eos) && eos) {
+ flags |= MediaCodec::BUFFER_FLAG_EOS;
+ } else if (buffer->meta()->findInt32("csd", &csd) && csd) {
+ flags |= MediaCodec::BUFFER_FLAG_CODECCONFIG;
}
- case kWhatShutdown:
- {
- onShutdown();
- break;
+ // copy into codec buffer
+ if (buffer != codecBuffer) {
+ CHECK_LE(buffer->size(), codecBuffer->capacity());
+ codecBuffer->setRange(0, buffer->size());
+ memcpy(codecBuffer->data(), buffer->data(), buffer->size());
}
- default:
- TRESPASS();
- break;
+ status_t err = mCodec->queueInputBuffer(
+ bufferIx,
+ codecBuffer->offset(),
+ codecBuffer->size(),
+ timeUs,
+ flags);
+ if (err != OK) {
+ if (mediaBuffer != NULL) {
+ mediaBuffer->release();
+ }
+ ALOGE("Failed to queue input buffer for %s (err=%d)",
+ mComponentName.c_str(), err);
+ handleError(err);
+ } else {
+ mInputBufferIsDequeued.editItemAt(bufferIx) = false;
+ if (mediaBuffer != NULL) {
+ CHECK(mMediaBuffers[bufferIx] == NULL);
+ mMediaBuffers.editItemAt(bufferIx) = mediaBuffer;
+ }
+ }
}
+ return true;
}
-void NuPlayer::Decoder::signalFlush(const sp<AMessage> &format) {
- sp<AMessage> msg = new AMessage(kWhatFlush, id());
- if (format != NULL) {
- msg->setMessage("new-format", format);
- }
- msg->post();
-}
+void NuPlayer::Decoder::onRenderBuffer(const sp<AMessage> &msg) {
+ status_t err;
+ int32_t render;
+ size_t bufferIx;
+ CHECK(msg->findSize("buffer-ix", &bufferIx));
-void NuPlayer::Decoder::signalResume() {
- (new AMessage(kWhatResume, id()))->post();
-}
+ if (!mIsAudio) {
+ int64_t timeUs;
+ sp<ABuffer> buffer = mOutputBuffers[bufferIx];
+ buffer->meta()->findInt64("timeUs", &timeUs);
-void NuPlayer::Decoder::initiateShutdown() {
- (new AMessage(kWhatShutdown, id()))->post();
+ if (mCCDecoder != NULL && mCCDecoder->isSelected()) {
+ mCCDecoder->display(timeUs);
+ }
+ }
+
+ if (msg->findInt32("render", &render) && render) {
+ int64_t timestampNs;
+ CHECK(msg->findInt64("timestampNs", &timestampNs));
+ err = mCodec->renderOutputBufferAndRelease(bufferIx, timestampNs);
+ } else {
+ err = mCodec->releaseOutputBuffer(bufferIx);
+ }
+ if (err != OK) {
+ ALOGE("failed to release output buffer for %s (err=%d)",
+ mComponentName.c_str(), err);
+ handleError(err);
+ }
}
-bool NuPlayer::Decoder::supportsSeamlessAudioFormatChange(const sp<AMessage> &targetFormat) const {
+bool NuPlayer::Decoder::supportsSeamlessAudioFormatChange(
+ const sp<AMessage> &targetFormat) const {
if (targetFormat == NULL) {
return true;
}
@@ -847,336 +868,20 @@ bool NuPlayer::Decoder::supportsSeamlessFormatChange(const sp<AMessage> &targetF
return seamless;
}
-bool NuPlayer::Decoder::isVideo() {
- return mNativeWindow != NULL;
-}
-
-struct CCData {
- CCData(uint8_t type, uint8_t data1, uint8_t data2)
- : mType(type), mData1(data1), mData2(data2) {
- }
- bool getChannel(size_t *channel) const {
- if (mData1 >= 0x10 && mData1 <= 0x1f) {
- *channel = (mData1 >= 0x18 ? 1 : 0) + (mType ? 2 : 0);
- return true;
- }
- return false;
- }
-
- uint8_t mType;
- uint8_t mData1;
- uint8_t mData2;
-};
-
-static bool isNullPad(CCData *cc) {
- return cc->mData1 < 0x10 && cc->mData2 < 0x10;
-}
-
-static void dumpBytePair(const sp<ABuffer> &ccBuf) {
- size_t offset = 0;
- AString out;
-
- while (offset < ccBuf->size()) {
- char tmp[128];
-
- CCData *cc = (CCData *) (ccBuf->data() + offset);
-
- if (isNullPad(cc)) {
- // 1 null pad or XDS metadata, ignore
- offset += sizeof(CCData);
- continue;
- }
-
- if (cc->mData1 >= 0x20 && cc->mData1 <= 0x7f) {
- // 2 basic chars
- sprintf(tmp, "[%d]Basic: %c %c", cc->mType, cc->mData1, cc->mData2);
- } else if ((cc->mData1 == 0x11 || cc->mData1 == 0x19)
- && cc->mData2 >= 0x30 && cc->mData2 <= 0x3f) {
- // 1 special char
- sprintf(tmp, "[%d]Special: %02x %02x", cc->mType, cc->mData1, cc->mData2);
- } else if ((cc->mData1 == 0x12 || cc->mData1 == 0x1A)
- && cc->mData2 >= 0x20 && cc->mData2 <= 0x3f){
- // 1 Spanish/French char
- sprintf(tmp, "[%d]Spanish: %02x %02x", cc->mType, cc->mData1, cc->mData2);
- } else if ((cc->mData1 == 0x13 || cc->mData1 == 0x1B)
- && cc->mData2 >= 0x20 && cc->mData2 <= 0x3f){
- // 1 Portuguese/German/Danish char
- sprintf(tmp, "[%d]German: %02x %02x", cc->mType, cc->mData1, cc->mData2);
- } else if ((cc->mData1 == 0x11 || cc->mData1 == 0x19)
- && cc->mData2 >= 0x20 && cc->mData2 <= 0x2f){
- // Mid-Row Codes (Table 69)
- sprintf(tmp, "[%d]Mid-row: %02x %02x", cc->mType, cc->mData1, cc->mData2);
- } else if (((cc->mData1 == 0x14 || cc->mData1 == 0x1c)
- && cc->mData2 >= 0x20 && cc->mData2 <= 0x2f)
- ||
- ((cc->mData1 == 0x17 || cc->mData1 == 0x1f)
- && cc->mData2 >= 0x21 && cc->mData2 <= 0x23)){
- // Misc Control Codes (Table 70)
- sprintf(tmp, "[%d]Ctrl: %02x %02x", cc->mType, cc->mData1, cc->mData2);
- } else if ((cc->mData1 & 0x70) == 0x10
- && (cc->mData2 & 0x40) == 0x40
- && ((cc->mData1 & 0x07) || !(cc->mData2 & 0x20)) ) {
- // Preamble Address Codes (Table 71)
- sprintf(tmp, "[%d]PAC: %02x %02x", cc->mType, cc->mData1, cc->mData2);
- } else {
- sprintf(tmp, "[%d]Invalid: %02x %02x", cc->mType, cc->mData1, cc->mData2);
- }
-
- if (out.size() > 0) {
- out.append(", ");
- }
-
- out.append(tmp);
-
- offset += sizeof(CCData);
- }
-
- ALOGI("%s", out.c_str());
-}
-
-NuPlayer::CCDecoder::CCDecoder(const sp<AMessage> &notify)
- : mNotify(notify),
- mCurrentChannel(0),
- mSelectedTrack(-1) {
- for (size_t i = 0; i < sizeof(mTrackIndices)/sizeof(mTrackIndices[0]); ++i) {
- mTrackIndices[i] = -1;
- }
-}
-
-size_t NuPlayer::CCDecoder::getTrackCount() const {
- return mFoundChannels.size();
-}
-
-sp<AMessage> NuPlayer::CCDecoder::getTrackInfo(size_t index) const {
- if (!isTrackValid(index)) {
- return NULL;
- }
-
- sp<AMessage> format = new AMessage();
-
- format->setInt32("type", MEDIA_TRACK_TYPE_SUBTITLE);
- format->setString("language", "und");
- format->setString("mime", MEDIA_MIMETYPE_TEXT_CEA_608);
- //CC1, field 0 channel 0
- bool isDefaultAuto = (mFoundChannels[index] == 0);
- format->setInt32("auto", isDefaultAuto);
- format->setInt32("default", isDefaultAuto);
- format->setInt32("forced", 0);
-
- return format;
-}
-
-status_t NuPlayer::CCDecoder::selectTrack(size_t index, bool select) {
- if (!isTrackValid(index)) {
- return BAD_VALUE;
- }
-
- if (select) {
- if (mSelectedTrack == (ssize_t)index) {
- ALOGE("track %zu already selected", index);
- return BAD_VALUE;
- }
- ALOGV("selected track %zu", index);
- mSelectedTrack = index;
- } else {
- if (mSelectedTrack != (ssize_t)index) {
- ALOGE("track %zu is not selected", index);
- return BAD_VALUE;
- }
- ALOGV("unselected track %zu", index);
- mSelectedTrack = -1;
- }
-
- return OK;
-}
-
-bool NuPlayer::CCDecoder::isSelected() const {
- return mSelectedTrack >= 0 && mSelectedTrack < (int32_t) getTrackCount();
-}
-
-bool NuPlayer::CCDecoder::isTrackValid(size_t index) const {
- return index < getTrackCount();
-}
-
-int32_t NuPlayer::CCDecoder::getTrackIndex(size_t channel) const {
- if (channel < sizeof(mTrackIndices)/sizeof(mTrackIndices[0])) {
- return mTrackIndices[channel];
- }
- return -1;
-}
-
-// returns true if a new CC track is found
-bool NuPlayer::CCDecoder::extractFromSEI(const sp<ABuffer> &accessUnit) {
- int64_t timeUs;
- CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs));
-
- sp<ABuffer> sei;
- if (!accessUnit->meta()->findBuffer("sei", &sei) || sei == NULL) {
- return false;
- }
-
- bool trackAdded = false;
-
- NALBitReader br(sei->data() + 1, sei->size() - 1);
- // sei_message()
- while (br.atLeastNumBitsLeft(16)) { // at least 16-bit for sei_message()
- uint32_t payload_type = 0;
- size_t payload_size = 0;
- uint8_t last_byte;
-
- do {
- last_byte = br.getBits(8);
- payload_type += last_byte;
- } while (last_byte == 0xFF);
-
- do {
- last_byte = br.getBits(8);
- payload_size += last_byte;
- } while (last_byte == 0xFF);
-
- // sei_payload()
- if (payload_type == 4) {
- // user_data_registered_itu_t_t35()
-
- // ATSC A/72: 6.4.2
- uint8_t itu_t_t35_country_code = br.getBits(8);
- uint16_t itu_t_t35_provider_code = br.getBits(16);
- uint32_t user_identifier = br.getBits(32);
- uint8_t user_data_type_code = br.getBits(8);
-
- payload_size -= 1 + 2 + 4 + 1;
-
- if (itu_t_t35_country_code == 0xB5
- && itu_t_t35_provider_code == 0x0031
- && user_identifier == 'GA94'
- && user_data_type_code == 0x3) {
- // MPEG_cc_data()
- // ATSC A/53 Part 4: 6.2.3.1
- br.skipBits(1); //process_em_data_flag
- bool process_cc_data_flag = br.getBits(1);
- br.skipBits(1); //additional_data_flag
- size_t cc_count = br.getBits(5);
- br.skipBits(8); // em_data;
- payload_size -= 2;
-
- if (process_cc_data_flag) {
- AString out;
-
- sp<ABuffer> ccBuf = new ABuffer(cc_count * sizeof(CCData));
- ccBuf->setRange(0, 0);
-
- for (size_t i = 0; i < cc_count; i++) {
- uint8_t marker = br.getBits(5);
- CHECK_EQ(marker, 0x1f);
-
- bool cc_valid = br.getBits(1);
- uint8_t cc_type = br.getBits(2);
- // remove odd parity bit
- uint8_t cc_data_1 = br.getBits(8) & 0x7f;
- uint8_t cc_data_2 = br.getBits(8) & 0x7f;
-
- if (cc_valid
- && (cc_type == 0 || cc_type == 1)) {
- CCData cc(cc_type, cc_data_1, cc_data_2);
- if (!isNullPad(&cc)) {
- size_t channel;
- if (cc.getChannel(&channel) && getTrackIndex(channel) < 0) {
- mTrackIndices[channel] = mFoundChannels.size();
- mFoundChannels.push_back(channel);
- trackAdded = true;
- }
- memcpy(ccBuf->data() + ccBuf->size(),
- (void *)&cc, sizeof(cc));
- ccBuf->setRange(0, ccBuf->size() + sizeof(CCData));
- }
- }
- }
- payload_size -= cc_count * 3;
-
- mCCMap.add(timeUs, ccBuf);
- break;
- }
- } else {
- ALOGV("Malformed SEI payload type 4");
- }
- } else {
- ALOGV("Unsupported SEI payload type %d", payload_type);
- }
-
- // skipping remaining bits of this payload
- br.skipBits(payload_size * 8);
- }
-
- return trackAdded;
-}
-
-sp<ABuffer> NuPlayer::CCDecoder::filterCCBuf(
- const sp<ABuffer> &ccBuf, size_t index) {
- sp<ABuffer> filteredCCBuf = new ABuffer(ccBuf->size());
- filteredCCBuf->setRange(0, 0);
-
- size_t cc_count = ccBuf->size() / sizeof(CCData);
- const CCData* cc_data = (const CCData*)ccBuf->data();
- for (size_t i = 0; i < cc_count; ++i) {
- size_t channel;
- if (cc_data[i].getChannel(&channel)) {
- mCurrentChannel = channel;
- }
- if (mCurrentChannel == mFoundChannels[index]) {
- memcpy(filteredCCBuf->data() + filteredCCBuf->size(),
- (void *)&cc_data[i], sizeof(CCData));
- filteredCCBuf->setRange(0, filteredCCBuf->size() + sizeof(CCData));
- }
- }
-
- return filteredCCBuf;
-}
-
-void NuPlayer::CCDecoder::decode(const sp<ABuffer> &accessUnit) {
- if (extractFromSEI(accessUnit)) {
- ALOGI("Found CEA-608 track");
- sp<AMessage> msg = mNotify->dup();
- msg->setInt32("what", kWhatTrackAdded);
- msg->post();
- }
- // TODO: extract CC from other sources
-}
-
-void NuPlayer::CCDecoder::display(int64_t timeUs) {
- if (!isTrackValid(mSelectedTrack)) {
- ALOGE("Could not find current track(index=%d)", mSelectedTrack);
- return;
- }
-
- ssize_t index = mCCMap.indexOfKey(timeUs);
- if (index < 0) {
- ALOGV("cc for timestamp %" PRId64 " not found", timeUs);
+void NuPlayer::Decoder::rememberCodecSpecificData(const sp<AMessage> &format) {
+ if (format == NULL) {
return;
}
-
- sp<ABuffer> ccBuf = filterCCBuf(mCCMap.valueAt(index), mSelectedTrack);
-
- if (ccBuf->size() > 0) {
-#if 0
- dumpBytePair(ccBuf);
-#endif
-
- ccBuf->meta()->setInt32("trackIndex", mSelectedTrack);
- ccBuf->meta()->setInt64("timeUs", timeUs);
- ccBuf->meta()->setInt64("durationUs", 0ll);
-
- sp<AMessage> msg = mNotify->dup();
- msg->setInt32("what", kWhatClosedCaptionData);
- msg->setBuffer("buffer", ccBuf);
- msg->post();
+ mCSDsForCurrentFormat.clear();
+ for (int32_t i = 0; ; ++i) {
+ AString tag = "csd-";
+ tag.append(i);
+ sp<ABuffer> buffer;
+ if (!format->findBuffer(tag.c_str(), &buffer)) {
+ break;
+ }
+ mCSDsForCurrentFormat.push(buffer);
}
-
- // remove all entries before timeUs
- mCCMap.removeItemsAt(0, index + 1);
-}
-
-void NuPlayer::CCDecoder::flush() {
- mCCMap.clear();
}
} // namespace android