Bug 909670 - MediaRecorder - Implement MediaRecorderSession. r=roc
authorCJKu <cku@mozilla.com>
Thu, 26 Sep 2013 09:40:40 -0400
changeset 148790 31d46902237220e76898325ee064c80de90d7abc
parent 148789 747b3fdceeb2acce1ede2234c39164502ab76be4
child 148791 704346e888a4b6577119978ce8a1cf057b4989b2
push id1229
push userryanvm@gmail.com
push dateThu, 26 Sep 2013 13:40:46 +0000
treeherderb2g-inbound@70ec111942ca [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs909670
milestone27.0a1
Bug 909670 - MediaRecorder - Implement MediaRecorderSession. r=roc
content/media/MediaRecorder.cpp
content/media/MediaRecorder.h
--- a/content/media/MediaRecorder.cpp
+++ b/content/media/MediaRecorder.cpp
@@ -25,257 +25,428 @@ NS_IMPL_CYCLE_COLLECTION_INHERITED_1(Med
                                      mStream)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MediaRecorder)
 NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
 
 NS_IMPL_ADDREF_INHERITED(MediaRecorder, nsDOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(MediaRecorder, nsDOMEventTargetHelper)
 
-// This task is used for triggering the creating blob object and it runs at main thread.
-class MediaRecorder::PushBlobTask : public nsRunnable
-{
-public:
-  PushBlobTask(MediaRecorder* recorder)
-    : mRecorder(recorder) {}
-
-  NS_IMETHODIMP Run()
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-    nsresult rv = mRecorder->CreateAndDispatchBlobEvent();
-    if (NS_FAILED(rv)) {
-      mRecorder->NotifyError(rv);
-    }
-    return NS_OK;
-  }
-  MediaRecorder* mRecorder;
-};
-
-// This task is for firing the error message from encoder and it runs in main thread
-class MediaRecorder::PushErrorMessageTask : public nsRunnable
+/**
+ * Session is an object to represent a single recording event.
+ * In original design, all recording context is stored in MediaRecorder, which causes
+ * a problem if someone calls MediaRecoder::Stop and MedaiRecorder::Start quickly.
+ * To prevent blocking main thread, media encoding is executed in a second thread,
+ * named as Read Thread. For the same reason, we do not wait Read Thread shutdown in
+ * MediaRecorder::Stop. If someone call MediaRecoder::Start before Read Thread shutdown,
+ * the same recording context in MediaRecoder might be access by two Reading Threads,
+ * which cause a  problem.
+ * In the new design, we put recording context into Session object, including Read
+ * Thread.  Each Session has its own recording context and Read Thread, problem is been
+ * resolved.
+ *
+ * Life cycle of a Session object.
+ * 1) Initialization Stage (in main thread)
+ *    Setup media streams in MSG, and bind MediaEncoder with Source Stream.
+ *    Resource allocation, such as encoded data cache buffer and MediaEncoder.
+ *    Create read thread.
+ *    Automatically switch to Extract stage in the end of this stage.
+ * 2) Extract Stage (in Read Thread)
+ *    Pull encoded A/V frames from MediaEncoder, dispatch to OnDataAvailable handler.
+ *    Unless a client calls Session::Stop, Session object keeps stay in this stage.
+ * 3) Destroy Stage (in main thread)
+ *    Switch from Extract stage to Destroy stage by calling Session::Stop.
+ *    Release session resource and remove associated streams from MSG.
+ *
+ * Lifetime of a Session object.
+ * 1) MediaRecorder creates a Session in MediaRecorder::Start function.
+ * 2) A Session is destroyed in DestroyRunnable after MediaRecorder::Stop being called
+ *    _and_ all encoded media data been passed to OnDataAvailable handler.
+ */
+class MediaRecorder::Session
 {
-public:
-  PushErrorMessageTask(MediaRecorder* recorder, nsresult aError)
-    : mRecorder(recorder),
-      mError(aError) { }
-
-  NS_IMETHODIMP Run()
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-    mRecorder->NotifyError(mError);
-    return NS_OK;
-  }
-
-private:
-  MediaRecorder* mRecorder;
-  nsresult mError;
-};
-
-// This class is used for avoiding abort by directly use the NS_NewRunnableMethod and invoke ExtractEncodedData function
-// The abort is checking if the destructor runs at main thread during the cycle-collect step in nsDOMEventTargetHelper
-class MediaRecorder::ExtractEncodedDataTask : public nsRunnable
-{
-public:
-  ExtractEncodedDataTask(MediaRecorder* aRecorder, MediaEncoder* aEncoder)
-    : mRecorder(aRecorder),
-      mEncoder(aEncoder) {}
-
-  class ReleaseEncoderThreadTask : public nsRunnable
+  // Main thread task.
+  // Create a blob event and send back to client.
+  class PushBlobRunnable : public nsRunnable
   {
   public:
-    ReleaseEncoderThreadTask(already_AddRefed<MediaRecorder> recorder)
-      : mRecorder(recorder) {}
+    PushBlobRunnable(Session* aSession)
+      : mSession(aSession)
+    { }
 
     NS_IMETHODIMP Run()
     {
       MOZ_ASSERT(NS_IsMainThread());
-      mRecorder->mReadThread->Shutdown();
-      mRecorder->mReadThread = nullptr;
+
+      MediaRecorder *recorder = mSession->mRecorder;
+      nsresult rv = recorder->CreateAndDispatchBlobEvent(mSession);
+      if (NS_FAILED(rv)) {
+        recorder->NotifyError(rv);
+      }
+
+      return NS_OK;
+    }
+
+  private:
+    Session *mSession;
+  };
+
+  // Record thread task.
+  // Fetch encoded Audio/Video data from MediaEncoder.
+  class ExtractRunnable : public nsRunnable
+  {
+  public:
+    ExtractRunnable(Session *aSession)
+      : mSession(aSession) {}
+
+    NS_IMETHODIMP Run()
+    {
+      MOZ_ASSERT(NS_GetCurrentThread() == mSession->mReadThread);
+
+      mSession->Extract();
+      return NS_OK;
+    }
 
-      // Setting mState to Inactive here is for the case where SourceStream
-      // ends itself, thus the recorder should stop itself too.
-      mRecorder->mState = RecordingState::Inactive;
-      mRecorder->DispatchSimpleEvent(NS_LITERAL_STRING("stop"));
+  private:
+    Session *mSession;
+  };
+
+  // Main thread task.
+  // To delete RecordingSession object.
+  class DestroyRunnable : public nsRunnable
+  {
+  public:
+    DestroyRunnable(Session *aSession)
+      : mSession(aSession) {}
+
+    NS_IMETHODIMP Run()
+    {
+      MOZ_ASSERT(NS_IsMainThread() && mSession.get());
+      MediaRecorder *recorder = mSession->mRecorder;
+
+      // If MediaRecoder is not in Inactive mode, call MediaRecoder::Stop
+      // and dispatch DestroyRunnable again.
+      if (recorder->mState != RecordingState::Inactive) {
+        ErrorResult result;
+        recorder->Stop(result);
+        NS_DispatchToMainThread(new DestroyRunnable(mSession.forget()));
+        return NS_OK;
+      }
+
+      // Dispatch stop event and clear MIME type.
+      recorder->DispatchSimpleEvent(NS_LITERAL_STRING("stop"));
+      recorder->SetMimeType(NS_LITERAL_STRING(""));
+
+      // Delete session object.
+      mSession = nullptr;
 
       return NS_OK;
     }
 
   private:
-    nsRefPtr<MediaRecorder> mRecorder;
+    nsAutoPtr<Session> mSession;
   };
 
-  NS_IMETHODIMP Run()
+  friend class PushBlobRunnable;
+  friend class ExtractRunnable;
+  friend class DestroyRunnable;
+
+public:
+  Session(MediaRecorder* aRecorder, int32_t aTimeSlice)
+    : mRecorder(aRecorder),
+      mTimeSlice(aTimeSlice)
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    mEncodedBufferCache = new EncodedBufferCache(MAX_ALLOW_MEMORY_BUFFER);
+  }
+
+  // Only DestroyRunnable is allowed to delete Session object.
+  ~Session()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    if (mInputPort.get()) {
+      mInputPort->Destroy();
+    }
+
+    if (mTrackUnionStream.get()) {
+      mTrackUnionStream->Destroy();
+    }
+  }
+
+  void Start()
   {
-    MOZ_ASSERT(!NS_IsMainThread());
-    mRecorder->ExtractEncodedData();
-    NS_DispatchToMainThread(new ReleaseEncoderThreadTask(mRecorder.forget()));
-    return NS_OK;
+    MOZ_ASSERT(NS_IsMainThread());
+
+    SetupStreams();
+
+    // Create a thread to read encode media data from MediaEncoder.
+    if (!mReadThread) {
+      nsresult rv = NS_NewNamedThread("Media Encoder", getter_AddRefs(mReadThread));
+      if (NS_FAILED(rv)) {
+        mRecorder->NotifyError(rv);
+        return;
+      }
+    }
+
+    mReadThread->Dispatch(new ExtractRunnable(this), NS_DISPATCH_NORMAL);
+  }
+
+  void Stop()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    // Shutdown mEncoder to stop Session::Extract
+    if (mInputPort.get())
+    {
+      mInputPort->Destroy();
+      mInputPort = nullptr;
+    }
+
+    if (mTrackUnionStream.get())
+    {
+      mTrackUnionStream->Destroy();
+      mTrackUnionStream = nullptr;
+    }
+  }
+
+  void Pause()
+  {
+    MOZ_ASSERT(NS_IsMainThread() && mTrackUnionStream);
+
+    mTrackUnionStream->ChangeExplicitBlockerCount(-1);
+  }
+
+  void Resume()
+  {
+    MOZ_ASSERT(NS_IsMainThread() && mTrackUnionStream);
+
+    mTrackUnionStream->ChangeExplicitBlockerCount(1);
+  }
+
+  already_AddRefed<nsIDOMBlob> GetEncodedData()
+  {
+    nsString mimeType;
+    mRecorder->GetMimeType(mimeType);
+    return mEncodedBufferCache->ExtractBlob(mimeType);
   }
 
 private:
