Bug 1168531 - Fix MediaCodecReader video playback problem r=bwu
authorSotaro Ikeda <sikeda@mozilla.com>
Thu, 28 May 2015 07:23:57 -0700
changeset 246033 343ccb3b257684de2e39ec5b04081b7f530ce9f4
parent 246032 8225a3b75df6ec1fecc43ab66855320c6fd924e9
child 246034 db3d40e8b4de09f3775b637d42428f39922c80ed
push id60334
push usersikeda@mozilla.com
push dateThu, 28 May 2015 14:24:07 +0000
treeherdermozilla-inbound@343ccb3b2576 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbwu
bugs1168531
milestone41.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1168531 - Fix MediaCodecReader video playback problem r=bwu
dom/media/omx/MediaCodecProxy.cpp
dom/media/omx/MediaCodecProxy.h
dom/media/omx/MediaCodecReader.cpp
dom/media/omx/MediaCodecReader.h
--- a/dom/media/omx/MediaCodecProxy.cpp
+++ b/dom/media/omx/MediaCodecProxy.cpp
@@ -133,16 +133,30 @@ MediaCodecProxy::AskMediaCodecAndWait()
   while (mPendingRequestMediaResource) {
     mMediaCodecLock.Wait();
   }
   MCP_LOG("AskMediaCodecAndWait complete");
 
   return true;
 }
 
+bool
+MediaCodecProxy::AsyncAskMediaCodec()
+{
+  if ((strncasecmp(mCodecMime.get(), "video/", 6) != 0) ||
+      (mResourceHandler == nullptr)) {
+    return false;
+  }
+  // request video codec
+  mResourceHandler->requestResource(mCodecEncoder
+    ? IMediaResourceManagerService::HW_VIDEO_ENCODER
+    : IMediaResourceManagerService::HW_VIDEO_DECODER);
+  return true;
+}
+
 void
 MediaCodecProxy::SetMediaCodecFree()
 {
   if (mResourceHandler == nullptr) {
     return;
   }
 
   mozilla::MonitorAutoLock mon(mMediaCodecLock);
--- a/dom/media/omx/MediaCodecProxy.h
+++ b/dom/media/omx/MediaCodecProxy.h
@@ -134,16 +134,20 @@ public:
   bool UpdateOutputBuffers();
 
   void ReleaseMediaBuffer(MediaBuffer* abuffer);
 
   // It asks for the OMX codec and blocked until the resource is grant to be
   // allocated.
   bool AskMediaCodecAndWait();
 
+  // It asks for the OMX codec asynchronously.
+  // Only video codec is supported.
+  bool AsyncAskMediaCodec();
+
   // Free the OMX codec so others can allocate it.
   void SetMediaCodecFree();
 
 protected:
   virtual ~MediaCodecProxy();
 
   // MediaResourceHandler::EventListener::resourceReserved()
   virtual void resourceReserved();
--- a/dom/media/omx/MediaCodecReader.cpp
+++ b/dom/media/omx/MediaCodecReader.cpp
@@ -62,16 +62,43 @@ IsValidDurationUs(int64_t aDuration)
 }
 
 inline bool
 IsValidTimestampUs(int64_t aTimestamp)
 {
   return aTimestamp >= INT64_C(0);
 }
 
