Bug 1033915 - Integrate MP3FrameParser into MediaCodecReader. r=cajbir, a=bajaj
authorBruce Sun <brsun@mozilla.com>
Mon, 01 Sep 2014 18:04:36 +0800
changeset 224601 18cee7bf567442be09259eaa2493c27f27034660
parent 224600 5443cac376fbd5bf48e8e3834e58beaaecc867ae
child 224602 ffb144a500a4e005cc2b39e76638bb881d5d663f
push id3979
push userraliiev@mozilla.com
push dateMon, 13 Oct 2014 16:35:44 +0000
treeherdermozilla-beta@30f2cc610691 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscajbir, bajaj
bugs1033915
milestone34.0a2
Bug 1033915 - Integrate MP3FrameParser into MediaCodecReader. r=cajbir, a=bajaj
content/media/omx/MediaCodecReader.cpp
content/media/omx/MediaCodecReader.h
--- a/content/media/omx/MediaCodecReader.cpp
+++ b/content/media/omx/MediaCodecReader.cpp
@@ -24,16 +24,17 @@
 #include <stagefright/Utils.h>
 
 #include "mozilla/TimeStamp.h"
 
 #include "gfx2DGlue.h"
 
 #include "MediaStreamSource.h"
 #include "MediaTaskQueue.h"
+#include "MP3FrameParser.h"
 #include "nsThreadUtils.h"
 #include "ImageContainer.h"
 #include "SharedThreadPool.h"
 #include "VideoFrameContainer.h"
 
 using namespace android;
 
 namespace mozilla {
@@ -126,16 +127,17 @@ MediaCodecReader::TrackInputCopier::Copy
          (uint8_t*)aSourceBuffer->data() + aSourceBuffer->range_offset(),
          aSourceBuffer->range_length());
 
   return true;
 }
 
 MediaCodecReader::Track::Track()
   : mSourceIsStopped(true)
+  , mDurationLock("MediaCodecReader::Track::mDurationLock")
   , mDurationUs(INT64_C(0))
   , mInputIndex(sInvalidInputIndex)
   , mInputEndOfStream(false)
   , mOutputEndOfStream(false)
   , mSeekTimeUs(sInvalidTimestampUs)
   , mFlushed(false)
   , mDiscontinuity(false)
   , mTaskQueue(nullptr)
@@ -188,20 +190,106 @@ MediaCodecReader::CodecBufferInfo::Codec
   : mIndex(0)
   , mOffset(0)
   , mSize(0)
   , mTimeUs(0)
   , mFlags(0)
 {
 }
 
+MediaCodecReader::SignalObject::SignalObject(const char* aName)
+  : mMonitor(aName)
+  , mSignaled(false)
+{
+}
+
+MediaCodecReader::SignalObject::~SignalObject()
+{
+}
+
+void
+MediaCodecReader::SignalObject::Wait()
+{
+  MonitorAutoLock al(mMonitor);
+  if (!mSignaled) {
+    mMonitor.Wait();
+  }
+}
+
+void
+MediaCodecReader::SignalObject::Signal()
+{
+  MonitorAutoLock al(mMonitor);
+  mSignaled = true;
+  mMonitor.Notify();
+}
+
+MediaCodecReader::ParseCachedDataRunnable::ParseCachedDataRunnable(nsRefPtr<MediaCodecReader> aReader,
+                                                                   const char* aBuffer,
+                                                                   uint32_t aLength,
+                                                                   int64_t aOffset,
+                                                                   nsRefPtr<SignalObject> aSignal)
+  : mReader(aReader)
+  , mBuffer(aBuffer)
+  , mLength(aLength)
+  , mOffset(aOffset)
+  , mSignal(aSignal)
+{
+  MOZ_ASSERT(mReader, "Should have a valid MediaCodecReader.");
+  MOZ_ASSERT(mBuffer, "Should have a valid buffer.");
+  MOZ_ASSERT(mOffset >= INT64_C(0), "Should have a valid offset.");
+}
+
+NS_IMETHODIMP
+MediaCodecReader::ParseCachedDataRunnable::Run()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
+
+  if (mReader->ParseDataSegment(mBuffer, mLength, mOffset)) {
+    MonitorAutoLock monLock(mReader->mParserMonitor);
+    if (mReader->mNextParserPosition >= mOffset + mLength &&
+        mReader->mParsedDataLength < mOffset + mLength) {
+      mReader->mParsedDataLength = mOffset + mLength;
+    }
+  }
+
+  if (mSignal != nullptr) {
+    mSignal->Signal();
+  }
+
+  return NS_OK;
+}
+
+MediaCodecReader::ProcessCachedDataTask::ProcessCachedDataTask(nsRefPtr<MediaCodecReader> aReader,
+                                                               int64_t aOffset)
+  : mReader(aReader)
+  , mOffset(aOffset)
+{
+  MOZ_ASSERT(mReader, "Should have a valid MediaCodecReader.");
+  MOZ_ASSERT(mOffset >= INT64_C(0), "Should have a valid offset.");
+}
+
+void
+MediaCodecReader::ProcessCachedDataTask::Run()
+{
+  mReader->ProcessCachedData(mOffset, nullptr);
+  nsRefPtr<ReferenceKeeperRunnable<MediaCodecReader>> runnable(
+      new ReferenceKeeperRunnable<MediaCodecReader>(mReader));
+  mReader = nullptr;
+  NS_DispatchToMainThread(runnable.get());
+}
+
 MediaCodecReader::MediaCodecReader(AbstractMediaDecoder* aDecoder)
   : MediaOmxCommonReader(aDecoder)
   , mColorConverterBufferSize(0)
   , mExtractor(nullptr)
