summaryrefslogtreecommitdiffstats
path: root/media/libstagefright
diff options
context:
space:
mode:
authorAndreas Huber <andih@google.com>2010-12-15 15:17:42 -0800
committerAndreas Huber <andih@google.com>2010-12-15 15:18:26 -0800
commitf933441648ef6a71dee783d733aac17b9508b452 (patch)
tree240f8068edb362cbea579659a963bbb029a2bac0 /media/libstagefright
parent60c5b57edd3c8f4bdf6b38cf5b8a193ba770bb72 (diff)
downloadframeworks_av-f933441648ef6a71dee783d733aac17b9508b452.zip
frameworks_av-f933441648ef6a71dee783d733aac17b9508b452.tar.gz
frameworks_av-f933441648ef6a71dee783d733aac17b9508b452.tar.bz2
Initial support for a true streaming player for mpeg2 transport streams.
Change-Id: I153eec439d260a5524b21270e16d36940ec3161a
Diffstat (limited to 'media/libstagefright')
-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
11 files changed, 2339 insertions, 50 deletions
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 {