+
+  // Pull encoded meida data from MediaEncoder and put into EncodedBufferCache.
+  // Destroy this session object in the end of this function.
+  void Extract()
+  {
+    MOZ_ASSERT(NS_GetCurrentThread() == mReadThread);
+
+    TimeStamp lastBlobTimeStamp = TimeStamp::Now();
+    // Whether push encoded data back to onDataAvailable automatically.
+    const bool pushBlob = (mTimeSlice > 0) ? true : false;
+
+    do {
+      // Pull encoded media data from MediaEncoder
+      nsTArray<nsTArray<uint8_t> > encodedBuf;
+      nsString mimeType;
+      mEncoder->GetEncodedData(&encodedBuf, mimeType);
+
+      mRecorder->SetMimeType(mimeType);
+
+      // Append pulled data into cache buffer.
+      for (uint32_t i = 0; i < encodedBuf.Length(); i++) {
+        mEncodedBufferCache->AppendBuffer(encodedBuf[i]);
+      }
+
+      if (pushBlob) {
+        if ((TimeStamp::Now() - lastBlobTimeStamp).ToMilliseconds() > mTimeSlice) {
+          NS_DispatchToMainThread(new PushBlobRunnable(this));
+          lastBlobTimeStamp = TimeStamp::Now();
+        }
+      }
+    } while (!mEncoder->IsShutdown());
+
+    // Flush out remainding encoded data.
+    NS_DispatchToMainThread(new PushBlobRunnable(this));
+
+    // Destroy this session object in main thread.
+    NS_DispatchToMainThread(new DestroyRunnable(this));
+  }
+
+  // Bind media source with MediaEncoder to receive raw media data.
+  void SetupStreams()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    MediaStreamGraph* gm = mRecorder->mStream->GetStream()->Graph();
+    mTrackUnionStream = gm->CreateTrackUnionStream(nullptr);
+    MOZ_ASSERT(mTrackUnionStream, "CreateTrackUnionStream failed");
+
+    mTrackUnionStream->SetAutofinish(true);
+
+    mInputPort = mTrackUnionStream->AllocateInputPort(mRecorder->mStream->GetStream(), MediaInputPort::FLAG_BLOCK_OUTPUT);
+
+    // Allocate encoder and bind with union stream.
+    mEncoder = MediaEncoder::CreateEncoder(NS_LITERAL_STRING(""));
+    MOZ_ASSERT(mEncoder, "CreateEncoder failed");
+
+    if (mEncoder) {
+      mTrackUnionStream->AddListener(mEncoder);
+    }
+  }
+
+private:
+  // Hold a reference to MediaRecoder to make sure MediaRecoder be
+  // destroyed after all session object dead.
   nsRefPtr<MediaRecorder> mRecorder;
