Bug 919905 - Media Recording - Refactor encoder architecture to support the video encoder module. r=roc, r=cjku
authorRandy Lin <rlin@mozilla.com>
Fri, 25 Oct 2013 10:59:52 +0800
changeset 167146 0c1d4dbe85c977095c718b4385b7d75db7e99c71
parent 167145 bd9d47feac878aa9f46e3ff180cb6a3eca820a3d
child 167147 9fbd0d6598a724b5f8f41586a596079def469f02
push id428
push userbbajaj@mozilla.com
push dateTue, 28 Jan 2014 00:16:25 +0000
treeherdermozilla-release@cd72a7ff3a75 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc, cjku
bugs919905
milestone27.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 919905 - Media Recording - Refactor encoder architecture to support the video encoder module. r=roc, r=cjku
content/media/encoder/ContainerWriter.h
content/media/encoder/EncodedFrameContainer.h
content/media/encoder/MediaEncoder.cpp
content/media/encoder/MediaEncoder.h
content/media/encoder/OpusTrackEncoder.cpp
content/media/encoder/OpusTrackEncoder.h
content/media/encoder/TrackEncoder.h
content/media/encoder/TrackMetadataBase.h
content/media/encoder/moz.build
content/media/ogg/OggWriter.cpp
content/media/ogg/OggWriter.h
--- a/content/media/encoder/ContainerWriter.h
+++ b/content/media/encoder/ContainerWriter.h
@@ -3,16 +3,18 @@
  * 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 ContainerWriter_h_
 #define ContainerWriter_h_
 
 #include "nsTArray.h"
 #include "nsAutoPtr.h"
+#include "EncodedFrameContainer.h"
+#include "TrackMetadataBase.h"
 
 namespace mozilla {
 /**
  * ContainerWriter packs encoded track data into a specific media container.
  */
 class ContainerWriter {
 public:
   ContainerWriter()
@@ -26,31 +28,41 @@ public:
 
   /**
    * Writes encoded track data from aBuffer to a packet, and insert this packet
    * into the internal stream of container writer. aDuration is the playback
    * duration of this packet in number of samples. aFlags is true with
    * END_OF_STREAM if this is the last packet of track.
    * Currently, WriteEncodedTrack doesn't support multiple tracks.
    */
-  virtual nsresult WriteEncodedTrack(const nsTArray<uint8_t>& aBuffer,
-                                     int aDuration, uint32_t aFlags = 0) = 0;
+  virtual nsresult WriteEncodedTrack(const EncodedFrameContainer& aData,
+                                     uint32_t aFlags = 0) = 0;
+
+  /**
+   * Set the meta data pointer into muxer
+   * This function will check the integrity of aMetadata.
+   * If the meta data isn't well format, this function will return NS_ERROR_FAILURE to caller,
+   * else save the pointer to mMetadata and return NS_OK.
+   */
+  virtual nsresult SetMetadata(nsRefPtr<TrackMetadataBase> aMetadata) = 0;
 
   enum {
-    FLUSH_NEEDED = 1 << 0
+    FLUSH_NEEDED = 1 << 0,
+    GET_HEADER = 1 << 1
   };
 
   /**
    * Copies the final container data to a buffer if it has accumulated enough
    * packets from WriteEncodedTrack. This buffer of data is appended to
    * aOutputBufs, and existing elements of aOutputBufs should not be modified.
    * aFlags is true with FLUSH_NEEDED will force OggWriter to flush an ogg page
    * even it is not full, and copy these container data to a buffer for
    * aOutputBufs to append.
    */
   virtual nsresult GetContainerData(nsTArray<nsTArray<uint8_t> >* aOutputBufs,
                                     uint32_t aFlags = 0) = 0;
 
 protected:
+  nsRefPtr<TrackMetadataBase> mMetadata;
   bool mInitialized;
 };
 }
 #endif