+  , mParserMonitor("MediaCodecReader::mParserMonitor")
+  , mParseDataFromCache(true)
+  , mNextParserPosition(INT64_C(0))
+  , mParsedDataLength(INT64_C(0))
 {
   mHandler = new MessageHandler(this);
   mVideoListener = new VideoResourceListener(this);
 }
 
 MediaCodecReader::~MediaCodecReader()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread.");
@@ -416,30 +504,172 @@ MediaCodecReader::HasAudio()
 }
 
 bool
 MediaCodecReader::HasVideo()
 {
   return mInfo.mVideo.mHasVideo;
 }
 
+void
+MediaCodecReader::NotifyDataArrived(const char* aBuffer,
+                                    uint32_t aLength,
+                                    int64_t aOffset)
+{
+  MonitorAutoLock monLock(mParserMonitor);
+  if (mNextParserPosition == mParsedDataLength &&
+      mNextParserPosition >= aOffset &&
+      mNextParserPosition <= aOffset + aLength) {
+    // No pending parsing runnable currently. And available data are adjacent to
+    // parsed data.
+    int64_t shift = mNextParserPosition - aOffset;
+    const char* buffer = aBuffer + shift;
+    uint32_t length = aLength - shift;
+    int64_t offset = mNextParserPosition;
+    if (length > 0) {
+      MonitorAutoUnlock monUnlock(mParserMonitor);
+      ParseDataSegment(buffer, length, offset);
+    }
+    mParseDataFromCache = false;
+    mParsedDataLength = offset + length;
+    mNextParserPosition = mParsedDataLength;
+  }
+}
+
+int64_t
+MediaCodecReader::ProcessCachedData(int64_t aOffset,
+                                    nsRefPtr<SignalObject> aSignal)
+{
+  // We read data in chunks of 32 KiB. We can reduce this
+  // value if media, such as sdcards, is too slow.
+  // Because of SD card's slowness, need to keep sReadSize to small size.
+  // See Bug 914870.
+  static const int64_t sReadSize = 32 * 1024;
+
+  MOZ_ASSERT(!NS_IsMainThread(), "Should not be on main thread.");
+
+  {
+    MonitorAutoLock monLock(mParserMonitor);
+    if (!mParseDataFromCache) {
+      // Skip cache processing since data can be continuously be parsed by
+      // ParseDataSegment() from NotifyDataArrived() directly.
+      return INT64_C(0);
+    }
+  }
+
+  MediaResource *resource = mDecoder->GetResource();
+  MOZ_ASSERT(resource);
+
+  int64_t resourceLength = resource->GetCachedDataEnd(0);
+  NS_ENSURE_TRUE(resourceLength >= 0, INT64_C(-1));
+
+  if (aOffset >= resourceLength) {
+    return INT64_C(0); // Cache is empty, nothing to do
+  }
+
+  int64_t bufferLength = std::min<int64_t>(resourceLength - aOffset, sReadSize);
+
+  nsAutoArrayPtr<char> buffer(new char[bufferLength]);
+
+  nsresult rv = resource->ReadFromCache(buffer.get(), aOffset, bufferLength);
+  NS_ENSURE_SUCCESS(rv, INT64_C(-1));
+
+  MonitorAutoLock monLock(mParserMonitor);
+  if (mParseDataFromCache) {
+    nsRefPtr<ParseCachedDataRunnable> runnable(
+      new ParseCachedDataRunnable(this,
+                                  buffer.forget(),
+                                  bufferLength,
+                                  aOffset,
+                                  aSignal));
+
+    rv = NS_DispatchToMainThread(runnable.get());
+    NS_ENSURE_SUCCESS(rv, INT64_C(-1));
+
+    mNextParserPosition = aOffset + bufferLength;
+    if (mNextParserPosition < resource->GetCachedDataEnd(0)) {
+      // We cannot read data in the main thread because it
+      // might block for too long. Instead we post an IO task
+      // to the IO thread if there is more data available.
+      XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
+          new ProcessCachedDataTask(this, mNextParserPosition));
+    }
+  }
+
+  return bufferLength;
+}
+
+bool
+MediaCodecReader::ParseDataSegment(const char* aBuffer,
+                                   uint32_t aLength,
+                                   int64_t aOffset)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
+
+  int64_t duration = INT64_C(-1);
+
+  {
+    MonitorAutoLock monLock(mParserMonitor);
+
+    // currently only mp3 files are supported for incremental parsing
+    if (mMP3FrameParser == nullptr) {
+      return false;
+    }
+
+    if (!mMP3FrameParser->IsMP3()) {
+      return true; // NO-OP
+    }
+
+    mMP3FrameParser->Parse(aBuffer, aLength, aOffset);
+
+    duration = mMP3FrameParser->GetDuration();
+  }
+
+  bool durationUpdateRequired = false;
+
+  {
+    MutexAutoLock al(mAudioTrack.mDurationLock);
+    if (duration > mAudioTrack.mDurationUs) {
+      mAudioTrack.mDurationUs = duration;
+      durationUpdateRequired = true;
+    }
+  }
+
+  if (durationUpdateRequired && HasVideo()) {
+    MutexAutoLock al(mVideoTrack.mDurationLock);
+    durationUpdateRequired = duration > mVideoTrack.mDurationUs;
+  }
+
+  if (durationUpdateRequired) {
+    MOZ_ASSERT(mDecoder);
+    ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
+    mDecoder->UpdateEstimatedMediaDuration(duration);
+  }
+
+  return true;
+}
+
 nsresult
 MediaCodecReader::ReadMetadata(MediaInfo* aInfo,
                                MetadataTags** aTags)
 {
   MOZ_ASSERT(mDecoder->OnDecodeThread(), "Should be on decode thread.");
 
   if (!ReallocateResources()) {
     return NS_ERROR_FAILURE;
   }
 
 #ifdef MOZ_AUDIO_OFFLOAD
   CheckAudioOffload();
 #endif
 
+  if (!TriggerIncrementalParser()) {
+    return NS_ERROR_FAILURE;
+  }
+
   if (IsWaitingMediaResources()) {
     return NS_OK;
   }
 
   // TODO: start streaming
 
   if (!UpdateDuration()) {
     return NS_ERROR_FAILURE;
@@ -449,19 +679,28 @@ MediaCodecReader::ReadMetadata(MediaInfo
     return NS_ERROR_FAILURE;
   }
 
   if (!UpdateVideoInfo()) {
     return NS_ERROR_FAILURE;
   }
 
   // Set the total duration (the max of the audio and video track).
-  int64_t duration = mAudioTrack.mDurationUs > mVideoTrack.mDurationUs ?
-    mAudioTrack.mDurationUs : mVideoTrack.mDurationUs;
-  if (duration >= 0LL) {
+  int64_t audioDuration = INT64_C(-1);
+  {
+    MutexAutoLock al(mAudioTrack.mDurationLock);
+    audioDuration = mAudioTrack.mDurationUs;
+  }
+  int64_t videoDuration = INT64_C(-1);
+  {
+    MutexAutoLock al(mVideoTrack.mDurationLock);
+    videoDuration = mVideoTrack.mDurationUs;
+  }
+  int64_t duration = audioDuration > videoDuration ? audioDuration : videoDuration;
+  if (duration >= INT64_C(0)) {
     ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
     mDecoder->SetMediaDuration(duration);
   }
 
   // Video track's frame sizes will not overflow. Activate the video track.
   VideoFrameContainer* container = mDecoder->GetVideoFrameContainer();
   if (container) {
     container->SetCurrentFrame(
@@ -809,18 +1048,17 @@ MediaCodecReader::DestroyExtractor()
 
 bool
 MediaCodecReader::CreateMediaSources()
 {
   if (mExtractor == nullptr) {
     return false;
   }
 
-  sp<MetaData> extractorMetaData = mExtractor->getMetaData();
-  // TODO: Check MP3 file format
+  mMetaData = mExtractor->getMetaData();
 
   const ssize_t invalidTrackIndex = -1;
   ssize_t audioTrackIndex = invalidTrackIndex;
   ssize_t videoTrackIndex = invalidTrackIndex;
 
   for (size_t i = 0; i < mExtractor->countTracks(); ++i) {
     sp<MetaData> trackFormat = mExtractor->getTrackMetaData(i);
 
@@ -955,17 +1193,16 @@ MediaCodecReader::CreateMediaCodec(sp<AL
   }
 
   return true;
 }
 
 bool
 MediaCodecReader::ConfigureMediaCodec(Track& aTrack)
 {
-
   if (aTrack.mSource != nullptr && aTrack.mCodec != nullptr) {
     if (!aTrack.mCodec->allocated()) {
       return false;
     }
 
     sp<MetaData> sourceFormat = aTrack.mSource->getFormat();
     sp<AMessage> codecFormat;
     convertMetaDataToMessage(sourceFormat, &codecFormat);
@@ -1005,39 +1242,90 @@ MediaCodecReader::DestroyMediaCodecs()
 
 void
 MediaCodecReader::DestroyMediaCodecs(Track& aTrack)
 {
   aTrack.mCodec = nullptr;
 }
 
 bool
+MediaCodecReader::TriggerIncrementalParser()
+{
+  if (mMetaData == nullptr) {
+    return false;
+  }
+
+  int64_t duration = INT64_C(-1);
+
+  {
+    MonitorAutoLock monLock(mParserMonitor);
+
+    // only support incremental parsing for mp3 currently.
+    if (mMP3FrameParser != nullptr) {
+      return true;
+    }
+
+    mParseDataFromCache = true;
+    mNextParserPosition = INT64_C(0);
+    mParsedDataLength = INT64_C(0);
+
+    // MP3 file duration
+    mMP3FrameParser = new MP3FrameParser(mDecoder->GetResource()->GetLength());
+    const char* mime = nullptr;
+    if (mMetaData->findCString(kKeyMIMEType, &mime) &&
+        !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_MPEG)) {
+      {
+        MonitorAutoUnlock monUnlock(mParserMonitor);
+        // trigger parsing logic and wait for finishing parsing data in the beginning.
+        nsRefPtr<SignalObject> signalObject = new SignalObject("MediaCodecReader::UpdateDuration()");
+        if (ProcessCachedData(INT64_C(0), signalObject) > INT64_C(0)) {
+          signalObject->Wait();
+        }
+      }
+      duration = mMP3FrameParser->GetDuration();
+    }
+  }
+
+  {
+    MutexAutoLock al(mAudioTrack.mDurationLock);
+    if (duration > mAudioTrack.mDurationUs) {
+      mAudioTrack.mDurationUs = duration;
+    }
+  }
+
+  return true;
+}
+
+bool
 MediaCodecReader::UpdateDuration()
 {
   // read audio duration
   if (mAudioTrack.mSource != nullptr) {
     sp<MetaData> audioFormat = mAudioTrack.mSource->getFormat();
     if (audioFormat != nullptr) {
-      int64_t audioDurationUs = 0LL;
-      if (audioFormat->findInt64(kKeyDuration, &audioDurationUs) &&
-          audioDurationUs > mAudioTrack.mDurationUs) {
-        mAudioTrack.mDurationUs = audioDurationUs;
+      int64_t duration = INT64_C(0);
+      if (audioFormat->findInt64(kKeyDuration, &duration)) {
+        MutexAutoLock al(mAudioTrack.mDurationLock);
+        if (duration > mAudioTrack.mDurationUs) {
+          mAudioTrack.mDurationUs = duration;
+        }
       }
     }
   }
-  // TODO: MP3 file duration
 
   // read video duration
   if (mVideoTrack.mSource != nullptr) {
     sp<MetaData> videoFormat = mVideoTrack.mSource->getFormat();
     if (videoFormat != nullptr) {
-      int64_t videoDurationUs = 0LL;
-      if (videoFormat->findInt64(kKeyDuration, &videoDurationUs) &&
-          videoDurationUs > mVideoTrack.mDurationUs) {
-        mVideoTrack.mDurationUs = videoDurationUs;
+      int64_t duration = INT64_C(0);
+      if (videoFormat->findInt64(kKeyDuration, &duration)) {
+        MutexAutoLock al(mVideoTrack.mDurationLock);
+        if (duration > mVideoTrack.mDurationUs) {
+          mVideoTrack.mDurationUs = duration;
+        }
       }
     }
   }
 
   return true;
 }
 
 bool
@@ -1391,17 +1679,17 @@ MediaCodecReader::EnsureCodecFormatParse
   if (aTrack.mCodec->getOutputFormat(&format) == OK) {
     return true;
   }
 
   status_t status = OK;
   size_t index = 0;
   size_t offset = 0;
   size_t size = 0;
-  int64_t timeUs = 0LL;
+  int64_t timeUs = INT64_C(0);
   uint32_t flags = 0;
   while ((status = aTrack.mCodec->dequeueOutputBuffer(&index, &offset, &size,
                      &timeUs, &flags)) != INFO_FORMAT_CHANGED) {
     if (status == OK) {
       aTrack.mCodec->releaseOutputBuffer(index);
     }
     status = FillCodecInputData(aTrack);
     if (status == INFO_FORMAT_CHANGED) {
--- a/content/media/omx/MediaCodecReader.h
+++ b/content/media/omx/MediaCodecReader.h
@@ -4,37 +4,42 @@
  * 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/. */
 
 #ifndef MEDIA_CODEC_READER_H
 #define MEDIA_CODEC_READER_H
 
 #include <utils/threads.h>
 
+#include <base/message_loop.h>
+
 #include <mozilla/CheckedInt.h>
+#include <mozilla/Mutex.h>
+#include <mozilla/Monitor.h>
 
 #include "MediaData.h"
 
 #include "I420ColorConverterHelper.h"
 #include "MediaCodecProxy.h"
 #include "MediaOmxCommonReader.h"
 
 namespace android {
 struct ALooper;
 struct AMessage;
 
 class MOZ_EXPORT MediaExtractor;
+class MOZ_EXPORT MetaData;
 class MOZ_EXPORT MediaBuffer;
 struct MOZ_EXPORT MediaSource;
-struct MediaCodec;
 } // namespace android
 
 namespace mozilla {
 
 class MediaTaskQueue;
+class MP3FrameParser;
 
 class MediaCodecReader : public MediaOmxCommonReader
 {
 public:
   MediaCodecReader(AbstractMediaDecoder* aDecoder);
   virtual ~MediaCodecReader();
 
   // Initializes the reader, returns NS_OK on success, or NS_ERROR_FAILURE
@@ -50,16 +55,21 @@ public:
   // Release media resources they should be released in dormant state
   virtual void ReleaseMediaResources();
 
   // Destroys the decoding state. The reader cannot be made usable again.
   // This is different from ReleaseMediaResources() as Shutdown() is
   // irreversible, whereas ReleaseMediaResources() is reversible.
   virtual void Shutdown();
 
+  // Used to retrieve some special information that can only be retrieved after
+  // all contents have been continuously parsed. (ex. total duration of some
+  // variable-bit-rate MP3 files.)
+  virtual void NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset);
+
   // Flush the MediaTaskQueue, flush MediaCodec and raise the mDiscontinuity.
   virtual nsresult ResetDecode() MOZ_OVERRIDE;
 
   // Disptach a DecodeVideoFrameTask to decode video data.
   virtual void RequestVideoData(bool aSkipToNextKeyframe,
                                 int64_t aTimeThreshold) MOZ_OVERRIDE;
 
   // Disptach a DecodeAduioDataTask to decode video data.
@@ -104,29 +114,36 @@ protected:
     android::sp<android::MediaCodecProxy> mCodec;
     android::Vector<android::sp<android::ABuffer> > mInputBuffers;
     android::Vector<android::sp<android::ABuffer> > mOutputBuffers;
 
     // pipeline copier
     nsAutoPtr<TrackInputCopier> mInputCopier;
 
     // media parameters
+    Mutex mDurationLock; // mDurationUs might be read or updated from multiple
+                         // threads.
     int64_t mDurationUs;
 
     // playback parameters
     CheckedUint32 mInputIndex;
     // mDiscontinuity, mFlushed, mInputEndOfStream, mInputEndOfStream,
     // mSeekTimeUs don't be protected by a lock because the
     // mTaskQueue->Flush() will flush all tasks.
     bool mInputEndOfStream;
     bool mOutputEndOfStream;
     int64_t mSeekTimeUs;
     bool mFlushed; // meaningless when mSeekTimeUs is invalid.
     bool mDiscontinuity;
     nsRefPtr<MediaTaskQueue> mTaskQueue;
+
+  private:
+    // Forbidden
+    Track(const Track &rhs) MOZ_DELETE;
+    const Track &operator=(const Track&) MOZ_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.
@@ -183,45 +200,150 @@ private:
   {
     virtual bool Copy(android::MediaBuffer* aSourceBuffer,
                       android::sp<android::ABuffer> aCodecBuffer);
   };
 
   struct AudioTrack : public Track
   {
     AudioTrack();
+
+  private:
+    // Forbidden
+    AudioTrack(const AudioTrack &rhs) MOZ_DELETE;
+    const AudioTrack &operator=(const AudioTrack &rhs) MOZ_DELETE;
   };
 
   struct VideoTrack : public Track
   {
     VideoTrack();
 
     int32_t mWidth;
     int32_t mHeight;
     int32_t mStride;
     int32_t mSliceHeight;
     int32_t mColorFormat;
     int32_t mRotation;
     nsIntSize mFrameSize;
     nsIntRect mPictureRect;
     gfx::IntRect mRelativePictureRect;
+
+  private:
+    // Forbidden
+    VideoTrack(const VideoTrack &rhs) MOZ_DELETE;
+    const VideoTrack &operator=(const VideoTrack &rhs) MOZ_DELETE;
   };
 
   struct CodecBufferInfo
   {
     CodecBufferInfo();
 
     android::sp<android::ABuffer> mBuffer;
     size_t mIndex;
     size_t mOffset;
     size_t mSize;
     int64_t mTimeUs;
     uint32_t mFlags;
   };
 
+  class SignalObject
+  {
+  public:
+    NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SignalObject)
+
+    SignalObject(const char* aName);
+    ~SignalObject();
+    void Wait();
+    void Signal();
+
+  private:
+    // Forbidden
+    SignalObject() MOZ_DELETE;
+    SignalObject(const SignalObject &rhs) MOZ_DELETE;
+    const SignalObject &operator=(const SignalObject &rhs) MOZ_DELETE;
+
+    Monitor mMonitor;
+    bool mSignaled;
+  };
+
+  class ParseCachedDataRunnable : public nsRunnable
+  {
+  public:
+    ParseCachedDataRunnable(nsRefPtr<MediaCodecReader> aReader,
+                            const char* aBuffer,
+                            uint32_t aLength,
+                            int64_t aOffset,
+                            nsRefPtr<SignalObject> aSignal);
+
+    NS_IMETHOD Run() MOZ_OVERRIDE;
+
+  private:
+    // Forbidden
+    ParseCachedDataRunnable() MOZ_DELETE;
+    ParseCachedDataRunnable(const ParseCachedDataRunnable &rhs) MOZ_DELETE;
+    const ParseCachedDataRunnable &operator=(const ParseCachedDataRunnable &rhs) MOZ_DELETE;
+
+    nsRefPtr<MediaCodecReader> mReader;
+    nsAutoArrayPtr<const char> mBuffer;
+    uint32_t mLength;
+    int64_t mOffset;
+    nsRefPtr<SignalObject> mSignal;
+  };
+  friend class ParseCachedDataRunnable;
+
+  class ProcessCachedDataTask : public Task
+  {
+  public:
+    ProcessCachedDataTask(nsRefPtr<MediaCodecReader> aReader,
+                          int64_t aOffset);
+
+    void Run() MOZ_OVERRIDE;
+
+  private:
+    // Forbidden
+    ProcessCachedDataTask() MOZ_DELETE;
+    ProcessCachedDataTask(const ProcessCachedDataTask &rhs) MOZ_DELETE;
+    const ProcessCachedDataTask &operator=(const ProcessCachedDataTask &rhs) MOZ_DELETE;
+
+    nsRefPtr<MediaCodecReader> mReader;
+    int64_t mOffset;
+  };
+  friend class ProcessCachedDataTask;
+
+  // This class is used to keep one reference count of T in it. And this class
+  // can make sure the stored reference count will be released on the dispatched
+  // thread. By using this class properly (ex. passing the pointer into this
+  // runnable first, then releasing the original pointer held by ourselves, and
+  // then dispatching this runnable onto the desired thread), we can avoid
+  // running the destructor of the referenced object on any other threads
+  // unexpectedly before this runnable has been executed.
+  template<class T>
+  class ReferenceKeeperRunnable : public nsRunnable
+  {
+  public:
+    ReferenceKeeperRunnable(nsRefPtr<T> aPointer)
+      : mPointer(aPointer)
+    {
+    }
+
+    NS_IMETHOD Run() MOZ_OVERRIDE
+    {
+      mPointer = nullptr;
+      return NS_OK;
+    }
+
+  private:
+    // Forbidden
+    ReferenceKeeperRunnable() MOZ_DELETE;
+    ReferenceKeeperRunnable(const ReferenceKeeperRunnable &rhs) MOZ_DELETE;
+    const ReferenceKeeperRunnable &operator=(const ReferenceKeeperRunnable &rhs) MOZ_DELETE;
+
+    nsRefPtr<T> mPointer;
+  };
+
   // Forbidden
   MediaCodecReader() MOZ_DELETE;
   const MediaCodecReader& operator=(const MediaCodecReader& rhs) MOZ_DELETE;
 
   bool ReallocateResources();
   void ReleaseCriticalResources();
   void ReleaseResources();
 
@@ -255,42 +377,58 @@ private:
             mVideoTrack.mTaskQueue);
   }
 
   inline bool CheckAudioResources() {
     return (HasAudio() && mAudioTrack.mSource != nullptr &&
             mAudioTrack.mTaskQueue);
   }
 
+  bool TriggerIncrementalParser();
+
   bool UpdateDuration();
   bool UpdateAudioInfo();
   bool UpdateVideoInfo();
 
   static android::status_t FlushCodecData(Track& aTrack);
   static android::status_t FillCodecInputData(Track& aTrack);
   static android::status_t GetCodecOutputData(Track& aTrack,
                                               CodecBufferInfo& aBuffer,
                                               int64_t aThreshold,
                                               const TimeStamp& aTimeout);
   static bool EnsureCodecFormatParsed(Track& aTrack);
 
   uint8_t* GetColorConverterBuffer(int32_t aWidth, int32_t aHeight);
   void ClearColorConverterBuffer();
 
+  int64_t ProcessCachedData(int64_t aOffset,
+                            nsRefPtr<SignalObject> aSignal);
+  bool ParseDataSegment(const char* aBuffer,
+                        uint32_t aLength,
+                        int64_t aOffset);
+
   android::sp<MessageHandler> mHandler;
   android::sp<VideoResourceListener> mVideoListener;
 
   android::sp<android::ALooper> mLooper;
+  android::sp<android::MetaData> mMetaData;
 
   // media tracks
   AudioTrack mAudioTrack;
   VideoTrack mVideoTrack;
   AudioTrack mAudioOffloadTrack; // only Track::mSource is valid
 
   // color converter
   android::I420ColorConverterHelper mColorConverter;
   nsAutoArrayPtr<uint8_t> mColorConverterBuffer;
   size_t mColorConverterBufferSize;
+
+  // incremental parser
+  Monitor mParserMonitor;
+  bool mParseDataFromCache;
+  int64_t mNextParserPosition;
+  int64_t mParsedDataLength;
+  nsAutoPtr<MP3FrameParser> mMP3FrameParser;
 };
 
 } // namespace mozilla
 
 #endif // MEDIA_CODEC_READER_H