+MediaCodecReader::VideoResourceListener::VideoResourceListener(
+  MediaCodecReader* aReader)
+  : mReader(aReader)
+{
+}
+
+MediaCodecReader::VideoResourceListener::~VideoResourceListener()
+{
+  mReader = nullptr;
+}
+
+void
+MediaCodecReader::VideoResourceListener::codecReserved()
+{
+  if (mReader) {
+    mReader->VideoCodecReserved();
+  }
+}
+
+void
+MediaCodecReader::VideoResourceListener::codecCanceled()
+{
+  if (mReader) {
+    mReader->VideoCodecCanceled();
+  }
+}
+
 MediaCodecReader::TrackInputCopier::~TrackInputCopier()
 {
 }
 
 bool
 MediaCodecReader::TrackInputCopier::Copy(MediaBuffer* aSourceBuffer,
                                          sp<ABuffer> aCodecBuffer)
 {
@@ -244,16 +271,17 @@ MediaCodecReader::MediaCodecReader(Abstr
   , mIsWaitingResources(false)
   , mTextureClientIndexesLock("MediaCodecReader::mTextureClientIndexesLock")
   , mColorConverterBufferSize(0)
   , mParserMonitor("MediaCodecReader::mParserMonitor")
   , mParseDataFromCache(true)
   , mNextParserPosition(INT64_C(0))
   , mParsedDataLength(INT64_C(0))
 {
+  mVideoListener = new VideoResourceListener(this);
 }
 
 MediaCodecReader::~MediaCodecReader()
 {
 }
 
 nsresult
 MediaCodecReader::Init(MediaDecoderReader* aCloneDonor)
@@ -643,16 +671,24 @@ MediaCodecReader::ReadMetadata(MediaInfo
   // relies on IsWaitingMediaResources() function. And the waiting state will be
   // changed by binder thread, so we store the waiting state in a cache value to
   // make them in the same waiting state.
   UpdateIsWaitingMediaResources();
   if (IsWaitingMediaResources()) {
     return NS_OK;
   }
 
+  // Configure video codec after the codecReserved.
+  if (mVideoTrack.mSource != nullptr) {
+    if (!ConfigureMediaCodec(mVideoTrack)) {
+      DestroyMediaCodec(mVideoTrack);
+      return NS_ERROR_FAILURE;
+    }
+  }
+
   // TODO: start streaming
 
   if (!UpdateDuration()) {
     return NS_ERROR_FAILURE;
   }
 
   if (!UpdateAudioInfo()) {
     return NS_ERROR_FAILURE;
@@ -1254,45 +1290,42 @@ MediaCodecReader::CreateTaskQueues()
   }
 
   return true;
 }
 
 bool
 MediaCodecReader::CreateMediaCodecs()
 {
-  if (CreateMediaCodec(mLooper, mAudioTrack, nullptr) &&
-      CreateMediaCodec(mLooper, mVideoTrack, nullptr)) {
+  if (CreateMediaCodec(mLooper, mAudioTrack, false, nullptr) &&
+      CreateMediaCodec(mLooper, mVideoTrack, true, mVideoListener)) {
     return true;
   }
 
   return false;
 }
 
 bool
 MediaCodecReader::CreateMediaCodec(sp<ALooper>& aLooper,
                                    Track& aTrack,
+                                   bool aAsync,
                                    wp<MediaCodecProxy::CodecResourceListener> aListener)
 {
   if (aTrack.mSource != nullptr && aTrack.mCodec == nullptr) {
     sp<MetaData> sourceFormat = aTrack.mSource->getFormat();
 
     const char* mime;
     if (sourceFormat->findCString(kKeyMIMEType, &mime)) {
       aTrack.mCodec = MediaCodecProxy::CreateByType(aLooper, mime, false, aListener);
     }
 
     if (aTrack.mCodec == nullptr) {
       NS_WARNING("Couldn't create MediaCodecProxy");
       return false;
     }
-    if (!aTrack.mCodec->AskMediaCodecAndWait()) {
-      NS_WARNING("AskMediaCodecAndWait fail");
-      return false;
-    }
 
     if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_VORBIS)) {
       aTrack.mInputCopier = new VorbisInputCopier;
     } else {
       aTrack.mInputCopier = new TrackInputCopier;
     }
 
     uint32_t capability = MediaCodecProxy::kEmptyCapability;
@@ -1305,18 +1338,26 @@ MediaCodecReader::CreateMediaCodec(sp<AL
       GonkBufferQueue::createBufferQueue(&producer, &consumer);
       aTrack.mNativeWindow = new GonkNativeWindow(consumer);
       aTrack.mGraphicBufferProducer = producer;
 #else
       aTrack.mNativeWindow = new GonkNativeWindow();
 #endif
     }
 
-    if (!aTrack.mCodec->allocated() || !ConfigureMediaCodec(aTrack)) {
-      NS_WARNING("Couldn't create and configure MediaCodec synchronously");
+    if (!aAsync && aTrack.mCodec->AskMediaCodecAndWait()) {
+      // Pending configure() and start() to codecReserved() if the creation
+      // should be asynchronous.
+      if (!aTrack.mCodec->allocated() || !ConfigureMediaCodec(aTrack)){
+        NS_WARNING("Couldn't create and configure MediaCodec synchronously");
+        DestroyMediaCodec(aTrack);
+        return false;
+      }
+    } else if (aAsync && !aTrack.mCodec->AsyncAskMediaCodec()) {
+      NS_WARNING("Couldn't request MediaCodec asynchronously");
       DestroyMediaCodec(aTrack);
       return false;
     }
   }
 
   return true;
 }
 
@@ -1838,18 +1879,17 @@ MediaCodecReader::EnsureCodecFormatParse
     if (status == OK) {
       aTrack.mCodec->releaseOutputBuffer(index);
     } else if (status == INFO_OUTPUT_BUFFERS_CHANGED) {
       // Update output buffers of MediaCodec.
       if (aTrack.mCodec->getOutputBuffers(&aTrack.mOutputBuffers) != OK) {
         NS_WARNING("Couldn't get output buffers from MediaCodec");
         return false;
       }
-    } else if (status != -EAGAIN && status != INVALID_OPERATION) {
-      // FIXME: let INVALID_OPERATION pass?
+    } else if (status != -EAGAIN) {
       return false; // something wrong!!!
     }
     FillCodecInputData(aTrack);
   }
   return aTrack.mCodec->getOutputFormat(&format) == OK;
 }
 
 uint8_t*
