diff --git a/dom/media/fmp4/gonk/GonkMediaDataDecoder.cpp b/dom/media/fmp4/gonk/GonkMediaDataDecoder.cpp index 6afce6f06b77..b417671b2484 100644 --- a/dom/media/fmp4/gonk/GonkMediaDataDecoder.cpp +++ b/dom/media/fmp4/gonk/GonkMediaDataDecoder.cpp @@ -26,94 +26,55 @@ using namespace android; namespace mozilla { GonkDecoderManager::GonkDecoderManager(MediaTaskQueue* aTaskQueue) - : mTaskQueue(aTaskQueue) + : mMonitor("GonkDecoderManager") + , mTaskQueue(aTaskQueue) { } nsresult GonkDecoderManager::Input(MediaRawData* aSample) { - MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn()); - - // To maintain the order of the MP4Sample, it needs to send the queued samples - // to OMX first. And then the current input aSample. - // If it fails to input sample to OMX, it needs to add current into queue - // for next round. - uint32_t len = mQueueSample.Length(); - status_t rv = OK; - - for (uint32_t i = 0; i < len; i++) { - rv = SendSampleToOMX(mQueueSample.ElementAt(0)); - if (rv != OK) { - break; - } - mQueueSample.RemoveElementAt(0); - } - - // When EOS, aSample will be null and sends this empty MediaRawData to nofity - // OMX it reachs EOS. + ReentrantMonitorAutoEnter mon(mMonitor); nsRefPtr sample; + if (!aSample) { + // It means EOS with empty sample. sample = new MediaRawData(); - } - - // If rv is OK, that means mQueueSample is empty, now try to queue current input - // aSample. - if (rv == OK) { - MOZ_ASSERT(!mQueueSample.Length()); - MediaRawData* tmp; - if (aSample) { - tmp = aSample; - if (!PerformFormatSpecificProcess(aSample)) { - return NS_ERROR_FAILURE; - } - } else { - tmp = sample; - } - rv = SendSampleToOMX(tmp); - if (rv == OK) { - return NS_OK; + } else { + sample = aSample; + if (!PerformFormatSpecificProcess(sample)) { + return NS_ERROR_FAILURE; } } - // Current valid sample can't be sent into OMX, adding the clone one into queue - // for next round. - if (!sample) { - sample = aSample->Clone(); - if (!sample) { - return NS_ERROR_OUT_OF_MEMORY; - } - } mQueueSample.AppendElement(sample); - // In most cases, EAGAIN or ETIMEOUT safe due to OMX can't process the - // filled buffer on time. It should be gone When requeuing sample next time. - if (rv == -EAGAIN || rv == -ETIMEDOUT) { - return NS_OK; + status_t rv; + while (mQueueSample.Length()) { + nsRefPtr data = mQueueSample.ElementAt(0); + { + ReentrantMonitorAutoExit mon_exit(mMonitor); + rv = SendSampleToOMX(data); + } + if (rv == OK) { + mQueueSample.RemoveElementAt(0); + } else if (rv == -EAGAIN || rv == -ETIMEDOUT) { + // In most cases, EAGAIN or ETIMEOUT are safe because OMX can't fill + // buffer on time. + return NS_OK; + } else { + return NS_ERROR_UNEXPECTED; + } } - return NS_ERROR_UNEXPECTED; + return NS_OK; } nsresult GonkDecoderManager::Flush() { - class ClearQueueRunnable : public nsRunnable - { - public: - explicit ClearQueueRunnable(GonkDecoderManager* aManager) - : mManager(aManager) {} - - NS_IMETHOD Run() - { - mManager->ClearQueuedSample(); - return NS_OK; - } - - GonkDecoderManager* mManager; - }; - - mTaskQueue->SyncDispatch(new ClearQueueRunnable(this)); + ReentrantMonitorAutoEnter mon(mMonitor); + mQueueSample.Clear(); return NS_OK; } diff --git a/dom/media/fmp4/gonk/GonkMediaDataDecoder.h b/dom/media/fmp4/gonk/GonkMediaDataDecoder.h index 4e621ec51caf..8b94b0d0d7e8 100644 --- a/dom/media/fmp4/gonk/GonkMediaDataDecoder.h +++ b/dom/media/fmp4/gonk/GonkMediaDataDecoder.h @@ -44,17 +44,12 @@ public: // in the overrided function. virtual nsresult Flush(); - // It should be called in MediaTash thread. + // It should be called in MediaTask thread. bool HasQueuedSample() { - MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn()); + ReentrantMonitorAutoEnter mon(mMonitor); return mQueueSample.Length(); } - void ClearQueuedSample() { - MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn()); - mQueueSample.Clear(); - } - protected: // It performs special operation to MP4 sample, the real action is depended on // the codec type. @@ -63,6 +58,9 @@ protected: // It sends MP4Sample to OMX layer. It must be overrided by subclass. virtual android::status_t SendSampleToOMX(MediaRawData* aSample) = 0; + // This monitor protects mQueueSample. + ReentrantMonitor mMonitor; + // An queue with the MP4 samples which are waiting to be sent into OMX. // If an element is an empty MP4Sample, that menas EOS. There should not // any sample be queued after EOS. diff --git a/dom/media/fmp4/gonk/GonkVideoDecoderManager.cpp b/dom/media/fmp4/gonk/GonkVideoDecoderManager.cpp index 72e2fe894670..7d38b4f3efec 100644 --- a/dom/media/fmp4/gonk/GonkVideoDecoderManager.cpp +++ b/dom/media/fmp4/gonk/GonkVideoDecoderManager.cpp @@ -50,6 +50,7 @@ GonkVideoDecoderManager::GonkVideoDecoderManager( : GonkDecoderManager(aTaskQueue) , mImageContainer(aImageContainer) , mReaderCallback(nullptr) + , mLastDecodedTime(0) , mColorConverterBufferSize(0) , mNativeWindow(nullptr) , mPendingVideoBuffersLock("GonkVideoDecoderManager::mPendingVideoBuffersLock") @@ -118,51 +119,6 @@ GonkVideoDecoderManager::Init(MediaDataDecoderCallback* aCallback) return mDecoder; } -void -GonkVideoDecoderManager::QueueFrameTimeIn(int64_t aPTS, int64_t aDuration) -{ - MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn()); - - FrameTimeInfo timeInfo = {aPTS, aDuration}; - mFrameTimeInfo.AppendElement(timeInfo); -} - -nsresult -GonkVideoDecoderManager::QueueFrameTimeOut(int64_t aPTS, int64_t& aDuration) -{ - MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn()); - - // Set default to 1 here. - // During seeking, frames could still in MediaCodec and the mFrameTimeInfo could - // be cleared before these frames are out from MediaCodec. This is ok because - // these frames are old frame before seeking. - aDuration = 1; - for (uint32_t i = 0; i < mFrameTimeInfo.Length(); i++) { - const FrameTimeInfo& entry = mFrameTimeInfo.ElementAt(i); - if (i == 0) { - if (entry.pts > aPTS) { - // Codec sent a frame with rollbacked PTS time. It could - // be codec's problem. - ReleaseVideoBuffer(); - return NS_ERROR_NOT_AVAILABLE; - } - } - - // Ideally, the first entry in mFrameTimeInfo should be the one we are looking - // for. However, MediaCodec could dropped frame and the first entry doesn't - // match current decoded frame's PTS. - if (entry.pts == aPTS) { - aDuration = entry.duration; - if (i > 0) { - LOG("Frame could be dropped by MediaCodec, %d dropped frames.", i); - } - mFrameTimeInfo.RemoveElementsAt(0, i+1); - break; - } - } - return NS_OK; -} - nsresult GonkVideoDecoderManager::CreateVideoData(int64_t aStreamOffset, VideoData **v) { @@ -181,9 +137,12 @@ GonkVideoDecoderManager::CreateVideoData(int64_t aStreamOffset, VideoData **v) return NS_ERROR_UNEXPECTED; } - int64_t duration; - nsresult rv = QueueFrameTimeOut(timeUs, duration); - NS_ENSURE_SUCCESS(rv, rv); + if (mLastDecodedTime > timeUs) { + ReleaseVideoBuffer(); + GVDM_LOG("Output decoded sample time is revert. time=%lld", timeUs); + return NS_ERROR_NOT_AVAILABLE; + } + mLastDecodedTime = timeUs; if (mVideoBuffer->range_length() == 0) { // Some decoders may return spurious empty buffers that we just want to ignore @@ -224,7 +183,8 @@ GonkVideoDecoderManager::CreateVideoData(int64_t aStreamOffset, VideoData **v) mImageContainer, aStreamOffset, timeUs, - duration, + 1, // No way to pass sample duration from muxer to + // OMX codec, so we hardcode the duration here. textureClient, keyFrame, -1, @@ -438,47 +398,17 @@ void GonkVideoDecoderManager::ReleaseVideoBuffer() { status_t GonkVideoDecoderManager::SendSampleToOMX(MediaRawData* aSample) { - // An empty MediaRawData is going to notify EOS to decoder. It doesn't need - // to keep PTS and duration. - if (aSample->mData && aSample->mDuration && aSample->mTime) { - QueueFrameTimeIn(aSample->mTime, aSample->mDuration); - } - return mDecoder->Input(reinterpret_cast(aSample->mData), aSample->mSize, aSample->mTime, 0); } -void -GonkVideoDecoderManager::ClearQueueFrameTime() -{ - MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn()); - mFrameTimeInfo.Clear(); -} - nsresult GonkVideoDecoderManager::Flush() { GonkDecoderManager::Flush(); - - class ClearFrameTimeRunnable : public nsRunnable - { - public: - explicit ClearFrameTimeRunnable(GonkVideoDecoderManager* aManager) - : mManager(aManager) {} - - NS_IMETHOD Run() - { - mManager->ClearQueueFrameTime(); - return NS_OK; - } - - GonkVideoDecoderManager* mManager; - }; - - mTaskQueue->SyncDispatch(new ClearFrameTimeRunnable(this)); - + mLastDecodedTime = 0; status_t err = mDecoder->flush(); if (err != OK) { return NS_ERROR_FAILURE; diff --git a/dom/media/fmp4/gonk/GonkVideoDecoderManager.h b/dom/media/fmp4/gonk/GonkVideoDecoderManager.h index 2e6d23f5b6bd..1c2a2ebd5ad3 100644 --- a/dom/media/fmp4/gonk/GonkVideoDecoderManager.h +++ b/dom/media/fmp4/gonk/GonkVideoDecoderManager.h @@ -108,15 +108,6 @@ private: }; friend class VideoResourceListener; - // FrameTimeInfo keeps the presentation time stamp (pts) and its duration. - // On MediaDecoderStateMachine, it needs pts and duration to display decoded - // frame correctly. But OMX can carry one field of time info (kKeyTime) so - // we use FrameTimeInfo to keep pts and duration. - struct FrameTimeInfo { - int64_t pts; // presentation time stamp of this frame. - int64_t duration; // the playback duration. - }; - bool SetVideoFormat(); nsresult CreateVideoData(int64_t aStreamOffset, VideoData** aOutData); @@ -131,10 +122,6 @@ private: void ReleaseAllPendingVideoBuffers(); void PostReleaseVideoBuffer(android::MediaBuffer *aBuffer); - void QueueFrameTimeIn(int64_t aPTS, int64_t aDuration); - nsresult QueueFrameTimeOut(int64_t aPTS, int64_t& aDuration); - void ClearQueueFrameTime(); - uint32_t mVideoWidth; uint32_t mVideoHeight; uint32_t mDisplayWidth; @@ -155,11 +142,7 @@ private: android::sp mManagerLooper; FrameInfo mFrameInfo; - // Array of FrameTimeInfo whose corresponding frames are sent to OMX. - // Ideally, it is a FIFO. Input() adds the entry to the end element and - // CreateVideoData() takes the first entry. However, there are exceptions - // due to MediaCodec error or seeking. - nsTArray mFrameTimeInfo; + int64_t mLastDecodedTime; // The last decoded frame presentation time. // color converter android::I420ColorConverterHelper mColorConverter;