new file mode 100644
--- /dev/null
+++ b/content/media/encoder/EncodedFrameContainer.h
@@ -0,0 +1,82 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
+/* 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/. */
+
+#ifndef EncodedFrameContainer_H_
+#define EncodedFrameContainer_H_
+
+#include "nsTArray.h"
+
+namespace mozilla {
+
+class EncodedFrame;
+
+/*
+ * This container is used to carry video or audio encoded data from encoder to muxer.
+ * The media data object is created by encoder and recycle by the destructor.
+ * Only allow to store audio or video encoded data in EncodedData.
+ */
+class EncodedFrameContainer
+{
+public:
+  // Append encoded frame data
+  void AppendEncodedFrame(EncodedFrame* aEncodedFrame)
+  {
+    mEncodedFrames.AppendElement(aEncodedFrame);
+  }
+  // Retrieve all of the encoded frames
+  const nsTArray<nsAutoPtr<EncodedFrame> >& GetEncodedFrames() const
+  {
+    return mEncodedFrames;
+  }
+private:
+  // This container is used to store the video or audio encoded packets.
+  // Muxer should check mFrameType and get the encoded data type from mEncodedFrames.
+  nsTArray<nsAutoPtr<EncodedFrame> > mEncodedFrames;
+};
+
+// Represent one encoded frame
+class EncodedFrame
+{
+public:
+  EncodedFrame() :
+    mTimeStamp(0),
+    mDuration(0),
+    mFrameType(UNKNOW)
+  {}
+  enum FrameType {
+    I_FRAME,      // intraframe
+    P_FRAME,      // predicted frame
+    B_FRAME,      // bidirectionally predicted frame
+    AUDIO_FRAME,  // audio frame
+    UNKNOW        // FrameType not set
+  };
+  const nsTArray<uint8_t>& GetFrameData() const
+  {
+    return mFrameData;
+  }
+  void SetFrameData(nsTArray<uint8_t> *aData)
+  {
+    mFrameData.SwapElements(*aData);
+  }
+  uint64_t GetTimeStamp() const { return mTimeStamp; }
+  void SetTimeStamp(uint64_t aTimeStamp) { mTimeStamp = aTimeStamp; }
+
+  uint64_t GetDuration() const { return mDuration; }
+  void SetDuration(uint64_t aDuration) { mDuration = aDuration; }
+
+  FrameType GetFrameType() const { return mFrameType; }
+  void SetFrameType(FrameType aFrameType) { mFrameType = aFrameType; }
+private:
+  // Encoded data
+  nsTArray<uint8_t> mFrameData;
+  uint64_t mTimeStamp;
+  // The playback duration of this packet in number of samples
+  uint64_t mDuration;
+  // Represent what is in the FrameData
+  FrameType mFrameType;
+};
+
+}
+#endif
--- a/content/media/encoder/MediaEncoder.cpp
+++ b/content/media/encoder/MediaEncoder.cpp
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
 /* 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 "MediaEncoder.h"
 #include "MediaDecoder.h"
 #include "nsIPrincipal.h"
+
 #ifdef MOZ_OGG
 #include "OggWriter.h"
 #endif
 #ifdef MOZ_OPUS
 #include "OpusTrackEncoder.h"
 #endif
 
 #ifdef MOZ_WIDGET_GONK
@@ -123,27 +124,25 @@ MediaEncoder::CreateEncoder(const nsAStr
                              videoEncoder.forget(), aMIMEType);
 
 
   return encoder.forget();
 }
 
 /**
  * GetEncodedData() runs as a state machine, starting with mState set to
- * ENCODE_HEADER, the procedure should be as follow:
+ * GET_METADDATA, the procedure should be as follow:
  *
  * While non-stop
- *   If mState is ENCODE_HEADER
- *     Create the header from audio/video encoder
- *     If a header is generated
- *       Insert header data into the container stream of writer
- *       Force copied the final container data from writer
- *       Return the copy of final container data
- *     Else
+ *   If mState is GET_METADDATA
+ *     Get the meta data from audio/video encoder
+ *     If a meta data is generated
+ *       Get meta data from audio/video encoder
  *       Set mState to ENCODE_TRACK
+ *       Return the final container data
  *
  *   If mState is ENCODE_TRACK
  *     Get encoded track data from audio/video encoder
  *     If a packet of track data is generated
  *       Insert encoded track data into the container stream of writer
  *       If the final container data is copied to aOutput
  *         Return the copy of final container data
  *       If this is the last packet of input stream
@@ -158,58 +157,46 @@ MediaEncoder::GetEncodedData(nsTArray<ns
 {
   MOZ_ASSERT(!NS_IsMainThread());
 
   aMIMEType = mMIMEType;
 
   bool reloop = true;
   while (reloop) {
     switch (mState) {
-    case ENCODE_HEADER: {
-      nsTArray<uint8_t> buffer;
-      nsresult rv = mAudioEncoder->GetHeader(&buffer);
+    case ENCODE_METADDATA: {
+      nsRefPtr<TrackMetadataBase> meta = mAudioEncoder->GetMetadata();
+      MOZ_ASSERT(meta);
+      nsresult rv = mWriter->SetMetadata(meta);
       if (NS_FAILED(rv)) {
-        // Encoding might be canceled.
-        mState = ENCODE_DONE;
-        break;
+       mState = ENCODE_DONE;
+       break;
       }
 
-      if (!buffer.IsEmpty()) {
-        rv = mWriter->WriteEncodedTrack(buffer, 0);
-        if (NS_FAILED(rv)) {
-          LOG("ERROR! Fail to write header to the media container.");
-          mState = ENCODE_DONE;
-          break;
-        }
+      rv = mWriter->GetContainerData(aOutputBufs,
+                                     ContainerWriter::GET_HEADER);
+      if (NS_FAILED(rv)) {
+       mState = ENCODE_DONE;
+       break;
+      }
 
-        rv = mWriter->GetContainerData(aOutputBufs,
-                                       ContainerWriter::FLUSH_NEEDED);
-        if (NS_SUCCEEDED(rv)) {
-          // Successfully get the copy of final container data from writer.
-          reloop = false;
-        }
-      } else {
-        // No more headers, starts to encode tracks.
-        mState = ENCODE_TRACK;
-      }
+      mState = ENCODE_TRACK;
       break;
     }
 
     case ENCODE_TRACK: {
-      nsTArray<uint8_t> buffer;
-      int encodedDuration = 0;
-      nsresult rv = mAudioEncoder->GetEncodedTrack(&buffer, encodedDuration);
+      EncodedFrameContainer encodedData;
+      nsresult rv = mAudioEncoder->GetEncodedTrack(encodedData);
       if (NS_FAILED(rv)) {
         // Encoding might be canceled.
         LOG("ERROR! Fail to get encoded data from encoder.");
         mState = ENCODE_DONE;
         break;
       }
-
-      rv = mWriter->WriteEncodedTrack(buffer, encodedDuration,
+      rv = mWriter->WriteEncodedTrack(encodedData,
                                       mAudioEncoder->IsEncodingComplete() ?
                                       ContainerWriter::END_OF_STREAM : 0);
       if (NS_FAILED(rv)) {
         LOG("ERROR! Fail to write encoded track to the media container.");
         mState = ENCODE_DONE;
         break;
       }
 
--- a/content/media/encoder/MediaEncoder.h
+++ b/content/media/encoder/MediaEncoder.h
@@ -46,30 +46,30 @@ namespace mozilla {
  *
  * 4) To stop encoding, remove this component from its source stream.
  *    => sourceStream->RemoveListener(encoder);
  */
 class MediaEncoder : public MediaStreamListener
 {
 public :
   enum {
-    ENCODE_HEADER,
+    ENCODE_METADDATA,
     ENCODE_TRACK,
     ENCODE_DONE,
   };
 
   MediaEncoder(ContainerWriter* aWriter,
                AudioTrackEncoder* aAudioEncoder,
                VideoTrackEncoder* aVideoEncoder,
                const nsAString& aMIMEType)
     : mWriter(aWriter)
     , mAudioEncoder(aAudioEncoder)
     , mVideoEncoder(aVideoEncoder)
     , mMIMEType(aMIMEType)
-    , mState(MediaEncoder::ENCODE_HEADER)
+    , mState(MediaEncoder::ENCODE_METADDATA)
     , mShutdown(false)
   {}
 
   ~MediaEncoder() {};
 
   /**
    * Notified by the control loop of MediaStreamGraph; aQueueMedia is the raw
    * track data in form of MediaSegment.
--- a/content/media/encoder/OpusTrackEncoder.cpp
+++ b/content/media/encoder/OpusTrackEncoder.cpp
@@ -110,17 +110,16 @@ SerializeOpusCommentHeader(const nsCStri
     SerializeToBuffer(aComments[i], aOutput);
   }
 }
 
 }  // Anonymous namespace.
 
 OpusTrackEncoder::OpusTrackEncoder()
   : AudioTrackEncoder()
-  , mEncoderState(ID_HEADER)
   , mEncoder(nullptr)
   , mSourceSegment(new AudioSegment())
   , mLookahead(0)
   , mResampler(nullptr)
 {
 }
 
 OpusTrackEncoder::~OpusTrackEncoder()
@@ -182,72 +181,57 @@ OpusTrackEncoder::GetOutputSampleRate()
 }
 
 int
 OpusTrackEncoder::GetPacketDuration()
 {
   return GetOutputSampleRate() * kFrameDurationMs / 1000;
 }
 
-nsresult
-OpusTrackEncoder::GetHeader(nsTArray<uint8_t>* aOutput)
+nsRefPtr<TrackMetadataBase>
+OpusTrackEncoder::GetMetadata()
 {
   {
     // Wait if mEncoder is not initialized.
     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     while (!mCanceled && !mEncoder) {
       mReentrantMonitor.Wait();
     }
   }
 
   if (mCanceled || mDoneEncoding) {
-    return NS_ERROR_FAILURE;
+    return nullptr;
+  }
+
+  OpusMetadata* meta = new OpusMetadata();
+
+  mLookahead = 0;
+  int error = opus_encoder_ctl(mEncoder, OPUS_GET_LOOKAHEAD(&mLookahead));
+  if (error != OPUS_OK) {
+    mLookahead = 0;
   }
 
-  switch (mEncoderState) {
-  case ID_HEADER:
-  {
-    mLookahead = 0;
-    int error = opus_encoder_ctl(mEncoder, OPUS_GET_LOOKAHEAD(&mLookahead));
-    if (error != OPUS_OK) {
-      mLookahead = 0;
-    }
+  // The ogg time stamping and pre-skip is always timed at 48000.
+  SerializeOpusIdHeader(mChannels, mLookahead*(kOpusSamplingRate/mSamplingRate),
+                        mSamplingRate, &meta->mIdHeader);
 
-    // The ogg time stamping and pre-skip is always timed at 48000.
-    SerializeOpusIdHeader(mChannels, mLookahead*(kOpusSamplingRate/mSamplingRate),
-                          mSamplingRate, aOutput);
+  nsCString vendor;
+  vendor.AppendASCII(opus_get_version_string());
 
-    mEncoderState = COMMENT_HEADER;
-    break;
-  }
-  case COMMENT_HEADER:
-  {
-    nsCString vendor;
-    vendor.AppendASCII(opus_get_version_string());
-
-    nsTArray<nsCString> comments;
-    comments.AppendElement(NS_LITERAL_CSTRING("ENCODER=Mozilla" MOZ_APP_UA_VERSION));
+  nsTArray<nsCString> comments;
+  comments.AppendElement(NS_LITERAL_CSTRING("ENCODER=Mozilla" MOZ_APP_UA_VERSION));
 
-    SerializeOpusCommentHeader(vendor, comments, aOutput);
+  SerializeOpusCommentHeader(vendor, comments,
+                             &meta->mCommentHeader);
 
-    mEncoderState = DATA;
-    break;
-  }
-  case DATA:
-    // No more headers.
-    break;
-  default:
-    MOZ_CRASH("Invalid state");
-  }
-  return NS_OK;
+  return meta;
 }
 
 nsresult
-OpusTrackEncoder::GetEncodedTrack(nsTArray<uint8_t>* aOutput,
-                                  int &aOutputDuration)
+OpusTrackEncoder::GetEncodedTrack(EncodedFrameContainer& aData)
 {
   {
     // Move all the samples from mRawSegment to mSourceSegment. We only hold
     // the monitor in this block.
     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
 
     // Wait if mEncoder is not initialized, or when not enough raw data, but is
     // not the end of stream nor is being canceled.
@@ -292,16 +276,18 @@ OpusTrackEncoder::GetEncodedTrack(nsTArr
       memset(pcm.Elements() + frameCopied * mChannels, 0,
              frameToCopy * mChannels * sizeof(AudioDataValue));
     }
 
     frameCopied += frameToCopy;
     iter.Next();
   }
 
+  EncodedFrame* audiodata = new EncodedFrame();
+  audiodata->SetFrameType(EncodedFrame::AUDIO_FRAME);
   if (mResampler) {
     nsAutoTArray<AudioDataValue, 9600> resamplingDest;
     // We want to consume all the input data, so we slightly oversize the
     // resampled data buffer so we can fit the output data in. We cannot really
     // predict the output frame count at each call.
     uint32_t outframes = frameCopied * kOpusSamplingRate / mSamplingRate + 1;
     uint32_t inframes = frameCopied;
 
@@ -316,20 +302,20 @@ OpusTrackEncoder::GetEncodedTrack(nsTArr
     float* in = reinterpret_cast<float*>(pcm.Elements());
     float* out = reinterpret_cast<float*>(resamplingDest.Elements());
     speex_resampler_process_interleaved_float(mResampler, in, &inframes,
                                                           out, &outframes);
 #endif
 
     pcm = resamplingDest;
     // This is always at 48000Hz.
-    aOutputDuration = outframes;
+    audiodata->SetDuration(outframes);
   } else {
     // The ogg time stamping and pre-skip is always timed at 48000.
-    aOutputDuration = frameCopied * (kOpusSamplingRate / mSamplingRate);
+    audiodata->SetDuration(frameCopied * (kOpusSamplingRate / mSamplingRate));
   }
 
   // Remove the raw data which has been pulled to pcm buffer.
   // The value of frameCopied should equal to (or smaller than, if eos)
   // GetPacketDuration().
   mSourceSegment->RemoveLeading(frameCopied);
 
   // Has reached the end of input stream and all queued data has pulled for
@@ -343,32 +329,34 @@ OpusTrackEncoder::GetEncodedTrack(nsTArr
   }
 
   // Append null data to pcm buffer if the leftover data is not enough for
   // opus encoder.
   if (frameCopied < GetPacketDuration() && mEndOfStream) {
     memset(pcm.Elements() + frameCopied * mChannels, 0,
            (GetPacketDuration()-frameCopied)*mChannels*sizeof(AudioDataValue));
   }
-
+  nsTArray<uint8_t> frameData;
   // Encode the data with Opus Encoder.
-  aOutput->SetLength(MAX_DATA_BYTES);
+  frameData.SetLength(MAX_DATA_BYTES);
   // result is returned as opus error code if it is negative.
   int result = 0;
 #ifdef MOZ_SAMPLE_TYPE_S16
   const opus_int16* pcmBuf = static_cast<opus_int16*>(pcm.Elements());
   result = opus_encode(mEncoder, pcmBuf, GetPacketDuration(),
-                       aOutput->Elements(), MAX_DATA_BYTES);
+                       frameData.Elements(), MAX_DATA_BYTES);
 #else
   const float* pcmBuf = static_cast<float*>(pcm.Elements());
   result = opus_encode_float(mEncoder, pcmBuf, GetPacketDuration(),
-                             aOutput->Elements(), MAX_DATA_BYTES);
+                             frameData.Elements(), MAX_DATA_BYTES);
 #endif
-  aOutput->SetLength(result >= 0 ? result : 0);
+  frameData.SetLength(result >= 0 ? result : 0);
 
   if (result < 0) {
     LOG("[Opus] Fail to encode data! Result: %s.", opus_strerror(result));
   }
 
+  audiodata->SetFrameData(&frameData);
+  aData.AppendEncodedFrame(audiodata);
   return result >= 0 ? NS_OK : NS_ERROR_FAILURE;
 }
 
 }
--- a/content/media/encoder/OpusTrackEncoder.h
+++ b/content/media/encoder/OpusTrackEncoder.h
@@ -4,44 +4,49 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef OpusTrackEncoder_h_
 #define OpusTrackEncoder_h_
 
 #include <stdint.h>
 #include <speex/speex_resampler.h>
 #include "TrackEncoder.h"
-#include "nsCOMPtr.h"
 
 struct OpusEncoder;
 
 namespace mozilla {
 
+// Opus meta data structure
+class OpusMetadata : public TrackMetadataBase
+{
+public:
+  // The ID Header of OggOpus. refer to http://wiki.xiph.org/OggOpus.
+  nsTArray<uint8_t> mIdHeader;
+  // The Comment Header of OggOpus.
+  nsTArray<uint8_t> mCommentHeader;
+
+  MetadataKind GetKind() const MOZ_OVERRIDE { return METADATA_OPUS; }
+};
+
 class OpusTrackEncoder : public AudioTrackEncoder
 {
 public:
   OpusTrackEncoder();
   virtual ~OpusTrackEncoder();
 
-  nsresult GetHeader(nsTArray<uint8_t>* aOutput) MOZ_OVERRIDE;
+  nsRefPtr<TrackMetadataBase> GetMetadata() MOZ_OVERRIDE;
 
-  nsresult GetEncodedTrack(nsTArray<uint8_t>* aOutput, int &aOutputDuration) MOZ_OVERRIDE;
+  nsresult GetEncodedTrack(EncodedFrameContainer& aData) MOZ_OVERRIDE;
 
 protected:
   int GetPacketDuration() MOZ_OVERRIDE;
 
   nsresult Init(int aChannels, int aSamplingRate) MOZ_OVERRIDE;
 
 private:
-  enum {
-    ID_HEADER,
-    COMMENT_HEADER,
-    DATA
-  } mEncoderState;
-
   /**
    * Get the samplerate of the data to be fed to the Opus encoder. This might be
    * different from the intput samplerate if resampling occurs.
    */
   int GetOutputSampleRate();
 
   /**
    * The Opus encoder from libopus.
--- a/content/media/encoder/TrackEncoder.h
+++ b/content/media/encoder/TrackEncoder.h
@@ -5,16 +5,18 @@
 
 #ifndef TrackEncoder_h_
 #define TrackEncoder_h_
 
 #include "mozilla/ReentrantMonitor.h"
 
 #include "AudioSegment.h"
 #include "StreamBuffer.h"
+#include "TrackMetadataBase.h"
+#include "EncodedFrameContainer.h"
 
 namespace mozilla {
 
 class MediaStreamGraph;
 
 /**
  * Base class of AudioTrackEncoder and VideoTrackEncoder. Lifetimes managed by
  * MediaEncoder. Most methods can only be called on the MediaEncoder's thread,
@@ -43,27 +45,24 @@ public:
 
   /**
    * Notified by the same callback of MediaEncoder when it has been removed from
    * MediaStreamGraph. Called on the MediaStreamGraph thread.
    */
   virtual void NotifyRemoved(MediaStreamGraph* aGraph) = 0;
 
   /**
-   * Creates and sets up header for a specific codec. Result data is returned
-   * in aOutput.
+   * Creates and sets up meta data for a specific codec
    */
-  virtual nsresult GetHeader(nsTArray<uint8_t>* aOutput) = 0;
+  virtual nsRefPtr<TrackMetadataBase> GetMetadata() = 0;
 
   /**
-   * Encodes raw segments. Result data is returned in aOutput. aOutputDuration
-   * is the playback duration of this packet in number of samples.
+   * Encodes raw segments. Result data is returned in aData.
    */
-  virtual nsresult GetEncodedTrack(nsTArray<uint8_t>* aOutput,
-                                   int &aOutputDuration) = 0;
+  virtual nsresult GetEncodedTrack(EncodedFrameContainer& aData) = 0;
 };
 
 class AudioTrackEncoder : public TrackEncoder
 {
 public:
   AudioTrackEncoder()
     : TrackEncoder()
     , mChannels(0)
new file mode 100644
--- /dev/null
+++ b/content/media/encoder/TrackMetadataBase.h
@@ -0,0 +1,29 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
+/* 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/. */
+
+#ifndef TrackMetadataBase_h_
+#define TrackMetadataBase_h_
+
+#include "nsTArray.h"
+#include "nsCOMPtr.h"
+namespace mozilla {
+
+// A class represent meta data for various codec format. Only support one track information.
+class TrackMetadataBase
+{
+public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TrackMetadataBase)
+  enum MetadataKind {
+    METADATA_OPUS,    // Represent the Opus metadata
+    METADATA_VP8,
+    METADATA_UNKNOW   // Metadata Kind not set
+  };
+  virtual ~TrackMetadataBase() {}
+  // Return the specific metadata kind
+  virtual MetadataKind GetKind() const = 0;
+};
+
+}
+#endif
--- a/content/media/encoder/moz.build
+++ b/content/media/encoder/moz.build
@@ -3,18 +3,20 @@
 # 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/.
 
 MODULE = 'content'
 
 EXPORTS += [
     'ContainerWriter.h',
+    'EncodedFrameContainer.h',
     'MediaEncoder.h',
     'TrackEncoder.h',
+    'TrackMetadataBase.h',
 ]
 
 SOURCES += [
     'MediaEncoder.cpp',
     'TrackEncoder.cpp',
 ]
 
 if CONFIG['MOZ_OPUS']:
