Bug 1154512 - Remove MediaTaskQueue::SyncDispatch() from PDM. r=cpearce
authorAlfredo Yang <ayang>
Mon, 04 May 2015 03:04:00 -0400
changeset 261165 8bc8f9c19385d97167982b31f08cd7fa73d3df3d
parent 261164 1bc82fa210381fd026ddd9dbe40d214929fd01d8
child 261166 87329d2ed8ce7a805872efb9ea9c4c9f7efd0525
push id8007
push userraliiev@mozilla.com
push dateMon, 11 May 2015 19:23:16 +0000
treeherdermozilla-aurora@e2ce1aac996e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscpearce
bugs1154512
milestone40.0a1
Bug 1154512 - Remove MediaTaskQueue::SyncDispatch() from PDM. r=cpearce
dom/media/fmp4/gonk/GonkMediaDataDecoder.cpp
dom/media/fmp4/gonk/GonkMediaDataDecoder.h
dom/media/fmp4/gonk/GonkVideoDecoderManager.cpp
dom/media/fmp4/gonk/GonkVideoDecoderManager.h
--- a/dom/media/fmp4/gonk/GonkMediaDataDecoder.cpp
+++ b/dom/media/fmp4/gonk/GonkMediaDataDecoder.cpp
@@ -21,104 +21,65 @@ PRLogModuleInfo* GetDemuxerLog();
 #define LOG(...)
 #endif
 
 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<MediaRawData> 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<MediaRawData> 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;
 }
 
 GonkMediaDataDecoder::GonkMediaDataDecoder(GonkDecoderManager* aManager,
                                            FlushableMediaTaskQueue* aTaskQueue,
                                            MediaDataDecoderCallback* aCallback)
   : mTaskQueue(aTaskQueue)
   , mCallback(aCallback)
--- a/dom/media/fmp4/gonk/GonkMediaDataDecoder.h
+++ b/dom/media/fmp4/gonk/GonkMediaDataDecoder.h
@@ -39,35 +39,33 @@ public:
   virtual nsresult Output(int64_t aStreamOffset,
                           nsRefPtr<MediaData>& aOutput) = 0;
 
   // Flush the queued sample.
   // It this function is overrided by subclass, this functino should be called
   // 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.
   virtual bool PerformFormatSpecificProcess(MediaRawData* aSample) { return true; }
 
   // 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.
   nsTArray<nsRefPtr<MediaRawData>> mQueueSample;
 
   RefPtr<MediaTaskQueue> mTaskQueue;
 
   nsRefPtr<MediaByteBuffer> mCodecSpecificData;
--- a/dom/media/fmp4/gonk/GonkVideoDecoderManager.cpp
+++ b/dom/media/fmp4/gonk/GonkVideoDecoderManager.cpp
@@ -45,16 +45,17 @@ namespace mozilla {
 
 GonkVideoDecoderManager::GonkVideoDecoderManager(
   MediaTaskQueue* aTaskQueue,
   mozilla::layers::ImageContainer* aImageContainer,
   const VideoInfo& aConfig)
   : GonkDecoderManager(aTaskQueue)
   , mImageContainer(aImageContainer)
   , mReaderCallback(nullptr)
+  , mLastDecodedTime(0)
   , mColorConverterBufferSize(0)
   , mNativeWindow(nullptr)
   , mPendingVideoBuffersLock("GonkVideoDecoderManager::mPendingVideoBuffersLock")
 {
   MOZ_COUNT_CTOR(GonkVideoDecoderManager);
   mMimeType = aConfig.mMimeType;
   mVideoWidth  = aConfig.mDisplay.width;
   mVideoHeight = aConfig.mDisplay.height;
@@ -113,61 +114,16 @@ GonkVideoDecoderManager::Init(MediaDataD
     mNativeWindow = new GonkNativeWindow();
   }
 
   mReaderCallback->NotifyResourcesStatusChanged();
 
   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)
 {
   *v = nullptr;
   nsRefPtr<VideoData> data;
   int64_t timeUs;
   int32_t keyFrame;
 
@@ -176,19 +132,22 @@ GonkVideoDecoderManager::CreateVideoData
     return NS_ERROR_UNEXPECTED;
   }
 
   if (!mVideoBuffer->meta_data()->findInt64(kKeyTime, &timeUs)) {
     GVDM_LOG("Decoder did not return frame time");
     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
     // quoted from Android's AwesomePlayer.cpp
     ReleaseVideoBuffer();
     return NS_ERROR_NOT_AVAILABLE;
   }
 
@@ -219,17 +178,18 @@ GonkVideoDecoderManager::CreateVideoData
     GrallocTextureClientOGL* grallocClient = static_cast<GrallocTextureClientOGL*>(textureClient.get());
     grallocClient->SetMediaBuffer(mVideoBuffer);
     textureClient->SetRecycleCallback(GonkVideoDecoderManager::RecycleCallback, this);
 
     data = VideoData::Create(mInfo.mVideo,
                              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,
                              picture);
   } else {
     if (!mVideoBuffer->data()) {
       GVDM_LOG("No data in Video Buffer!");
       return NS_ERROR_UNEXPECTED;
@@ -433,57 +393,27 @@ void GonkVideoDecoderManager::ReleaseVid
     mDecoder->ReleaseMediaBuffer(mVideoBuffer);
     mVideoBuffer = nullptr;
   }
 }
 
 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<const uint8_t*>(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;
   }
   return NS_OK;
 }
 
 void
--- a/dom/media/fmp4/gonk/GonkVideoDecoderManager.h
+++ b/dom/media/fmp4/gonk/GonkVideoDecoderManager.h
@@ -103,43 +103,30 @@ private:
     VideoResourceListener() = delete;
     VideoResourceListener(const VideoResourceListener &rhs) = delete;
     const VideoResourceListener &operator=(const VideoResourceListener &rhs) = delete;
 
     GonkVideoDecoderManager *mManager;
   };
   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);
   void ReleaseVideoBuffer();
   uint8_t* GetColorConverterBuffer(int32_t aWidth, int32_t aHeight);
 
   // For codec resource management
   void codecReserved();
   void codecCanceled();
   void onMessageReceived(const sp<AMessage> &aMessage);
 
   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;
   uint32_t mDisplayHeight;
   nsIntRect mPicture;
   nsIntSize mInitialFrame;
 
   android::sp<MediaCodecProxy> mDecoder;
@@ -150,21 +137,17 @@ private:
   MediaDataDecoderCallback*  mReaderCallback;
   MediaInfo mInfo;
   android::sp<VideoResourceListener> mVideoListener;
   android::sp<MessageHandler> mHandler;
   android::sp<ALooper> mLooper;
   android::sp<ALooper> 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<FrameTimeInfo> mFrameTimeInfo;
+  int64_t mLastDecodedTime;  // The last decoded frame presentation time.
 
   // color converter
   android::I420ColorConverterHelper mColorConverter;
   nsAutoArrayPtr<uint8_t> mColorConverterBuffer;
   size_t mColorConverterBufferSize;
 
   android::sp<android::GonkNativeWindow> mNativeWindow;
   enum {