@@ -1870,9 +1910,27 @@ MediaCodecReader::GetColorConverterBuffe
 
 void
 MediaCodecReader::ClearColorConverterBuffer()
 {
   mColorConverterBuffer = nullptr;
   mColorConverterBufferSize = 0;
 }
 
+// Called on Binder thread.
+void
+MediaCodecReader::VideoCodecReserved()
+{
+  mDecoder->NotifyWaitingForResourcesStatusChanged();
+}
+
+// Called on Binder thread.
+void
+MediaCodecReader::VideoCodecCanceled()
+{
+  if (mVideoTrack.mTaskQueue) {
+    RefPtr<nsIRunnable> task =
+      NS_NewRunnableMethod(this, &MediaCodecReader::ReleaseCriticalResources);
+    mVideoTrack.mTaskQueue->Dispatch(task.forget());
+  }
+}
+
 } // namespace mozilla
--- a/dom/media/omx/MediaCodecReader.h
+++ b/dom/media/omx/MediaCodecReader.h
@@ -172,28 +172,55 @@ protected:
     Track(const Track &rhs) = delete;
     const Track &operator=(const Track&) = delete;
   };
 
   // Receive a message from MessageHandler.
   // Called on MediaCodecReader::mLooper thread.
   void onMessageReceived(const android::sp<android::AMessage>& aMessage);
 
+  // Receive a notify from ResourceListener.
+  // Called on Binder thread.
+  virtual void VideoCodecReserved();
+  virtual void VideoCodecCanceled();
+
   virtual bool CreateExtractor();
 
   // Check the underlying HW resource is available and store the result in
   // mIsWaitingResources.
   void UpdateIsWaitingMediaResources();
 
   android::sp<android::MediaExtractor> mExtractor;
   // A cache value updated by UpdateIsWaitingMediaResources(), makes the
   // "waiting resources state" is synchronous to StateMachine.
   bool mIsWaitingResources;
 
 private:
+
+  // An intermediary class that can be managed by android::sp<T>.
+  // Redirect codecReserved() and codecCanceled() to MediaCodecReader.
+  class VideoResourceListener : public android::MediaCodecProxy::CodecResourceListener
+  {
+  public:
+    VideoResourceListener(MediaCodecReader* aReader);
+    ~VideoResourceListener();
+
+    virtual void codecReserved();
+    virtual void codecCanceled();
+
+  private:
+    // Forbidden
+    VideoResourceListener() = delete;
+    VideoResourceListener(const VideoResourceListener& rhs) = delete;
+    const VideoResourceListener& operator=(const VideoResourceListener& rhs) = delete;
+
+    MediaCodecReader* mReader;
+  };
+  friend class VideoResourceListener;
+
   class VorbisInputCopier : public TrackInputCopier
   {
     virtual bool Copy(android::MediaBuffer* aSourceBuffer,
                       android::sp<android::ABuffer> aCodecBuffer);
   };
 
   struct AudioTrack : public Track
   {
@@ -322,16 +349,17 @@ private:
   void DestroyExtractor();
 
   bool CreateMediaSources();
   void DestroyMediaSources();
 
   bool CreateMediaCodecs();
   static bool CreateMediaCodec(android::sp<android::ALooper>& aLooper,
                                Track& aTrack,
+                               bool aAsync,
                                android::wp<android::MediaCodecProxy::CodecResourceListener> aListener);
   static bool ConfigureMediaCodec(Track& aTrack);
   void DestroyMediaCodecs();
   static void DestroyMediaCodec(Track& aTrack);
 
   bool CreateTaskQueues();
   void ShutdownTaskQueues();
   void DecodeVideoFrameTask(int64_t aTimeThreshold);
@@ -382,16 +410,18 @@ private:
   static PLDHashOperator ReleaseTextureClient(TextureClient* aClient,
                                               size_t& aIndex,
                                               void* aUserArg);
   PLDHashOperator ReleaseTextureClient(TextureClient* aClient,
                                        size_t& aIndex);
 
   void ReleaseAllTextureClients();
 
+  android::sp<VideoResourceListener> mVideoListener;
+
   android::sp<android::ALooper> mLooper;
   android::sp<android::MetaData> mMetaData;
 
   Mutex mTextureClientIndexesLock;
   nsDataHashtable<nsPtrHashKey<TextureClient>, size_t> mTextureClientIndexes;
 
   // media tracks
   AudioTrack mAudioTrack;