--- a/content/media/ogg/OggWriter.cpp
+++ b/content/media/ogg/OggWriter.cpp
@@ -1,14 +1,15 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
 /* 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 "OggWriter.h"
 #include "prtime.h"
+#include "OpusTrackEncoder.h"
 
 #undef LOG
 #ifdef MOZ_WIDGET_GONK
 #include <android/log.h>
 #define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "MediaEncoder", ## args);
 #else
 #define LOG(args, ...)
 #endif
@@ -41,19 +42,40 @@ OggWriter::Init()
   mPacket.bytes = 0;
 
   mInitialized = (rc == 0);
 
   return (rc == 0) ? NS_OK : NS_ERROR_NOT_INITIALIZED;
 }
 
 nsresult
-OggWriter::WriteEncodedTrack(const nsTArray<uint8_t>& aBuffer, int aDuration,
+OggWriter::WriteEncodedTrack(const EncodedFrameContainer& aData,
                              uint32_t aFlags)
 {
+  for (uint32_t i = 0; i < aData.GetEncodedFrames().Length(); i++) {
+    if (aData.GetEncodedFrames()[i]->GetFrameType() != EncodedFrame::AUDIO_FRAME) {
+      LOG("[OggWriter] wrong encoded data type!");
+      return NS_ERROR_FAILURE;
+    }
+
+    nsresult rv = WriteEncodedData(aData.GetEncodedFrames()[i]->GetFrameData(),
+                                   aData.GetEncodedFrames()[i]->GetDuration(),
+                                   aFlags);
+    if (NS_FAILED(rv)) {
+      LOG("%p Failed to WriteEncodedTrack!", this);
+      return rv;
+    }
+  }
+  return NS_OK;
+}
+
+nsresult
+OggWriter::WriteEncodedData(const nsTArray<uint8_t>& aBuffer, int aDuration,
+                            uint32_t aFlags)
+{
   if (!mInitialized) {
     LOG("[OggWriter] OggWriter has not initialized!");
     return NS_ERROR_FAILURE;
   }
 
   MOZ_ASSERT(!ogg_stream_eos(&mOggStreamState),
              "No data can be written after eos has marked.");
 
@@ -82,38 +104,89 @@ OggWriter::WriteEncodedTrack(const nsTAr
     mPacket.b_o_s = 0;
   }
   mPacket.packetno++;
   mPacket.packet = nullptr;
 
   return NS_OK;
 }
 
+void
+OggWriter::ProduceOggPage(nsTArray<nsTArray<uint8_t> >* aOutputBufs)
+{
+  aOutputBufs->AppendElement();
+  aOutputBufs->LastElement().SetLength(mOggPage.header_len +
+                                       mOggPage.body_len);
+  memcpy(aOutputBufs->LastElement().Elements(), mOggPage.header,
+         mOggPage.header_len);
+  memcpy(aOutputBufs->LastElement().Elements() + mOggPage.header_len,
+         mOggPage.body, mOggPage.body_len);
+}
+
 nsresult
 OggWriter::GetContainerData(nsTArray<nsTArray<uint8_t> >* aOutputBufs,
                             uint32_t aFlags)
 {
   int rc = -1;
+  // Generate the oggOpus Header
+  if (aFlags & ContainerWriter::GET_HEADER) {
+    OpusMetadata* meta = static_cast<OpusMetadata*>(mMetadata.get());
+    NS_ASSERTION(meta, "should have meta data");
+    NS_ASSERTION(meta->GetKind() == TrackMetadataBase::METADATA_OPUS,
+                 "should have Opus meta data");
+
+    nsresult rv = WriteEncodedData(meta->mIdHeader, 0);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rc = ogg_stream_flush(&mOggStreamState, &mOggPage);
+    NS_ENSURE_TRUE(rc > 0, NS_ERROR_FAILURE);
+    ProduceOggPage(aOutputBufs);
+
+    rv = WriteEncodedData(meta->mCommentHeader, 0);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rc = ogg_stream_flush(&mOggStreamState, &mOggPage);
+    NS_ENSURE_TRUE(rc > 0, NS_ERROR_FAILURE);
+
+    ProduceOggPage(aOutputBufs);
+    return NS_OK;
+
   // Force generate a page even if the amount of packet data is not enough.
   // Usually do so after a header packet.
-  if (aFlags & ContainerWriter::FLUSH_NEEDED) {
+  } else if (aFlags & ContainerWriter::FLUSH_NEEDED) {
     // rc = 0 means no packet to put into a page, or an internal error.
     rc = ogg_stream_flush(&mOggStreamState, &mOggPage);
   } else {
     // rc = 0 means insufficient data has accumulated to fill a page, or an
     // internal error has occurred.
     rc = ogg_stream_pageout(&mOggStreamState, &mOggPage);
   }
 
   if (rc) {
-    aOutputBufs->AppendElement();
-    aOutputBufs->LastElement().SetLength(mOggPage.header_len +
-                                         mOggPage.body_len);
-    memcpy(aOutputBufs->LastElement().Elements(), mOggPage.header,
-           mOggPage.header_len);
-    memcpy(aOutputBufs->LastElement().Elements() + mOggPage.header_len,
-           mOggPage.body, mOggPage.body_len);
+    ProduceOggPage(aOutputBufs);
   }
 
   return (rc > 0) ? NS_OK : NS_ERROR_FAILURE;
 }
 
+nsresult
+OggWriter::SetMetadata(nsRefPtr<TrackMetadataBase> aMetadata)
+{
+  if (aMetadata->GetKind() != TrackMetadataBase::METADATA_OPUS) {
+    LOG("wrong meta data type!");
+    return NS_ERROR_FAILURE;
+  }
+  // Validate each field of METADATA
+  OpusMetadata* meta = static_cast<OpusMetadata*>(aMetadata.get());
+  if (meta->mIdHeader.Length() == 0) {
+    LOG("miss mIdHeader!");
+    return NS_ERROR_FAILURE;
+  }
+  if (meta->mCommentHeader.Length() == 0) {
+    LOG("miss mCommentHeader!");
+    return NS_ERROR_FAILURE;
+  }
+
+  mMetadata = aMetadata;
+  return NS_OK;
 }
+
+}
--- a/content/media/ogg/OggWriter.h
+++ b/content/media/ogg/OggWriter.h
@@ -17,23 +17,31 @@ namespace mozilla {
  * For more details, please reference:
  * http://www.xiph.org/ogg/doc/libogg/encoding.html
  */
 class OggWriter : public ContainerWriter
 {
 public:
   OggWriter();
 
-  nsresult WriteEncodedTrack(const nsTArray<uint8_t>& aBuffer, int aDuration,
+  nsresult WriteEncodedTrack(const EncodedFrameContainer &aData,
                              uint32_t aFlags = 0) MOZ_OVERRIDE;
 
   nsresult GetContainerData(nsTArray<nsTArray<uint8_t> >* aOutputBufs,
                             uint32_t aFlags = 0) MOZ_OVERRIDE;
 
+  // Check metadata type integrity and reject unacceptable track encoder.
+  nsresult SetMetadata(nsRefPtr<TrackMetadataBase> aMetadata) MOZ_OVERRIDE;
+
 private:
   nsresult Init();
 
+  nsresult WriteEncodedData(const nsTArray<uint8_t>& aBuffer, int aDuration,
+                            uint32_t aFlags = 0);
+
+  void ProduceOggPage(nsTArray<nsTArray<uint8_t> >* aOutputBufs);
+
   ogg_stream_state mOggStreamState;
   ogg_page mOggPage;
   ogg_packet mPacket;
 };
 }
 #endif