-  nsRefPtr<MediaEncoder>  mEncoder;
+
+  // Pause/ Resume controller.
+  nsRefPtr<ProcessedMediaStream> mTrackUnionStream;
+  nsRefPtr<MediaInputPort> mInputPort;
+
+  // Runnable thread for read data from MediaEncode.
+  nsCOMPtr<nsIThread> mReadThread;
+  // MediaEncoder pipeline.
+  nsRefPtr<MediaEncoder> mEncoder;
+  // A buffer to cache encoded meda data.
+  nsAutoPtr<EncodedBufferCache> mEncodedBufferCache;
+  // The interval of passing encoded data from EncodedBufferCache to onDataAvailable
+  // handler. "mTimeSlice < 0" means Session object does not push encoded data to
+  // onDataAvailable, instead, it passive wait the client side pull encoded data
+  // by calling requestData API.
+  const int32_t mTimeSlice;
 };
 
 MediaRecorder::~MediaRecorder()
 {
-  if (mStreamPort) {
-    mStreamPort->Destroy();
-  }
-  if (mTrackUnionStream) {
-    mTrackUnionStream->Destroy();
-  }
+  MOZ_ASSERT(mSession == nullptr);
 }
 
 void
 MediaRecorder::Init(nsPIDOMWindow* aOwnerWindow)
 {
   MOZ_ASSERT(aOwnerWindow);
   MOZ_ASSERT(aOwnerWindow->IsInnerWindow());
   BindToOwner(aOwnerWindow);
 }
 
 MediaRecorder::MediaRecorder(DOMMediaStream& aStream)
