Bug 1033910 - Enable RTSP capability while using android::MediaCodec. r=cpearce
authorBenjamin Chen <bechen@mozilla.com>
Thu, 28 Aug 2014 18:36:34 +0800
changeset 202358 6e8db8f5d14270f9cfa25d993aa76e3e4c134a34
parent 202357 faece15110ef19190c179ed309d6391303a50b6c
child 202359 dfcd20daf182bc8f688a9655333c99a7630ebff9
push id48402
push userryanvm@gmail.com
push dateFri, 29 Aug 2014 13:35:39 +0000
treeherdermozilla-inbound@de5cefa8e52e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscpearce
bugs1033910
milestone34.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 1033910 - Enable RTSP capability while using android::MediaCodec. r=cpearce
content/media/DecoderTraits.cpp
content/media/omx/MediaCodecDecoder.cpp
content/media/omx/MediaCodecReader.cpp
content/media/omx/MediaCodecReader.h
content/media/omx/MediaOmxCommonDecoder.cpp
content/media/omx/MediaOmxCommonDecoder.h
content/media/omx/RtspExtractor.cpp
content/media/omx/RtspExtractor.h
content/media/omx/RtspMediaCodecDecoder.cpp
content/media/omx/RtspMediaCodecDecoder.h
content/media/omx/RtspMediaCodecReader.cpp
content/media/omx/RtspMediaCodecReader.h
content/media/omx/RtspOmxReader.cpp
content/media/omx/moz.build
--- a/content/media/DecoderTraits.cpp
+++ b/content/media/DecoderTraits.cpp
@@ -43,16 +43,20 @@
 #include "nsIPrincipal.h"
 #include "mozilla/dom/HTMLMediaElement.h"
 #if ANDROID_VERSION >= 18
 #include "MediaCodecDecoder.h"
 #include "MediaCodecReader.h"
 #endif
 #endif
 #ifdef NECKO_PROTOCOL_rtsp
+#if ANDROID_VERSION >= 18
+#include "RtspMediaCodecDecoder.h"
+#include "RtspMediaCodecReader.h"
+#endif
 #include "RtspOmxDecoder.h"
 #include "RtspOmxReader.h"
 #endif
 #ifdef MOZ_WMF
 #include "WMFDecoder.h"
 #include "WMFReader.h"
 #endif
 #ifdef MOZ_DIRECTSHOW
