diff options
author | Andreas Huber <andih@google.com> | 2010-12-15 15:17:42 -0800 |
---|---|---|
committer | Andreas Huber <andih@google.com> | 2010-12-15 15:18:26 -0800 |
commit | f933441648ef6a71dee783d733aac17b9508b452 (patch) | |
tree | 240f8068edb362cbea579659a963bbb029a2bac0 /media/libstagefright | |
parent | 60c5b57edd3c8f4bdf6b38cf5b8a193ba770bb72 (diff) | |
download | frameworks_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.cpp | 2097 | ||||
-rw-r--r-- | media/libstagefright/Android.mk | 1 | ||||
-rw-r--r-- | media/libstagefright/foundation/AHierarchicalStateMachine.cpp | 97 | ||||
-rw-r--r-- | media/libstagefright/foundation/Android.mk | 21 | ||||
-rw-r--r-- | media/libstagefright/mpeg2ts/ATSParser.cpp | 95 | ||||
-rw-r--r-- | media/libstagefright/mpeg2ts/ATSParser.h | 11 | ||||
-rw-r--r-- | media/libstagefright/mpeg2ts/AnotherPacketSource.cpp | 48 | ||||
-rw-r--r-- | media/libstagefright/mpeg2ts/AnotherPacketSource.h | 4 | ||||
-rw-r--r-- | media/libstagefright/mpeg2ts/ESQueue.cpp | 11 | ||||
-rw-r--r-- | media/libstagefright/mpeg2ts/ESQueue.h | 2 | ||||
-rw-r--r-- | media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp | 2 |
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 { |