-  : mTimeSlice(0),
-    mState(RecordingState::Inactive)
+  : mState(RecordingState::Inactive),
+    mSession(nullptr),
+    mMutex("Session.Data.Mutex")
 {
   mStream = &aStream;
   SetIsDOMBinding();
 }
 
 void
-MediaRecorder::ExtractEncodedData()
+MediaRecorder::SetMimeType(const nsString &aMimeType)
 {
-  TimeStamp lastBlobTimeStamp = TimeStamp::Now();
-  do {
-    nsTArray<nsTArray<uint8_t> > outputBufs;
-    mEncoder->GetEncodedData(&outputBufs, mMimeType);
-    for (uint32_t i = 0; i < outputBufs.Length(); i++) {
-      mEncodedBufferCache->AppendBuffer(outputBufs[i]);
-    }
+  MutexAutoLock lock(mMutex);
+  mMimeType = aMimeType;
+}
 
-    if (mTimeSlice > 0 && (TimeStamp::Now() - lastBlobTimeStamp).ToMilliseconds() > mTimeSlice) {
-      NS_DispatchToMainThread(new PushBlobTask(this));
-      lastBlobTimeStamp = TimeStamp::Now();
-    }
-  } while (!mEncoder->IsShutdown());
-
-  NS_DispatchToMainThread(new PushBlobTask(this));
+void
+MediaRecorder::GetMimeType(nsString &aMimeType)
+{
+  MutexAutoLock lock(mMutex);
+  aMimeType = mMimeType;
 }
 
 void
 MediaRecorder::Start(const Optional<int32_t>& aTimeSlice, ErrorResult& aResult)
 {
   if (mState != RecordingState::Inactive) {
     aResult.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
   if (mStream->GetStream()->IsFinished() || mStream->GetStream()->IsDestroyed()) {
     aResult.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
+  int32_t timeSlice = 0;
   if (aTimeSlice.WasPassed()) {
     if (aTimeSlice.Value() < 0) {
       aResult.Throw(NS_ERROR_INVALID_ARG);
       return;
     }
-    mTimeSlice = aTimeSlice.Value();
-  } else {
-    mTimeSlice = 0;
+
+    timeSlice = aTimeSlice.Value();
   }
 
-  // Create a TrackUnionStream to support Pause/Resume by using ChangeExplicitBlockerCount
-  MediaStreamGraph* gm = mStream->GetStream()->Graph();
-  mTrackUnionStream = gm->CreateTrackUnionStream(nullptr);
-  MOZ_ASSERT(mTrackUnionStream, "CreateTrackUnionStream failed");
-
   if (!CheckPrincipal()) {
     aResult.Throw(NS_ERROR_DOM_SECURITY_ERR);
     return;
   }
 
-  if (mEncodedBufferCache == nullptr) {
-    mEncodedBufferCache = new EncodedBufferCache(MAX_ALLOW_MEMORY_BUFFER);
-  }
-
-  mEncoder = MediaEncoder::CreateEncoder(NS_LITERAL_STRING(""));
-  MOZ_ASSERT(mEncoder, "CreateEncoder failed");
-
-  mTrackUnionStream->SetAutofinish(true);
-  mStreamPort = mTrackUnionStream->AllocateInputPort(mStream->GetStream(), MediaInputPort::FLAG_BLOCK_OUTPUT);
-
-  if (mEncoder) {
-    mTrackUnionStream->AddListener(mEncoder);
-  } else {
-    aResult.Throw(NS_ERROR_DOM_ABORT_ERR);
-  }
-
-  if (!mReadThread) {
-    nsresult rv = NS_NewNamedThread("Media Encoder",
-                                    getter_AddRefs(mReadThread));
-    if (NS_FAILED(rv)) {
-      aResult.Throw(rv);
-      return;
-    }
-    nsRefPtr<ExtractEncodedDataTask> event = new ExtractEncodedDataTask(this, mEncoder);
-    mReadThread->Dispatch(event, NS_DISPATCH_NORMAL);
-    mState = RecordingState::Recording;
-  }
+  mState = RecordingState::Recording;
+  // Start a session
+  mSession = new Session(this, timeSlice);
+  mSession->Start();
 }
 
 void
 MediaRecorder::Stop(ErrorResult& aResult)
 {
   if (mState == RecordingState::Inactive) {
     aResult.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
-  mState = RecordingState::Inactive;
 
-  mStreamPort->Destroy();
-  mStreamPort = nullptr;
+  mSession->Stop();
+  mSession = nullptr;
 
-  mTrackUnionStream->Destroy();
-  mTrackUnionStream = nullptr;
+  mState = RecordingState::Inactive;
 }
 
 void
 MediaRecorder::Pause(ErrorResult& aResult)
 {
   if (mState != RecordingState::Recording) {
     aResult.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
-  mTrackUnionStream->ChangeExplicitBlockerCount(-1);
 
   mState = RecordingState::Paused;
+
+  MOZ_ASSERT(mSession != nullptr);
+  if (mSession) {
+    mSession->Pause();
+    mState = RecordingState::Paused;
+  }
 }
 
 void
 MediaRecorder::Resume(ErrorResult& aResult)
 {
   if (mState != RecordingState::Paused) {
     aResult.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
-  mTrackUnionStream->ChangeExplicitBlockerCount(1);
-  mState = RecordingState::Recording;
+
+  MOZ_ASSERT(mSession != nullptr);
+  if (mSession) {
+    mSession->Resume();
+    mState = RecordingState::Recording;
+  }
 }
 
 void
 MediaRecorder::RequestData(ErrorResult& aResult)
 {
   if (mState != RecordingState::Recording) {
     aResult.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
-  NS_DispatchToMainThread(NS_NewRunnableMethod(this, &MediaRecorder::CreateAndDispatchBlobEvent),
-                                               NS_DISPATCH_NORMAL);
+
+  NS_DispatchToMainThread(
+    NS_NewRunnableMethodWithArg<Session *>(this,
+                                           &MediaRecorder::CreateAndDispatchBlobEvent, mSession),
+    NS_DISPATCH_NORMAL);
 }
 
 JSObject*
 MediaRecorder::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
 {
   return MediaRecorderBinding::Wrap(aCx, aScope, this);
 }
 
@@ -296,29 +467,29 @@ MediaRecorder::Constructor(const GlobalO
   }
 
   nsRefPtr<MediaRecorder> object = new MediaRecorder(aStream);
   object->Init(ownerWindow);
   return object.forget();
 }
 
 nsresult
-MediaRecorder::CreateAndDispatchBlobEvent()
+MediaRecorder::CreateAndDispatchBlobEvent(Session *aSession)
 {
   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
 
   if (!CheckPrincipal()) {
     // Media is not same-origin, don't allow the data out.
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
   BlobEventInitInitializer init;
   init.mBubbles = false;
   init.mCancelable = false;
-  init.mData = mEncodedBufferCache->ExtractBlob(mMimeType);
+  init.mData = aSession->GetEncodedData();
   nsRefPtr<BlobEvent> event =
     BlobEvent::Constructor(this,
                            NS_LITERAL_STRING("dataavailable"),
                            init);
   event->SetTrusted(true);
   return DispatchDOMEvent(nullptr, event, nullptr, nullptr);
 }
 
--- a/content/media/MediaRecorder.h
+++ b/content/media/MediaRecorder.h
@@ -31,19 +31,18 @@ namespace dom {
  * The encoded data will be extracted on every timeslice passed from Start function call or by RequestData function.
  * Thread model:
  * When the recorder starts, it creates a "Media Encoder" thread to read data from MediaEncoder object and store buffer in EncodedBufferCache object.
  * Also extract the encoded data and create blobs on every timeslice passed from start function or RequestData function called by UA.
  */
 
 class MediaRecorder : public nsDOMEventTargetHelper
 {
-  class ExtractEncodedDataTask;
-  class PushBlobTask;
-  class PushErrorMessageTask;
+  class Session;
+
 public:
   MediaRecorder(DOMMediaStream&);
   virtual ~MediaRecorder();
 
   // nsWrapperCache
   virtual JSObject* WrapObject(JSContext* aCx,
                                JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
 
@@ -66,65 +65,53 @@ public:
   void Resume(ErrorResult& aResult);
   // Extract encoded data Blob from EncodedBufferCache.
   void RequestData(ErrorResult& aResult);
   // Return the The DOMMediaStream passed from UA.
   DOMMediaStream* Stream() const { return mStream; }
   // The current state of the MediaRecorder object.
   RecordingState State() const { return mState; }
   // Return the current encoding MIME type selected by the MediaEncoder.
-  void GetMimeType(nsString &aMimeType) { aMimeType = mMimeType; }
+  void GetMimeType(nsString &aMimeType);
 
   static already_AddRefed<MediaRecorder>
   Constructor(const GlobalObject& aGlobal,
               DOMMediaStream& aStream, ErrorResult& aRv);
 
   // EventHandler
   IMPL_EVENT_HANDLER(dataavailable)
   IMPL_EVENT_HANDLER(error)
   IMPL_EVENT_HANDLER(stop)
   IMPL_EVENT_HANDLER(warning)
 
-  friend class ExtractEncodedData;
-
 protected:
   void Init(nsPIDOMWindow* aOwnerWindow);
-  // Copy encoded data from encoder to EncodedBufferCache. This function runs in the Media Encoder Thread.
-  void ExtractEncodedData();
 
   MediaRecorder& operator = (const MediaRecorder& x) MOZ_DELETE;
   // Create dataavailable event with Blob data and it runs in main thread
-  nsresult CreateAndDispatchBlobEvent();
+  nsresult CreateAndDispatchBlobEvent(Session *session);
   // Creating a simple event to notify UA simple event.
   void DispatchSimpleEvent(const nsAString & aStr);
   // Creating a error event with message.
   void NotifyError(nsresult aRv);
   // Check if the recorder's principal is the subsume of mediaStream
   bool CheckPrincipal();
+  // Set encoded MIME type.
+  void SetMimeType(const nsString &aMimeType);
 
   MediaRecorder(const MediaRecorder& x) MOZ_DELETE; // prevent bad usage
 
-
-  // Runnable thread for read data from mediaEncoder. It starts at MediaRecorder::Start() and stops at MediaRecorder::Stop().
-  nsCOMPtr<nsIThread> mReadThread;
-  // The MediaEncoder object initializes on start() and destroys in ~MediaRecorder.
-  nsRefPtr<MediaEncoder> mEncoder;
   // MediaStream passed from js context
   nsRefPtr<DOMMediaStream> mStream;
-  // This media stream is used for notifying raw data to encoder and can be blocked.
-  nsRefPtr<ProcessedMediaStream> mTrackUnionStream;
-  // This is used for destroing the inputport when destroy the mediaRecorder
-  nsRefPtr<MediaInputPort> mStreamPort;
-  // This object creates on start() and destroys in ~MediaRecorder.
-  nsAutoPtr<EncodedBufferCache> mEncodedBufferCache;
+  // The current state of the MediaRecorder object.
+  RecordingState mState;
+  // Current recording session.
+  Session *mSession;
+  // Thread safe for mMimeType.
+  Mutex mMutex;
   // It specifies the container format as well as the audio and video capture formats.
   nsString mMimeType;
-  // The interval of timer passed from Start(). On every mTimeSlice milliseconds, if there are buffers store in the EncodedBufferCache,
-  // a dataavailable event will be fired.
-  int32_t mTimeSlice;
-  // The current state of the MediaRecorder object.
-  RecordingState mState;
 };
 
 }
 }
 
 #endif