@@ -550,17 +554,23 @@ InstantiateDecoder(const nsACString& aTy
 #else
     decoder = new MediaOmxDecoder();
 #endif
     return decoder.forget();
   }
 #endif
 #ifdef NECKO_PROTOCOL_rtsp
   if (IsRtspSupportedType(aType)) {
+#if ANDROID_VERSION >= 18
+    decoder = MediaDecoder::IsOmxAsyncEnabled()
+      ? static_cast<MediaDecoder*>(new RtspMediaCodecDecoder())
+      : static_cast<MediaDecoder*>(new RtspOmxDecoder());
+#else
     decoder = new RtspOmxDecoder();
+#endif
     return decoder.forget();
   }
 #endif
 #ifdef MOZ_ANDROID_OMX
   if (MediaDecoder::IsAndroidMediaEnabled() &&
       GetAndroidMediaPluginHost()->FindDecoder(aType, nullptr)) {
     decoder = new AndroidMediaDecoder(aType);
     return decoder.forget();
--- a/content/media/omx/MediaCodecDecoder.cpp
+++ b/content/media/omx/MediaCodecDecoder.cpp
@@ -1,18 +1,16 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "MediaCodecDecoder.h"
 
-#include <stagefright/MediaSource.h>
-
 #include "MediaCodecReader.h"
 #include "MediaDecoderStateMachine.h"
 
 namespace mozilla {
 
 MediaDecoder*
 MediaCodecDecoder::Clone()
 {
--- a/content/media/omx/MediaCodecReader.cpp
+++ b/content/media/omx/MediaCodecReader.cpp
@@ -191,16 +191,17 @@ MediaCodecReader::CodecBufferInfo::Codec
   , mTimeUs(0)
   , mFlags(0)
 {
 }
 
 MediaCodecReader::MediaCodecReader(AbstractMediaDecoder* aDecoder)
   : MediaOmxCommonReader(aDecoder)
   , mColorConverterBufferSize(0)
+  , mExtractor(nullptr)
 {
   mHandler = new MessageHandler(this);
   mVideoListener = new VideoResourceListener(this);
 }
 
 MediaCodecReader::~MediaCodecReader()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread.");
--- a/content/media/omx/MediaCodecReader.h
+++ b/content/media/omx/MediaCodecReader.h
@@ -128,16 +128,20 @@ protected:
   // Called on MediaCodecReader::mLooper thread.
   void onMessageReceived(const android::sp<android::AMessage>& aMessage);
 
   // Receive a notify from ResourceListener.
   // Called on Binder thread.
   virtual void codecReserved(Track& aTrack);
   virtual void codecCanceled(Track& aTrack);
 
+  virtual bool CreateExtractor();
+
+  android::sp<android::MediaExtractor> mExtractor;
+
 private:
   // An intermediary class that can be managed by android::sp<T>.
   // Redirect onMessageReceived() to MediaCodecReader.
   class MessageHandler : public android::AHandler
   {
   public:
     MessageHandler(MediaCodecReader* aReader);
     ~MessageHandler();
@@ -219,17 +223,16 @@ private:
 
   bool ReallocateResources();
   void ReleaseCriticalResources();
   void ReleaseResources();
 
   bool CreateLooper();
   void DestroyLooper();
 
-  bool CreateExtractor();
   void DestroyExtractor();
 
   bool CreateMediaSources();
   void DestroyMediaSources();
 
   bool CreateMediaCodecs();
   static bool CreateMediaCodec(android::sp<android::ALooper>& aLooper,
                                Track& aTrack,
@@ -271,17 +274,16 @@ private:
 
   uint8_t* GetColorConverterBuffer(int32_t aWidth, int32_t aHeight);
   void ClearColorConverterBuffer();
 
   android::sp<MessageHandler> mHandler;
   android::sp<VideoResourceListener> mVideoListener;
 
   android::sp<android::ALooper> mLooper;
-  android::sp<android::MediaExtractor> mExtractor;
 
   // media tracks
   AudioTrack mAudioTrack;
   VideoTrack mVideoTrack;
   AudioTrack mAudioOffloadTrack; // only Track::mSource is valid
 
   // color converter
   android::I420ColorConverterHelper mColorConverter;
--- a/content/media/omx/MediaOmxCommonDecoder.cpp
+++ b/content/media/omx/MediaOmxCommonDecoder.cpp
@@ -35,16 +35,18 @@ MediaOmxCommonDecoder::MediaOmxCommonDec
 {
 #ifdef PR_LOGGING
   if (!gMediaDecoderLog) {
     gMediaDecoderLog = PR_NewLogModule("MediaDecoder");
   }
 #endif
 }
 
+MediaOmxCommonDecoder::~MediaOmxCommonDecoder() {}
+
 void
 MediaOmxCommonDecoder::SetPlatformCanOffloadAudio(bool aCanOffloadAudio)
 {
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
   mCanOffloadAudio = aCanOffloadAudio;
 }
 
 bool
--- a/content/media/omx/MediaOmxCommonDecoder.h
+++ b/content/media/omx/MediaOmxCommonDecoder.h
@@ -40,16 +40,17 @@ public:
   void AudioOffloadTearDown();
 
   virtual MediaDecoderStateMachine* CreateStateMachine();
 
   virtual MediaOmxCommonReader* CreateReader() = 0;
   virtual MediaDecoderStateMachine* CreateStateMachine(MediaOmxCommonReader* aReader) = 0;
 
 protected:
+  virtual ~MediaOmxCommonDecoder();
   void PauseStateMachine();
   void ResumeStateMachine();
 
   MediaOmxCommonReader* mReader;
 
   // Offloaded audio track
   android::sp<android::MediaSource> mAudioTrack;
 
new file mode 100644
--- /dev/null
+++ b/content/media/omx/RtspExtractor.cpp
@@ -0,0 +1,221 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "RtspExtractor.h"
+
+#include "mozilla/ReentrantMonitor.h"
+
+using namespace android;
+
+#define FRAME_DEFAULT_SIZE 1024
+
+namespace mozilla {
+
+/* class RtspMediaSource : implements MediaSource for OMX.
+ * The decoder thread will trigger the MediaDecodeStateMachine to read a/v frame.
+ * Then RtspOmxReader calls OMX decoder to decode a/v frame. Finally the code
+ * path run into the read() here, it reads un-decoded frame data from mResource
+ * and construct a MediaBuffer for output to OMX decoder.
+ * */
+class RtspMediaSource MOZ_FINAL : public MediaSource {
+public:
+  RtspMediaSource(RtspMediaResource* aRtspMediaResource,
+                  ssize_t aTrackIdx,
+                  uint32_t aFrameMaxSize,
+                  const sp<MetaData>& aMeta)
+  : mRtspResource(aRtspMediaResource)
+  , mFormat(aMeta)
+  , mTrackIdx(aTrackIdx)
+  , mMonitor("RtspMediaSource.mMonitor")
+  , mIsStarted(false)
+  , mGroup(nullptr)
+  , mBuffer(nullptr)
+  , mFrameMaxSize(aFrameMaxSize) {}
+  virtual ~RtspMediaSource() {}
+  virtual status_t start(MetaData* params = nullptr) MOZ_OVERRIDE;
+  virtual status_t stop() MOZ_OVERRIDE;
+  virtual sp<MetaData> getFormat() MOZ_OVERRIDE {
+    ReentrantMonitorAutoEnter mon(mMonitor);
+    return mFormat;
+  }
+  virtual status_t read(MediaBuffer** buffer,
+                        const ReadOptions* options = nullptr) MOZ_OVERRIDE ;
+private:
+  nsRefPtr<RtspMediaResource> mRtspResource;
+  sp<MetaData> mFormat;
+  uint32_t mTrackIdx;
+  ReentrantMonitor mMonitor;
+  bool mIsStarted;
+
+  // mGroup owns the mBuffer. mFrameMaxSize is the mBuffer size.
+  // mBuffer is the input buffer for omx decoder.
+  nsAutoPtr<MediaBufferGroup> mGroup;
+  MediaBuffer* mBuffer;
+  uint32_t mFrameMaxSize;
+};
+
+status_t
+RtspMediaSource::start(MetaData* params)
+{
+  ReentrantMonitorAutoEnter mon(mMonitor);
+  if (!mIsStarted) {
+    // RtspMediaSource relinquish the ownership of MediaBuffer |buf| to mGroup.
+    mGroup = new MediaBufferGroup();
+    MediaBuffer* buf = new MediaBuffer(mFrameMaxSize);
+    mGroup->add_buffer(buf);
+    mIsStarted = true;
+  }
+  return OK;
+}
+
+status_t
+RtspMediaSource::stop()
+{
+  ReentrantMonitorAutoEnter mon(mMonitor);
+  if (mIsStarted) {
+    if (mBuffer) {
+      mBuffer->release();
+      mBuffer = nullptr;
+    }
+    mGroup = nullptr;
+    mIsStarted = false;
+  }
+  return OK;
+}
+
+status_t
+RtspMediaSource::read(MediaBuffer** out, const ReadOptions* options)
+{
+  ReentrantMonitorAutoEnter mon(mMonitor);
+  NS_ENSURE_TRUE(mIsStarted, MEDIA_ERROR_BASE);
+  NS_ENSURE_TRUE(out, MEDIA_ERROR_BASE);
+  *out = nullptr;
+
+  // Video/audio track's initial frame size is FRAME_DEFAULT_SIZE.
+  // We need to realloc the mBuffer if the mBuffer doesn't have enough space
+  // for next ReadFrameFromTrack function. (actualFrameSize > mFrameMaxSize)
+  status_t err;
+  uint32_t readCount;
+  uint32_t actualFrameSize;
+  uint64_t time;
+  nsresult rv;
+
+  while (1) {
+    err = mGroup->acquire_buffer(&mBuffer);
+    NS_ENSURE_TRUE(err == OK, err);
+    rv = mRtspResource->ReadFrameFromTrack((uint8_t *)mBuffer->data(),
+                                           mFrameMaxSize, mTrackIdx, readCount,
+                                           time, actualFrameSize);
+    if (NS_FAILED(rv)) {
+      // Release mGroup and mBuffer.
+      stop();
+      // Since RtspMediaSource is an implementation of Android media source,
+      // it's held by OMXCodec and isn't released yet. So we have to re-construct
+      // mGroup and mBuffer.
+      start();
+      NS_WARNING("ReadFrameFromTrack failed; releasing buffers and returning.");
+      return ERROR_END_OF_STREAM;
+    }
+    if (actualFrameSize > mFrameMaxSize) {
+      // release mGroup and mBuffer
+      stop();
+      // re-construct mGroup and mBuffer
+      mFrameMaxSize = actualFrameSize;
+      err = start();
+      NS_ENSURE_TRUE(err == OK, err);
+    } else {
+      // ReadFrameFromTrack success, break the while loop.
+      break;
+    }
+  }
+  mBuffer->set_range(0, readCount);
+  if (NS_SUCCEEDED(rv)) {
+    mBuffer->meta_data()->clear();
+    // fill the meta data
+    mBuffer->meta_data()->setInt64(kKeyTime, time);
+    *out = mBuffer;
+    mBuffer = nullptr;
+    return OK;
+  }
+
+  return ERROR_END_OF_STREAM;
+}
+
+size_t
+RtspExtractor::countTracks()
+{
+  uint8_t tracks = 0;
+  if (mController) {
+    mController->GetTotalTracks(&tracks);
+  }
+  return size_t(tracks);
+}
+
+sp<MediaSource>
+RtspExtractor::getTrack(size_t index)
+{
+  NS_ENSURE_TRUE(index < countTracks(), nullptr);
+  sp<MetaData> meta = getTrackMetaData(index);
+  sp<MediaSource> source = new RtspMediaSource(mRtspResource,
+                                               index,
+                                               FRAME_DEFAULT_SIZE,
+                                               meta);
+  return source;
+}
+
+sp<MetaData>
+RtspExtractor::getTrackMetaData(size_t index, uint32_t flag)
+{
+  NS_ENSURE_TRUE(index < countTracks(), nullptr);
+  sp<MetaData> meta = new MetaData();
+  nsCOMPtr<nsIStreamingProtocolMetaData> rtspMetadata;
+  mController->GetTrackMetaData(index, getter_AddRefs(rtspMetadata));
+
+  if (rtspMetadata) {
+    // Convert msMeta into meta.
+    // The getter function of nsIStreamingProtocolMetaData will initialize the
+    // metadata values to 0 before setting them.
+    nsCString mime;
+    rtspMetadata->GetMimeType(mime);
+    meta->setCString(kKeyMIMEType, mime.get());
+    uint32_t temp32;
+    rtspMetadata->GetWidth(&temp32);
+    meta->setInt32(kKeyWidth, temp32);
+    rtspMetadata->GetHeight(&temp32);
+    meta->setInt32(kKeyHeight, temp32);
+    rtspMetadata->GetSampleRate(&temp32);
+    meta->setInt32(kKeySampleRate, temp32);
+    rtspMetadata->GetChannelCount(&temp32);
+    meta->setInt32(kKeyChannelCount, temp32);
+    uint64_t temp64;
+    rtspMetadata->GetDuration(&temp64);
+    meta->setInt64(kKeyDuration, temp64);
+
+    nsCString tempCString;
+    rtspMetadata->GetEsdsData(tempCString);
+    if (tempCString.Length()) {
+      meta->setData(kKeyESDS, 0, tempCString.get(), tempCString.Length());
+    }
+    rtspMetadata->GetAvccData(tempCString);
+    if (tempCString.Length()) {
+      meta->setData(kKeyAVCC, 0, tempCString.get(), tempCString.Length());
+    }
+  }
+  return meta;
+}
+
+uint32_t
+RtspExtractor::flags() const
+{
+  if (mRtspResource->IsRealTime()) {
+    return 0;
+  } else {
+    return MediaExtractor::CAN_SEEK;
+  }
+}
+
+} // namespace mozilla
+
new file mode 100644
--- /dev/null
+++ b/content/media/omx/RtspExtractor.h
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#if !defined(RtspExtractor_h_)
+#define RtspExtractor_h_
+
+#include "RtspMediaResource.h"
+
+#include <stagefright/MediaBufferGroup.h>
+#include <stagefright/MediaExtractor.h>
+#include <stagefright/MediaSource.h>
+#include <stagefright/MetaData.h>
+
+namespace mozilla {
+
+// RtspExtractor is a custom extractor for Rtsp stream, whereas the other
+// XXXExtractors are made for container media content.
+// The extractor is used for |OmxDecoder::Init|, it provides the essential
+// information for creating OMXCodec instance.
+// For example, the |getTrackMetaData| returns metadata that includes the
+// codec type.
+class RtspExtractor: public android::MediaExtractor
+{
+public:
+  virtual size_t countTracks() MOZ_FINAL MOZ_OVERRIDE;
+  virtual android::sp<android::MediaSource> getTrack(size_t index)
+    MOZ_FINAL MOZ_OVERRIDE;
+  virtual android::sp<android::MetaData> getTrackMetaData(
+    size_t index, uint32_t flag = 0) MOZ_FINAL MOZ_OVERRIDE;
+  virtual uint32_t flags() const MOZ_FINAL MOZ_OVERRIDE;
+
+  RtspExtractor(RtspMediaResource* aResource)
+    : mRtspResource(aResource) {
+    MOZ_ASSERT(aResource);
+    mController = mRtspResource->GetMediaStreamController();
+    MOZ_ASSERT(mController);
+  }
+  virtual ~RtspExtractor() MOZ_OVERRIDE {}
+private:
+  // mRtspResource is a pointer to RtspMediaResource. When |getTrack| is called
+  // we use mRtspResource to construct a RtspMediaSource.
+  RtspMediaResource* mRtspResource;
+  // Through the mController in mRtspResource, we can get the essential
+  // information for the extractor.
+  nsRefPtr<nsIStreamingProtocolController> mController;
+};
+
+} // namespace mozilla
+
+#endif
new file mode 100644
--- /dev/null
+++ b/content/media/omx/RtspMediaCodecDecoder.cpp
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "RtspMediaCodecDecoder.h"
+
+#include "MediaDecoderStateMachine.h"
+#include "RtspMediaResource.h"
+#include "RtspMediaCodecReader.h"
+
+namespace mozilla {
+
+MediaDecoder*
+RtspMediaCodecDecoder::Clone()
+{
+  return new RtspMediaCodecDecoder();
+}
+
+MediaOmxCommonReader*
+RtspMediaCodecDecoder::CreateReader()
+{
+  return new RtspMediaCodecReader(this);
+}
+
+MediaDecoderStateMachine*
+RtspMediaCodecDecoder::CreateStateMachine(MediaOmxCommonReader* aReader)
+{
+  return new MediaDecoderStateMachine(this, aReader,
+                                      mResource->IsRealTime());
+}
+
+void
+RtspMediaCodecDecoder::ApplyStateToStateMachine(PlayState aState)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  MediaDecoder::ApplyStateToStateMachine(aState);
+
+  // Notify RTSP controller if the play state is ended.
+  // This is necessary for RTSP controller to transit its own state.
+  if (aState == PLAY_STATE_ENDED) {
+    nsRefPtr<RtspMediaResource> resource = mResource->GetRtspPointer();
+    if (resource) {
+      nsIStreamingProtocolController* controller =
+        resource->GetMediaStreamController();
+      if (controller) {
+        controller->PlaybackEnded();
+      }
+    }
+  }
+}
+
+} // namespace mozilla
+
new file mode 100644
--- /dev/null
+++ b/content/media/omx/RtspMediaCodecDecoder.h
@@ -0,0 +1,28 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#if !defined(RtspMediaCodecDecoder_h_)
+#define RtspMediaCodecDecoder_h_
+
+#include "MediaOmxCommonDecoder.h"
+
+namespace mozilla {
+
+class RtspMediaCodecDecoder MOZ_FINAL : public MediaOmxCommonDecoder
+{
+public:
+  virtual MediaDecoder* Clone() MOZ_OVERRIDE;
+
+  virtual MediaOmxCommonReader* CreateReader() MOZ_OVERRIDE;
+
+  virtual MediaDecoderStateMachine* CreateStateMachine(MediaOmxCommonReader* aReader) MOZ_OVERRIDE;
+
+  virtual void ApplyStateToStateMachine(PlayState aState) MOZ_OVERRIDE;
+};
+
+} // namespace mozilla
+
+#endif
new file mode 100644
--- /dev/null
+++ b/content/media/omx/RtspMediaCodecReader.cpp
@@ -0,0 +1,116 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "RtspMediaCodecReader.h"
+
+#include "RtspExtractor.h"
+#include "RtspMediaResource.h"
+#include "RtspMediaCodecDecoder.h"
+
+using namespace android;
+
+namespace mozilla {
+
+RtspMediaCodecReader::RtspMediaCodecReader(AbstractMediaDecoder* aDecoder)
+  : MediaCodecReader(aDecoder)
+{
+  NS_ASSERTION(mDecoder, "RtspMediaCodecReader mDecoder is null.");
+  NS_ASSERTION(mDecoder->GetResource(),
+               "RtspMediaCodecReader mDecoder->GetResource() is null.");
+  mRtspResource = mDecoder->GetResource()->GetRtspPointer();
+  MOZ_ASSERT(mRtspResource);
+}
+
+RtspMediaCodecReader::~RtspMediaCodecReader() {}
+
+bool
+RtspMediaCodecReader::CreateExtractor()
+{
+  if (mExtractor != nullptr) {
+    return true;
+  }
+
+  mExtractor = new RtspExtractor(mRtspResource);
+
+  return mExtractor != nullptr;
+}
+
+nsresult
+RtspMediaCodecReader::Seek(int64_t aTime, int64_t aStartTime,
+                           int64_t aEndTime, int64_t aCurrentTime)
+{
+  // The seek function of Rtsp is time-based, we call the SeekTime function in
+  // RtspMediaResource. The SeekTime function finally send a seek command to
+  // Rtsp stream server through network and also clear the buffer data in
+  // RtspMediaResource.
+  mRtspResource->SeekTime(aTime);
+
+  return MediaCodecReader::Seek(aTime, aStartTime, aEndTime, aCurrentTime);
+}
+
+void
+RtspMediaCodecReader::SetIdle()
+{
+  nsIStreamingProtocolController* controller =
+    mRtspResource->GetMediaStreamController();
+  if (controller) {
+    controller->Pause();
+  }
+  mRtspResource->SetSuspend(true);
+}
+
+void
+RtspMediaCodecReader::EnsureActive()
+{
+  // Need to start RTSP streaming OMXCodec decoding.
+  nsIStreamingProtocolController* controller =
+    mRtspResource->GetMediaStreamController();
+  if (controller) {
+    controller->Play();
+  }
+  mRtspResource->SetSuspend(false);
+}
+
+void
+RtspMediaCodecReader::RequestAudioData()
+{
+  EnsureActive();
+  MediaCodecReader::RequestAudioData();
+}
+
+void
+RtspMediaCodecReader::RequestVideoData(bool aSkipToNextKeyframe,
+                                       int64_t aTimeThreshold)
+{
+  EnsureActive();
+  MediaCodecReader::RequestVideoData(aSkipToNextKeyframe, aTimeThreshold);
+}
+
+nsresult
+RtspMediaCodecReader::ReadMetadata(MediaInfo* aInfo,
+                                   MetadataTags** aTags)
+{
+  nsresult rv = MediaCodecReader::ReadMetadata(aInfo, aTags);
+  if (rv == NS_OK && !IsWaitingMediaResources()) {
+    EnsureActive();
+  }
+
+  return rv;
+}
+
+// Called on Binder thread.
+void
+RtspMediaCodecReader::codecReserved(Track& aTrack)
+{
+  // TODO: fix me, we need a SeekTime(0) here because the
+  // MediaDecoderStateMachine will update the mStartTime after ReadMetadata.
+  MediaCodecReader::codecReserved(aTrack);
+  if (aTrack.mCodec != nullptr) {
+    mRtspResource->SeekTime(0);
+  }
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/media/omx/RtspMediaCodecReader.h
@@ -0,0 +1,80 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#if !defined(RtspMediaCodecReader_h_)
+#define RtspMediaCodecReader_h_
+
+#include "MediaCodecReader.h"
+
+namespace mozilla {
+
+namespace dom {
+  class TimeRanges;
+}
+
+class AbstractMediaDecoder;
+class RtspMediaResource;
+
+/* RtspMediaCodecReader is a subclass of MediaCodecReader.
+ * The major reason that RtspMediaCodecReader inherit from MediaCodecReader is
+ * the same video/audio decoding logic we can reuse.
+ */
+class RtspMediaCodecReader MOZ_FINAL : public MediaCodecReader
+{
+protected:
+  // Provide a Rtsp extractor.
+  virtual bool CreateExtractor() MOZ_OVERRIDE;
+  // Ensure the play command is sent to Rtsp server.
+  void EnsureActive();
+
+public:
+  RtspMediaCodecReader(AbstractMediaDecoder* aDecoder);
+
+  virtual ~RtspMediaCodecReader();
+
+  // Implement a time-based seek instead of byte-based.
+  virtual nsresult Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime,
+                        int64_t aCurrentTime) MOZ_OVERRIDE;
+
+  // Override GetBuffered() to do nothing for below reasons:
+  // 1. Because the Rtsp stream is a/v separated. The buffered data in a/v
+  // tracks are not consistent with time stamp.
+  // For example: audio buffer: 1~2s, video buffer: 1.5~2.5s
+  // 2. Since the Rtsp is a realtime streaming, the buffer we made for
+  // RtspMediaResource is quite small. The small buffer implies the time ranges
+  // we returned are not useful for the MediaDecodeStateMachine. Unlike the
+  // ChannelMediaResource, it has a "cache" that can store the whole streaming
+  // data so the |GetBuffered| function can retrieve useful time ranges.
+  virtual nsresult GetBuffered(dom::TimeRanges* aBuffered,
+                               int64_t aStartTime) MOZ_OVERRIDE {
+    return NS_OK;
+  }
+
+  virtual void SetIdle() MOZ_OVERRIDE;
+
+  // Disptach a DecodeVideoFrameTask to decode video data.
+  virtual void RequestVideoData(bool aSkipToNextKeyframe,
+                                int64_t aTimeThreshold) MOZ_OVERRIDE;
+
+  // Disptach a DecodeAudioDataTask to decode audio data.
+  virtual void RequestAudioData() MOZ_OVERRIDE;
+
+  virtual nsresult ReadMetadata(MediaInfo* aInfo,
+                                MetadataTags** aTags) MOZ_OVERRIDE;
+
+  virtual void codecReserved(Track& aTrack) MOZ_OVERRIDE;
+
+private:
+  // A pointer to RtspMediaResource for calling the Rtsp specific function.
+  // The lifetime of mRtspResource is controlled by MediaDecoder. MediaDecoder
+  // holds the MediaDecoderStateMachine and RtspMediaResource.
+  // And MediaDecoderStateMachine holds this RtspMediaCodecReader.
+  RtspMediaResource* mRtspResource;
+};
+
+} // namespace mozilla
+
+#endif
--- a/content/media/omx/RtspOmxReader.cpp
+++ b/content/media/omx/RtspOmxReader.cpp
@@ -3,274 +3,25 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "RtspOmxReader.h"
 
 #include "AbstractMediaDecoder.h"
 #include "MediaDecoderStateMachine.h"
-#include "MPAPI.h"
-#include "mozilla/dom/TimeRanges.h"
-#include "mozilla/Mutex.h"
-#include "mozilla/TimeStamp.h"
 #include "OmxDecoder.h"
+#include "RtspExtractor.h"
 #include "RtspMediaResource.h"
 #include "RtspOmxDecoder.h"
-#include "VideoUtils.h"
-
-#include <stagefright/MediaExtractor.h>
-#include <stagefright/MediaBufferGroup.h>
-#include <stagefright/MetaData.h>
-
-#define FRAME_DEFAULT_SIZE 1024
 
 using namespace android;
 
 namespace mozilla {
 
-/* class RtspMediaSource : implements MediaSource for OMX.
- * The decoder thread will trigger the MediaDecodeStateMachine to read a/v frame.
- * Then RtspOmxReader calls OMX decoder to decode a/v frame. Finally the code
- * path run into the read() here, it reads un-decoded frame data from mResource
- * and construct a MediaBuffer for output to OMX decoder.
- * */
-class RtspMediaSource : public android::MediaSource {
-public:
-  RtspMediaSource(RtspMediaResource *aRtspMediaResource,
-                  ssize_t aTrackIdx,
-                  uint32_t aFrameMaxSize,
-                  const sp<MetaData>& aMeta)
-  : mRtspResource(aRtspMediaResource)
-  , mFormat(aMeta)
-  , mTrackIdx(aTrackIdx)
-  , mMonitor("RtspMediaSource.mMonitor")
-  , mIsStarted(false)
-  , mGroup(nullptr)
-  , mBuffer(nullptr)
-  , mFrameMaxSize(aFrameMaxSize) {
-    MOZ_COUNT_CTOR(RtspMediaSource);
-  };
-  virtual ~RtspMediaSource() {
-    MOZ_COUNT_DTOR(RtspMediaSource);
-  }
-  virtual status_t start(MetaData *params = nullptr) MOZ_FINAL MOZ_OVERRIDE;
-  virtual status_t stop() MOZ_FINAL MOZ_OVERRIDE;
-  virtual sp<MetaData> getFormat() MOZ_FINAL MOZ_OVERRIDE {
-    ReentrantMonitorAutoEnter mon(mMonitor);
-    return mFormat;
-  };
-  virtual status_t read(MediaBuffer **buffer,
-                        const ReadOptions *options = nullptr) MOZ_FINAL MOZ_OVERRIDE ;
-private:
-  nsRefPtr<RtspMediaResource> mRtspResource;
-  sp<MetaData> mFormat;
-  uint32_t mTrackIdx;
-  ReentrantMonitor mMonitor;
-  bool mIsStarted;
-
-  // mGroup owns the mBuffer. mFrameMaxSize is the mBuffer size.
-  // mBuffer is the input buffer for omx decoder.
-  nsAutoPtr<MediaBufferGroup> mGroup;
-  MediaBuffer* mBuffer;
-  uint32_t mFrameMaxSize;
-};
-
-status_t RtspMediaSource::start(MetaData *params)
-{
-  ReentrantMonitorAutoEnter mon(mMonitor);
-  if (!mIsStarted) {
-    // RtspMediaSource relinquish the ownership of MediaBuffer |buf| to mGroup.
-    mGroup = new MediaBufferGroup();
-    MediaBuffer* buf = new MediaBuffer(mFrameMaxSize);
-    mGroup->add_buffer(buf);
-    mIsStarted = true;
-  }
-  return OK;
-}
-
-status_t RtspMediaSource::stop()
-{
-  ReentrantMonitorAutoEnter mon(mMonitor);
-  if (mIsStarted) {
-    if (mBuffer) {
-      mBuffer->release();
-      mBuffer = nullptr;
-    }
-    mGroup = nullptr;
-    mIsStarted = false;
-  }
-  return OK;
-}
-
-status_t RtspMediaSource::read(MediaBuffer **out, const ReadOptions *options)
-{
-  ReentrantMonitorAutoEnter mon(mMonitor);
-  NS_ENSURE_TRUE(mIsStarted, MEDIA_ERROR_BASE);
-  NS_ENSURE_TRUE(out, MEDIA_ERROR_BASE);
-  *out = nullptr;
-
-  // Video/audio track's initial frame size is FRAME_DEFAULT_SIZE.
-  // We need to realloc the mBuffer if the mBuffer doesn't have enough space
-  // for next ReadFrameFromTrack function. (actualFrameSize > mFrameMaxSize)
-  status_t err;
-  uint32_t readCount;
-  uint32_t actualFrameSize;
-  uint64_t time;
-  nsresult rv;
-
-  while (1) {
-    err = mGroup->acquire_buffer(&mBuffer);
-    NS_ENSURE_TRUE(err == OK, err);
-
-    rv = mRtspResource->ReadFrameFromTrack((uint8_t *)mBuffer->data(),
-                                           mFrameMaxSize, mTrackIdx, readCount,
-                                           time, actualFrameSize);
-    if (NS_FAILED(rv)) {
-      // Release mGroup and mBuffer.
-      stop();
-      // Since RtspMediaSource is an implementation of Android media source,
-      // it's held by OMXCodec and isn't released yet. So we have to re-construct
-      // mGroup and mBuffer.
-      start();
-      NS_WARNING("ReadFrameFromTrack failed; releasing buffers and returning.");
-      return ERROR_CONNECTION_LOST;
-    }
-    if (actualFrameSize > mFrameMaxSize) {
-      // release mGroup and mBuffer
-      stop();
-      // re-construct mGroup and mBuffer
-      mFrameMaxSize = actualFrameSize;
-      err = start();
-      NS_ENSURE_TRUE(err == OK, err);
-    } else {
-      // ReadFrameFromTrack success, break the while loop.
-      break;
-    }
-  }
-  mBuffer->set_range(0, readCount);
-  if (NS_SUCCEEDED(rv)) {
-    mBuffer->meta_data()->clear();
-    // fill the meta data
-    mBuffer->meta_data()->setInt64(kKeyTime, time);
-    *out = mBuffer;
-    mBuffer = nullptr;
-    return OK;
-  }
-
-  return ERROR_END_OF_STREAM;
-}
-
-
-// RtspExtractor is a custom extractor for Rtsp stream, whereas the other
-// XXXExtractors are made for container media content.
-// The extractor is used for |OmxDecoder::Init|, it provides the essential
-// information for creating OMXCodec instance.
-// For example, the |getTrackMetaData| returns metadata that includes the
-// codec type.
-class RtspExtractor: public MediaExtractor
-{
-public:
-  virtual size_t countTracks() MOZ_FINAL MOZ_OVERRIDE;
-  virtual sp<android::MediaSource> getTrack(size_t index)
-    MOZ_FINAL MOZ_OVERRIDE;
-  virtual sp<MetaData> getTrackMetaData(
-    size_t index, uint32_t flag = 0) MOZ_FINAL MOZ_OVERRIDE;
-  virtual uint32_t flags() const MOZ_FINAL MOZ_OVERRIDE;
-
-  RtspExtractor(RtspMediaResource *aResource)
-    : mRtspResource(aResource) {
-    MOZ_COUNT_CTOR(RtspExtractor);
-    MOZ_ASSERT(aResource);
-    mController = mRtspResource->GetMediaStreamController();
-    MOZ_ASSERT(mController);
-  }
-  virtual ~RtspExtractor() MOZ_OVERRIDE {
-    MOZ_COUNT_DTOR(RtspExtractor);
-  }
-private:
-  // mRtspResource is a pointer to RtspMediaResource. When |getTrack| is called
-  // we use mRtspResource to construct a RtspMediaSource.
-  RtspMediaResource* mRtspResource;
-  // Through the mController in mRtspResource, we can get the essential
-  // information for the extractor.
-  nsRefPtr<nsIStreamingProtocolController> mController;
-};
-
-size_t RtspExtractor::countTracks()
-{
-  uint8_t tracks = 0;
-  if (mController) {
-    mController->GetTotalTracks(&tracks);
-  }
-  return size_t(tracks);
-}
-
-sp<android::MediaSource> RtspExtractor::getTrack(size_t index)
-{
-  NS_ENSURE_TRUE(index < countTracks(), nullptr);
-
-  sp<MetaData> meta = getTrackMetaData(index);
-  sp<android::MediaSource> source = new RtspMediaSource(mRtspResource,
-                                                        index,
-                                                        FRAME_DEFAULT_SIZE,
-                                                        meta);
-  return source;
-}
-
-sp<MetaData> RtspExtractor::getTrackMetaData(size_t index, uint32_t flag)
-{
-  NS_ENSURE_TRUE(index < countTracks(), nullptr);;
-
-  sp<MetaData> meta = new MetaData();
-  nsCOMPtr<nsIStreamingProtocolMetaData> rtspMetadata;
-  mController->GetTrackMetaData(index, getter_AddRefs(rtspMetadata));
-
-  if (rtspMetadata) {
-    // Convert msMeta into meta.
-    // The getter function of nsIStreamingProtocolMetaData will initialize the
-    // metadata values to 0 before setting them.
-    nsCString mime;
-    rtspMetadata->GetMimeType(mime);
-    meta->setCString(kKeyMIMEType, mime.get());
-    uint32_t temp32;
-    rtspMetadata->GetWidth(&temp32);
-    meta->setInt32(kKeyWidth, temp32);
-    rtspMetadata->GetHeight(&temp32);
-    meta->setInt32(kKeyHeight, temp32);
-    rtspMetadata->GetSampleRate(&temp32);
-    meta->setInt32(kKeySampleRate, temp32);
-    rtspMetadata->GetChannelCount(&temp32);
-    meta->setInt32(kKeyChannelCount, temp32);
-    uint64_t temp64;
-    rtspMetadata->GetDuration(&temp64);
-    meta->setInt64(kKeyDuration, temp64);
-
-    nsCString tempCString;
-    rtspMetadata->GetEsdsData(tempCString);
-    if (tempCString.Length()) {
-      meta->setData(kKeyESDS, 0, tempCString.get(), tempCString.Length());
-    }
-    rtspMetadata->GetAvccData(tempCString);
-    if (tempCString.Length()) {
-      meta->setData(kKeyAVCC, 0, tempCString.get(), tempCString.Length());
-    }
-  }
-  return meta;
-}
-
-uint32_t RtspExtractor::flags() const
-{
-  if (mRtspResource->IsRealTime()) {
-    return 0;
-  } else {
-    return MediaExtractor::CAN_SEEK;
-  }
-}
-
 nsresult RtspOmxReader::InitOmxDecoder()
 {
   if (!mOmxDecoder.get()) {
     NS_ASSERTION(mDecoder, "RtspOmxReader mDecoder is null.");
     NS_ASSERTION(mDecoder->GetResource(),
                  "RtspOmxReader mDecoder->GetResource() is null.");
     mExtractor = new RtspExtractor(mRtspResource);
     mOmxDecoder = new OmxDecoder(mDecoder->GetResource(), mDecoder);
--- a/content/media/omx/moz.build
+++ b/content/media/omx/moz.build
@@ -39,23 +39,34 @@ if CONFIG['MOZ_OMX_ENCODER']:
     ]
     SOURCES += [
         'OMXCodecDescriptorUtil.cpp',
         'OMXCodecWrapper.cpp',
     ]
 
 if 'rtsp' in CONFIG['NECKO_PROTOCOLS']:
     EXPORTS += [
+        'RtspExtractor.h',
         'RtspOmxDecoder.h',
         'RtspOmxReader.h',
     ]
     SOURCES += [
+        'RtspExtractor.cpp',
         'RtspOmxDecoder.cpp',
         'RtspOmxReader.cpp',
     ]
+    if CONFIG['ANDROID_VERSION'] >= '18':
+        EXPORTS += [
+            'RtspMediaCodecDecoder.h',
+            'RtspMediaCodecReader.h',
+        ]
+        SOURCES += [
+            'RtspMediaCodecDecoder.cpp',
+            'RtspMediaCodecReader.cpp',
+        ]
 
 if CONFIG['ANDROID_VERSION'] >= '18':
     EXPORTS += [
         'I420ColorConverterHelper.h',
         'MediaCodecDecoder.h',
         'MediaCodecProxy.h',
         'MediaCodecReader.h',
     ]