summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cmds/stagefright/Android.mk26
-rw-r--r--cmds/stagefright/sf2.cpp564
-rw-r--r--cmds/stagefright/stream.cpp23
-rw-r--r--include/media/MediaPlayerInterface.h3
-rw-r--r--include/media/stagefright/ACodec.h152
-rw-r--r--include/media/stagefright/foundation/ADebug.h11
-rw-r--r--include/media/stagefright/foundation/AHierarchicalStateMachine.h49
-rw-r--r--include/media/stagefright/foundation/AMessage.h3
-rw-r--r--media/libmediaplayerservice/Android.mk7
-rw-r--r--media/libmediaplayerservice/MediaPlayerService.cpp7
-rw-r--r--media/libmediaplayerservice/nuplayer/Android.mk22
-rw-r--r--media/libmediaplayerservice/nuplayer/DecoderWrapper.cpp467
-rw-r--r--media/libmediaplayerservice/nuplayer/DecoderWrapper.h81
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayer.cpp477
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayer.h108
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp286
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h64
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp136
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerDriver.h69
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp496
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h108
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.cpp151
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.h74
-rw-r--r--media/libstagefright/ACodec.cpp2097
-rw-r--r--media/libstagefright/Android.mk1
-rw-r--r--media/libstagefright/foundation/AHierarchicalStateMachine.cpp97
-rw-r--r--media/libstagefright/foundation/Android.mk21
-rw-r--r--media/libstagefright/mpeg2ts/ATSParser.cpp95
-rw-r--r--media/libstagefright/mpeg2ts/ATSParser.h11
-rw-r--r--media/libstagefright/mpeg2ts/AnotherPacketSource.cpp48
-rw-r--r--media/libstagefright/mpeg2ts/AnotherPacketSource.h4
-rw-r--r--media/libstagefright/mpeg2ts/ESQueue.cpp11
-rw-r--r--media/libstagefright/mpeg2ts/ESQueue.h2
-rw-r--r--media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp2
34 files changed, 5713 insertions, 60 deletions
diff --git a/cmds/stagefright/Android.mk b/cmds/stagefright/Android.mk
index f8650eb..178032d 100644
--- a/cmds/stagefright/Android.mk
+++ b/cmds/stagefright/Android.mk
@@ -122,3 +122,29 @@ LOCAL_MODULE_TAGS := debug
LOCAL_MODULE:= stream
include $(BUILD_EXECUTABLE)
+
+################################################################################
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ sf2.cpp \
+
+LOCAL_SHARED_LIBRARIES := \
+ libstagefright liblog libutils libbinder libstagefright_foundation \
+ libmedia libsurfaceflinger_client libcutils libui
+
+LOCAL_C_INCLUDES:= \
+ $(JNI_H_INCLUDE) \
+ frameworks/base/media/libstagefright \
+ $(TOP)/frameworks/base/include/media/stagefright/openmax
+
+LOCAL_CFLAGS += -Wno-multichar
+
+LOCAL_MODULE_TAGS := debug
+
+LOCAL_MODULE:= sf2
+
+include $(BUILD_EXECUTABLE)
+
+
diff --git a/cmds/stagefright/sf2.cpp b/cmds/stagefright/sf2.cpp
new file mode 100644
index 0000000..1dc08ea
--- /dev/null
+++ b/cmds/stagefright/sf2.cpp
@@ -0,0 +1,564 @@
+#include <binder/ProcessState.h>
+
+#include <media/stagefright/foundation/hexdump.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/ALooper.h>
+#include <media/stagefright/foundation/AMessage.h>
+
+#include <media/stagefright/ACodec.h>
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaExtractor.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/Utils.h>
+
+#include <surfaceflinger/ISurfaceComposer.h>
+#include <surfaceflinger/SurfaceComposerClient.h>
+
+#include "include/ESDS.h"
+
+using namespace android;
+
+struct Controller : public AHandler {
+ Controller(const char *uri, bool decodeAudio, const sp<Surface> &surface)
+ : mURI(uri),
+ mDecodeAudio(decodeAudio),
+ mSurface(surface),
+ mCodec(new ACodec) {
+ CHECK(!mDecodeAudio || mSurface == NULL);
+ }
+
+ void startAsync() {
+ (new AMessage(kWhatStart, id()))->post();
+ }
+
+protected:
+ virtual ~Controller() {
+ }
+
+ virtual void onMessageReceived(const sp<AMessage> &msg) {
+ switch (msg->what()) {
+ case kWhatStart:
+ {
+#if 1
+ mDecodeLooper = looper();
+#else
+ mDecodeLooper = new ALooper;
+ mDecodeLooper->setName("sf2 decode looper");
+ mDecodeLooper->start();
+#endif
+
+ sp<DataSource> dataSource =
+ DataSource::CreateFromURI(mURI.c_str());
+
+ sp<MediaExtractor> extractor =
+ MediaExtractor::Create(dataSource);
+
+ for (size_t i = 0; i < extractor->countTracks(); ++i) {
+ sp<MetaData> meta = extractor->getTrackMetaData(i);
+
+ const char *mime;
+ CHECK(meta->findCString(kKeyMIMEType, &mime));
+
+ if (!strncasecmp(mDecodeAudio ? "audio/" : "video/",
+ mime, 6)) {
+ mSource = extractor->getTrack(i);
+ break;
+ }
+ }
+ CHECK(mSource != NULL);
+
+ CHECK_EQ(mSource->start(), (status_t)OK);
+
+ mDecodeLooper->registerHandler(mCodec);
+
+ mCodec->setNotificationMessage(
+ new AMessage(kWhatCodecNotify, id()));
+
+ sp<AMessage> format = makeFormat(mSource->getFormat());
+
+ if (mSurface != NULL) {
+ format->setObject("surface", mSurface);
+ }
+
+ mCodec->initiateSetup(format);
+
+ mCSDIndex = 0;
+ mStartTimeUs = ALooper::GetNowUs();
+ mNumOutputBuffersReceived = 0;
+ mTotalBytesReceived = 0;
+ mLeftOverBuffer = NULL;
+ mFinalResult = OK;
+ mSeekState = SEEK_NONE;
+
+ // (new AMessage(kWhatSeek, id()))->post(5000000ll);
+ break;
+ }
+
+ case kWhatSeek:
+ {
+ printf("+");
+ fflush(stdout);
+
+ CHECK(mSeekState == SEEK_NONE
+ || mSeekState == SEEK_FLUSH_COMPLETED);
+
+ if (mLeftOverBuffer != NULL) {
+ mLeftOverBuffer->release();
+ mLeftOverBuffer = NULL;
+ }
+
+ mSeekState = SEEK_FLUSHING;
+ mSeekTimeUs = 30000000ll;
+
+ mCodec->signalFlush();
+ break;
+ }
+
+ case kWhatStop:
+ {
+ if (mLeftOverBuffer != NULL) {
+ mLeftOverBuffer->release();
+ mLeftOverBuffer = NULL;
+ }
+
+ CHECK_EQ(mSource->stop(), (status_t)OK);
+ mSource.clear();
+
+ mCodec->initiateShutdown();
+ break;
+ }
+
+ case kWhatCodecNotify:
+ {
+ int32_t what;
+ CHECK(msg->findInt32("what", &what));
+
+ if (what == ACodec::kWhatFillThisBuffer) {
+ onFillThisBuffer(msg);
+ } else if (what == ACodec::kWhatDrainThisBuffer) {
+ if ((mNumOutputBuffersReceived++ % 16) == 0) {
+ printf(".");
+ fflush(stdout);
+ }
+
+ onDrainThisBuffer(msg);
+ } else if (what == ACodec::kWhatEOS) {
+ printf("$\n");
+
+ int64_t delayUs = ALooper::GetNowUs() - mStartTimeUs;
+
+ if (mDecodeAudio) {
+ printf("%lld bytes received. %.2f KB/sec\n",
+ mTotalBytesReceived,
+ mTotalBytesReceived * 1E6 / 1024 / delayUs);
+ } else {
+ printf("%d frames decoded, %.2f fps. %lld bytes "
+ "received. %.2f KB/sec\n",
+ mNumOutputBuffersReceived,
+ mNumOutputBuffersReceived * 1E6 / delayUs,
+ mTotalBytesReceived,
+ mTotalBytesReceived * 1E6 / 1024 / delayUs);
+ }
+
+ (new AMessage(kWhatStop, id()))->post();
+ } else if (what == ACodec::kWhatFlushCompleted) {
+ mSeekState = SEEK_FLUSH_COMPLETED;
+ mCodec->signalResume();
+
+ (new AMessage(kWhatSeek, id()))->post(5000000ll);
+ } else {
+ CHECK_EQ(what, (int32_t)ACodec::kWhatShutdownCompleted);
+
+ mDecodeLooper->unregisterHandler(mCodec->id());
+
+ if (mDecodeLooper != looper()) {
+ mDecodeLooper->stop();
+ }
+
+ looper()->stop();
+ }
+ break;
+ }
+
+ default:
+ TRESPASS();
+ break;
+ }
+ }
+
+private:
+ enum {
+ kWhatStart = 'strt',
+ kWhatStop = 'stop',
+ kWhatCodecNotify = 'noti',
+ kWhatSeek = 'seek',
+ };
+
+ sp<ALooper> mDecodeLooper;
+
+ AString mURI;
+ bool mDecodeAudio;
+ sp<Surface> mSurface;
+ sp<ACodec> mCodec;
+ sp<MediaSource> mSource;
+
+ Vector<sp<ABuffer> > mCSD;
+ size_t mCSDIndex;
+
+ MediaBuffer *mLeftOverBuffer;
+ status_t mFinalResult;
+
+ int64_t mStartTimeUs;
+ int32_t mNumOutputBuffersReceived;
+ int64_t mTotalBytesReceived;
+
+ enum SeekState {
+ SEEK_NONE,
+ SEEK_FLUSHING,
+ SEEK_FLUSH_COMPLETED,
+ };
+ SeekState mSeekState;
+ int64_t mSeekTimeUs;
+
+ sp<AMessage> makeFormat(const sp<MetaData> &meta) {
+ CHECK(mCSD.isEmpty());
+
+ const char *mime;
+ CHECK(meta->findCString(kKeyMIMEType, &mime));
+
+ sp<AMessage> msg = new AMessage;
+ msg->setString("mime", mime);
+
+ if (!strncasecmp("video/", mime, 6)) {
+ int32_t width, height;
+ CHECK(meta->findInt32(kKeyWidth, &width));
+ CHECK(meta->findInt32(kKeyHeight, &height));
+
+ msg->setInt32("width", width);
+ msg->setInt32("height", height);
+ } else {
+ CHECK(!strncasecmp("audio/", mime, 6));
+
+ int32_t numChannels, sampleRate;
+ CHECK(meta->findInt32(kKeyChannelCount, &numChannels));
+ CHECK(meta->findInt32(kKeySampleRate, &sampleRate));
+
+ msg->setInt32("channel-count", numChannels);
+ msg->setInt32("sample-rate", sampleRate);
+ }
+
+ uint32_t type;
+ const void *data;
+ size_t size;
+ if (meta->findData(kKeyAVCC, &type, &data, &size)) {
+ // Parse the AVCDecoderConfigurationRecord
+
+ const uint8_t *ptr = (const uint8_t *)data;
+
+ CHECK(size >= 7);
+ CHECK_EQ((unsigned)ptr[0], 1u); // configurationVersion == 1
+ uint8_t profile = ptr[1];
+ uint8_t level = ptr[3];
+
+ // There is decodable content out there that fails the following
+ // assertion, let's be lenient for now...
+ // CHECK((ptr[4] >> 2) == 0x3f); // reserved
+
+ size_t lengthSize = 1 + (ptr[4] & 3);
+
+ // commented out check below as H264_QVGA_500_NO_AUDIO.3gp
+ // violates it...
+ // CHECK((ptr[5] >> 5) == 7); // reserved
+
+ size_t numSeqParameterSets = ptr[5] & 31;
+
+ ptr += 6;
+ size -= 6;
+
+ sp<ABuffer> buffer = new ABuffer(1024);
+ buffer->setRange(0, 0);
+
+ for (size_t i = 0; i < numSeqParameterSets; ++i) {
+ CHECK(size >= 2);
+ size_t length = U16_AT(ptr);
+
+ ptr += 2;
+ size -= 2;
+
+ CHECK(size >= length);
+
+ memcpy(buffer->data() + buffer->size(), "\x00\x00\x00\x01", 4);
+ memcpy(buffer->data() + buffer->size() + 4, ptr, length);
+ buffer->setRange(0, buffer->size() + 4 + length);
+
+ ptr += length;
+ size -= length;
+ }
+
+ buffer->meta()->setInt32("csd", true);
+ mCSD.push(buffer);
+
+ buffer = new ABuffer(1024);
+ buffer->setRange(0, 0);
+
+ CHECK(size >= 1);
+ size_t numPictureParameterSets = *ptr;
+ ++ptr;
+ --size;
+
+ for (size_t i = 0; i < numPictureParameterSets; ++i) {
+ CHECK(size >= 2);
+ size_t length = U16_AT(ptr);
+
+ ptr += 2;
+ size -= 2;
+
+ CHECK(size >= length);
+
+ memcpy(buffer->data() + buffer->size(), "\x00\x00\x00\x01", 4);
+ memcpy(buffer->data() + buffer->size() + 4, ptr, length);
+ buffer->setRange(0, buffer->size() + 4 + length);
+
+ ptr += length;
+ size -= length;
+ }
+
+ buffer->meta()->setInt32("csd", true);
+ mCSD.push(buffer);
+
+ msg->setObject("csd", buffer);
+ } else if (meta->findData(kKeyESDS, &type, &data, &size)) {
+ ESDS esds((const char *)data, size);
+ CHECK_EQ(esds.InitCheck(), (status_t)OK);
+
+ const void *codec_specific_data;
+ size_t codec_specific_data_size;
+ esds.getCodecSpecificInfo(
+ &codec_specific_data, &codec_specific_data_size);
+
+ sp<ABuffer> buffer = new ABuffer(codec_specific_data_size);
+
+ memcpy(buffer->data(), codec_specific_data,
+ codec_specific_data_size);
+
+ buffer->meta()->setInt32("csd", true);
+ mCSD.push(buffer);
+ }
+
+ int32_t maxInputSize;
+ if (meta->findInt32(kKeyMaxInputSize, &maxInputSize)) {
+ msg->setInt32("max-input-size", maxInputSize);
+ }
+
+ return msg;
+ }
+
+ void onFillThisBuffer(const sp<AMessage> &msg) {
+ sp<AMessage> reply;
+ CHECK(msg->findMessage("reply", &reply));
+
+ if (mSeekState == SEEK_FLUSHING) {
+ reply->post();
+ return;
+ }
+
+ sp<RefBase> obj;
+ CHECK(msg->findObject("buffer", &obj));
+ sp<ABuffer> outBuffer = static_cast<ABuffer *>(obj.get());
+
+ if (mCSDIndex < mCSD.size()) {
+ outBuffer = mCSD.editItemAt(mCSDIndex++);
+ outBuffer->meta()->setInt64("timeUs", 0);
+ } else {
+ size_t sizeLeft = outBuffer->capacity();
+ outBuffer->setRange(0, 0);
+
+ int32_t n = 0;
+
+ for (;;) {
+ MediaBuffer *inBuffer;
+
+ if (mLeftOverBuffer != NULL) {
+ inBuffer = mLeftOverBuffer;
+ mLeftOverBuffer = NULL;
+ } else if (mFinalResult != OK) {
+ break;
+ } else {
+ MediaSource::ReadOptions options;
+ if (mSeekState == SEEK_FLUSH_COMPLETED) {
+ options.setSeekTo(mSeekTimeUs);
+ mSeekState = SEEK_NONE;
+ }
+ status_t err = mSource->read(&inBuffer, &options);
+
+ if (err != OK) {
+ mFinalResult = err;
+ break;
+ }
+ }
+
+ if (inBuffer->range_length() > sizeLeft) {
+ if (outBuffer->size() == 0) {
+ LOGE("Unable to fit even a single input buffer of size %d.",
+ inBuffer->range_length());
+ }
+ CHECK_GT(outBuffer->size(), 0u);
+
+ mLeftOverBuffer = inBuffer;
+ break;
+ }
+
+ ++n;
+
+ if (outBuffer->size() == 0) {
+ int64_t timeUs;
+ CHECK(inBuffer->meta_data()->findInt64(kKeyTime, &timeUs));
+
+ outBuffer->meta()->setInt64("timeUs", timeUs);
+ }
+
+ memcpy(outBuffer->data() + outBuffer->size(),
+ (const uint8_t *)inBuffer->data()
+ + inBuffer->range_offset(),
+ inBuffer->range_length());
+
+ outBuffer->setRange(
+ 0, outBuffer->size() + inBuffer->range_length());
+
+ sizeLeft -= inBuffer->range_length();
+
+ inBuffer->release();
+ inBuffer = NULL;
+
+ // break; // Don't coalesce
+ }
+
+ LOGV("coalesced %d input buffers", n);
+
+ if (outBuffer->size() == 0) {
+ CHECK_NE(mFinalResult, (status_t)OK);
+
+ reply->setInt32("err", mFinalResult);
+ reply->post();
+ return;
+ }
+ }
+
+ reply->setObject("buffer", outBuffer);
+ reply->post();
+ }
+
+ void onDrainThisBuffer(const sp<AMessage> &msg) {
+ sp<RefBase> obj;
+ CHECK(msg->findObject("buffer", &obj));
+
+ sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get());
+ mTotalBytesReceived += buffer->size();
+
+ sp<AMessage> reply;
+ CHECK(msg->findMessage("reply", &reply));
+
+ reply->post();
+ }
+
+ DISALLOW_EVIL_CONSTRUCTORS(Controller);
+};
+
+static void usage(const char *me) {
+ fprintf(stderr, "usage: %s\n", me);
+ fprintf(stderr, " -h(elp)\n");
+ fprintf(stderr, " -a(udio)\n");
+
+ fprintf(stderr,
+ " -s(surface) Allocate output buffers on a surface.\n");
+}
+
+int main(int argc, char **argv) {
+ android::ProcessState::self()->startThreadPool();
+
+ bool decodeAudio = false;
+ bool useSurface = false;
+
+ int res;
+ while ((res = getopt(argc, argv, "has")) >= 0) {
+ switch (res) {
+ case 'a':
+ decodeAudio = true;
+ break;
+
+ case 's':
+ useSurface = true;
+ break;
+
+ case '?':
+ case 'h':
+ default:
+ {
+ usage(argv[0]);
+ return 1;
+ }
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 1) {
+ usage(argv[-optind]);
+ return 1;
+ }
+
+ DataSource::RegisterDefaultSniffers();
+
+ sp<ALooper> looper = new ALooper;
+ looper->setName("sf2");
+
+ sp<SurfaceComposerClient> composerClient;
+ sp<SurfaceControl> control;
+ sp<Surface> surface;
+
+ if (!decodeAudio && useSurface) {
+ composerClient = new SurfaceComposerClient;
+ CHECK_EQ(composerClient->initCheck(), (status_t)OK);
+
+ control = composerClient->createSurface(
+ getpid(),
+ String8("A Surface"),
+ 0,
+ 1280,
+ 800,
+ PIXEL_FORMAT_RGB_565,
+ 0);
+
+ CHECK(control != NULL);
+ CHECK(control->isValid());
+
+ CHECK_EQ(composerClient->openTransaction(), (status_t)OK);
+ CHECK_EQ(control->setLayer(30000), (status_t)OK);
+ CHECK_EQ(control->show(), (status_t)OK);
+ CHECK_EQ(composerClient->closeTransaction(), (status_t)OK);
+
+ surface = control->getSurface();
+ CHECK(surface != NULL);
+ }
+
+ sp<Controller> controller = new Controller(argv[0], decodeAudio, surface);
+ looper->registerHandler(controller);
+
+ controller->startAsync();
+
+ CHECK_EQ(looper->start(true /* runOnCallingThread */), (status_t)OK);
+
+ looper->unregisterHandler(controller->id());
+
+ if (!decodeAudio && useSurface) {
+ composerClient->dispose();
+ }
+
+ return 0;
+}
+
diff --git a/cmds/stagefright/stream.cpp b/cmds/stagefright/stream.cpp
index a3d7a6e..ccae92e 100644
--- a/cmds/stagefright/stream.cpp
+++ b/cmds/stagefright/stream.cpp
@@ -28,6 +28,8 @@ protected:
private:
int mFd;
+ off64_t mFileSize;
+ int64_t mNextSeekTimeUs;
sp<IStreamListener> mListener;
Vector<sp<IMemory> > mBuffers;
@@ -36,8 +38,13 @@ private:
};
MyStreamSource::MyStreamSource(int fd)
- : mFd(fd) {
+ : mFd(fd),
+ mFileSize(0),
+ mNextSeekTimeUs(ALooper::GetNowUs() + 5000000ll) {
CHECK_GE(fd, 0);
+
+ mFileSize = lseek64(fd, 0, SEEK_END);
+ lseek64(fd, 0, SEEK_SET);
}
MyStreamSource::~MyStreamSource() {
@@ -53,6 +60,20 @@ void MyStreamSource::setBuffers(const Vector<sp<IMemory> > &buffers) {
void MyStreamSource::onBufferAvailable(size_t index) {
CHECK_LT(index, mBuffers.size());
+
+ if (mNextSeekTimeUs >= 0 && mNextSeekTimeUs <= ALooper::GetNowUs()) {
+ off64_t offset = (off64_t)(((float)rand() / RAND_MAX) * mFileSize * 0.8);
+ offset = (offset / 188) * 188;
+
+ lseek(mFd, offset, SEEK_SET);
+
+ mListener->issueCommand(
+ IStreamListener::DISCONTINUITY, false /* synchronous */);
+
+ mNextSeekTimeUs = -1;
+ mNextSeekTimeUs = ALooper::GetNowUs() + 5000000ll;
+ }
+
sp<IMemory> mem = mBuffers.itemAt(index);
ssize_t n = read(mFd, mem->pointer(), mem->size());
diff --git a/include/media/MediaPlayerInterface.h b/include/media/MediaPlayerInterface.h
index e7f1d6d..c0963a6 100644
--- a/include/media/MediaPlayerInterface.h
+++ b/include/media/MediaPlayerInterface.h
@@ -40,7 +40,8 @@ template<typename T> class SortedVector;
enum player_type {
PV_PLAYER = 1,
SONIVOX_PLAYER = 2,
- STAGEFRIGHT_PLAYER = 4,
+ STAGEFRIGHT_PLAYER = 3,
+ NU_PLAYER = 4,
// Test players are available only in the 'test' and 'eng' builds.
// The shared library with the test player is passed passed as an
// argument to the 'test:' url in the setDataSource call.
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h
new file mode 100644
index 0000000..940470d
--- /dev/null
+++ b/include/media/stagefright/ACodec.h
@@ -0,0 +1,152 @@
+#ifndef A_CODEC_H_
+
+#define A_CODEC_H_
+
+#include <stdint.h>
+#include <android/native_window.h>
+#include <media/IOMX.h>
+#include <media/stagefright/foundation/AHierarchicalStateMachine.h>
+
+namespace android {
+
+struct ABuffer;
+struct MemoryDealer;
+
+struct ACodec : public AHierarchicalStateMachine {
+ enum {
+ kWhatFillThisBuffer = 'fill',
+ kWhatDrainThisBuffer = 'drai',
+ kWhatEOS = 'eos ',
+ kWhatShutdownCompleted = 'scom',
+ kWhatFlushCompleted = 'fcom',
+ };
+
+ ACodec();
+
+ void setNotificationMessage(const sp<AMessage> &msg);
+ void initiateSetup(const sp<AMessage> &msg);
+ void signalFlush();
+ void signalResume();
+ void initiateShutdown();
+
+protected:
+ virtual ~ACodec();
+
+private:
+ struct BaseState;
+ struct UninitializedState;
+ struct LoadedToIdleState;
+ struct IdleToExecutingState;
+ struct ExecutingState;
+ struct OutputPortSettingsChangedState;
+ struct ExecutingToIdleState;
+ struct IdleToLoadedState;
+ struct ErrorState;
+ struct FlushingState;
+
+ enum {
+ kWhatSetup = 'setu',
+ kWhatOMXMessage = 'omx ',
+ kWhatInputBufferFilled = 'inpF',
+ kWhatOutputBufferDrained = 'outD',
+ kWhatShutdown = 'shut',
+ kWhatFlush = 'flus',
+ kWhatResume = 'resm',
+ kWhatDrainDeferredMessages = 'drai',
+ };
+
+ enum {
+ kPortIndexInput = 0,
+ kPortIndexOutput = 1
+ };
+
+ struct BufferInfo {
+ enum Status {
+ OWNED_BY_US,
+ OWNED_BY_COMPONENT,
+ OWNED_BY_UPSTREAM,
+ OWNED_BY_DOWNSTREAM,
+ OWNED_BY_NATIVE_WINDOW,
+ };
+
+ IOMX::buffer_id mBufferID;
+ Status mStatus;
+
+ sp<ABuffer> mData;
+ sp<GraphicBuffer> mGraphicBuffer;
+ };
+
+ sp<AMessage> mNotify;
+
+ sp<UninitializedState> mUninitializedState;
+ sp<LoadedToIdleState> mLoadedToIdleState;
+ sp<IdleToExecutingState> mIdleToExecutingState;
+ sp<ExecutingState> mExecutingState;
+ sp<OutputPortSettingsChangedState> mOutputPortSettingsChangedState;
+ sp<ExecutingToIdleState> mExecutingToIdleState;
+ sp<IdleToLoadedState> mIdleToLoadedState;
+ sp<ErrorState> mErrorState;
+ sp<FlushingState> mFlushingState;
+
+ AString mComponentName;
+ sp<IOMX> mOMX;
+ IOMX::node_id mNode;
+ sp<MemoryDealer> mDealer[2];
+
+ sp<ANativeWindow> mNativeWindow;
+
+ Vector<BufferInfo> mBuffers[2];
+ bool mPortEOS[2];
+
+ List<sp<AMessage> > mDeferredQueue;
+
+ status_t allocateBuffersOnPort(OMX_U32 portIndex);
+ status_t freeBuffersOnPort(OMX_U32 portIndex);
+ status_t freeBuffer(OMX_U32 portIndex, size_t i);
+
+ status_t allocateOutputBuffersFromNativeWindow();
+ status_t cancelBufferToNativeWindow(BufferInfo *info);
+ status_t freeOutputBuffersOwnedByNativeWindow();
+ BufferInfo *dequeueBufferFromNativeWindow();
+
+ BufferInfo *findBufferByID(
+ uint32_t portIndex, IOMX::buffer_id bufferID,
+ ssize_t *index = NULL);
+
+ void setComponentRole(bool isEncoder, const char *mime);
+ void configureCodec(const char *mime, const sp<AMessage> &msg);
+
+ status_t setVideoPortFormatType(
+ OMX_U32 portIndex,
+ OMX_VIDEO_CODINGTYPE compressionFormat,
+ OMX_COLOR_FORMATTYPE colorFormat);
+
+ status_t setSupportedOutputFormat();
+
+ status_t setupVideoDecoder(
+ const char *mime, int32_t width, int32_t height);
+
+ status_t setVideoFormatOnPort(
+ OMX_U32 portIndex,
+ int32_t width, int32_t height,
+ OMX_VIDEO_CODINGTYPE compressionFormat);
+
+ status_t setupAACDecoder(int32_t numChannels, int32_t sampleRate);
+ status_t setMinBufferSize(OMX_U32 portIndex, size_t size);
+
+ status_t initNativeWindow();
+
+ // Returns true iff all buffers on the given port have status OWNED_BY_US.
+ bool allYourBuffersAreBelongToUs(OMX_U32 portIndex);
+
+ bool allYourBuffersAreBelongToUs();
+
+ void deferMessage(const sp<AMessage> &msg);
+ void processDeferredMessages();
+
+ DISALLOW_EVIL_CONSTRUCTORS(ACodec);
+};
+
+} // namespace android
+
+#endif // A_CODEC_H_
diff --git a/include/media/stagefright/foundation/ADebug.h b/include/media/stagefright/foundation/ADebug.h
index 69021d8..eb5e494 100644
--- a/include/media/stagefright/foundation/ADebug.h
+++ b/include/media/stagefright/foundation/ADebug.h
@@ -32,6 +32,7 @@ namespace android {
#define CHECK(condition) \
LOG_ALWAYS_FATAL_IF( \
!(condition), \
+ "%s", \
__FILE__ ":" LITERAL_TO_STRING(__LINE__) \
" CHECK(" #condition ") failed.")
@@ -58,10 +59,12 @@ MAKE_COMPARATOR(GT,>)
do { \
AString ___res = Compare_##suffix(x, y); \
if (!___res.empty()) { \
- LOG_ALWAYS_FATAL( \
- __FILE__ ":" LITERAL_TO_STRING(__LINE__) \
- " CHECK_" #suffix "( " #x "," #y ") failed: %s", \
- ___res.c_str()); \
+ AString ___full = \
+ __FILE__ ":" LITERAL_TO_STRING(__LINE__) \
+ " CHECK_" #suffix "( " #x "," #y ") failed: "; \
+ ___full.append(___res); \
+ \
+ LOG_ALWAYS_FATAL("%s", ___full.c_str()); \
} \
} while (false)
diff --git a/include/media/stagefright/foundation/AHierarchicalStateMachine.h b/include/media/stagefright/foundation/AHierarchicalStateMachine.h
new file mode 100644
index 0000000..b5786fb
--- /dev/null
+++ b/include/media/stagefright/foundation/AHierarchicalStateMachine.h
@@ -0,0 +1,49 @@
+#ifndef A_HIERARCHICAL_STATE_MACHINE_H_
+
+#define A_HIERARCHICAL_STATE_MACHINE_H_
+
+#include <media/stagefright/foundation/AHandler.h>
+
+namespace android {
+
+struct AState : public RefBase {
+ AState(const sp<AState> &parentState = NULL);
+
+ sp<AState> parentState();
+
+protected:
+ virtual ~AState();
+
+ virtual void stateEntered();
+ virtual void stateExited();
+
+ virtual bool onMessageReceived(const sp<AMessage> &msg) = 0;
+
+private:
+ friend struct AHierarchicalStateMachine;
+
+ sp<AState> mParentState;
+
+ DISALLOW_EVIL_CONSTRUCTORS(AState);
+};
+
+struct AHierarchicalStateMachine : public AHandler {
+ AHierarchicalStateMachine();
+
+protected:
+ virtual ~AHierarchicalStateMachine();
+
+ virtual void onMessageReceived(const sp<AMessage> &msg);
+
+ // Only to be called in response to a message.
+ void changeState(const sp<AState> &state);
+
+private:
+ sp<AState> mState;
+
+ DISALLOW_EVIL_CONSTRUCTORS(AHierarchicalStateMachine);
+};
+
+} // namespace android
+
+#endif // A_HIERARCHICAL_STATE_MACHINE_H_
diff --git a/include/media/stagefright/foundation/AMessage.h b/include/media/stagefright/foundation/AMessage.h
index 2fbdddc..941f6b9 100644
--- a/include/media/stagefright/foundation/AMessage.h
+++ b/include/media/stagefright/foundation/AMessage.h
@@ -40,6 +40,8 @@ struct AMessage : public RefBase {
void setTarget(ALooper::handler_id target);
ALooper::handler_id target() const;
+ void clear();
+
void setInt32(const char *name, int32_t value);
void setInt64(const char *name, int64_t value);
void setSize(const char *name, size_t value);
@@ -106,7 +108,6 @@ private:
Item mItems[kMaxNumItems];
size_t mNumItems;
- void clear();
Item *allocateItem(const char *name);
void freeItem(Item *item);
const Item *findItem(const char *name, Type type) const;
diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk
index a9d537f..f7f0d95 100644
--- a/media/libmediaplayerservice/Android.mk
+++ b/media/libmediaplayerservice/Android.mk
@@ -35,7 +35,8 @@ LOCAL_SHARED_LIBRARIES := \
libsurfaceflinger_client
LOCAL_STATIC_LIBRARIES := \
- libstagefright_rtsp
+ libstagefright_rtsp \
+ libstagefright_nuplayer \
ifneq ($(TARGET_SIMULATOR),true)
LOCAL_SHARED_LIBRARIES += libdl
@@ -47,9 +48,11 @@ LOCAL_C_INCLUDES := \
$(TOP)/frameworks/base/include/media/stagefright/openmax \
$(TOP)/frameworks/base/media/libstagefright/include \
$(TOP)/frameworks/base/media/libstagefright/rtsp \
- $(TOP)/external/tremolo/Tremolo
+ $(TOP)/external/tremolo/Tremolo \
LOCAL_MODULE:= libmediaplayerservice
include $(BUILD_SHARED_LIBRARY)
+include $(call all-makefiles-under,$(LOCAL_PATH))
+
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 63d09d6..6f011ce 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -58,6 +58,7 @@
#include "MidiFile.h"
#include "TestPlayerStub.h"
#include "StagefrightPlayer.h"
+#include "nuplayer/NuPlayerDriver.h"
#include <OMX.h>
@@ -759,6 +760,10 @@ static sp<MediaPlayerBase> createPlayer(player_type playerType, void* cookie,
LOGV(" create StagefrightPlayer");
p = new StagefrightPlayer;
break;
+ case NU_PLAYER:
+ LOGV(" create NuPlayer");
+ p = new NuPlayerDriver;
+ break;
case TEST_PLAYER:
LOGV("Create Test Player stub");
p = new TestPlayerStub();
@@ -887,7 +892,7 @@ status_t MediaPlayerService::Client::setDataSource(int fd, int64_t offset, int64
status_t MediaPlayerService::Client::setDataSource(
const sp<IStreamSource> &source) {
// create the right type of player
- sp<MediaPlayerBase> p = createPlayer(STAGEFRIGHT_PLAYER);
+ sp<MediaPlayerBase> p = createPlayer(NU_PLAYER);
if (p == NULL) {
return NO_INIT;
diff --git a/media/libmediaplayerservice/nuplayer/Android.mk b/media/libmediaplayerservice/nuplayer/Android.mk
new file mode 100644
index 0000000..c4f3764
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/Android.mk
@@ -0,0 +1,22 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ NuPlayer.cpp \
+ NuPlayerDecoder.cpp \
+ NuPlayerDriver.cpp \
+ NuPlayerRenderer.cpp \
+ NuPlayerStreamListener.cpp \
+ DecoderWrapper.cpp \
+
+LOCAL_C_INCLUDES := \
+ $(TOP)/frameworks/base/include/media/stagefright/openmax \
+ $(TOP)/frameworks/base/media/libstagefright/include \
+ $(TOP)/frameworks/base/media/libstagefright/mpeg2ts \
+
+LOCAL_MODULE:= libstagefright_nuplayer
+
+LOCAL_MODULE_TAGS := eng
+
+include $(BUILD_STATIC_LIBRARY)
+
diff --git a/media/libmediaplayerservice/nuplayer/DecoderWrapper.cpp b/media/libmediaplayerservice/nuplayer/DecoderWrapper.cpp
new file mode 100644
index 0000000..89a5e69
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/DecoderWrapper.cpp
@@ -0,0 +1,467 @@
+/*
+ * Copyright (C) 2010 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "DecoderWrapper"
+#include <utils/Log.h>
+
+#include "DecoderWrapper.h"
+
+#include "AACDecoder.h"
+
+#include <media/stagefright/foundation/hexdump.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/ACodec.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+
+namespace android {
+
+struct DecoderWrapper::WrapperSource : public MediaSource {
+ WrapperSource(
+ const sp<MetaData> &meta,
+ const sp<AMessage> &notify);
+
+ virtual status_t start(MetaData *params);
+ virtual status_t stop();
+ virtual sp<MetaData> getFormat();
+
+ virtual status_t read(
+ MediaBuffer **buffer, const ReadOptions *options);
+
+ void queueBuffer(const sp<ABuffer> &buffer);
+ void queueEOS(status_t finalResult);
+ void clear();
+
+protected:
+ virtual ~WrapperSource();
+
+private:
+ Mutex mLock;
+ Condition mCondition;
+
+ sp<MetaData> mMeta;
+ sp<AMessage> mNotify;
+
+ List<sp<ABuffer> > mQueue;
+ status_t mFinalResult;
+
+ DISALLOW_EVIL_CONSTRUCTORS(WrapperSource);
+};
+
+DecoderWrapper::WrapperSource::WrapperSource(
+ const sp<MetaData> &meta, const sp<AMessage> &notify)
+ : mMeta(meta),
+ mNotify(notify),
+ mFinalResult(OK) {
+}
+
+DecoderWrapper::WrapperSource::~WrapperSource() {
+}
+
+status_t DecoderWrapper::WrapperSource::start(MetaData *params) {
+ return OK;
+}
+
+status_t DecoderWrapper::WrapperSource::stop() {
+ return OK;
+}
+
+sp<MetaData> DecoderWrapper::WrapperSource::getFormat() {
+ return mMeta;
+}
+
+status_t DecoderWrapper::WrapperSource::read(
+ MediaBuffer **out, const ReadOptions *options) {
+ Mutex::Autolock autoLock(mLock);
+
+ bool requestedBuffer = false;
+
+ while (mQueue.empty() && mFinalResult == OK) {
+ if (!requestedBuffer) {
+ mNotify->dup()->post();
+ requestedBuffer = true;
+ }
+
+ mCondition.wait(mLock);
+ }
+
+ if (mQueue.empty()) {
+ return mFinalResult;
+ }
+
+ sp<ABuffer> src = *mQueue.begin();
+ mQueue.erase(mQueue.begin());
+
+ MediaBuffer *dst = new MediaBuffer(src->size());
+ memcpy(dst->data(), src->data(), src->size());
+
+ int64_t timeUs;
+ CHECK(src->meta()->findInt64("timeUs", &timeUs));
+
+ dst->meta_data()->setInt64(kKeyTime, timeUs);
+
+ *out = dst;
+
+ return OK;
+}
+
+void DecoderWrapper::WrapperSource::queueBuffer(const sp<ABuffer> &buffer) {
+ Mutex::Autolock autoLock(mLock);
+ mQueue.push_back(buffer);
+ mCondition.broadcast();
+}
+
+void DecoderWrapper::WrapperSource::queueEOS(status_t finalResult) {
+ CHECK_NE(finalResult, (status_t)OK);
+
+ Mutex::Autolock autoLock(mLock);
+ mFinalResult = finalResult;
+ mCondition.broadcast();
+}
+
+void DecoderWrapper::WrapperSource::clear() {
+ Mutex::Autolock autoLock(mLock);
+ mQueue.clear();
+ mFinalResult = OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+struct DecoderWrapper::WrapperReader : public AHandler {
+ WrapperReader(
+ const sp<MediaSource> &decoder,
+ const sp<AMessage> &notify);
+
+ void start();
+ void readMore(bool flush = false);
+
+protected:
+ virtual ~WrapperReader();
+
+ virtual void onMessageReceived(const sp<AMessage> &msg);
+
+private:
+ enum {
+ kWhatRead
+ };
+
+ sp<MediaSource> mDecoder;
+ sp<AMessage> mNotify;
+ bool mEOS;
+
+ DISALLOW_EVIL_CONSTRUCTORS(WrapperReader);
+};
+
+DecoderWrapper::WrapperReader::WrapperReader(
+ const sp<MediaSource> &decoder, const sp<AMessage> &notify)
+ : mDecoder(decoder),
+ mNotify(notify),
+ mEOS(false) {
+}
+
+DecoderWrapper::WrapperReader::~WrapperReader() {
+}
+
+void DecoderWrapper::WrapperReader::start() {
+ CHECK_EQ(mDecoder->start(), (status_t)OK);
+ readMore();
+}
+
+void DecoderWrapper::WrapperReader::readMore(bool flush) {
+ if (!flush && mEOS) {
+ return;
+ }
+
+ sp<AMessage> msg = new AMessage(kWhatRead, id());
+ msg->setInt32("flush", static_cast<int32_t>(flush));
+ msg->post();
+}
+
+void DecoderWrapper::WrapperReader::onMessageReceived(
+ const sp<AMessage> &msg) {
+ switch (msg->what()) {
+ case kWhatRead:
+ {
+ int32_t flush;
+ CHECK(msg->findInt32("flush", &flush));
+
+ MediaSource::ReadOptions options;
+ if (flush) {
+ // Dummy seek
+ options.setSeekTo(0);
+ mEOS = false;
+ }
+
+ CHECK(!mEOS);
+
+ MediaBuffer *src;
+ status_t err = mDecoder->read(&src, &options);
+
+ sp<AMessage> notify = mNotify->dup();
+
+ sp<AMessage> realNotify;
+ CHECK(notify->findMessage("real-notify", &realNotify));
+
+ if (err == OK) {
+ realNotify->setInt32("what", ACodec::kWhatDrainThisBuffer);
+
+ sp<ABuffer> dst = new ABuffer(src->range_length());
+ memcpy(dst->data(),
+ (const uint8_t *)src->data() + src->range_offset(),
+ src->range_length());
+
+ int64_t timeUs;
+ CHECK(src->meta_data()->findInt64(kKeyTime, &timeUs));
+ src->release();
+ src = NULL;
+
+ dst->meta()->setInt64("timeUs", timeUs);
+
+ realNotify->setObject("buffer", dst);
+ } else {
+ realNotify->setInt32("what", ACodec::kWhatEOS);
+ mEOS = true;
+ }
+
+ notify->post();
+ break;
+ }
+
+ default:
+ TRESPASS();
+ break;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+DecoderWrapper::DecoderWrapper()
+ : mNumOutstandingInputBuffers(0),
+ mNumOutstandingOutputBuffers(0),
+ mNumPendingDecodes(0),
+ mFlushing(false) {
+}
+
+DecoderWrapper::~DecoderWrapper() {
+}
+
+void DecoderWrapper::setNotificationMessage(const sp<AMessage> &msg) {
+ mNotify = msg;
+}
+
+void DecoderWrapper::initiateSetup(const sp<AMessage> &msg) {
+ msg->setWhat(kWhatSetup);
+ msg->setTarget(id());
+ msg->post();
+}
+
+void DecoderWrapper::initiateShutdown() {
+ (new AMessage(kWhatShutdown, id()))->post();
+}
+
+void DecoderWrapper::signalFlush() {
+ (new AMessage(kWhatFlush, id()))->post();
+}
+
+void DecoderWrapper::signalResume() {
+ (new AMessage(kWhatResume, id()))->post();
+}
+
+void DecoderWrapper::onMessageReceived(const sp<AMessage> &msg) {
+ switch (msg->what()) {
+ case kWhatSetup:
+ onSetup(msg);
+ break;
+
+ case kWhatInputDataRequested:
+ {
+ postFillBuffer();
+ ++mNumOutstandingInputBuffers;
+ break;
+ }
+
+ case kWhatInputBufferFilled:
+ {
+ CHECK_GT(mNumOutstandingInputBuffers, 0);
+ --mNumOutstandingInputBuffers;
+
+ if (mFlushing) {
+ mSource->queueEOS(INFO_DISCONTINUITY);
+
+ completeFlushIfPossible();
+ break;
+ }
+
+ sp<RefBase> obj;
+ if (!msg->findObject("buffer", &obj)) {
+ int32_t err = OK;
+ CHECK(msg->findInt32("err", &err));
+
+ mSource->queueEOS(err);
+ break;
+ }
+
+ sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get());
+
+ mSource->queueBuffer(buffer);
+ break;
+ }
+
+ case kWhatFillBufferDone:
+ {
+ CHECK_GT(mNumPendingDecodes, 0);
+ --mNumPendingDecodes;
+
+ if (mFlushing) {
+ completeFlushIfPossible();
+ break;
+ }
+
+ sp<AMessage> notify;
+ CHECK(msg->findMessage("real-notify", &notify));
+
+ sp<AMessage> reply =
+ new AMessage(kWhatOutputBufferDrained, id());
+
+ notify->setMessage("reply", reply);
+ notify->post();
+
+ ++mNumOutstandingOutputBuffers;
+ break;
+ }
+
+ case kWhatOutputBufferDrained:
+ {
+ CHECK_GT(mNumOutstandingOutputBuffers, 0);
+ --mNumOutstandingOutputBuffers;
+
+ if (mFlushing) {
+ completeFlushIfPossible();
+ break;
+ }
+
+ ++mNumPendingDecodes;
+ mReader->readMore();
+ break;
+ }
+
+ case kWhatFlush:
+ {
+ onFlush();
+ break;
+ }
+
+ case kWhatResume:
+ {
+ onResume();
+ break;
+ }
+
+ default:
+ TRESPASS();
+ break;
+ }
+}
+
+void DecoderWrapper::onSetup(const sp<AMessage> &msg) {
+ AString mime;
+ CHECK(msg->findString("mime", &mime));
+
+ CHECK(!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_AUDIO_AAC));
+
+ int32_t numChannels, sampleRate;
+ CHECK(msg->findInt32("channel-count", &numChannels));
+ CHECK(msg->findInt32("sample-rate", &sampleRate));
+
+ sp<RefBase> obj;
+ CHECK(msg->findObject("esds", &obj));
+ sp<ABuffer> esds = static_cast<ABuffer *>(obj.get());
+
+ sp<MetaData> meta = new MetaData;
+ meta->setCString(kKeyMIMEType, mime.c_str());
+ meta->setInt32(kKeySampleRate, sampleRate);
+ meta->setInt32(kKeyChannelCount, numChannels);
+ meta->setData(kKeyESDS, 0, esds->data(), esds->size());
+
+ mSource = new WrapperSource(
+ meta, new AMessage(kWhatInputDataRequested, id()));
+
+ sp<MediaSource> decoder = new AACDecoder(mSource);
+
+ mReaderLooper = new ALooper;
+ mReaderLooper->setName("DecoderWrapper looper");
+
+ mReaderLooper->start(
+ false, /* runOnCallingThread */
+ false, /* canCallJava */
+ PRIORITY_AUDIO);
+
+ sp<AMessage> notify = new AMessage(kWhatFillBufferDone, id());
+ notify->setMessage("real-notify", mNotify);
+
+ mReader = new WrapperReader(decoder, notify);
+ mReaderLooper->registerHandler(mReader);
+
+ mReader->start();
+ ++mNumPendingDecodes;
+}
+
+void DecoderWrapper::postFillBuffer() {
+ sp<AMessage> notify = mNotify->dup();
+ notify->setInt32("what", ACodec::kWhatFillThisBuffer);
+ sp<AMessage> reply = new AMessage(kWhatInputBufferFilled, id());
+ notify->setMessage("reply", reply);
+ notify->post();
+}
+
+void DecoderWrapper::onFlush() {
+ CHECK(!mFlushing);
+ mFlushing = true;
+
+ completeFlushIfPossible();
+}
+
+void DecoderWrapper::completeFlushIfPossible() {
+ CHECK(mFlushing);
+
+ if (mNumOutstandingInputBuffers > 0
+ || mNumOutstandingOutputBuffers > 0
+ || mNumPendingDecodes > 0) {
+ return;
+ }
+
+ mFlushing = false;
+
+ sp<AMessage> notify = mNotify->dup();
+ notify->setInt32("what", ACodec::kWhatFlushCompleted);
+ notify->post();
+}
+
+void DecoderWrapper::onResume() {
+ CHECK(!mFlushing);
+
+ ++mNumPendingDecodes;
+
+ mSource->clear();
+ mReader->readMore(true /* flush */);
+}
+
+} // namespace android
diff --git a/media/libmediaplayerservice/nuplayer/DecoderWrapper.h b/media/libmediaplayerservice/nuplayer/DecoderWrapper.h
new file mode 100644
index 0000000..883b356
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/DecoderWrapper.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2010 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef DECODER_WRAPPER_H_
+
+#define DECODER_WRAPPER_H_
+
+#include <media/stagefright/foundation/AHandler.h>
+
+namespace android {
+
+struct MediaSource;
+
+struct DecoderWrapper : public AHandler {
+ DecoderWrapper();
+
+ void setNotificationMessage(const sp<AMessage> &msg);
+ void initiateSetup(const sp<AMessage> &msg);
+ void initiateShutdown();
+ void signalFlush();
+ void signalResume();
+
+protected:
+ virtual ~DecoderWrapper();
+
+ virtual void onMessageReceived(const sp<AMessage> &msg);
+
+private:
+ struct WrapperSource;
+ struct WrapperReader;
+
+ enum {
+ kWhatSetup,
+ kWhatInputBufferFilled,
+ kWhatOutputBufferDrained,
+ kWhatShutdown,
+ kWhatFillBufferDone,
+ kWhatInputDataRequested,
+ kWhatFlush,
+ kWhatResume,
+ };
+
+ sp<AMessage> mNotify;
+
+ sp<WrapperSource> mSource;
+
+ sp<ALooper> mReaderLooper;
+ sp<WrapperReader> mReader;
+
+ int32_t mNumOutstandingInputBuffers;
+ int32_t mNumOutstandingOutputBuffers;
+ int32_t mNumPendingDecodes;
+ bool mFlushing;
+
+ void onSetup(const sp<AMessage> &msg);
+ void onFlush();
+ void onResume();
+
+ void postFillBuffer();
+ void completeFlushIfPossible();
+
+ DISALLOW_EVIL_CONSTRUCTORS(DecoderWrapper);
+};
+
+} // namespace android
+
+#endif // DECODER_WRAPPER_H_
+
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
new file mode 100644
index 0000000..403029a
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -0,0 +1,477 @@
+/*
+ * Copyright (C) 2010 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "NuPlayer"
+#include <utils/Log.h>
+
+#include "NuPlayer.h"
+#include "NuPlayerDecoder.h"
+#include "NuPlayerRenderer.h"
+#include "NuPlayerStreamListener.h"
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/ACodec.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MetaData.h>
+#include <surfaceflinger/Surface.h>
+
+namespace android {
+
+////////////////////////////////////////////////////////////////////////////////
+
+NuPlayer::NuPlayer()
+ : mEOS(false),
+ mAudioEOS(false),
+ mVideoEOS(false),
+ mFlushingAudio(NONE),
+ mFlushingVideo(NONE) {
+}
+
+NuPlayer::~NuPlayer() {
+}
+
+void NuPlayer::setListener(const wp<MediaPlayerBase> &listener) {
+ mListener = listener;
+}
+
+void NuPlayer::setDataSource(const sp<IStreamSource> &source) {
+ sp<AMessage> msg = new AMessage(kWhatSetDataSource, id());
+
+ source->incStrong(this);
+ msg->setPointer("source", source.get()); // XXX unsafe.
+
+ msg->post();
+}
+
+void NuPlayer::setVideoSurface(const sp<Surface> &surface) {
+ sp<AMessage> msg = new AMessage(kWhatSetVideoSurface, id());
+ msg->setObject("surface", surface);
+ msg->post();
+}
+
+void NuPlayer::setAudioSink(const sp<MediaPlayerBase::AudioSink> &sink) {
+ sp<AMessage> msg = new AMessage(kWhatSetAudioSink, id());
+ msg->setObject("sink", sink);
+ msg->post();
+}
+
+void NuPlayer::start() {
+ (new AMessage(kWhatStart, id()))->post();
+}
+
+void NuPlayer::onMessageReceived(const sp<AMessage> &msg) {
+ switch (msg->what()) {
+ case kWhatSetDataSource:
+ {
+ LOGI("kWhatSetDataSource");
+
+ CHECK(mSource == NULL);
+
+ void *ptr;
+ CHECK(msg->findPointer("source", &ptr));
+
+ mSource = static_cast<IStreamSource *>(ptr);
+ mSource->decStrong(this);
+
+ mStreamListener = new NuPlayerStreamListener(mSource, id());
+ mTSParser = new ATSParser;
+ break;
+ }
+
+ case kWhatSetVideoSurface:
+ {
+ LOGI("kWhatSetVideoSurface");
+
+ sp<RefBase> obj;
+ CHECK(msg->findObject("surface", &obj));
+
+ mSurface = static_cast<Surface *>(obj.get());
+ break;
+ }
+
+ case kWhatSetAudioSink:
+ {
+ LOGI("kWhatSetAudioSink");
+
+ sp<RefBase> obj;
+ CHECK(msg->findObject("sink", &obj));
+
+ mAudioSink = static_cast<MediaPlayerBase::AudioSink *>(obj.get());
+ break;
+ }
+
+ case kWhatStart:
+ {
+ mStreamListener->start();
+
+ mRenderer = new Renderer(
+ mAudioSink,
+ new AMessage(kWhatRendererNotify, id()));
+
+ looper()->registerHandler(mRenderer);
+
+ (new AMessage(kWhatScanSources, id()))->post();
+ break;
+ }
+
+ case kWhatScanSources:
+ {
+ instantiateDecoder(false, &mVideoDecoder);
+
+ if (mAudioSink != NULL) {
+ instantiateDecoder(true, &mAudioDecoder);
+ }
+
+ if (mEOS) {
+ break;
+ }
+
+ feedMoreTSData();
+
+ if (mAudioDecoder == NULL || mVideoDecoder == NULL) {
+ msg->post(100000ll);
+ }
+ break;
+ }
+
+ case kWhatVideoNotify:
+ case kWhatAudioNotify:
+ {
+ bool audio = msg->what() == kWhatAudioNotify;
+
+ sp<AMessage> codecRequest;
+ CHECK(msg->findMessage("codec-request", &codecRequest));
+
+ int32_t what;
+ CHECK(codecRequest->findInt32("what", &what));
+
+ if (what == ACodec::kWhatFillThisBuffer) {
+ status_t err = feedDecoderInputData(
+ audio, codecRequest);
+
+ if (err == -EWOULDBLOCK && !mEOS) {
+ feedMoreTSData();
+ msg->post();
+ }
+ } else if (what == ACodec::kWhatEOS) {
+ mRenderer->queueEOS(audio, ERROR_END_OF_STREAM);
+ } else if (what == ACodec::kWhatFlushCompleted) {
+ if (audio) {
+ CHECK_EQ((int)mFlushingAudio, (int)FLUSHING_DECODER);
+ mFlushingAudio = FLUSHED;
+ } else {
+ CHECK_EQ((int)mFlushingVideo, (int)FLUSHING_DECODER);
+ mFlushingVideo = FLUSHED;
+ }
+
+ LOGI("decoder %s flush completed", audio ? "audio" : "video");
+
+ if (mFlushingAudio == FLUSHED && mFlushingVideo == FLUSHED) {
+ LOGI("both audio and video are flushed now.");
+
+ mRenderer->signalTimeDiscontinuity();
+
+ if (mAudioDecoder != NULL) {
+ mAudioDecoder->signalResume();
+ }
+
+ if (mVideoDecoder != NULL) {
+ mVideoDecoder->signalResume();
+ }
+
+ mFlushingAudio = NONE;
+ mFlushingVideo = NONE;
+ }
+ } else {
+ CHECK_EQ((int)what, (int)ACodec::kWhatDrainThisBuffer);
+
+ renderBuffer(audio, codecRequest);
+ }
+
+ break;
+ }
+
+ case kWhatRendererNotify:
+ {
+ int32_t what;
+ CHECK(msg->findInt32("what", &what));
+
+ if (what == Renderer::kWhatEOS) {
+ int32_t audio;
+ CHECK(msg->findInt32("audio", &audio));
+
+ if (audio) {
+ mAudioEOS = true;
+ } else {
+ mVideoEOS = true;
+ }
+
+ LOGI("reached %s EOS", audio ? "audio" : "video");
+
+ if ((mAudioEOS || mAudioDecoder == NULL)
+ && (mVideoEOS || mVideoDecoder == NULL)) {
+ notifyListener(MEDIA_PLAYBACK_COMPLETE, 0, 0);
+ }
+ } else {
+ CHECK_EQ(what, (int32_t)Renderer::kWhatFlushComplete);
+
+ int32_t audio;
+ CHECK(msg->findInt32("audio", &audio));
+
+ LOGI("renderer %s flush completed.", audio ? "audio" : "video");
+ }
+ break;
+ }
+
+ case kWhatMoreDataQueued:
+ {
+ break;
+ }
+
+ default:
+ TRESPASS();
+ break;
+ }
+}
+
+void NuPlayer::feedMoreTSData() {
+ CHECK(!mEOS);
+
+ for (int32_t i = 0; i < 10; ++i) {
+ char buffer[188];
+ ssize_t n = mStreamListener->read(buffer, sizeof(buffer));
+
+ if (n == 0) {
+ LOGI("input data EOS reached.");
+ mTSParser->signalEOS(ERROR_END_OF_STREAM);
+ mEOS = true;
+ break;
+ } else if (n == INFO_DISCONTINUITY) {
+ mTSParser->signalDiscontinuity(ATSParser::DISCONTINUITY_SEEK);
+ } else if (n < 0) {
+ CHECK_EQ(n, -EWOULDBLOCK);
+ break;
+ } else {
+ if (buffer[0] == 0x00) {
+ // XXX legacy
+ mTSParser->signalDiscontinuity(ATSParser::DISCONTINUITY_SEEK);
+ } else {
+ mTSParser->feedTSPacket(buffer, sizeof(buffer));
+ }
+ }
+ }
+}
+
+status_t NuPlayer::dequeueNextAccessUnit(
+ ATSParser::SourceType *type, sp<ABuffer> *accessUnit) {
+ accessUnit->clear();
+
+ status_t audioErr = -EWOULDBLOCK;
+ int64_t audioTimeUs;
+
+ sp<AnotherPacketSource> audioSource =
+ static_cast<AnotherPacketSource *>(
+ mTSParser->getSource(ATSParser::MPEG2ADTS_AUDIO).get());
+
+ if (audioSource != NULL) {
+ audioErr = audioSource->nextBufferTime(&audioTimeUs);
+ }
+
+ status_t videoErr = -EWOULDBLOCK;
+ int64_t videoTimeUs;
+
+ sp<AnotherPacketSource> videoSource =
+ static_cast<AnotherPacketSource *>(
+ mTSParser->getSource(ATSParser::AVC_VIDEO).get());
+
+ if (videoSource != NULL) {
+ videoErr = videoSource->nextBufferTime(&videoTimeUs);
+ }
+
+ if (audioErr == -EWOULDBLOCK || videoErr == -EWOULDBLOCK) {
+ return -EWOULDBLOCK;
+ }
+
+ if (audioErr != OK && videoErr != OK) {
+ return audioErr;
+ }
+
+ if (videoErr != OK || (audioErr == OK && audioTimeUs < videoTimeUs)) {
+ *type = ATSParser::MPEG2ADTS_AUDIO;
+ return audioSource->dequeueAccessUnit(accessUnit);
+ } else {
+ *type = ATSParser::AVC_VIDEO;
+ return videoSource->dequeueAccessUnit(accessUnit);
+ }
+}
+
+status_t NuPlayer::dequeueAccessUnit(
+ ATSParser::SourceType type, sp<ABuffer> *accessUnit) {
+ sp<AnotherPacketSource> source =
+ static_cast<AnotherPacketSource *>(mTSParser->getSource(type).get());
+
+ if (source == NULL) {
+ return -EWOULDBLOCK;
+ }
+
+ status_t finalResult;
+ if (!source->hasBufferAvailable(&finalResult)) {
+ return finalResult == OK ? -EWOULDBLOCK : finalResult;
+ }
+
+ return source->dequeueAccessUnit(accessUnit);
+}
+
+status_t NuPlayer::instantiateDecoder(
+ bool audio, sp<Decoder> *decoder) {
+ if (*decoder != NULL) {
+ return OK;
+ }
+
+ ATSParser::SourceType type =
+ audio ? ATSParser::MPEG2ADTS_AUDIO : ATSParser::AVC_VIDEO;
+
+ sp<AnotherPacketSource> source =
+ static_cast<AnotherPacketSource *>(
+ mTSParser->getSource(type).get());
+
+ if (source == NULL) {
+ return -EWOULDBLOCK;
+ }
+
+ sp<AMessage> notify =
+ new AMessage(audio ? kWhatAudioNotify : kWhatVideoNotify,
+ id());
+
+ *decoder = new Decoder(notify, audio ? NULL : mSurface);
+ looper()->registerHandler(*decoder);
+
+ const sp<MetaData> &meta = source->getFormat();
+ (*decoder)->configure(meta);
+
+ if (audio) {
+ int32_t sampleRate;
+ int32_t channelCount;
+ CHECK(meta->findInt32(kKeySampleRate, &sampleRate));
+ CHECK(meta->findInt32(kKeyChannelCount, &channelCount));
+
+ channelCount = 2; // XXX
+
+ CHECK_EQ(mAudioSink->open(sampleRate, channelCount), (status_t)OK);
+ mAudioSink->start();
+ }
+
+ return OK;
+}
+
+status_t NuPlayer::feedDecoderInputData(bool audio, const sp<AMessage> &msg) {
+ sp<AMessage> reply;
+ CHECK(msg->findMessage("reply", &reply));
+
+ if ((audio && mFlushingAudio == FLUSHING_DECODER)
+ || (!audio && mFlushingVideo == FLUSHING_DECODER)) {
+ reply->setInt32("err", INFO_DISCONTINUITY);
+ reply->post();
+ return OK;
+ }
+
+ sp<ABuffer> accessUnit;
+ status_t err = dequeueAccessUnit(
+ audio ? ATSParser::MPEG2ADTS_AUDIO : ATSParser::AVC_VIDEO,
+ &accessUnit);
+
+ if (err == -EWOULDBLOCK) {
+ return err;
+ } else if (err != OK) {
+ if (err == INFO_DISCONTINUITY) {
+ LOGI("%s discontinuity", audio ? "audio" : "video");
+ (audio ? mAudioDecoder : mVideoDecoder)->signalFlush();
+ mRenderer->flush(audio);
+
+ if (audio) {
+ CHECK(mFlushingAudio == NONE
+ || mFlushingAudio == AWAITING_DISCONTINUITY);
+ mFlushingAudio = FLUSHING_DECODER;
+ if (mFlushingVideo == NONE) {
+ mFlushingVideo = (mVideoDecoder != NULL)
+ ? AWAITING_DISCONTINUITY
+ : FLUSHED;
+ }
+ } else {
+ CHECK(mFlushingVideo == NONE
+ || mFlushingVideo == AWAITING_DISCONTINUITY);
+ mFlushingVideo = FLUSHING_DECODER;
+ if (mFlushingAudio == NONE) {
+ mFlushingAudio = (mAudioDecoder != NULL)
+ ? AWAITING_DISCONTINUITY
+ : FLUSHED;
+ }
+ }
+ }
+
+ reply->setInt32("err", err);
+ reply->post();
+ return OK;
+ }
+
+ LOGV("returned a valid buffer of %s data", audio ? "audio" : "video");
+
+#if 0
+ int64_t mediaTimeUs;
+ CHECK(accessUnit->meta()->findInt64("timeUs", &mediaTimeUs));
+ LOGI("feeding %s input buffer at media time %.2f secs",
+ audio ? "audio" : "video",
+ mediaTimeUs / 1E6);
+#endif
+
+ reply->setObject("buffer", accessUnit);
+ reply->post();
+
+ return OK;
+}
+
+void NuPlayer::renderBuffer(bool audio, const sp<AMessage> &msg) {
+ LOGV("renderBuffer %s", audio ? "audio" : "video");
+
+ sp<AMessage> reply;
+ CHECK(msg->findMessage("reply", &reply));
+
+ sp<RefBase> obj;
+ CHECK(msg->findObject("buffer", &obj));
+
+ sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get());
+
+ mRenderer->queueBuffer(audio, buffer, reply);
+}
+
+void NuPlayer::notifyListener(int msg, int ext1, int ext2) {
+ if (mListener == NULL) {
+ return;
+ }
+
+ sp<MediaPlayerBase> listener = mListener.promote();
+
+ if (listener == NULL) {
+ return;
+ }
+
+ listener->sendEvent(msg, ext1, ext2);
+}
+
+} // namespace android
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h
new file mode 100644
index 0000000..9a5a6c4
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2010 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef NU_PLAYER_H_
+
+#define NU_PLAYER_H_
+
+#include <media/MediaPlayerInterface.h>
+#include <media/stagefright/foundation/AHandler.h>
+
+#include "ATSParser.h"
+#include "AnotherPacketSource.h"
+
+namespace android {
+
+struct ACodec;
+struct MetaData;
+
+struct NuPlayer : public AHandler {
+ NuPlayer();
+
+ void setListener(const wp<MediaPlayerBase> &listener);
+
+ void setDataSource(const sp<IStreamSource> &source);
+ void setVideoSurface(const sp<Surface> &surface);
+ void setAudioSink(const sp<MediaPlayerBase::AudioSink> &sink);
+ void start();
+
+protected:
+ virtual ~NuPlayer();
+
+ virtual void onMessageReceived(const sp<AMessage> &msg);
+
+private:
+ struct Renderer;
+ struct Decoder;
+ struct NuPlayerStreamListener;
+
+ enum {
+ kWhatSetDataSource,
+ kWhatSetVideoSurface,
+ kWhatSetAudioSink,
+ kWhatMoreDataQueued,
+ kWhatStart,
+ kWhatScanSources,
+ kWhatVideoNotify,
+ kWhatAudioNotify,
+ kWhatRendererNotify,
+ };
+
+ wp<MediaPlayerBase> mListener;
+ sp<IStreamSource> mSource;
+ sp<Surface> mSurface;
+ sp<MediaPlayerBase::AudioSink> mAudioSink;
+ sp<NuPlayerStreamListener> mStreamListener;
+ sp<ATSParser> mTSParser;
+ sp<Decoder> mVideoDecoder;
+ sp<Decoder> mAudioDecoder;
+ sp<Renderer> mRenderer;
+
+ bool mEOS;
+ bool mAudioEOS;
+ bool mVideoEOS;
+
+ enum FlushStatus {
+ NONE,
+ AWAITING_DISCONTINUITY,
+ FLUSHING_DECODER,
+ FLUSHED
+ };
+
+ FlushStatus mFlushingAudio;
+ FlushStatus mFlushingVideo;
+
+ status_t instantiateDecoder(
+ bool audio, sp<Decoder> *decoder);
+
+ status_t feedDecoderInputData(bool audio, const sp<AMessage> &msg);
+ void renderBuffer(bool audio, const sp<AMessage> &msg);
+
+ status_t dequeueNextAccessUnit(
+ ATSParser::SourceType *type, sp<ABuffer> *accessUnit);
+
+ status_t dequeueAccessUnit(
+ ATSParser::SourceType type, sp<ABuffer> *accessUnit);
+
+ void feedMoreTSData();
+ void notifyListener(int msg, int ext1, int ext2);
+
+ DISALLOW_EVIL_CONSTRUCTORS(NuPlayer);
+};
+
+} // namespace android
+
+#endif // NU_PLAYER_H_
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
new file mode 100644
index 0000000..d1ed222
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -0,0 +1,286 @@
+/*
+ * Copyright (C) 2010 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "NuPlayerDecoder"
+#include <utils/Log.h>
+
+#include "NuPlayerDecoder.h"
+
+#include "DecoderWrapper.h"
+#include "ESDS.h"
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/ACodec.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/Utils.h>
+#include <surfaceflinger/Surface.h>
+
+namespace android {
+
+NuPlayer::Decoder::Decoder(
+ const sp<AMessage> &notify, const sp<Surface> &surface)
+ : mNotify(notify),
+ mSurface(surface) {
+}
+
+NuPlayer::Decoder::~Decoder() {
+}
+
+void NuPlayer::Decoder::configure(const sp<MetaData> &meta) {
+ CHECK(mCodec == NULL);
+ CHECK(mWrapper == NULL);
+
+ const char *mime;
+ CHECK(meta->findCString(kKeyMIMEType, &mime));
+
+ sp<AMessage> notifyMsg =
+ new AMessage(kWhatCodecNotify, id());
+
+ sp<AMessage> format = makeFormat(meta);
+
+ if (mSurface != NULL) {
+ format->setObject("surface", mSurface);
+ }
+
+ if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) {
+ mWrapper = new DecoderWrapper;
+ looper()->registerHandler(mWrapper);
+
+ mWrapper->setNotificationMessage(notifyMsg);
+ mWrapper->initiateSetup(format);
+ } else {
+ mCodec = new ACodec;
+ looper()->registerHandler(mCodec);
+
+ mCodec->setNotificationMessage(notifyMsg);
+ mCodec->initiateSetup(format);
+ }
+}
+
+void NuPlayer::Decoder::onMessageReceived(const sp<AMessage> &msg) {
+ switch (msg->what()) {
+ case kWhatCodecNotify:
+ {
+ int32_t what;
+ CHECK(msg->findInt32("what", &what));
+
+ if (what == ACodec::kWhatFillThisBuffer) {
+ onFillThisBuffer(msg);
+ } else {
+ sp<AMessage> notify = mNotify->dup();
+ notify->setMessage("codec-request", msg);
+ notify->post();
+ }
+ break;
+ }
+
+ default:
+ TRESPASS();
+ break;
+ }
+}
+
+sp<AMessage> NuPlayer::Decoder::makeFormat(const sp<MetaData> &meta) {
+ CHECK(mCSD.isEmpty());
+
+ const char *mime;
+ CHECK(meta->findCString(kKeyMIMEType, &mime));
+
+ sp<AMessage> msg = new AMessage;
+ msg->setString("mime", mime);
+
+ if (!strncasecmp("video/", mime, 6)) {
+ int32_t width, height;
+ CHECK(meta->findInt32(kKeyWidth, &width));
+ CHECK(meta->findInt32(kKeyHeight, &height));
+
+ msg->setInt32("width", width);
+ msg->setInt32("height", height);
+ } else {
+ CHECK(!strncasecmp("audio/", mime, 6));
+
+ int32_t numChannels, sampleRate;
+ CHECK(meta->findInt32(kKeyChannelCount, &numChannels));
+ CHECK(meta->findInt32(kKeySampleRate, &sampleRate));
+
+ msg->setInt32("channel-count", numChannels);
+ msg->setInt32("sample-rate", sampleRate);
+ }
+
+ uint32_t type;
+ const void *data;
+ size_t size;
+ if (meta->findData(kKeyAVCC, &type, &data, &size)) {
+ // Parse the AVCDecoderConfigurationRecord
+
+ const uint8_t *ptr = (const uint8_t *)data;
+
+ CHECK(size >= 7);
+ CHECK_EQ((unsigned)ptr[0], 1u); // configurationVersion == 1
+ uint8_t profile = ptr[1];
+ uint8_t level = ptr[3];
+
+ // There is decodable content out there that fails the following
+ // assertion, let's be lenient for now...
+ // CHECK((ptr[4] >> 2) == 0x3f); // reserved
+
+ size_t lengthSize = 1 + (ptr[4] & 3);
+
+ // commented out check below as H264_QVGA_500_NO_AUDIO.3gp
+ // violates it...
+ // CHECK((ptr[5] >> 5) == 7); // reserved
+
+ size_t numSeqParameterSets = ptr[5] & 31;
+
+ ptr += 6;
+ size -= 6;
+
+ sp<ABuffer> buffer = new ABuffer(1024);
+ buffer->setRange(0, 0);
+
+ for (size_t i = 0; i < numSeqParameterSets; ++i) {
+ CHECK(size >= 2);
+ size_t length = U16_AT(ptr);
+
+ ptr += 2;
+ size -= 2;
+
+ CHECK(size >= length);
+
+ memcpy(buffer->data() + buffer->size(), "\x00\x00\x00\x01", 4);
+ memcpy(buffer->data() + buffer->size() + 4, ptr, length);
+ buffer->setRange(0, buffer->size() + 4 + length);
+
+ ptr += length;
+ size -= length;
+ }
+
+ buffer->meta()->setInt32("csd", true);
+ mCSD.push(buffer);
+
+ buffer = new ABuffer(1024);
+ buffer->setRange(0, 0);
+
+ CHECK(size >= 1);
+ size_t numPictureParameterSets = *ptr;
+ ++ptr;
+ --size;
+
+ for (size_t i = 0; i < numPictureParameterSets; ++i) {
+ CHECK(size >= 2);
+ size_t length = U16_AT(ptr);
+
+ ptr += 2;
+ size -= 2;
+
+ CHECK(size >= length);
+
+ memcpy(buffer->data() + buffer->size(), "\x00\x00\x00\x01", 4);
+ memcpy(buffer->data() + buffer->size() + 4, ptr, length);
+ buffer->setRange(0, buffer->size() + 4 + length);
+
+ ptr += length;
+ size -= length;
+ }
+
+ buffer->meta()->setInt32("csd", true);
+ mCSD.push(buffer);
+
+ msg->setObject("csd", buffer);
+ } else if (meta->findData(kKeyESDS, &type, &data, &size)) {
+#if 0
+ ESDS esds((const char *)data, size);
+ CHECK_EQ(esds.InitCheck(), (status_t)OK);
+
+ const void *codec_specific_data;
+ size_t codec_specific_data_size;
+ esds.getCodecSpecificInfo(
+ &codec_specific_data, &codec_specific_data_size);
+
+ sp<ABuffer> buffer = new ABuffer(codec_specific_data_size);
+
+ memcpy(buffer->data(), codec_specific_data,
+ codec_specific_data_size);
+
+ buffer->meta()->setInt32("csd", true);
+ mCSD.push(buffer);
+#else
+ sp<ABuffer> buffer = new ABuffer(size);
+ memcpy(buffer->data(), data, size);
+
+ msg->setObject("esds", buffer);
+#endif
+ }
+
+ int32_t maxInputSize;
+ if (meta->findInt32(kKeyMaxInputSize, &maxInputSize)) {
+ msg->setInt32("max-input-size", maxInputSize);
+ }
+
+ mCSDIndex = 0;
+
+ return msg;
+}
+
+void NuPlayer::Decoder::onFillThisBuffer(const sp<AMessage> &msg) {
+ sp<AMessage> reply;
+ CHECK(msg->findMessage("reply", &reply));
+
+#if 0
+ sp<RefBase> obj;
+ CHECK(msg->findObject("buffer", &obj));
+ sp<ABuffer> outBuffer = static_cast<ABuffer *>(obj.get());
+#else
+ sp<ABuffer> outBuffer;
+#endif
+
+ if (mCSDIndex < mCSD.size()) {
+ outBuffer = mCSD.editItemAt(mCSDIndex++);
+ outBuffer->meta()->setInt64("timeUs", 0);
+
+ reply->setObject("buffer", outBuffer);
+ reply->post();
+ return;
+ }
+
+ sp<AMessage> notify = mNotify->dup();
+ notify->setMessage("codec-request", msg);
+ notify->post();
+}
+
+void NuPlayer::Decoder::signalFlush() {
+ if (mCodec != NULL) {
+ mCodec->signalFlush();
+ } else {
+ CHECK(mWrapper != NULL);
+ mWrapper->signalFlush();
+ }
+}
+
+void NuPlayer::Decoder::signalResume() {
+ if (mCodec != NULL) {
+ mCodec->signalResume();
+ } else {
+ CHECK(mWrapper != NULL);
+ mWrapper->signalResume();
+ }
+}
+
+} // namespace android
+
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
new file mode 100644
index 0000000..77800be
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2010 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef NUPLAYER_DECODER_H_
+
+#define NUPLAYER_DECODER_H_
+
+#include "NuPlayer.h"
+
+#include <media/stagefright/foundation/AHandler.h>
+
+namespace android {
+
+struct DecoderWrapper;
+
+struct NuPlayer::Decoder : public AHandler {
+ Decoder(const sp<AMessage> &notify, const sp<Surface> &surface = NULL);
+
+ void configure(const sp<MetaData> &meta);
+ void signalFlush();
+ void signalResume();
+
+protected:
+ virtual ~Decoder();
+
+ virtual void onMessageReceived(const sp<AMessage> &msg);
+
+private:
+ enum {
+ kWhatCodecNotify,
+ };
+
+ sp<AMessage> mNotify;
+ sp<Surface> mSurface;
+
+ sp<ACodec> mCodec;
+ sp<DecoderWrapper> mWrapper;
+
+ Vector<sp<ABuffer> > mCSD;
+ size_t mCSDIndex;
+
+ sp<AMessage> makeFormat(const sp<MetaData> &meta);
+
+ void onFillThisBuffer(const sp<AMessage> &msg);
+
+ DISALLOW_EVIL_CONSTRUCTORS(Decoder);
+};
+
+} // namespace android
+
+#endif // NUPLAYER_DECODER_H_
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
new file mode 100644
index 0000000..b79251a
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2010 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "NuPlayerDriver"
+#include <utils/Log.h>
+
+#include "NuPlayerDriver.h"
+
+#include "NuPlayer.h"
+
+#include <media/stagefright/foundation/ALooper.h>
+
+namespace android {
+
+NuPlayerDriver::NuPlayerDriver()
+ : mLooper(new ALooper) {
+ mLooper->setName("NuPlayerDriver Looper");
+
+ mLooper->start(
+ false, /* runOnCallingThread */
+ true, /* canCallJava */
+ PRIORITY_AUDIO);
+
+ mPlayer = new NuPlayer;
+ mLooper->registerHandler(mPlayer);
+
+ mPlayer->setListener(this);
+}
+
+NuPlayerDriver::~NuPlayerDriver() {
+ mLooper->stop();
+}
+
+status_t NuPlayerDriver::initCheck() {
+ return OK;
+}
+
+status_t NuPlayerDriver::setDataSource(
+ const char *url, const KeyedVector<String8, String8> *headers) {
+ return INVALID_OPERATION;
+}
+
+status_t NuPlayerDriver::setDataSource(int fd, int64_t offset, int64_t length) {
+ return INVALID_OPERATION;
+}
+
+status_t NuPlayerDriver::setDataSource(const sp<IStreamSource> &source) {
+ mPlayer->setDataSource(source);
+
+ return OK;
+}
+
+status_t NuPlayerDriver::setVideoSurface(const sp<Surface> &surface) {
+ mPlayer->setVideoSurface(surface);
+
+ return OK;
+}
+
+status_t NuPlayerDriver::prepare() {
+ return OK;
+}
+
+status_t NuPlayerDriver::prepareAsync() {
+ return OK;
+}
+
+status_t NuPlayerDriver::start() {
+ mPlayer->start();
+
+ return OK;
+}
+
+status_t NuPlayerDriver::stop() {
+ return OK;
+}
+
+status_t NuPlayerDriver::pause() {
+ return OK;
+}
+
+bool NuPlayerDriver::isPlaying() {
+ return false;
+}
+
+status_t NuPlayerDriver::seekTo(int msec) {
+ return INVALID_OPERATION;
+}
+
+status_t NuPlayerDriver::getCurrentPosition(int *msec) {
+ return INVALID_OPERATION;
+}
+
+status_t NuPlayerDriver::getDuration(int *msec) {
+ return INVALID_OPERATION;
+}
+
+status_t NuPlayerDriver::reset() {
+ return OK;
+}
+
+status_t NuPlayerDriver::setLooping(int loop) {
+ return INVALID_OPERATION;
+}
+
+player_type NuPlayerDriver::playerType() {
+ return NU_PLAYER;
+}
+
+status_t NuPlayerDriver::invoke(const Parcel &request, Parcel *reply) {
+ return INVALID_OPERATION;
+}
+
+void NuPlayerDriver::setAudioSink(const sp<AudioSink> &audioSink) {
+ mPlayer->setAudioSink(audioSink);
+}
+
+status_t NuPlayerDriver::getMetadata(
+ const media::Metadata::Filter& ids, Parcel *records) {
+ return INVALID_OPERATION;
+}
+
+} // namespace android
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
new file mode 100644
index 0000000..245f1dd
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2010 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <media/MediaPlayerInterface.h>
+
+#include <media/stagefright/foundation/ABase.h>
+
+namespace android {
+
+struct ALooper;
+struct NuPlayer;
+
+struct NuPlayerDriver : public MediaPlayerInterface {
+ NuPlayerDriver();
+
+ virtual status_t initCheck();
+
+ virtual status_t setDataSource(
+ const char *url, const KeyedVector<String8, String8> *headers);
+
+ virtual status_t setDataSource(int fd, int64_t offset, int64_t length);
+
+ virtual status_t setDataSource(const sp<IStreamSource> &source);
+
+ virtual status_t setVideoSurface(const sp<Surface> &surface);
+ virtual status_t prepare();
+ virtual status_t prepareAsync();
+ virtual status_t start();
+ virtual status_t stop();
+ virtual status_t pause();
+ virtual bool isPlaying();
+ virtual status_t seekTo(int msec);
+ virtual status_t getCurrentPosition(int *msec);
+ virtual status_t getDuration(int *msec);
+ virtual status_t reset();
+ virtual status_t setLooping(int loop);
+ virtual player_type playerType();
+ virtual status_t invoke(const Parcel &request, Parcel *reply);
+ virtual void setAudioSink(const sp<AudioSink> &audioSink);
+
+ virtual status_t getMetadata(
+ const media::Metadata::Filter& ids, Parcel *records);
+
+protected:
+ virtual ~NuPlayerDriver();
+
+private:
+ sp<ALooper> mLooper;
+ sp<NuPlayer> mPlayer;
+
+ DISALLOW_EVIL_CONSTRUCTORS(NuPlayerDriver);
+};
+
+} // namespace android
+
+
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
new file mode 100644
index 0000000..855bc0a
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -0,0 +1,496 @@
+/*
+ * Copyright (C) 2010 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "NuPlayerRenderer"
+#include <utils/Log.h>
+
+#include "NuPlayerRenderer.h"
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+
+namespace android {
+
+NuPlayer::Renderer::Renderer(
+ const sp<MediaPlayerBase::AudioSink> &sink,
+ const sp<AMessage> &notify)
+ : mAudioSink(sink),
+ mNotify(notify),
+ mNumFramesWritten(0),
+ mDrainAudioQueuePending(false),
+ mDrainVideoQueuePending(false),
+ mAudioQueueGeneration(0),
+ mVideoQueueGeneration(0),
+ mAnchorTimeMediaUs(-1),
+ mAnchorTimeRealUs(-1),
+ mFlushingAudio(false),
+ mFlushingVideo(false),
+ mSyncQueues(true) {
+}
+
+NuPlayer::Renderer::~Renderer() {
+}
+
+void NuPlayer::Renderer::queueBuffer(
+ bool audio,
+ const sp<ABuffer> &buffer,
+ const sp<AMessage> &notifyConsumed) {
+ sp<AMessage> msg = new AMessage(kWhatQueueBuffer, id());
+ msg->setInt32("audio", static_cast<int32_t>(audio));
+ msg->setObject("buffer", buffer);
+ msg->setMessage("notifyConsumed", notifyConsumed);
+ msg->post();
+}
+
+void NuPlayer::Renderer::queueEOS(bool audio, status_t finalResult) {
+ CHECK_NE(finalResult, (status_t)OK);
+
+ sp<AMessage> msg = new AMessage(kWhatQueueEOS, id());
+ msg->setInt32("audio", static_cast<int32_t>(audio));
+ msg->setInt32("finalResult", finalResult);
+ msg->post();
+}
+
+void NuPlayer::Renderer::flush(bool audio) {
+ {
+ Mutex::Autolock autoLock(mFlushLock);
+ if (audio) {
+ CHECK(!mFlushingAudio);
+ mFlushingAudio = true;
+ } else {
+ CHECK(!mFlushingVideo);
+ mFlushingVideo = true;
+ }
+ }
+
+ sp<AMessage> msg = new AMessage(kWhatFlush, id());
+ msg->setInt32("audio", static_cast<int32_t>(audio));
+ msg->post();
+}
+
+void NuPlayer::Renderer::signalTimeDiscontinuity() {
+ CHECK(mAudioQueue.empty());
+ CHECK(mVideoQueue.empty());
+ mAnchorTimeMediaUs = -1;
+ mAnchorTimeRealUs = -1;
+ mSyncQueues = true;
+}
+
+void NuPlayer::Renderer::onMessageReceived(const sp<AMessage> &msg) {
+ switch (msg->what()) {
+ case kWhatDrainAudioQueue:
+ {
+ int32_t generation;
+ CHECK(msg->findInt32("generation", &generation));
+ if (generation != mAudioQueueGeneration) {
+ break;
+ }
+
+ mDrainAudioQueuePending = false;
+
+ onDrainAudioQueue();
+
+ postDrainAudioQueue();
+ break;
+ }
+
+ case kWhatDrainVideoQueue:
+ {
+ int32_t generation;
+ CHECK(msg->findInt32("generation", &generation));
+ if (generation != mVideoQueueGeneration) {
+ break;
+ }
+
+ mDrainVideoQueuePending = false;
+
+ onDrainVideoQueue();
+
+ postDrainVideoQueue();
+ break;
+ }
+
+ case kWhatQueueBuffer:
+ {
+ onQueueBuffer(msg);
+ break;
+ }
+
+ case kWhatQueueEOS:
+ {
+ onQueueEOS(msg);
+ break;
+ }
+
+ case kWhatFlush:
+ {
+ onFlush(msg);
+ break;
+ }
+
+ default:
+ TRESPASS();
+ break;
+ }
+}
+
+void NuPlayer::Renderer::postDrainAudioQueue() {
+ if (mDrainAudioQueuePending || mSyncQueues) {
+ return;
+ }
+
+ if (mAudioQueue.empty()) {
+ return;
+ }
+
+ mDrainAudioQueuePending = true;
+ sp<AMessage> msg = new AMessage(kWhatDrainAudioQueue, id());
+ msg->setInt32("generation", mAudioQueueGeneration);
+ msg->post(10000);
+}
+
+void NuPlayer::Renderer::onDrainAudioQueue() {
+ uint32_t numFramesPlayed;
+ CHECK_EQ(mAudioSink->getPosition(&numFramesPlayed), (status_t)OK);
+
+ ssize_t numFramesAvailableToWrite =
+ mAudioSink->frameCount() - (mNumFramesWritten - numFramesPlayed);
+
+ CHECK_GE(numFramesAvailableToWrite, 0);
+
+ size_t numBytesAvailableToWrite =
+ numFramesAvailableToWrite * mAudioSink->frameSize();
+
+ while (numBytesAvailableToWrite > 0) {
+ if (mAudioQueue.empty()) {
+ break;
+ }
+
+ QueueEntry *entry = &*mAudioQueue.begin();
+
+ if (entry->mBuffer == NULL) {
+ // EOS
+
+ notifyEOS(true /* audio */);
+
+ mAudioQueue.erase(mAudioQueue.begin());
+ entry = NULL;
+ return;
+ }
+
+ if (entry->mOffset == 0) {
+ int64_t mediaTimeUs;
+ CHECK(entry->mBuffer->meta()->findInt64("timeUs", &mediaTimeUs));
+
+ LOGV("rendering audio at media time %.2f secs", mediaTimeUs / 1E6);
+
+ mAnchorTimeMediaUs = mediaTimeUs;
+
+ uint32_t numFramesPlayed;
+ CHECK_EQ(mAudioSink->getPosition(&numFramesPlayed), (status_t)OK);
+
+ uint32_t numFramesPendingPlayout =
+ mNumFramesWritten - numFramesPlayed;
+
+ int64_t realTimeOffsetUs =
+ (mAudioSink->latency() / 2 /* XXX */
+ + numFramesPendingPlayout
+ * mAudioSink->msecsPerFrame()) * 1000ll;
+
+ // LOGI("realTimeOffsetUs = %lld us", realTimeOffsetUs);
+
+ mAnchorTimeRealUs =
+ ALooper::GetNowUs() + realTimeOffsetUs;
+ }
+
+ size_t copy = entry->mBuffer->size() - entry->mOffset;
+ if (copy > numBytesAvailableToWrite) {
+ copy = numBytesAvailableToWrite;
+ }
+
+ CHECK_EQ(mAudioSink->write(
+ entry->mBuffer->data() + entry->mOffset, copy),
+ (ssize_t)copy);
+
+ entry->mOffset += copy;
+ if (entry->mOffset == entry->mBuffer->size()) {
+ entry->mNotifyConsumed->post();
+ mAudioQueue.erase(mAudioQueue.begin());
+ entry = NULL;
+ }
+
+ numBytesAvailableToWrite -= copy;
+ mNumFramesWritten += copy / mAudioSink->frameSize();
+ }
+}
+
+void NuPlayer::Renderer::postDrainVideoQueue() {
+ if (mDrainVideoQueuePending || mSyncQueues) {
+ return;
+ }
+
+ if (mVideoQueue.empty()) {
+ return;
+ }
+
+ QueueEntry &entry = *mVideoQueue.begin();
+
+ sp<AMessage> msg = new AMessage(kWhatDrainVideoQueue, id());
+ msg->setInt32("generation", mVideoQueueGeneration);
+
+ int64_t delayUs;
+
+ if (entry.mBuffer == NULL) {
+ // EOS doesn't carry a timestamp.
+ delayUs = 0;
+ } else {
+ int64_t mediaTimeUs;
+ CHECK(entry.mBuffer->meta()->findInt64("timeUs", &mediaTimeUs));
+
+ if (mAnchorTimeMediaUs < 0) {
+ delayUs = 0;
+
+ if (mAudioSink == NULL) {
+ mAnchorTimeMediaUs = mediaTimeUs;
+ mAnchorTimeRealUs = ALooper::GetNowUs();
+ }
+ } else {
+ int64_t realTimeUs =
+ (mediaTimeUs - mAnchorTimeMediaUs) + mAnchorTimeRealUs;
+
+ delayUs = realTimeUs - ALooper::GetNowUs();
+ }
+ }
+
+ msg->post(delayUs);
+
+ mDrainVideoQueuePending = true;
+}
+
+void NuPlayer::Renderer::onDrainVideoQueue() {
+ if (mVideoQueue.empty()) {
+ return;
+ }
+
+ QueueEntry *entry = &*mVideoQueue.begin();
+
+ if (entry->mBuffer == NULL) {
+ // EOS
+
+ notifyEOS(false /* audio */);
+
+ mVideoQueue.erase(mVideoQueue.begin());
+ entry = NULL;
+ return;
+ }
+
+#if 0
+ int64_t mediaTimeUs;
+ CHECK(entry->mBuffer->meta()->findInt64("timeUs", &mediaTimeUs));
+
+ LOGI("rendering video at media time %.2f secs", mediaTimeUs / 1E6);
+#endif
+
+ entry->mNotifyConsumed->setInt32("render", true);
+ entry->mNotifyConsumed->post();
+ mVideoQueue.erase(mVideoQueue.begin());
+ entry = NULL;
+}
+
+void NuPlayer::Renderer::notifyEOS(bool audio) {
+ sp<AMessage> notify = mNotify->dup();
+ notify->setInt32("what", kWhatEOS);
+ notify->setInt32("audio", static_cast<int32_t>(audio));
+ notify->post();
+}
+
+void NuPlayer::Renderer::onQueueBuffer(const sp<AMessage> &msg) {
+ int32_t audio;
+ CHECK(msg->findInt32("audio", &audio));
+
+ if (dropBufferWhileFlushing(audio, msg)) {
+ return;
+ }
+
+ sp<RefBase> obj;
+ CHECK(msg->findObject("buffer", &obj));
+ sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get());
+
+ sp<AMessage> notifyConsumed;
+ CHECK(msg->findMessage("notifyConsumed", &notifyConsumed));
+
+ QueueEntry entry;
+ entry.mBuffer = buffer;
+ entry.mNotifyConsumed = notifyConsumed;
+ entry.mOffset = 0;
+ entry.mFinalResult = OK;
+
+ if (audio) {
+ mAudioQueue.push_back(entry);
+ postDrainAudioQueue();
+ } else {
+ mVideoQueue.push_back(entry);
+ postDrainVideoQueue();
+ }
+
+ if (mSyncQueues && !mAudioQueue.empty() && !mVideoQueue.empty()) {
+ int64_t firstAudioTimeUs;
+ int64_t firstVideoTimeUs;
+ CHECK((*mAudioQueue.begin()).mBuffer->meta()
+ ->findInt64("timeUs", &firstAudioTimeUs));
+ CHECK((*mVideoQueue.begin()).mBuffer->meta()
+ ->findInt64("timeUs", &firstVideoTimeUs));
+
+ int64_t diff = firstVideoTimeUs - firstAudioTimeUs;
+
+ LOGV("queueDiff = %.2f secs", diff / 1E6);
+
+ if (diff > 100000ll) {
+ // Audio data starts More than 0.1 secs before video.
+ // Drop some audio.
+
+ (*mAudioQueue.begin()).mNotifyConsumed->post();
+ mAudioQueue.erase(mAudioQueue.begin());
+ return;
+ }
+
+ syncQueuesDone();
+ }
+}
+
+void NuPlayer::Renderer::syncQueuesDone() {
+ if (!mSyncQueues) {
+ return;
+ }
+
+ mSyncQueues = false;
+
+ if (!mAudioQueue.empty()) {
+ postDrainAudioQueue();
+ }
+
+ if (!mVideoQueue.empty()) {
+ postDrainVideoQueue();
+ }
+}
+
+void NuPlayer::Renderer::onQueueEOS(const sp<AMessage> &msg) {
+ int32_t audio;
+ CHECK(msg->findInt32("audio", &audio));
+
+ if (dropBufferWhileFlushing(audio, msg)) {
+ return;
+ }
+
+ int32_t finalResult;
+ CHECK(msg->findInt32("finalResult", &finalResult));
+
+ QueueEntry entry;
+ entry.mOffset = 0;
+ entry.mFinalResult = finalResult;
+
+ if (audio) {
+ mAudioQueue.push_back(entry);
+ postDrainAudioQueue();
+ } else {
+ mVideoQueue.push_back(entry);
+ postDrainVideoQueue();
+ }
+}
+
+void NuPlayer::Renderer::onFlush(const sp<AMessage> &msg) {
+ int32_t audio;
+ CHECK(msg->findInt32("audio", &audio));
+
+ // If we're currently syncing the queues, i.e. dropping audio while
+ // aligning the first audio/video buffer times and only one of the
+ // two queues has data, we may starve that queue by not requesting
+ // more buffers from the decoder. If the other source then encounters
+ // a discontinuity that leads to flushing, we'll never find the
+ // corresponding discontinuity on the other queue.
+ // Therefore we'll stop syncing the queues if at least one of them
+ // is flushed.
+ syncQueuesDone();
+
+ if (audio) {
+ flushQueue(&mAudioQueue);
+
+ Mutex::Autolock autoLock(mFlushLock);
+ mFlushingAudio = false;
+
+ mDrainAudioQueuePending = false;
+ ++mAudioQueueGeneration;
+ } else {
+ flushQueue(&mVideoQueue);
+
+ Mutex::Autolock autoLock(mFlushLock);
+ mFlushingVideo = false;
+
+ mDrainVideoQueuePending = false;
+ ++mVideoQueueGeneration;
+ }
+
+ notifyFlushComplete(audio);
+}
+
+void NuPlayer::Renderer::flushQueue(List<QueueEntry> *queue) {
+ while (!queue->empty()) {
+ QueueEntry *entry = &*queue->begin();
+
+ if (entry->mBuffer != NULL) {
+ entry->mNotifyConsumed->post();
+ }
+
+ queue->erase(queue->begin());
+ entry = NULL;
+ }
+}
+
+void NuPlayer::Renderer::notifyFlushComplete(bool audio) {
+ sp<AMessage> notify = mNotify->dup();
+ notify->setInt32("what", kWhatFlushComplete);
+ notify->setInt32("audio", static_cast<int32_t>(audio));
+ notify->post();
+}
+
+bool NuPlayer::Renderer::dropBufferWhileFlushing(
+ bool audio, const sp<AMessage> &msg) {
+ bool flushing = false;
+
+ {
+ Mutex::Autolock autoLock(mFlushLock);
+ if (audio) {
+ flushing = mFlushingAudio;
+ } else {
+ flushing = mFlushingVideo;
+ }
+ }
+
+ if (!flushing) {
+ return false;
+ }
+
+ sp<AMessage> notifyConsumed;
+ if (msg->findMessage("notifyConsumed", &notifyConsumed)) {
+ notifyConsumed->post();
+ }
+
+ return true;
+}
+
+} // namespace android
+
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
new file mode 100644
index 0000000..834ddc5
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2010 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef NUPLAYER_RENDERER_H_
+
+#define NUPLAYER_RENDERER_H_
+
+#include "NuPlayer.h"
+
+namespace android {
+
+struct NuPlayer::Renderer : public AHandler {
+ Renderer(const sp<MediaPlayerBase::AudioSink> &sink,
+ const sp<AMessage> &notify);
+
+ void queueBuffer(
+ bool audio,
+ const sp<ABuffer> &buffer,
+ const sp<AMessage> &notifyConsumed);
+
+ void queueEOS(bool audio, status_t finalResult);
+
+ void flush(bool audio);
+
+ void signalTimeDiscontinuity();
+
+ enum {
+ kWhatEOS,
+ kWhatFlushComplete,
+ };
+
+protected:
+ virtual ~Renderer();
+
+ virtual void onMessageReceived(const sp<AMessage> &msg);
+
+private:
+ enum {
+ kWhatDrainAudioQueue,
+ kWhatDrainVideoQueue,
+ kWhatQueueBuffer,
+ kWhatQueueEOS,
+ kWhatFlush,
+ };
+
+ struct QueueEntry {
+ sp<ABuffer> mBuffer;
+ sp<AMessage> mNotifyConsumed;
+ size_t mOffset;
+ status_t mFinalResult;
+ };
+
+ sp<MediaPlayerBase::AudioSink> mAudioSink;
+ sp<AMessage> mNotify;
+ List<QueueEntry> mAudioQueue;
+ List<QueueEntry> mVideoQueue;
+ uint32_t mNumFramesWritten;
+
+ bool mDrainAudioQueuePending;
+ bool mDrainVideoQueuePending;
+ int32_t mAudioQueueGeneration;
+ int32_t mVideoQueueGeneration;
+
+ int64_t mAnchorTimeMediaUs;
+ int64_t mAnchorTimeRealUs;
+
+ Mutex mFlushLock; // protects the following 2 member vars.
+ bool mFlushingAudio;
+ bool mFlushingVideo;
+
+ bool mSyncQueues;
+
+ void onDrainAudioQueue();
+ void postDrainAudioQueue();
+
+ void onDrainVideoQueue();
+ void postDrainVideoQueue();
+
+ void onQueueBuffer(const sp<AMessage> &msg);
+ void onQueueEOS(const sp<AMessage> &msg);
+ void onFlush(const sp<AMessage> &msg);
+
+ void notifyEOS(bool audio);
+ void notifyFlushComplete(bool audio);
+
+ void flushQueue(List<QueueEntry> *queue);
+ bool dropBufferWhileFlushing(bool audio, const sp<AMessage> &msg);
+ void syncQueuesDone();
+
+ DISALLOW_EVIL_CONSTRUCTORS(Renderer);
+};
+
+} // namespace android
+
+#endif // NUPLAYER_RENDERER_H_
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.cpp
new file mode 100644
index 0000000..92642a8
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.cpp
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2010 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "NuPlayerStreamListener"
+#include <utils/Log.h>
+
+#include "NuPlayerStreamListener.h"
+
+#include <binder/MemoryDealer.h>
+#include <media/stagefright/foundation/ADebug.h>
+
+namespace android {
+
+NuPlayer::NuPlayerStreamListener::NuPlayerStreamListener(
+ const sp<IStreamSource> &source,
+ ALooper::handler_id id)
+ : mSource(source),
+ mTargetID(id),
+ mEOS(false),
+ mSendDataNotification(true) {
+ mSource->setListener(this);
+
+ mMemoryDealer = new MemoryDealer(kNumBuffers * kBufferSize);
+ for (size_t i = 0; i < kNumBuffers; ++i) {
+ sp<IMemory> mem = mMemoryDealer->allocate(kBufferSize);
+ CHECK(mem != NULL);
+
+ mBuffers.push(mem);
+ }
+ mSource->setBuffers(mBuffers);
+}
+
+void NuPlayer::NuPlayerStreamListener::start() {
+ for (size_t i = 0; i < kNumBuffers; ++i) {
+ mSource->onBufferAvailable(i);
+ }
+}
+
+void NuPlayer::NuPlayerStreamListener::queueBuffer(size_t index, size_t size) {
+ QueueEntry entry;
+ entry.mIsCommand = false;
+ entry.mIndex = index;
+ entry.mSize = size;
+ entry.mOffset = 0;
+
+ Mutex::Autolock autoLock(mLock);
+ mQueue.push_back(entry);
+
+ if (mSendDataNotification) {
+ mSendDataNotification = false;
+ (new AMessage(kWhatMoreDataQueued, mTargetID))->post();
+ }
+}
+
+void NuPlayer::NuPlayerStreamListener::issueCommand(
+ Command cmd, bool synchronous, const sp<AMessage> &extra) {
+ CHECK(!synchronous);
+
+ QueueEntry entry;
+ entry.mIsCommand = true;
+ entry.mCommand = cmd;
+ entry.mExtra = extra;
+
+ Mutex::Autolock autoLock(mLock);
+ mQueue.push_back(entry);
+
+ if (mSendDataNotification) {
+ mSendDataNotification = false;
+ (new AMessage(kWhatMoreDataQueued, mTargetID))->post();
+ }
+}
+
+ssize_t NuPlayer::NuPlayerStreamListener::read(void *data, size_t size) {
+ CHECK_GT(size, 0u);
+
+ Mutex::Autolock autoLock(mLock);
+
+ if (mEOS) {
+ return 0;
+ }
+
+ if (mQueue.empty()) {
+ mSendDataNotification = true;
+
+ return -EWOULDBLOCK;
+ }
+
+ QueueEntry *entry = &*mQueue.begin();
+
+ if (entry->mIsCommand) {
+ switch (entry->mCommand) {
+ case EOS:
+ {
+ mQueue.erase(mQueue.begin());
+ entry = NULL;
+
+ mEOS = true;
+ return 0;
+ }
+
+ case DISCONTINUITY:
+ {
+ mQueue.erase(mQueue.begin());
+ entry = NULL;
+
+ return INFO_DISCONTINUITY;
+ }
+
+ default:
+ TRESPASS();
+ break;
+ }
+ }
+
+ size_t copy = entry->mSize;
+ if (copy > size) {
+ copy = size;
+ }
+
+ memcpy(data,
+ (const uint8_t *)mBuffers.editItemAt(entry->mIndex)->pointer()
+ + entry->mOffset,
+ copy);
+
+ entry->mOffset += copy;
+ entry->mSize -= copy;
+
+ if (entry->mSize == 0) {
+ mSource->onBufferAvailable(entry->mIndex);
+ mQueue.erase(mQueue.begin());
+ entry = NULL;
+ }
+
+ return copy;
+}
+
+} // namespace android
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.h b/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.h
new file mode 100644
index 0000000..f88e945
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2010 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef NUPLAYER_STREAM_LISTENER_H_
+
+#define NUPLAYER_STREAM_LISTENER_H_
+
+#include "NuPlayer.h"
+
+#include <media/IStreamSource.h>
+
+namespace android {
+
+struct MemoryDealer;
+
+struct NuPlayer::NuPlayerStreamListener : public BnStreamListener {
+ NuPlayerStreamListener(
+ const sp<IStreamSource> &source,
+ ALooper::handler_id targetID);
+
+ virtual void queueBuffer(size_t index, size_t size);
+
+ virtual void issueCommand(
+ Command cmd, bool synchronous, const sp<AMessage> &extra);
+
+ void start();
+ ssize_t read(void *data, size_t size);
+
+private:
+ enum {
+ kNumBuffers = 16,
+ kBufferSize = 188 * 20
+ };
+
+ struct QueueEntry {
+ bool mIsCommand;
+
+ size_t mIndex;
+ size_t mSize;
+ size_t mOffset;
+
+ Command mCommand;
+ sp<AMessage> mExtra;
+ };
+
+ Mutex mLock;
+
+ sp<IStreamSource> mSource;
+ ALooper::handler_id mTargetID;
+ sp<MemoryDealer> mMemoryDealer;
+ Vector<sp<IMemory> > mBuffers;
+ List<QueueEntry> mQueue;
+ bool mEOS;
+ bool mSendDataNotification;
+
+ DISALLOW_EVIL_CONSTRUCTORS(NuPlayerStreamListener);
+};
+
+} // namespace android
+
+#endif // NUPLAYER_STREAM_LISTENER_H_
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
new file mode 100644
index 0000000..77276ab
--- /dev/null
+++ b/media/libstagefright/ACodec.cpp
@@ -0,0 +1,2097 @@
+//#define LOG_NDEBUG 0
+#define LOG_TAG "ACodec"
+
+#include <media/stagefright/ACodec.h>
+
+#include <binder/MemoryDealer.h>
+
+#include <media/stagefright/foundation/hexdump.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/OMXClient.h>
+
+#include <surfaceflinger/Surface.h>
+
+#include <OMX_Component.h>
+
+namespace android {
+
+template<class T>
+static void InitOMXParams(T *params) {
+ params->nSize = sizeof(T);
+ params->nVersion.s.nVersionMajor = 1;
+ params->nVersion.s.nVersionMinor = 0;
+ params->nVersion.s.nRevision = 0;
+ params->nVersion.s.nStep = 0;
+}
+
+struct CodecObserver : public BnOMXObserver {
+ CodecObserver() {}
+
+ void setNotificationMessage(const sp<AMessage> &msg) {
+ mNotify = msg;
+ }
+
+ // from IOMXObserver
+ virtual void onMessage(const omx_message &omx_msg) {
+ sp<AMessage> msg = mNotify->dup();
+
+ msg->setInt32("type", omx_msg.type);
+ msg->setPointer("node", omx_msg.node);
+
+ switch (omx_msg.type) {
+ case omx_message::EVENT:
+ {
+ msg->setInt32("event", omx_msg.u.event_data.event);
+ msg->setInt32("data1", omx_msg.u.event_data.data1);
+ msg->setInt32("data2", omx_msg.u.event_data.data2);
+ break;
+ }
+
+ case omx_message::EMPTY_BUFFER_DONE:
+ {
+ msg->setPointer("buffer", omx_msg.u.buffer_data.buffer);
+ break;
+ }
+
+ case omx_message::FILL_BUFFER_DONE:
+ {
+ msg->setPointer(
+ "buffer", omx_msg.u.extended_buffer_data.buffer);
+ msg->setInt32(
+ "range_offset",
+ omx_msg.u.extended_buffer_data.range_offset);
+ msg->setInt32(
+ "range_length",
+ omx_msg.u.extended_buffer_data.range_length);
+ msg->setInt32(
+ "flags",
+ omx_msg.u.extended_buffer_data.flags);
+ msg->setInt64(
+ "timestamp",
+ omx_msg.u.extended_buffer_data.timestamp);
+ msg->setPointer(
+ "platform_private",
+ omx_msg.u.extended_buffer_data.platform_private);
+ msg->setPointer(
+ "data_ptr",
+ omx_msg.u.extended_buffer_data.data_ptr);
+ break;
+ }
+
+ default:
+ TRESPASS();
+ break;
+ }
+
+ msg->post();
+ }
+
+protected:
+ virtual ~CodecObserver() {}
+
+private:
+ sp<AMessage> mNotify;
+
+ DISALLOW_EVIL_CONSTRUCTORS(CodecObserver);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+struct ACodec::BaseState : public AState {
+ BaseState(ACodec *codec, const sp<AState> &parentState = NULL);
+
+protected:
+ enum PortMode {
+ KEEP_BUFFERS,
+ RESUBMIT_BUFFERS,
+ FREE_BUFFERS,
+ };
+
+ ACodec *mCodec;
+
+ virtual PortMode getPortMode(OMX_U32 portIndex);
+
+ virtual bool onMessageReceived(const sp<AMessage> &msg);
+
+ virtual bool onOMXEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2);
+
+ virtual void onOutputBufferDrained(const sp<AMessage> &msg);
+ virtual void onInputBufferFilled(const sp<AMessage> &msg);
+
+ void postFillThisBuffer(BufferInfo *info);
+
+private:
+ bool onOMXMessage(const sp<AMessage> &msg);
+
+ bool onOMXEmptyBufferDone(IOMX::buffer_id bufferID);
+
+ bool onOMXFillBufferDone(
+ IOMX::buffer_id bufferID,
+ size_t rangeOffset, size_t rangeLength,
+ OMX_U32 flags,
+ int64_t timeUs,
+ void *platformPrivate,
+ void *dataPtr);
+
+ void getMoreInputDataIfPossible();
+
+ DISALLOW_EVIL_CONSTRUCTORS(BaseState);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+struct ACodec::UninitializedState : public ACodec::BaseState {
+ UninitializedState(ACodec *codec);
+
+protected:
+ virtual bool onMessageReceived(const sp<AMessage> &msg);
+
+private:
+ void onSetup(const sp<AMessage> &msg);
+
+ DISALLOW_EVIL_CONSTRUCTORS(UninitializedState);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+struct ACodec::LoadedToIdleState : public ACodec::BaseState {
+ LoadedToIdleState(ACodec *codec);
+
+protected:
+ virtual bool onMessageReceived(const sp<AMessage> &msg);
+ virtual bool onOMXEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2);
+ virtual void stateEntered();
+
+private:
+ status_t allocateBuffers();
+
+ DISALLOW_EVIL_CONSTRUCTORS(LoadedToIdleState);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+struct ACodec::IdleToExecutingState : public ACodec::BaseState {
+ IdleToExecutingState(ACodec *codec);
+
+protected:
+ virtual bool onMessageReceived(const sp<AMessage> &msg);
+ virtual bool onOMXEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2);
+ virtual void stateEntered();
+
+private:
+ DISALLOW_EVIL_CONSTRUCTORS(IdleToExecutingState);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+struct ACodec::ExecutingState : public ACodec::BaseState {
+ ExecutingState(ACodec *codec);
+
+ void submitOutputBuffers();
+
+ // Submit output buffers to the decoder, submit input buffers to client
+ // to fill with data.
+ void resume();
+
+protected:
+ virtual PortMode getPortMode(OMX_U32 portIndex);
+ virtual bool onMessageReceived(const sp<AMessage> &msg);
+ virtual void stateEntered();
+
+ virtual bool onOMXEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2);
+
+private:
+ DISALLOW_EVIL_CONSTRUCTORS(ExecutingState);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+struct ACodec::OutputPortSettingsChangedState : public ACodec::BaseState {
+ OutputPortSettingsChangedState(ACodec *codec);
+
+protected:
+ virtual PortMode getPortMode(OMX_U32 portIndex);
+ virtual bool onMessageReceived(const sp<AMessage> &msg);
+ virtual void stateEntered();
+
+ virtual bool onOMXEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2);
+
+private:
+ DISALLOW_EVIL_CONSTRUCTORS(OutputPortSettingsChangedState);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+struct ACodec::ExecutingToIdleState : public ACodec::BaseState {
+ ExecutingToIdleState(ACodec *codec);
+
+protected:
+ virtual bool onMessageReceived(const sp<AMessage> &msg);
+ virtual void stateEntered();
+
+ virtual bool onOMXEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2);
+
+ virtual void onOutputBufferDrained(const sp<AMessage> &msg);
+ virtual void onInputBufferFilled(const sp<AMessage> &msg);
+
+private:
+ void changeStateIfWeOwnAllBuffers();
+
+ DISALLOW_EVIL_CONSTRUCTORS(ExecutingToIdleState);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+struct ACodec::IdleToLoadedState : public ACodec::BaseState {
+ IdleToLoadedState(ACodec *codec);
+
+protected:
+ virtual bool onMessageReceived(const sp<AMessage> &msg);
+ virtual void stateEntered();
+
+ virtual bool onOMXEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2);
+
+private:
+ DISALLOW_EVIL_CONSTRUCTORS(IdleToLoadedState);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+struct ACodec::ErrorState : public ACodec::BaseState {
+ ErrorState(ACodec *codec);
+
+protected:
+ virtual bool onMessageReceived(const sp<AMessage> &msg);
+ virtual void stateEntered();
+
+ virtual bool onOMXEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2);
+
+private:
+ DISALLOW_EVIL_CONSTRUCTORS(ErrorState);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+struct ACodec::FlushingState : public ACodec::BaseState {
+ FlushingState(ACodec *codec);
+
+protected:
+ virtual bool onMessageReceived(const sp<AMessage> &msg);
+ virtual void stateEntered();
+
+ virtual bool onOMXEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2);
+
+ virtual void onOutputBufferDrained(const sp<AMessage> &msg);
+ virtual void onInputBufferFilled(const sp<AMessage> &msg);
+
+private:
+ bool mFlushComplete[2];
+
+ void changeStateIfWeOwnAllBuffers();
+
+ DISALLOW_EVIL_CONSTRUCTORS(FlushingState);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+ACodec::ACodec()
+ : mNode(NULL) {
+ mUninitializedState = new UninitializedState(this);
+ mLoadedToIdleState = new LoadedToIdleState(this);
+ mIdleToExecutingState = new IdleToExecutingState(this);
+ mExecutingState = new ExecutingState(this);
+
+ mOutputPortSettingsChangedState =
+ new OutputPortSettingsChangedState(this);
+
+ mExecutingToIdleState = new ExecutingToIdleState(this);
+ mIdleToLoadedState = new IdleToLoadedState(this);
+ mErrorState = new ErrorState(this);
+ mFlushingState = new FlushingState(this);
+
+ mPortEOS[kPortIndexInput] = mPortEOS[kPortIndexOutput] = false;
+
+ changeState(mUninitializedState);
+}
+
+ACodec::~ACodec() {
+}
+
+void ACodec::setNotificationMessage(const sp<AMessage> &msg) {
+ mNotify = msg;
+}
+
+void ACodec::initiateSetup(const sp<AMessage> &msg) {
+ msg->setWhat(kWhatSetup);
+ msg->setTarget(id());
+ msg->post();
+}
+
+void ACodec::signalFlush() {
+ (new AMessage(kWhatFlush, id()))->post();
+}
+
+void ACodec::signalResume() {
+ (new AMessage(kWhatResume, id()))->post();
+}
+
+void ACodec::initiateShutdown() {
+ (new AMessage(kWhatShutdown, id()))->post();
+}
+
+status_t ACodec::allocateBuffersOnPort(OMX_U32 portIndex) {
+ CHECK(portIndex == kPortIndexInput || portIndex == kPortIndexOutput);
+
+ CHECK(mDealer[portIndex] == NULL);
+ CHECK(mBuffers[portIndex].isEmpty());
+
+ if (mNativeWindow != NULL && portIndex == kPortIndexOutput) {
+ return allocateOutputBuffersFromNativeWindow();
+ }
+
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ InitOMXParams(&def);
+ def.nPortIndex = portIndex;
+
+ status_t err = mOMX->getParameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+
+ if (err != OK) {
+ return err;
+ }
+
+ LOGV("[%s] Allocating %lu buffers of size %lu on %s port",
+ mComponentName.c_str(),
+ def.nBufferCountActual, def.nBufferSize,
+ portIndex == kPortIndexInput ? "input" : "output");
+
+ size_t totalSize = def.nBufferCountActual * def.nBufferSize;
+ mDealer[portIndex] = new MemoryDealer(totalSize, "OMXCodec");
+
+ for (OMX_U32 i = 0; i < def.nBufferCountActual; ++i) {
+ sp<IMemory> mem = mDealer[portIndex]->allocate(def.nBufferSize);
+ CHECK(mem.get() != NULL);
+
+ IOMX::buffer_id buffer;
+#if 0
+ err = mOMX->allocateBufferWithBackup(mNode, portIndex, mem, &buffer);
+#else
+ err = mOMX->useBuffer(mNode, portIndex, mem, &buffer);
+#endif
+
+ if (err != OK) {
+ return err;
+ }
+
+ BufferInfo info;
+ info.mBufferID = buffer;
+ info.mStatus = BufferInfo::OWNED_BY_US;
+ info.mData = new ABuffer(mem->pointer(), def.nBufferSize);
+ mBuffers[portIndex].push(info);
+ }
+
+ return OK;
+}
+
+status_t ACodec::allocateOutputBuffersFromNativeWindow() {
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ InitOMXParams(&def);
+ def.nPortIndex = kPortIndexOutput;
+
+ status_t err = mOMX->getParameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+
+ if (err != OK) {
+ return err;
+ }
+
+ err = native_window_set_buffers_geometry(
+ mNativeWindow.get(),
+ def.format.video.nFrameWidth,
+ def.format.video.nFrameHeight,
+ def.format.video.eColorFormat);
+
+ if (err != 0) {
+ LOGE("native_window_set_buffers_geometry failed: %s (%d)",
+ strerror(-err), -err);
+ return err;
+ }
+
+ // Increase the buffer count by one to allow for the ANativeWindow to hold
+ // on to one of the buffers.
+ def.nBufferCountActual++;
+ err = mOMX->setParameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+
+ if (err != OK) {
+ return err;
+ }
+
+ // Set up the native window.
+ // XXX TODO: Get the gralloc usage flags from the OMX plugin!
+ err = native_window_set_usage(
+ mNativeWindow.get(),
+ GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_EXTERNAL_DISP);
+
+ if (err != 0) {
+ LOGE("native_window_set_usage failed: %s (%d)", strerror(-err), -err);
+ return err;
+ }
+
+ err = native_window_set_buffer_count(
+ mNativeWindow.get(), def.nBufferCountActual);
+
+ if (err != 0) {
+ LOGE("native_window_set_buffer_count failed: %s (%d)", strerror(-err),
+ -err);
+ return err;
+ }
+
+ // XXX TODO: Do something so the ANativeWindow knows that we'll need to get
+ // the same set of buffers.
+
+ LOGV("[%s] Allocating %lu buffers from a native window of size %lu on "
+ "output port",
+ mComponentName.c_str(), def.nBufferCountActual, def.nBufferSize);
+
+ // Dequeue buffers and send them to OMX
+ OMX_U32 i;
+ for (i = 0; i < def.nBufferCountActual; i++) {
+ android_native_buffer_t *buf;
+ err = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buf);
+ if (err != 0) {
+ LOGE("dequeueBuffer failed: %s (%d)", strerror(-err), -err);
+ break;
+ }
+
+ sp<GraphicBuffer> graphicBuffer(new GraphicBuffer(buf, false));
+ IOMX::buffer_id bufferId;
+ err = mOMX->useGraphicBuffer(mNode, kPortIndexOutput, graphicBuffer,
+ &bufferId);
+ if (err != 0) {
+ break;
+ }
+
+ LOGV("[%s] Registered graphic buffer with ID %p (pointer = %p)",
+ mComponentName.c_str(),
+ bufferId, graphicBuffer.get());
+
+ BufferInfo info;
+ info.mBufferID = bufferId;
+ info.mStatus = BufferInfo::OWNED_BY_US;
+ info.mData = new ABuffer(0);
+ info.mGraphicBuffer = graphicBuffer;
+ mBuffers[kPortIndexOutput].push(info);
+ }
+
+ OMX_U32 cancelStart;
+ OMX_U32 cancelEnd;
+
+ if (err != 0) {
+ // If an error occurred while dequeuing we need to cancel any buffers
+ // that were dequeued.
+ cancelStart = 0;
+ cancelEnd = i;
+ } else {
+ // Return the last two buffers to the native window.
+ // XXX TODO: The number of buffers the native window owns should
+ // probably be queried from it when we put the native window in
+ // fixed buffer pool mode (which needs to be implemented).
+ // Currently it's hard-coded to 2.
+ cancelStart = def.nBufferCountActual - 2;
+ cancelEnd = def.nBufferCountActual;
+ }
+
+ for (OMX_U32 i = cancelStart; i < cancelEnd; i++) {
+ BufferInfo *info = &mBuffers[kPortIndexOutput].editItemAt(i);
+ cancelBufferToNativeWindow(info);
+ }
+
+ return err;
+}
+
+status_t ACodec::cancelBufferToNativeWindow(BufferInfo *info) {
+ CHECK_EQ((int)info->mStatus, (int)BufferInfo::OWNED_BY_US);
+
+ LOGV("[%s] Calling cancelBuffer on buffer %p",
+ mComponentName.c_str(), info->mBufferID);
+
+ int err = mNativeWindow->cancelBuffer(
+ mNativeWindow.get(), info->mGraphicBuffer.get());
+
+ CHECK_EQ(err, 0);
+
+ info->mStatus = BufferInfo::OWNED_BY_NATIVE_WINDOW;
+
+ return OK;
+}
+
+ACodec::BufferInfo *ACodec::dequeueBufferFromNativeWindow() {
+ android_native_buffer_t *buf;
+ CHECK_EQ(mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buf), 0);
+
+ for (size_t i = mBuffers[kPortIndexOutput].size(); i-- > 0;) {
+ BufferInfo *info =
+ &mBuffers[kPortIndexOutput].editItemAt(i);
+
+ if (info->mGraphicBuffer->handle == buf->handle) {
+ CHECK_EQ((int)info->mStatus,
+ (int)BufferInfo::OWNED_BY_NATIVE_WINDOW);
+
+ info->mStatus = BufferInfo::OWNED_BY_US;
+
+ return info;
+ }
+ }
+
+ TRESPASS();
+
+ return NULL;
+}
+
+status_t ACodec::freeBuffersOnPort(OMX_U32 portIndex) {
+ for (size_t i = mBuffers[portIndex].size(); i-- > 0;) {
+ CHECK_EQ((status_t)OK, freeBuffer(portIndex, i));
+ }
+
+ mDealer[portIndex].clear();
+
+ return OK;
+}
+
+status_t ACodec::freeOutputBuffersOwnedByNativeWindow() {
+ for (size_t i = mBuffers[kPortIndexOutput].size(); i-- > 0;) {
+ BufferInfo *info =
+ &mBuffers[kPortIndexOutput].editItemAt(i);
+
+ if (info->mStatus ==
+ BufferInfo::OWNED_BY_NATIVE_WINDOW) {
+ CHECK_EQ((status_t)OK, freeBuffer(kPortIndexOutput, i));
+ }
+ }
+
+ return OK;
+}
+
+status_t ACodec::freeBuffer(OMX_U32 portIndex, size_t i) {
+ BufferInfo *info = &mBuffers[portIndex].editItemAt(i);
+
+ CHECK(info->mStatus == BufferInfo::OWNED_BY_US
+ || info->mStatus == BufferInfo::OWNED_BY_NATIVE_WINDOW);
+
+ if (portIndex == kPortIndexOutput && mNativeWindow != NULL
+ && info->mStatus == BufferInfo::OWNED_BY_US) {
+ CHECK_EQ((status_t)OK, cancelBufferToNativeWindow(info));
+ }
+
+ CHECK_EQ(mOMX->freeBuffer(
+ mNode, portIndex, info->mBufferID),
+ (status_t)OK);
+
+ mBuffers[portIndex].removeAt(i);
+
+ return OK;
+}
+
+ACodec::BufferInfo *ACodec::findBufferByID(
+ uint32_t portIndex, IOMX::buffer_id bufferID,
+ ssize_t *index) {
+ for (size_t i = 0; i < mBuffers[portIndex].size(); ++i) {
+ BufferInfo *info = &mBuffers[portIndex].editItemAt(i);
+
+ if (info->mBufferID == bufferID) {
+ if (index != NULL) {
+ *index = i;
+ }
+ return info;
+ }
+ }
+
+ TRESPASS();
+
+ return NULL;
+}
+
+void ACodec::setComponentRole(
+ bool isEncoder, const char *mime) {
+ struct MimeToRole {
+ const char *mime;
+ const char *decoderRole;
+ const char *encoderRole;
+ };
+
+ static const MimeToRole kMimeToRole[] = {
+ { MEDIA_MIMETYPE_AUDIO_MPEG,
+ "audio_decoder.mp3", "audio_encoder.mp3" },
+ { MEDIA_MIMETYPE_AUDIO_AMR_NB,
+ "audio_decoder.amrnb", "audio_encoder.amrnb" },
+ { MEDIA_MIMETYPE_AUDIO_AMR_WB,
+ "audio_decoder.amrwb", "audio_encoder.amrwb" },
+ { MEDIA_MIMETYPE_AUDIO_AAC,
+ "audio_decoder.aac", "audio_encoder.aac" },
+ { MEDIA_MIMETYPE_VIDEO_AVC,
+ "video_decoder.avc", "video_encoder.avc" },
+ { MEDIA_MIMETYPE_VIDEO_MPEG4,
+ "video_decoder.mpeg4", "video_encoder.mpeg4" },
+ { MEDIA_MIMETYPE_VIDEO_H263,
+ "video_decoder.h263", "video_encoder.h263" },
+ };
+
+ static const size_t kNumMimeToRole =
+ sizeof(kMimeToRole) / sizeof(kMimeToRole[0]);
+
+ size_t i;
+ for (i = 0; i < kNumMimeToRole; ++i) {
+ if (!strcasecmp(mime, kMimeToRole[i].mime)) {
+ break;
+ }
+ }
+
+ if (i == kNumMimeToRole) {
+ return;
+ }
+
+ const char *role =
+ isEncoder ? kMimeToRole[i].encoderRole
+ : kMimeToRole[i].decoderRole;
+
+ if (role != NULL) {
+ OMX_PARAM_COMPONENTROLETYPE roleParams;
+ InitOMXParams(&roleParams);
+
+ strncpy((char *)roleParams.cRole,
+ role, OMX_MAX_STRINGNAME_SIZE - 1);
+
+ roleParams.cRole[OMX_MAX_STRINGNAME_SIZE - 1] = '\0';
+
+ status_t err = mOMX->setParameter(
+ mNode, OMX_IndexParamStandardComponentRole,
+ &roleParams, sizeof(roleParams));
+
+ if (err != OK) {
+ LOGW("[%s] Failed to set standard component role '%s'.",
+ mComponentName.c_str(), role);
+ }
+ }
+}
+
+void ACodec::configureCodec(
+ const char *mime, const sp<AMessage> &msg) {
+ setComponentRole(false /* isEncoder */, mime);
+
+ if (!strncasecmp(mime, "video/", 6)) {
+ int32_t width, height;
+ CHECK(msg->findInt32("width", &width));
+ CHECK(msg->findInt32("height", &height));
+
+ CHECK_EQ(setupVideoDecoder(mime, width, height),
+ (status_t)OK);
+ } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) {
+ int32_t numChannels, sampleRate;
+ CHECK(msg->findInt32("channel-count", &numChannels));
+ CHECK(msg->findInt32("sample-rate", &sampleRate));
+
+ CHECK_EQ(setupAACDecoder(numChannels, sampleRate), (status_t)OK);
+ } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_MPEG)) {
+ } else {
+ TRESPASS();
+ }
+
+ int32_t maxInputSize;
+ if (msg->findInt32("max-input-size", &maxInputSize)) {
+ CHECK_EQ(setMinBufferSize(kPortIndexInput, (size_t)maxInputSize),
+ (status_t)OK);
+ } else if (!strcmp("OMX.Nvidia.aac.decoder", mComponentName.c_str())) {
+ CHECK_EQ(setMinBufferSize(kPortIndexInput, 8192), // XXX
+ (status_t)OK);
+ }
+}
+
+status_t ACodec::setMinBufferSize(OMX_U32 portIndex, size_t size) {
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ InitOMXParams(&def);
+ def.nPortIndex = portIndex;
+
+ status_t err = mOMX->getParameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+
+ if (err != OK) {
+ return err;
+ }
+
+ if (def.nBufferSize >= size) {
+ return OK;
+ }
+
+ def.nBufferSize = size;
+
+ err = mOMX->setParameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+
+ if (err != OK) {
+ return err;
+ }
+
+ err = mOMX->getParameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+
+ if (err != OK) {
+ return err;
+ }
+
+ CHECK(def.nBufferSize >= size);
+
+ return OK;
+}
+
+status_t ACodec::setupAACDecoder(int32_t numChannels, int32_t sampleRate) {
+ OMX_AUDIO_PARAM_AACPROFILETYPE profile;
+ InitOMXParams(&profile);
+ profile.nPortIndex = kPortIndexInput;
+
+ status_t err = mOMX->getParameter(
+ mNode, OMX_IndexParamAudioAac, &profile, sizeof(profile));
+
+ if (err != OK) {
+ return err;
+ }
+
+ profile.nChannels = numChannels;
+ profile.nSampleRate = sampleRate;
+ profile.eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4ADTS;
+
+ err = mOMX->setParameter(
+ mNode, OMX_IndexParamAudioAac, &profile, sizeof(profile));
+
+ return err;
+}
+
+status_t ACodec::setVideoPortFormatType(
+ OMX_U32 portIndex,
+ OMX_VIDEO_CODINGTYPE compressionFormat,
+ OMX_COLOR_FORMATTYPE colorFormat) {
+ OMX_VIDEO_PARAM_PORTFORMATTYPE format;
+ InitOMXParams(&format);
+ format.nPortIndex = portIndex;
+ format.nIndex = 0;
+ bool found = false;
+
+ OMX_U32 index = 0;
+ for (;;) {
+ format.nIndex = index;
+ status_t err = mOMX->getParameter(
+ mNode, OMX_IndexParamVideoPortFormat,
+ &format, sizeof(format));
+
+ if (err != OK) {
+ return err;
+ }
+
+ // The following assertion is violated by TI's video decoder.
+ // CHECK_EQ(format.nIndex, index);
+
+ if (!strcmp("OMX.TI.Video.encoder", mComponentName.c_str())) {
+ if (portIndex == kPortIndexInput
+ && colorFormat == format.eColorFormat) {
+ // eCompressionFormat does not seem right.
+ found = true;
+ break;
+ }
+ if (portIndex == kPortIndexOutput
+ && compressionFormat == format.eCompressionFormat) {
+ // eColorFormat does not seem right.
+ found = true;
+ break;
+ }
+ }
+
+ if (format.eCompressionFormat == compressionFormat
+ && format.eColorFormat == colorFormat) {
+ found = true;
+ break;
+ }
+
+ ++index;
+ }
+
+ if (!found) {
+ return UNKNOWN_ERROR;
+ }
+
+ status_t err = mOMX->setParameter(
+ mNode, OMX_IndexParamVideoPortFormat,
+ &format, sizeof(format));
+
+ return err;
+}
+
+status_t ACodec::setSupportedOutputFormat() {
+ OMX_VIDEO_PARAM_PORTFORMATTYPE format;
+ InitOMXParams(&format);
+ format.nPortIndex = kPortIndexOutput;
+ format.nIndex = 0;
+
+ status_t err = mOMX->getParameter(
+ mNode, OMX_IndexParamVideoPortFormat,
+ &format, sizeof(format));
+ CHECK_EQ(err, (status_t)OK);
+ CHECK_EQ((int)format.eCompressionFormat, (int)OMX_VIDEO_CodingUnused);
+
+ static const int OMX_QCOM_COLOR_FormatYVU420SemiPlanar = 0x7FA30C00;
+
+ CHECK(format.eColorFormat == OMX_COLOR_FormatYUV420Planar
+ || format.eColorFormat == OMX_COLOR_FormatYUV420SemiPlanar
+ || format.eColorFormat == OMX_COLOR_FormatCbYCrY
+ || format.eColorFormat == OMX_QCOM_COLOR_FormatYVU420SemiPlanar);
+
+ return mOMX->setParameter(
+ mNode, OMX_IndexParamVideoPortFormat,
+ &format, sizeof(format));
+}
+
+status_t ACodec::setupVideoDecoder(
+ const char *mime, int32_t width, int32_t height) {
+ OMX_VIDEO_CODINGTYPE compressionFormat = OMX_VIDEO_CodingUnused;
+ if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
+ compressionFormat = OMX_VIDEO_CodingAVC;
+ } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) {
+ compressionFormat = OMX_VIDEO_CodingMPEG4;
+ } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) {
+ compressionFormat = OMX_VIDEO_CodingH263;
+ } else {
+ TRESPASS();
+ }
+
+ status_t err = setVideoPortFormatType(
+ kPortIndexInput, compressionFormat, OMX_COLOR_FormatUnused);
+
+ if (err != OK) {
+ return err;
+ }
+
+ err = setSupportedOutputFormat();
+
+ if (err != OK) {
+ return err;
+ }
+
+ err = setVideoFormatOnPort(
+ kPortIndexInput, width, height, compressionFormat);
+
+ if (err != OK) {
+ return err;
+ }
+
+ err = setVideoFormatOnPort(
+ kPortIndexOutput, width, height, OMX_VIDEO_CodingUnused);
+
+ if (err != OK) {
+ return err;
+ }
+
+ return OK;
+}
+
+status_t ACodec::setVideoFormatOnPort(
+ OMX_U32 portIndex,
+ int32_t width, int32_t height, OMX_VIDEO_CODINGTYPE compressionFormat) {
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ InitOMXParams(&def);
+ def.nPortIndex = portIndex;
+
+ OMX_VIDEO_PORTDEFINITIONTYPE *video_def = &def.format.video;
+
+ status_t err = mOMX->getParameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+
+ CHECK_EQ(err, (status_t)OK);
+
+ if (portIndex == kPortIndexInput) {
+ // XXX Need a (much) better heuristic to compute input buffer sizes.
+ const size_t X = 64 * 1024;
+ if (def.nBufferSize < X) {
+ def.nBufferSize = X;
+ }
+ }
+
+ CHECK_EQ((int)def.eDomain, (int)OMX_PortDomainVideo);
+
+ video_def->nFrameWidth = width;
+ video_def->nFrameHeight = height;
+
+ if (portIndex == kPortIndexInput) {
+ video_def->eCompressionFormat = compressionFormat;
+ video_def->eColorFormat = OMX_COLOR_FormatUnused;
+ }
+
+ err = mOMX->setParameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+
+ return err;
+}
+
+status_t ACodec::initNativeWindow() {
+ if (mNativeWindow != NULL) {
+ return mOMX->enableGraphicBuffers(mNode, kPortIndexOutput, OMX_TRUE);
+ }
+
+ mOMX->enableGraphicBuffers(mNode, kPortIndexOutput, OMX_FALSE);
+ return OK;
+}
+
+bool ACodec::allYourBuffersAreBelongToUs(
+ OMX_U32 portIndex) {
+ for (size_t i = 0; i < mBuffers[portIndex].size(); ++i) {
+ BufferInfo *info = &mBuffers[portIndex].editItemAt(i);
+
+ if (info->mStatus != BufferInfo::OWNED_BY_US
+ && info->mStatus != BufferInfo::OWNED_BY_NATIVE_WINDOW) {
+ LOGV("[%s] Buffer %p on port %ld still has status %d",
+ mComponentName.c_str(),
+ info->mBufferID, portIndex, info->mStatus);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ACodec::allYourBuffersAreBelongToUs() {
+ return allYourBuffersAreBelongToUs(kPortIndexInput)
+ && allYourBuffersAreBelongToUs(kPortIndexOutput);
+}
+
+void ACodec::deferMessage(const sp<AMessage> &msg) {
+ bool wasEmptyBefore = mDeferredQueue.empty();
+ mDeferredQueue.push_back(msg);
+}
+
+void ACodec::processDeferredMessages() {
+ List<sp<AMessage> > queue = mDeferredQueue;
+ mDeferredQueue.clear();
+
+ List<sp<AMessage> >::iterator it = queue.begin();
+ while (it != queue.end()) {
+ onMessageReceived(*it++);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+ACodec::BaseState::BaseState(ACodec *codec, const sp<AState> &parentState)
+ : AState(parentState),
+ mCodec(codec) {
+}
+
+ACodec::BaseState::PortMode ACodec::BaseState::getPortMode(OMX_U32 portIndex) {
+ return KEEP_BUFFERS;
+}
+
+bool ACodec::BaseState::onMessageReceived(const sp<AMessage> &msg) {
+ switch (msg->what()) {
+ case kWhatInputBufferFilled:
+ {
+ onInputBufferFilled(msg);
+ break;
+ }
+
+ case kWhatOutputBufferDrained:
+ {
+ onOutputBufferDrained(msg);
+ break;
+ }
+
+ case ACodec::kWhatOMXMessage:
+ {
+ return onOMXMessage(msg);
+ }
+
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+bool ACodec::BaseState::onOMXMessage(const sp<AMessage> &msg) {
+ int32_t type;
+ CHECK(msg->findInt32("type", &type));
+
+ IOMX::node_id nodeID;
+ CHECK(msg->findPointer("node", &nodeID));
+ CHECK_EQ(nodeID, mCodec->mNode);
+
+ switch (type) {
+ case omx_message::EVENT:
+ {
+ int32_t event, data1, data2;
+ CHECK(msg->findInt32("event", &event));
+ CHECK(msg->findInt32("data1", &data1));
+ CHECK(msg->findInt32("data2", &data2));
+
+ return onOMXEvent(
+ static_cast<OMX_EVENTTYPE>(event),
+ static_cast<OMX_U32>(data1),
+ static_cast<OMX_U32>(data2));
+ }
+
+ case omx_message::EMPTY_BUFFER_DONE:
+ {
+ IOMX::buffer_id bufferID;
+ CHECK(msg->findPointer("buffer", &bufferID));
+
+ return onOMXEmptyBufferDone(bufferID);
+ }
+
+ case omx_message::FILL_BUFFER_DONE:
+ {
+ IOMX::buffer_id bufferID;
+ CHECK(msg->findPointer("buffer", &bufferID));
+
+ int32_t rangeOffset, rangeLength, flags;
+ int64_t timeUs;
+ void *platformPrivate;
+ void *dataPtr;
+
+ CHECK(msg->findInt32("range_offset", &rangeOffset));
+ CHECK(msg->findInt32("range_length", &rangeLength));
+ CHECK(msg->findInt32("flags", &flags));
+ CHECK(msg->findInt64("timestamp", &timeUs));
+ CHECK(msg->findPointer("platform_private", &platformPrivate));
+ CHECK(msg->findPointer("data_ptr", &dataPtr));
+
+ return onOMXFillBufferDone(
+ bufferID,
+ (size_t)rangeOffset, (size_t)rangeLength,
+ (OMX_U32)flags,
+ timeUs,
+ platformPrivate,
+ dataPtr);
+ }
+
+ default:
+ TRESPASS();
+ break;
+ }
+}
+
+bool ACodec::BaseState::onOMXEvent(
+ OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) {
+ if (event != OMX_EventError) {
+ LOGI("[%s] EVENT(%d, 0x%08lx, 0x%08lx)",
+ mCodec->mComponentName.c_str(), event, data1, data2);
+
+ return false;
+ }
+
+ LOGE("[%s] ERROR(0x%08lx, 0x%08lx)",
+ mCodec->mComponentName.c_str(), data1, data2);
+
+ mCodec->changeState(mCodec->mErrorState);
+
+ return true;
+}
+
+bool ACodec::BaseState::onOMXEmptyBufferDone(IOMX::buffer_id bufferID) {
+ BufferInfo *info =
+ mCodec->findBufferByID(kPortIndexInput, bufferID);
+
+ CHECK_EQ((int)info->mStatus, (int)BufferInfo::OWNED_BY_COMPONENT);
+ info->mStatus = BufferInfo::OWNED_BY_US;
+
+ PortMode mode = getPortMode(kPortIndexInput);
+
+ switch (mode) {
+ case KEEP_BUFFERS:
+ break;
+
+ case RESUBMIT_BUFFERS:
+ postFillThisBuffer(info);
+ break;
+
+ default:
+ {
+ CHECK_EQ((int)mode, (int)FREE_BUFFERS);
+ TRESPASS(); // Not currently used
+ break;
+ }
+ }
+
+ return true;
+}
+
+void ACodec::BaseState::postFillThisBuffer(BufferInfo *info) {
+ if (mCodec->mPortEOS[kPortIndexInput]) {
+ return;
+ }
+
+ CHECK_EQ((int)info->mStatus, (int)BufferInfo::OWNED_BY_US);
+
+ sp<AMessage> notify = mCodec->mNotify->dup();
+ notify->setInt32("what", ACodec::kWhatFillThisBuffer);
+ notify->setPointer("buffer-id", info->mBufferID);
+
+ info->mData->meta()->clear();
+ notify->setObject("buffer", info->mData);
+
+ sp<AMessage> reply = new AMessage(kWhatInputBufferFilled, mCodec->id());
+ reply->setPointer("buffer-id", info->mBufferID);
+
+ notify->setMessage("reply", reply);
+
+ notify->post();
+
+ info->mStatus = BufferInfo::OWNED_BY_UPSTREAM;
+}
+
+void ACodec::BaseState::onInputBufferFilled(const sp<AMessage> &msg) {
+ IOMX::buffer_id bufferID;
+ CHECK(msg->findPointer("buffer-id", &bufferID));
+
+ sp<RefBase> obj;
+ int32_t err = OK;
+ if (!msg->findObject("buffer", &obj)) {
+ CHECK(msg->findInt32("err", &err));
+
+ obj.clear();
+ }
+
+ sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get());
+
+ BufferInfo *info = mCodec->findBufferByID(kPortIndexInput, bufferID);
+ CHECK_EQ((int)info->mStatus, (int)BufferInfo::OWNED_BY_UPSTREAM);
+
+ info->mStatus = BufferInfo::OWNED_BY_US;
+
+ PortMode mode = getPortMode(kPortIndexInput);
+
+ switch (mode) {
+ case KEEP_BUFFERS:
+ {
+ if (buffer == NULL) {
+ mCodec->mPortEOS[kPortIndexInput] = true;
+ }
+ break;
+ }
+
+ case RESUBMIT_BUFFERS:
+ {
+ if (buffer != NULL) {
+ CHECK(!mCodec->mPortEOS[kPortIndexInput]);
+
+ int64_t timeUs;
+ CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
+
+ OMX_U32 flags = OMX_BUFFERFLAG_ENDOFFRAME;
+
+ int32_t isCSD;
+ if (buffer->meta()->findInt32("csd", &isCSD) && isCSD != 0) {
+ flags |= OMX_BUFFERFLAG_CODECCONFIG;
+ }
+
+ if (buffer != info->mData) {
+ if (!(flags & OMX_BUFFERFLAG_CODECCONFIG)) {
+ LOGV("[%s] Needs to copy input data.",
+ mCodec->mComponentName.c_str());
+ }
+
+ CHECK_LE(buffer->size(), info->mData->capacity());
+ memcpy(info->mData->data(), buffer->data(), buffer->size());
+ }
+
+ CHECK_EQ(mCodec->mOMX->emptyBuffer(
+ mCodec->mNode,
+ bufferID,
+ 0,
+ buffer->size(),
+ flags,
+ timeUs),
+ (status_t)OK);
+
+ info->mStatus = BufferInfo::OWNED_BY_COMPONENT;
+
+ getMoreInputDataIfPossible();
+ } else if (!mCodec->mPortEOS[kPortIndexInput]) {
+ LOGV("[%s] Signalling EOS on the input port",
+ mCodec->mComponentName.c_str());
+
+ CHECK_EQ(mCodec->mOMX->emptyBuffer(
+ mCodec->mNode,
+ bufferID,
+ 0,
+ 0,
+ OMX_BUFFERFLAG_EOS,
+ 0),
+ (status_t)OK);
+
+ info->mStatus = BufferInfo::OWNED_BY_COMPONENT;
+
+ mCodec->mPortEOS[kPortIndexInput] = true;
+ }
+ break;
+
+ default:
+ CHECK_EQ((int)mode, (int)FREE_BUFFERS);
+ break;
+ }
+ }
+}
+
+void ACodec::BaseState::getMoreInputDataIfPossible() {
+ if (mCodec->mPortEOS[kPortIndexInput]) {
+ return;
+ }
+
+ BufferInfo *eligible = NULL;
+
+ for (size_t i = 0; i < mCodec->mBuffers[kPortIndexInput].size(); ++i) {
+ BufferInfo *info = &mCodec->mBuffers[kPortIndexInput].editItemAt(i);
+
+#if 0
+ if (info->mStatus == BufferInfo::OWNED_BY_UPSTREAM) {
+ // There's already a "read" pending.
+ return;
+ }
+#endif
+
+ if (info->mStatus == BufferInfo::OWNED_BY_US) {
+ eligible = info;
+ }
+ }
+
+ if (eligible == NULL) {
+ return;
+ }
+
+ postFillThisBuffer(eligible);
+}
+
+bool ACodec::BaseState::onOMXFillBufferDone(
+ IOMX::buffer_id bufferID,
+ size_t rangeOffset, size_t rangeLength,
+ OMX_U32 flags,
+ int64_t timeUs,
+ void *platformPrivate,
+ void *dataPtr) {
+ ssize_t index;
+ BufferInfo *info =
+ mCodec->findBufferByID(kPortIndexOutput, bufferID, &index);
+
+ CHECK_EQ((int)info->mStatus, (int)BufferInfo::OWNED_BY_COMPONENT);
+
+ info->mStatus = BufferInfo::OWNED_BY_US;
+
+ PortMode mode = getPortMode(kPortIndexOutput);
+
+ switch (mode) {
+ case KEEP_BUFFERS:
+ break;
+
+ case RESUBMIT_BUFFERS:
+ {
+ if (rangeLength == 0) {
+ if (!(flags & OMX_BUFFERFLAG_EOS)) {
+ CHECK_EQ(mCodec->mOMX->fillBuffer(
+ mCodec->mNode, info->mBufferID),
+ (status_t)OK);
+
+ info->mStatus = BufferInfo::OWNED_BY_COMPONENT;
+ }
+ } else {
+ if (mCodec->mNativeWindow == NULL) {
+ info->mData->setRange(rangeOffset, rangeLength);
+ }
+
+ info->mData->meta()->setInt64("timeUs", timeUs);
+
+ sp<AMessage> notify = mCodec->mNotify->dup();
+ notify->setInt32("what", ACodec::kWhatDrainThisBuffer);
+ notify->setPointer("buffer-id", info->mBufferID);
+ notify->setObject("buffer", info->mData);
+
+ sp<AMessage> reply =
+ new AMessage(kWhatOutputBufferDrained, mCodec->id());
+
+ reply->setPointer("buffer-id", info->mBufferID);
+
+ notify->setMessage("reply", reply);
+
+ notify->post();
+
+ info->mStatus = BufferInfo::OWNED_BY_DOWNSTREAM;
+ }
+
+ if (flags & OMX_BUFFERFLAG_EOS) {
+ sp<AMessage> notify = mCodec->mNotify->dup();
+ notify->setInt32("what", ACodec::kWhatEOS);
+ notify->post();
+
+ mCodec->mPortEOS[kPortIndexOutput] = true;
+ }
+ break;
+ }
+
+ default:
+ {
+ CHECK_EQ((int)mode, (int)FREE_BUFFERS);
+
+ CHECK_EQ((status_t)OK,
+ mCodec->freeBuffer(kPortIndexOutput, index));
+ break;
+ }
+ }
+
+ return true;
+}
+
+void ACodec::BaseState::onOutputBufferDrained(const sp<AMessage> &msg) {
+ IOMX::buffer_id bufferID;
+ CHECK(msg->findPointer("buffer-id", &bufferID));
+
+ ssize_t index;
+ BufferInfo *info =
+ mCodec->findBufferByID(kPortIndexOutput, bufferID, &index);
+ CHECK_EQ((int)info->mStatus, (int)BufferInfo::OWNED_BY_DOWNSTREAM);
+
+ int32_t render;
+ if (mCodec->mNativeWindow != NULL
+ && msg->findInt32("render", &render) && render != 0) {
+ // The client wants this buffer to be rendered.
+
+ CHECK_EQ(mCodec->mNativeWindow->queueBuffer(
+ mCodec->mNativeWindow.get(),
+ info->mGraphicBuffer.get()),
+ 0);
+
+ info->mStatus = BufferInfo::OWNED_BY_NATIVE_WINDOW;
+ } else {
+ info->mStatus = BufferInfo::OWNED_BY_US;
+ }
+
+ PortMode mode = getPortMode(kPortIndexOutput);
+
+ switch (mode) {
+ case KEEP_BUFFERS:
+ {
+ // XXX fishy, revisit!!! What about the FREE_BUFFERS case below?
+
+ if (info->mStatus == BufferInfo::OWNED_BY_NATIVE_WINDOW) {
+ // We cannot resubmit the buffer we just rendered, dequeue
+ // the spare instead.
+
+ info = mCodec->dequeueBufferFromNativeWindow();
+ }
+ break;
+ }
+
+ case RESUBMIT_BUFFERS:
+ {
+ if (!mCodec->mPortEOS[kPortIndexOutput]) {
+ if (info->mStatus == BufferInfo::OWNED_BY_NATIVE_WINDOW) {
+ // We cannot resubmit the buffer we just rendered, dequeue
+ // the spare instead.
+
+ info = mCodec->dequeueBufferFromNativeWindow();
+ }
+
+ CHECK_EQ(mCodec->mOMX->fillBuffer(mCodec->mNode, info->mBufferID),
+ (status_t)OK);
+
+ info->mStatus = BufferInfo::OWNED_BY_COMPONENT;
+ }
+ break;
+ }
+
+ default:
+ {
+ CHECK_EQ((int)mode, (int)FREE_BUFFERS);
+
+ CHECK_EQ((status_t)OK,
+ mCodec->freeBuffer(kPortIndexOutput, index));
+ break;
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+ACodec::UninitializedState::UninitializedState(ACodec *codec)
+ : BaseState(codec) {
+}
+
+bool ACodec::UninitializedState::onMessageReceived(const sp<AMessage> &msg) {
+ bool handled = false;
+
+ switch (msg->what()) {
+ case ACodec::kWhatSetup:
+ {
+ onSetup(msg);
+
+ handled = true;
+ break;
+ }
+
+ case ACodec::kWhatShutdown:
+ {
+ sp<AMessage> notify = mCodec->mNotify->dup();
+ notify->setInt32("what", ACodec::kWhatShutdownCompleted);
+ notify->post();
+
+ handled = true;
+ }
+
+ case ACodec::kWhatFlush:
+ {
+ sp<AMessage> notify = mCodec->mNotify->dup();
+ notify->setInt32("what", ACodec::kWhatFlushCompleted);
+ notify->post();
+
+ handled = true;
+ }
+
+ default:
+ return BaseState::onMessageReceived(msg);
+ }
+
+ return handled;
+}
+
+void ACodec::UninitializedState::onSetup(
+ const sp<AMessage> &msg) {
+ OMXClient client;
+ CHECK_EQ(client.connect(), (status_t)OK);
+
+ sp<IOMX> omx = client.interface();
+
+ AString mime;
+ CHECK(msg->findString("mime", &mime));
+
+ AString componentName;
+
+ if (!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_VIDEO_AVC)) {
+ componentName = "OMX.Nvidia.h264.decode";
+ } else if (!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_AUDIO_AAC)) {
+ componentName = "OMX.Nvidia.aac.decoder";
+ } else if (!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_AUDIO_MPEG)) {
+ componentName = "OMX.Nvidia.mp3.decoder";
+ } else {
+ TRESPASS();
+ }
+
+ sp<CodecObserver> observer = new CodecObserver;
+
+ IOMX::node_id node;
+ CHECK_EQ(omx->allocateNode(componentName.c_str(), observer, &node),
+ (status_t)OK);
+
+ sp<AMessage> notify = new AMessage(kWhatOMXMessage, mCodec->id());
+ observer->setNotificationMessage(notify);
+
+ mCodec->mComponentName = componentName;
+ mCodec->mOMX = omx;
+ mCodec->mNode = node;
+
+ mCodec->configureCodec(mime.c_str(), msg);
+
+ sp<RefBase> obj;
+ if (msg->findObject("surface", &obj)) {
+ mCodec->mNativeWindow = static_cast<Surface *>(obj.get());
+ }
+
+ CHECK_EQ((status_t)OK, mCodec->initNativeWindow());
+
+ CHECK_EQ(omx->sendCommand(node, OMX_CommandStateSet, OMX_StateIdle),
+ (status_t)OK);
+
+ mCodec->changeState(mCodec->mLoadedToIdleState);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+ACodec::LoadedToIdleState::LoadedToIdleState(ACodec *codec)
+ : BaseState(codec) {
+}
+
+void ACodec::LoadedToIdleState::stateEntered() {
+ LOGI("[%s] Now Loaded->Idle", mCodec->mComponentName.c_str());
+
+ CHECK_EQ(allocateBuffers(), (status_t)OK);
+}
+
+status_t ACodec::LoadedToIdleState::allocateBuffers() {
+ status_t err = mCodec->allocateBuffersOnPort(kPortIndexInput);
+
+ if (err != OK) {
+ return err;
+ }
+
+ return mCodec->allocateBuffersOnPort(kPortIndexOutput);
+}
+
+bool ACodec::LoadedToIdleState::onMessageReceived(const sp<AMessage> &msg) {
+ switch (msg->what()) {
+ case kWhatShutdown:
+ {
+ mCodec->deferMessage(msg);
+ return true;
+ }
+
+ default:
+ return BaseState::onMessageReceived(msg);
+ }
+}
+
+bool ACodec::LoadedToIdleState::onOMXEvent(
+ OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) {
+ switch (event) {
+ case OMX_EventCmdComplete:
+ {
+ CHECK_EQ(data1, (OMX_U32)OMX_CommandStateSet);
+ CHECK_EQ(data2, (OMX_U32)OMX_StateIdle);
+
+ CHECK_EQ(mCodec->mOMX->sendCommand(
+ mCodec->mNode, OMX_CommandStateSet, OMX_StateExecuting),
+ (status_t)OK);
+
+ mCodec->changeState(mCodec->mIdleToExecutingState);
+
+ return true;
+ }
+
+ default:
+ return BaseState::onOMXEvent(event, data1, data2);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+ACodec::IdleToExecutingState::IdleToExecutingState(ACodec *codec)
+ : BaseState(codec) {
+}
+
+void ACodec::IdleToExecutingState::stateEntered() {
+ LOGI("[%s] Now Idle->Executing", mCodec->mComponentName.c_str());
+}
+
+bool ACodec::IdleToExecutingState::onMessageReceived(const sp<AMessage> &msg) {
+ switch (msg->what()) {
+ case kWhatShutdown:
+ {
+ mCodec->deferMessage(msg);
+ return true;
+ }
+
+ default:
+ return BaseState::onMessageReceived(msg);
+ }
+}
+
+bool ACodec::IdleToExecutingState::onOMXEvent(
+ OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) {
+ switch (event) {
+ case OMX_EventCmdComplete:
+ {
+ CHECK_EQ(data1, (OMX_U32)OMX_CommandStateSet);
+ CHECK_EQ(data2, (OMX_U32)OMX_StateExecuting);
+
+ mCodec->mExecutingState->resume();
+ mCodec->changeState(mCodec->mExecutingState);
+
+ return true;
+ }
+
+ default:
+ return BaseState::onOMXEvent(event, data1, data2);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+ACodec::ExecutingState::ExecutingState(ACodec *codec)
+ : BaseState(codec) {
+}
+
+ACodec::BaseState::PortMode ACodec::ExecutingState::getPortMode(
+ OMX_U32 portIndex) {
+ return RESUBMIT_BUFFERS;
+}
+
+void ACodec::ExecutingState::submitOutputBuffers() {
+ for (size_t i = 0; i < mCodec->mBuffers[kPortIndexOutput].size(); ++i) {
+ BufferInfo *info = &mCodec->mBuffers[kPortIndexOutput].editItemAt(i);
+
+ if (mCodec->mNativeWindow != NULL) {
+ CHECK(info->mStatus == BufferInfo::OWNED_BY_US
+ || info->mStatus == BufferInfo::OWNED_BY_NATIVE_WINDOW);
+
+ if (info->mStatus == BufferInfo::OWNED_BY_NATIVE_WINDOW) {
+ continue;
+ }
+
+ status_t err = mCodec->mNativeWindow->lockBuffer(
+ mCodec->mNativeWindow.get(),
+ info->mGraphicBuffer.get());
+ CHECK_EQ(err, (status_t)OK);
+ } else {
+ CHECK_EQ((int)info->mStatus, (int)BufferInfo::OWNED_BY_US);
+ }
+
+ CHECK_EQ(mCodec->mOMX->fillBuffer(mCodec->mNode, info->mBufferID),
+ (status_t)OK);
+
+ info->mStatus = BufferInfo::OWNED_BY_COMPONENT;
+ }
+}
+
+void ACodec::ExecutingState::resume() {
+ submitOutputBuffers();
+
+ // Post the first input buffer.
+ CHECK_GT(mCodec->mBuffers[kPortIndexInput].size(), 0u);
+ BufferInfo *info = &mCodec->mBuffers[kPortIndexInput].editItemAt(0);
+
+ postFillThisBuffer(info);
+}
+
+void ACodec::ExecutingState::stateEntered() {
+ LOGI("[%s] Now Executing", mCodec->mComponentName.c_str());
+
+ mCodec->processDeferredMessages();
+}
+
+bool ACodec::ExecutingState::onMessageReceived(const sp<AMessage> &msg) {
+ bool handled = false;
+
+ switch (msg->what()) {
+ case kWhatShutdown:
+ {
+ CHECK_EQ(mCodec->mOMX->sendCommand(
+ mCodec->mNode, OMX_CommandStateSet, OMX_StateIdle),
+ (status_t)OK);
+
+ mCodec->changeState(mCodec->mExecutingToIdleState);
+
+ handled = true;
+ break;
+ }
+
+ case kWhatFlush:
+ {
+ CHECK_EQ(mCodec->mOMX->sendCommand(
+ mCodec->mNode, OMX_CommandFlush, OMX_ALL),
+ (status_t)OK);
+
+ mCodec->changeState(mCodec->mFlushingState);
+
+ handled = true;
+ break;
+ }
+
+ case kWhatResume:
+ {
+ resume();
+
+ handled = true;
+ break;
+ }
+
+ default:
+ handled = BaseState::onMessageReceived(msg);
+ break;
+ }
+
+ return handled;
+}
+
+bool ACodec::ExecutingState::onOMXEvent(
+ OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) {
+ switch (event) {
+ case OMX_EventPortSettingsChanged:
+ {
+ CHECK_EQ(data1, (OMX_U32)kPortIndexOutput);
+
+ if (data2 == OMX_IndexParamPortDefinition) {
+ CHECK_EQ(mCodec->mOMX->sendCommand(
+ mCodec->mNode,
+ OMX_CommandPortDisable, kPortIndexOutput),
+ (status_t)OK);
+
+ if (mCodec->mNativeWindow != NULL) {
+ CHECK_EQ((status_t)OK,
+ mCodec->freeOutputBuffersOwnedByNativeWindow());
+ }
+
+ mCodec->changeState(mCodec->mOutputPortSettingsChangedState);
+ } else {
+ LOGV("[%s] OMX_EventPortSettingsChanged 0x%08lx",
+ mCodec->mComponentName.c_str(), data2);
+ }
+
+ return true;
+ }
+
+ case OMX_EventBufferFlag:
+ {
+ return true;
+ }
+
+ default:
+ return BaseState::onOMXEvent(event, data1, data2);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+ACodec::OutputPortSettingsChangedState::OutputPortSettingsChangedState(
+ ACodec *codec)
+ : BaseState(codec) {
+}
+
+ACodec::BaseState::PortMode ACodec::OutputPortSettingsChangedState::getPortMode(
+ OMX_U32 portIndex) {
+ if (portIndex == kPortIndexOutput) {
+ return FREE_BUFFERS;
+ }
+
+ CHECK_EQ(portIndex, (OMX_U32)kPortIndexInput);
+
+ return RESUBMIT_BUFFERS;
+}
+
+bool ACodec::OutputPortSettingsChangedState::onMessageReceived(
+ const sp<AMessage> &msg) {
+ bool handled = false;
+
+ switch (msg->what()) {
+ case kWhatFlush:
+ case kWhatShutdown:
+ {
+ mCodec->deferMessage(msg);
+ handled = true;
+ break;
+ }
+
+ default:
+ handled = BaseState::onMessageReceived(msg);
+ break;
+ }
+
+ return handled;
+}
+
+void ACodec::OutputPortSettingsChangedState::stateEntered() {
+ LOGI("[%s] Now handling output port settings change",
+ mCodec->mComponentName.c_str());
+}
+
+bool ACodec::OutputPortSettingsChangedState::onOMXEvent(
+ OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) {
+ switch (event) {
+ case OMX_EventCmdComplete:
+ {
+ if (data1 == (OMX_U32)OMX_CommandPortDisable) {
+ CHECK_EQ(data2, (OMX_U32)kPortIndexOutput);
+
+ LOGV("[%s] Output port now disabled.",
+ mCodec->mComponentName.c_str());
+
+ CHECK(mCodec->mBuffers[kPortIndexOutput].isEmpty());
+ mCodec->mDealer[kPortIndexOutput].clear();
+
+ CHECK_EQ(mCodec->mOMX->sendCommand(
+ mCodec->mNode, OMX_CommandPortEnable, kPortIndexOutput),
+ (status_t)OK);
+
+ CHECK_EQ(mCodec->allocateBuffersOnPort(kPortIndexOutput),
+ (status_t)OK);
+
+ return true;
+ } else if (data1 == (OMX_U32)OMX_CommandPortEnable) {
+ CHECK_EQ(data2, (OMX_U32)kPortIndexOutput);
+
+ LOGV("[%s] Output port now reenabled.",
+ mCodec->mComponentName.c_str());
+
+ mCodec->mExecutingState->submitOutputBuffers();
+ mCodec->changeState(mCodec->mExecutingState);
+
+ return true;
+ }
+
+ return false;
+ }
+
+ default:
+ return false;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+ACodec::ExecutingToIdleState::ExecutingToIdleState(ACodec *codec)
+ : BaseState(codec) {
+}
+
+bool ACodec::ExecutingToIdleState::onMessageReceived(const sp<AMessage> &msg) {
+ bool handled = false;
+
+ switch (msg->what()) {
+ case kWhatFlush:
+ {
+ // Don't send me a flush request if you previously wanted me
+ // to shutdown.
+ TRESPASS();
+ break;
+ }
+
+ case kWhatShutdown:
+ {
+ // We're already doing that...
+
+ handled = true;
+ break;
+ }
+
+ default:
+ handled = BaseState::onMessageReceived(msg);
+ break;
+ }
+
+ return handled;
+}
+
+void ACodec::ExecutingToIdleState::stateEntered() {
+ LOGI("[%s] Now Executing->Idle", mCodec->mComponentName.c_str());
+}
+
+bool ACodec::ExecutingToIdleState::onOMXEvent(
+ OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) {
+ switch (event) {
+ case OMX_EventCmdComplete:
+ {
+ CHECK_EQ(data1, (OMX_U32)OMX_CommandStateSet);
+ CHECK_EQ(data2, (OMX_U32)OMX_StateIdle);
+
+ changeStateIfWeOwnAllBuffers();
+
+ return true;
+ }
+
+ default:
+ return BaseState::onOMXEvent(event, data1, data2);
+ }
+}
+void ACodec::ExecutingToIdleState::changeStateIfWeOwnAllBuffers() {
+ if (mCodec->allYourBuffersAreBelongToUs()) {
+ CHECK_EQ(mCodec->mOMX->sendCommand(
+ mCodec->mNode, OMX_CommandStateSet, OMX_StateLoaded),
+ (status_t)OK);
+
+ CHECK_EQ(mCodec->freeBuffersOnPort(kPortIndexInput), (status_t)OK);
+ CHECK_EQ(mCodec->freeBuffersOnPort(kPortIndexOutput), (status_t)OK);
+
+ mCodec->changeState(mCodec->mIdleToLoadedState);
+ }
+}
+
+void ACodec::ExecutingToIdleState::onInputBufferFilled(
+ const sp<AMessage> &msg) {
+ BaseState::onInputBufferFilled(msg);
+
+ changeStateIfWeOwnAllBuffers();
+}
+
+void ACodec::ExecutingToIdleState::onOutputBufferDrained(
+ const sp<AMessage> &msg) {
+ BaseState::onOutputBufferDrained(msg);
+
+ changeStateIfWeOwnAllBuffers();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+ACodec::IdleToLoadedState::IdleToLoadedState(ACodec *codec)
+ : BaseState(codec) {
+}
+
+bool ACodec::IdleToLoadedState::onMessageReceived(const sp<AMessage> &msg) {
+ bool handled = false;
+
+ switch (msg->what()) {
+ case kWhatShutdown:
+ {
+ // We're already doing that...
+
+ handled = true;
+ break;
+ }
+
+ case kWhatFlush:
+ {
+ // Don't send me a flush request if you previously wanted me
+ // to shutdown.
+ TRESPASS();
+ break;
+ }
+
+ default:
+ handled = BaseState::onMessageReceived(msg);
+ break;
+ }
+
+ return handled;
+}
+
+void ACodec::IdleToLoadedState::stateEntered() {
+ LOGI("[%s] Now Idle->Loaded", mCodec->mComponentName.c_str());
+}
+
+bool ACodec::IdleToLoadedState::onOMXEvent(
+ OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) {
+ switch (event) {
+ case OMX_EventCmdComplete:
+ {
+ CHECK_EQ(data1, (OMX_U32)OMX_CommandStateSet);
+ CHECK_EQ(data2, (OMX_U32)OMX_StateLoaded);
+
+ LOGI("[%s] Now Loaded", mCodec->mComponentName.c_str());
+
+ CHECK_EQ(mCodec->mOMX->freeNode(mCodec->mNode), (status_t)OK);
+
+ mCodec->mNativeWindow.clear();
+ mCodec->mNode = NULL;
+ mCodec->mOMX.clear();
+ mCodec->mComponentName.clear();
+
+ mCodec->changeState(mCodec->mUninitializedState);
+
+ sp<AMessage> notify = mCodec->mNotify->dup();
+ notify->setInt32("what", ACodec::kWhatShutdownCompleted);
+ notify->post();
+
+ return true;
+ }
+
+ default:
+ return BaseState::onOMXEvent(event, data1, data2);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+ACodec::ErrorState::ErrorState(ACodec *codec)
+ : BaseState(codec) {
+}
+
+bool ACodec::ErrorState::onMessageReceived(const sp<AMessage> &msg) {
+ return BaseState::onMessageReceived(msg);
+}
+
+void ACodec::ErrorState::stateEntered() {
+ LOGI("[%s] Now in ErrorState", mCodec->mComponentName.c_str());
+}
+
+bool ACodec::ErrorState::onOMXEvent(
+ OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) {
+ LOGI("EVENT(%d, 0x%08lx, 0x%08lx)", event, data1, data2);
+ return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+ACodec::FlushingState::FlushingState(ACodec *codec)
+ : BaseState(codec) {
+}
+
+void ACodec::FlushingState::stateEntered() {
+ LOGI("[%s] Now Flushing", mCodec->mComponentName.c_str());
+
+ mFlushComplete[kPortIndexInput] = mFlushComplete[kPortIndexOutput] = false;
+}
+
+bool ACodec::FlushingState::onMessageReceived(const sp<AMessage> &msg) {
+ bool handled = false;
+
+ switch (msg->what()) {
+ case kWhatShutdown:
+ {
+ mCodec->deferMessage(msg);
+ break;
+ }
+
+ case kWhatFlush:
+ {
+ // We're already doing this right now.
+ handled = true;
+ break;
+ }
+
+ default:
+ handled = BaseState::onMessageReceived(msg);
+ break;
+ }
+
+ return handled;
+}
+
+bool ACodec::FlushingState::onOMXEvent(
+ OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) {
+ switch (event) {
+ case OMX_EventCmdComplete:
+ {
+ CHECK_EQ(data1, (OMX_U32)OMX_CommandFlush);
+
+ if (data2 == kPortIndexInput || data2 == kPortIndexOutput) {
+ CHECK(!mFlushComplete[data2]);
+ mFlushComplete[data2] = true;
+ } else {
+ CHECK_EQ(data2, OMX_ALL);
+ CHECK(mFlushComplete[kPortIndexInput]);
+ CHECK(mFlushComplete[kPortIndexOutput]);
+
+ changeStateIfWeOwnAllBuffers();
+ }
+
+ return true;
+ }
+
+ default:
+ return BaseState::onOMXEvent(event, data1, data2);
+ }
+
+ return true;
+}
+
+void ACodec::FlushingState::onOutputBufferDrained(const sp<AMessage> &msg) {
+ BaseState::onOutputBufferDrained(msg);
+
+ changeStateIfWeOwnAllBuffers();
+}
+
+void ACodec::FlushingState::onInputBufferFilled(const sp<AMessage> &msg) {
+ BaseState::onInputBufferFilled(msg);
+
+ changeStateIfWeOwnAllBuffers();
+}
+
+void ACodec::FlushingState::changeStateIfWeOwnAllBuffers() {
+ if (mFlushComplete[kPortIndexInput]
+ && mFlushComplete[kPortIndexOutput]
+ && mCodec->allYourBuffersAreBelongToUs()) {
+ sp<AMessage> notify = mCodec->mNotify->dup();
+ notify->setInt32("what", ACodec::kWhatFlushCompleted);
+ notify->post();
+
+ mCodec->mPortEOS[kPortIndexInput] =
+ mCodec->mPortEOS[kPortIndexOutput] = false;
+
+ mCodec->changeState(mCodec->mExecutingState);
+ }
+}
+
+} // namespace android
+
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index db23836..2d486e3 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -4,6 +4,7 @@ include $(CLEAR_VARS)
include frameworks/base/media/libstagefright/codecs/common/Config.mk
LOCAL_SRC_FILES:= \
+ ACodec.cpp \
AMRExtractor.cpp \
AMRWriter.cpp \
AudioPlayer.cpp \
diff --git a/media/libstagefright/foundation/AHierarchicalStateMachine.cpp b/media/libstagefright/foundation/AHierarchicalStateMachine.cpp
new file mode 100644
index 0000000..30286d8
--- /dev/null
+++ b/media/libstagefright/foundation/AHierarchicalStateMachine.cpp
@@ -0,0 +1,97 @@
+#include <media/stagefright/foundation/AHierarchicalStateMachine.h>
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <utils/Vector.h>
+
+namespace android {
+
+AState::AState(const sp<AState> &parentState)
+ : mParentState(parentState) {
+}
+
+AState::~AState() {
+}
+
+sp<AState> AState::parentState() {
+ return mParentState;
+}
+
+void AState::stateEntered() {
+}
+
+void AState::stateExited() {
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+AHierarchicalStateMachine::AHierarchicalStateMachine() {
+}
+
+AHierarchicalStateMachine::~AHierarchicalStateMachine() {
+}
+
+void AHierarchicalStateMachine::onMessageReceived(const sp<AMessage> &msg) {
+ sp<AState> save = mState;
+
+ sp<AState> cur = mState;
+ while (cur != NULL && !cur->onMessageReceived(msg)) {
+ // If you claim not to have handled the message you shouldn't
+ // have called setState...
+ CHECK(save == mState);
+
+ cur = cur->parentState();
+ }
+
+ if (cur != NULL) {
+ return;
+ }
+
+ LOGW("Warning message %s unhandled in root state.",
+ msg->debugString().c_str());
+}
+
+void AHierarchicalStateMachine::changeState(const sp<AState> &state) {
+ if (state == mState) {
+ // Quick exit for the easy case.
+ return;
+ }
+
+ Vector<sp<AState> > A;
+ sp<AState> cur = mState;
+ for (;;) {
+ A.push(cur);
+ if (cur == NULL) {
+ break;
+ }
+ cur = cur->parentState();
+ }
+
+ Vector<sp<AState> > B;
+ cur = state;
+ for (;;) {
+ B.push(cur);
+ if (cur == NULL) {
+ break;
+ }
+ cur = cur->parentState();
+ }
+
+ // Remove the common tail.
+ while (A.size() > 0 && B.size() > 0 && A.top() == B.top()) {
+ A.pop();
+ B.pop();
+ }
+
+ mState = state;
+
+ for (size_t i = 0; i < A.size(); ++i) {
+ A.editItemAt(i)->stateExited();
+ }
+
+ for (size_t i = B.size(); i-- > 0;) {
+ B.editItemAt(i)->stateEntered();
+ }
+}
+
+} // namespace android
diff --git a/media/libstagefright/foundation/Android.mk b/media/libstagefright/foundation/Android.mk
index a4d4809..4e07f6f 100644
--- a/media/libstagefright/foundation/Android.mk
+++ b/media/libstagefright/foundation/Android.mk
@@ -1,16 +1,17 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_SRC_FILES:= \
- AAtomizer.cpp \
- ABitReader.cpp \
- ABuffer.cpp \
- AHandler.cpp \
- ALooper.cpp \
- ALooperRoster.cpp \
- AMessage.cpp \
- AString.cpp \
- base64.cpp \
+LOCAL_SRC_FILES:= \
+ AAtomizer.cpp \
+ ABitReader.cpp \
+ ABuffer.cpp \
+ AHandler.cpp \
+ AHierarchicalStateMachine.cpp \
+ ALooper.cpp \
+ ALooperRoster.cpp \
+ AMessage.cpp \
+ AString.cpp \
+ base64.cpp \
hexdump.cpp
LOCAL_C_INCLUDES:= \
diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp
index a559b21..de6346b 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.cpp
+++ b/media/libstagefright/mpeg2ts/ATSParser.cpp
@@ -43,19 +43,21 @@ namespace android {
static const size_t kTSPacketSize = 188;
struct ATSParser::Program : public RefBase {
- Program(unsigned programMapPID);
+ Program(ATSParser *parser, unsigned programMapPID);
bool parsePID(
unsigned pid, unsigned payload_unit_start_indicator,
ABitReader *br);
- void signalDiscontinuity(bool isASeek);
+ void signalDiscontinuity(DiscontinuityType type);
+ void signalEOS(status_t finalResult);
sp<MediaSource> getSource(SourceType type);
int64_t convertPTSToTimestamp(uint64_t PTS);
private:
+ ATSParser *mParser;
unsigned mProgramMapPID;
KeyedVector<unsigned, sp<Stream> > mStreams;
bool mFirstPTSValid;
@@ -73,7 +75,8 @@ struct ATSParser::Stream : public RefBase {
unsigned payload_unit_start_indicator,
ABitReader *br);
- void signalDiscontinuity(bool isASeek);
+ void signalDiscontinuity(DiscontinuityType type);
+ void signalEOS(status_t finalResult);
sp<MediaSource> getSource(SourceType type);
@@ -105,8 +108,9 @@ private:
////////////////////////////////////////////////////////////////////////////////
-ATSParser::Program::Program(unsigned programMapPID)
- : mProgramMapPID(programMapPID),
+ATSParser::Program::Program(ATSParser *parser, unsigned programMapPID)
+ : mParser(parser),
+ mProgramMapPID(programMapPID),
mFirstPTSValid(false),
mFirstPTS(0) {
}
@@ -135,9 +139,15 @@ bool ATSParser::Program::parsePID(
return true;
}
-void ATSParser::Program::signalDiscontinuity(bool isASeek) {
+void ATSParser::Program::signalDiscontinuity(DiscontinuityType type) {
for (size_t i = 0; i < mStreams.size(); ++i) {
- mStreams.editValueAt(i)->signalDiscontinuity(isASeek);
+ mStreams.editValueAt(i)->signalDiscontinuity(type);
+ }
+}
+
+void ATSParser::Program::signalEOS(status_t finalResult) {
+ for (size_t i = 0; i < mStreams.size(); ++i) {
+ mStreams.editValueAt(i)->signalEOS(finalResult);
}
}
@@ -155,7 +165,7 @@ void ATSParser::Program::parseProgramMap(ABitReader *br) {
unsigned section_length = br->getBits(12);
LOGV(" section_length = %u", section_length);
- CHECK((section_length & 0xc00) == 0);
+ CHECK_EQ(section_length & 0xc00, 0u);
CHECK_LE(section_length, 1021u);
MY_LOGV(" program_number = %u", br->getBits(16));
@@ -170,7 +180,7 @@ void ATSParser::Program::parseProgramMap(ABitReader *br) {
unsigned program_info_length = br->getBits(12);
LOGV(" program_info_length = %u", program_info_length);
- CHECK((program_info_length & 0xc00) == 0);
+ CHECK_EQ(program_info_length & 0xc00, 0u);
br->skipBits(program_info_length * 8); // skip descriptors
@@ -194,7 +204,7 @@ void ATSParser::Program::parseProgramMap(ABitReader *br) {
unsigned ES_info_length = br->getBits(12);
LOGV(" ES_info_length = %u", ES_info_length);
- CHECK((ES_info_length & 0xc00) == 0);
+ CHECK_EQ(ES_info_length & 0xc00, 0u);
CHECK_GE(infoBytesRemaining - 5, ES_info_length);
@@ -305,7 +315,7 @@ void ATSParser::Stream::parse(
}
size_t payloadSizeBits = br->numBitsLeft();
- CHECK((payloadSizeBits % 8) == 0);
+ CHECK_EQ(payloadSizeBits % 8, 0u);
CHECK_LE(mBuffer->size() + payloadSizeBits / 8, mBuffer->capacity());
@@ -313,27 +323,45 @@ void ATSParser::Stream::parse(
mBuffer->setRange(0, mBuffer->size() + payloadSizeBits / 8);
}
-void ATSParser::Stream::signalDiscontinuity(bool isASeek) {
- isASeek = false; // Always signal a "real" discontinuity
-
+void ATSParser::Stream::signalDiscontinuity(DiscontinuityType type) {
mPayloadStarted = false;
mBuffer->setRange(0, 0);
- mQueue.clear();
+ switch (type) {
+ case DISCONTINUITY_HTTPLIVE:
+ {
+ mQueue.clear(true);
- if (isASeek) {
- // This is only a "minor" discontinuity, we stay within the same
- // bitstream.
+ if (mStreamType == 0x1b && mSource != NULL) {
+ // Don't signal discontinuities on audio streams.
+ mSource->queueDiscontinuity();
+ }
+ break;
+ }
+
+ case DISCONTINUITY_SEEK:
+ case DISCONTINUITY_FORMATCHANGE:
+ {
+ bool isASeek = (type == DISCONTINUITY_SEEK);
- if (mSource != NULL) {
- mSource->clear();
+ mQueue.clear(!isASeek);
+
+ if (mSource != NULL) {
+ mSource->clear();
+ mSource->queueDiscontinuity();
+ }
+ break;
}
- return;
+
+ default:
+ TRESPASS();
+ break;
}
+}
- if (mStreamType == 0x1b && mSource != NULL) {
- // Don't signal discontinuities on audio streams.
- mSource->queueDiscontinuity();
+void ATSParser::Stream::signalEOS(status_t finalResult) {
+ if (mSource != NULL) {
+ mSource->signalEOS(finalResult);
}
}
@@ -478,7 +506,7 @@ void ATSParser::Stream::parsePES(ABitReader *br) {
br->data(), br->numBitsLeft() / 8);
size_t payloadSizeBits = br->numBitsLeft();
- CHECK((payloadSizeBits % 8) == 0);
+ CHECK_EQ(payloadSizeBits % 8, 0u);
LOGV("There's %d bytes of payload.", payloadSizeBits / 8);
}
@@ -526,6 +554,7 @@ void ATSParser::Stream::onPayloadData(
if (meta != NULL) {
LOGV("created source!");
mSource = new AnotherPacketSource(meta);
+
mSource->queueAccessUnit(accessUnit);
}
} else if (mQueue.getFormat() != NULL) {
@@ -561,9 +590,17 @@ void ATSParser::feedTSPacket(const void *data, size_t size) {
parseTS(&br);
}
-void ATSParser::signalDiscontinuity(bool isASeek) {
+void ATSParser::signalDiscontinuity(DiscontinuityType type) {
+ for (size_t i = 0; i < mPrograms.size(); ++i) {
+ mPrograms.editItemAt(i)->signalDiscontinuity(type);
+ }
+}
+
+void ATSParser::signalEOS(status_t finalResult) {
+ CHECK_NE(finalResult, (status_t)OK);
+
for (size_t i = 0; i < mPrograms.size(); ++i) {
- mPrograms.editItemAt(i)->signalDiscontinuity(isASeek);
+ mPrograms.editItemAt(i)->signalEOS(finalResult);
}
}
@@ -581,7 +618,7 @@ void ATSParser::parseProgramAssociationTable(ABitReader *br) {
unsigned section_length = br->getBits(12);
LOGV(" section_length = %u", section_length);
- CHECK((section_length & 0xc00) == 0);
+ CHECK_EQ(section_length & 0xc00, 0u);
MY_LOGV(" transport_stream_id = %u", br->getBits(16));
MY_LOGV(" reserved = %u", br->getBits(2));
@@ -606,7 +643,7 @@ void ATSParser::parseProgramAssociationTable(ABitReader *br) {
LOGV(" program_map_PID = 0x%04x", programMapPID);
- mPrograms.push(new Program(programMapPID));
+ mPrograms.push(new Program(this, programMapPID));
}
}
diff --git a/media/libstagefright/mpeg2ts/ATSParser.h b/media/libstagefright/mpeg2ts/ATSParser.h
index 11b1de4..ef78c77 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.h
+++ b/media/libstagefright/mpeg2ts/ATSParser.h
@@ -21,19 +21,28 @@
#include <sys/types.h>
#include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/foundation/AMessage.h>
#include <utils/Vector.h>
#include <utils/RefBase.h>
namespace android {
struct ABitReader;
+struct ABuffer;
struct MediaSource;
struct ATSParser : public RefBase {
+ enum DiscontinuityType {
+ DISCONTINUITY_HTTPLIVE,
+ DISCONTINUITY_SEEK,
+ DISCONTINUITY_FORMATCHANGE
+ };
+
ATSParser();
void feedTSPacket(const void *data, size_t size);
- void signalDiscontinuity(bool isASeek = false);
+ void signalDiscontinuity(DiscontinuityType type);
+ void signalEOS(status_t finalResult);
enum SourceType {
AVC_VIDEO,
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
index ea747c8..7a1d5b0 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
@@ -48,6 +48,32 @@ sp<MetaData> AnotherPacketSource::getFormat() {
return mFormat;
}
+status_t AnotherPacketSource::dequeueAccessUnit(sp<ABuffer> *buffer) {
+ buffer->clear();
+
+ Mutex::Autolock autoLock(mLock);
+ while (mEOSResult == OK && mBuffers.empty()) {
+ mCondition.wait(mLock);
+ }
+
+ if (!mBuffers.empty()) {
+ *buffer = *mBuffers.begin();
+ mBuffers.erase(mBuffers.begin());
+
+ int32_t discontinuity;
+ if ((*buffer)->meta()->findInt32("discontinuity", &discontinuity)
+ && discontinuity) {
+ buffer->clear();
+
+ return INFO_DISCONTINUITY;
+ }
+
+ return OK;
+ }
+
+ return mEOSResult;
+}
+
status_t AnotherPacketSource::read(
MediaBuffer **out, const ReadOptions *) {
*out = NULL;
@@ -66,9 +92,8 @@ status_t AnotherPacketSource::read(
&& discontinuity) {
return INFO_DISCONTINUITY;
} else {
- uint64_t timeUs;
- CHECK(buffer->meta()->findInt64(
- "time", (int64_t *)&timeUs));
+ int64_t timeUs;
+ CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
MediaBuffer *mediaBuffer = new MediaBuffer(buffer->size());
mediaBuffer->meta_data()->setInt64(kKeyTime, timeUs);
@@ -92,7 +117,7 @@ void AnotherPacketSource::queueAccessUnit(const sp<ABuffer> &buffer) {
}
int64_t timeUs;
- CHECK(buffer->meta()->findInt64("time", &timeUs));
+ CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
LOGV("queueAccessUnit timeUs=%lld us (%.2f secs)", timeUs, timeUs / 1E6);
Mutex::Autolock autoLock(mLock);
@@ -134,4 +159,19 @@ bool AnotherPacketSource::hasBufferAvailable(status_t *finalResult) {
return false;
}
+status_t AnotherPacketSource::nextBufferTime(int64_t *timeUs) {
+ *timeUs = 0;
+
+ Mutex::Autolock autoLock(mLock);
+
+ if (mBuffers.empty()) {
+ return mEOSResult != OK ? mEOSResult : -EWOULDBLOCK;
+ }
+
+ sp<ABuffer> buffer = *mBuffers.begin();
+ CHECK(buffer->meta()->findInt64("timeUs", timeUs));
+
+ return OK;
+}
+
} // namespace android
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.h b/media/libstagefright/mpeg2ts/AnotherPacketSource.h
index 6999175..2bc7404 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.h
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.h
@@ -39,12 +39,16 @@ struct AnotherPacketSource : public MediaSource {
bool hasBufferAvailable(status_t *finalResult);
+ status_t nextBufferTime(int64_t *timeUs);
+
void queueAccessUnit(const sp<ABuffer> &buffer);
void queueDiscontinuity();
void signalEOS(status_t result);
void clear();
+ status_t dequeueAccessUnit(sp<ABuffer> *buffer);
+
protected:
virtual ~AnotherPacketSource();
diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp
index 4b4490d..4e7759d 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.cpp
+++ b/media/libstagefright/mpeg2ts/ESQueue.cpp
@@ -40,13 +40,16 @@ sp<MetaData> ElementaryStreamQueue::getFormat() {
return mFormat;
}
-void ElementaryStreamQueue::clear() {
+void ElementaryStreamQueue::clear(bool clearFormat) {
if (mBuffer != NULL) {
mBuffer->setRange(0, 0);
}
mRangeInfos.clear();
- mFormat.clear();
+
+ if (clearFormat) {
+ mFormat.clear();
+ }
}
static bool IsSeeminglyValidADTSHeader(const uint8_t *ptr, size_t size) {
@@ -289,7 +292,7 @@ sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitAAC() {
mBuffer->setRange(0, mBuffer->size() - offset);
if (timeUs >= 0) {
- accessUnit->meta()->setInt64("time", timeUs);
+ accessUnit->meta()->setInt64("timeUs", timeUs);
} else {
LOGW("no time for AAC access unit");
}
@@ -470,7 +473,7 @@ sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitH264() {
int64_t timeUs = fetchTimestamp(nextScan);
CHECK_GE(timeUs, 0ll);
- accessUnit->meta()->setInt64("time", timeUs);
+ accessUnit->meta()->setInt64("timeUs", timeUs);
if (mFormat == NULL) {
mFormat = MakeAVCCodecSpecificData(accessUnit);
diff --git a/media/libstagefright/mpeg2ts/ESQueue.h b/media/libstagefright/mpeg2ts/ESQueue.h
index adce153..5b7957e 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.h
+++ b/media/libstagefright/mpeg2ts/ESQueue.h
@@ -35,7 +35,7 @@ struct ElementaryStreamQueue {
ElementaryStreamQueue(Mode mode);
status_t appendData(const void *data, size_t size, int64_t timeUs);
- void clear();
+ void clear(bool clearFormat);
sp<ABuffer> dequeueAccessUnit();
diff --git a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
index 600116e..a1f0796 100644
--- a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
+++ b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
@@ -214,7 +214,7 @@ status_t MPEG2TSExtractor::feedMore() {
if (isDiscontinuity(packet, n)) {
LOGI("XXX discontinuity detected");
- mParser->signalDiscontinuity();
+ mParser->signalDiscontinuity(ATSParser::DISCONTINUITY_HTTPLIVE);
} else if (n < (ssize_t)kTSPacketSize) {
return (n < 0) ? (status_t)n : ERROR_END_OF_STREAM;
} else {