Bug 1205927 - Part 1: [MediaEncoder] Support *.3g2 with EVRC audio format. r=ayang
authorMunro Chiang <mchiang@mozilla.com>
Tue, 27 Oct 2015 15:12:26 +0800
changeset 292139 888a6bae2a4b0eeba790e40809f524f08fce4cc6
parent 291929 899a340b00b5bc27269b72b2fa34818a81383505
child 292140 65995f4d518328c3ec2c939cfe3db3c3b611db0a
push id8824
push userraliiev@mozilla.com
push dateMon, 14 Dec 2015 20:18:56 +0000
treeherdermozilla-aurora@e2031358e2a6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersayang
bugs1205927
milestone45.0a1
Bug 1205927 - Part 1: [MediaEncoder] Support *.3g2 with EVRC audio format. r=ayang
dom/media/MediaRecorder.cpp
dom/media/encoder/EncodedFrameContainer.h
dom/media/encoder/MediaEncoder.cpp
dom/media/encoder/OmxTrackEncoder.cpp
dom/media/encoder/OmxTrackEncoder.h
dom/media/encoder/TrackMetadataBase.h
dom/media/encoder/fmp4_muxer/EVRCBox.cpp
dom/media/encoder/fmp4_muxer/EVRCBox.h
dom/media/encoder/fmp4_muxer/ISOControl.cpp
dom/media/encoder/fmp4_muxer/ISOMediaBoxes.cpp
dom/media/encoder/fmp4_muxer/ISOMediaWriter.cpp
dom/media/encoder/fmp4_muxer/ISOMediaWriter.h
dom/media/encoder/fmp4_muxer/ISOTrackMetadata.h
dom/media/encoder/fmp4_muxer/moz.build
dom/media/omx/OMXCodecWrapper.cpp
dom/media/omx/OMXCodecWrapper.h
netwerk/mime/nsMimeTypes.h
uriloader/exthandler/nsExternalHelperAppService.cpp
--- a/dom/media/MediaRecorder.cpp
+++ b/dom/media/MediaRecorder.cpp
@@ -594,16 +594,22 @@ private:
 
     // Make sure the application has permission to assign AUDIO_3GPP
     if (mRecorder->mMimeType.EqualsLiteral(AUDIO_3GPP) && Check3gppPermission()) {
       mEncoder = MediaEncoder::CreateEncoder(NS_LITERAL_STRING(AUDIO_3GPP),
                                              mRecorder->GetAudioBitrate(),
                                              mRecorder->GetVideoBitrate(),
                                              mRecorder->GetBitrate(),
                                              aTrackTypes);
+    } else if (mRecorder->mMimeType.EqualsLiteral(AUDIO_3GPP2)) {
+      mEncoder = MediaEncoder::CreateEncoder(NS_LITERAL_STRING(AUDIO_3GPP2),
+                                             mRecorder->GetAudioBitrate(),
+                                             mRecorder->GetVideoBitrate(),
+                                             mRecorder->GetBitrate(),
+                                             aTrackTypes);
     } else {
       mEncoder = MediaEncoder::CreateEncoder(NS_LITERAL_STRING(""),
                                              mRecorder->GetAudioBitrate(),
                                              mRecorder->GetVideoBitrate(),
                                              mRecorder->GetBitrate(),
                                              aTrackTypes);
     }
 
--- a/dom/media/encoder/EncodedFrameContainer.h
+++ b/dom/media/encoder/EncodedFrameContainer.h
@@ -55,16 +55,18 @@ public:
     AVC_I_FRAME,
     AVC_P_FRAME,
     AVC_B_FRAME,
     AVC_CSD,          // AVC codec specific data
     AAC_AUDIO_FRAME,
     AAC_CSD,          // AAC codec specific data
     AMR_AUDIO_CSD,
     AMR_AUDIO_FRAME,
+    EVRC_AUDIO_CSD,
+    EVRC_AUDIO_FRAME,
     UNKNOWN           // FrameType not set
   };
   void SwapInFrameData(nsTArray<uint8_t>& aData)
   {
     mFrameData.SwapElements(aData);
   }
   nsresult SwapOutFrameData(nsTArray<uint8_t>& aData)
   {
--- a/dom/media/encoder/MediaEncoder.cpp
+++ b/dom/media/encoder/MediaEncoder.cpp
@@ -125,16 +125,24 @@ MediaEncoder::CreateEncoder(const nsAStr
   } else if (MediaEncoder::IsOMXEncoderEnabled() &&
             (aMIMEType.EqualsLiteral(AUDIO_3GPP))) {
     audioEncoder = new OmxAMRAudioTrackEncoder();
     NS_ENSURE_TRUE(audioEncoder, nullptr);
 
     writer = new ISOMediaWriter(aTrackTypes, ISOMediaWriter::TYPE_FRAG_3GP);
     NS_ENSURE_TRUE(writer, nullptr);
     mimeType = NS_LITERAL_STRING(AUDIO_3GPP);
+  } else if (MediaEncoder::IsOMXEncoderEnabled() &&
+            (aMIMEType.EqualsLiteral(AUDIO_3GPP2))) {
+    audioEncoder = new OmxEVRCAudioTrackEncoder();
+    NS_ENSURE_TRUE(audioEncoder, nullptr);
+
+    writer = new ISOMediaWriter(aTrackTypes, ISOMediaWriter::TYPE_FRAG_3G2);
+    NS_ENSURE_TRUE(writer, nullptr);
+    mimeType = NS_LITERAL_STRING(AUDIO_3GPP2) ;
   }
 #endif // MOZ_OMX_ENCODER
   else if (MediaDecoder::IsOggEnabled() && MediaDecoder::IsOpusEnabled() &&
            (aMIMEType.EqualsLiteral(AUDIO_OGG) ||
            (aTrackTypes & ContainerWriter::CREATE_AUDIO_TRACK))) {
     writer = new OggWriter();
     audioEncoder = new OpusTrackEncoder();
     NS_ENSURE_TRUE(writer, nullptr);
--- a/dom/media/encoder/OmxTrackEncoder.cpp
+++ b/dom/media/encoder/OmxTrackEncoder.cpp
@@ -179,31 +179,34 @@ OmxAudioTrackEncoder::AppendEncodedFrame
   nsTArray<uint8_t> frameData;
   int outFlags = 0;
   int64_t outTimeUs = -1;
 
   nsresult rv = mEncoder->GetNextEncodedFrame(&frameData, &outTimeUs, &outFlags,
                                               3000); // wait up to 3ms
   NS_ENSURE_SUCCESS(rv, rv);
 
-  if (!frameData.IsEmpty()) {
+  if (!frameData.IsEmpty() || outFlags & OMXCodecWrapper::BUFFER_EOS) { // Some hw codec may send out EOS with an empty frame
     bool isCSD = false;
     if (outFlags & OMXCodecWrapper::BUFFER_CODEC_CONFIG) { // codec specific data
       isCSD = true;
     } else if (outFlags & OMXCodecWrapper::BUFFER_EOS) { // last frame
       mEncodingComplete = true;
     }
 
     RefPtr<EncodedFrame> audiodata = new EncodedFrame();
     if (mEncoder->GetCodecType() == OMXCodecWrapper::AAC_ENC) {
       audiodata->SetFrameType(isCSD ?
         EncodedFrame::AAC_CSD : EncodedFrame::AAC_AUDIO_FRAME);
     } else if (mEncoder->GetCodecType() == OMXCodecWrapper::AMR_NB_ENC){
       audiodata->SetFrameType(isCSD ?
         EncodedFrame::AMR_AUDIO_CSD : EncodedFrame::AMR_AUDIO_FRAME);
+    } else if (mEncoder->GetCodecType() == OMXCodecWrapper::EVRC_ENC){
+      audiodata->SetFrameType(isCSD ?
+        EncodedFrame::EVRC_AUDIO_CSD : EncodedFrame::EVRC_AUDIO_FRAME);
     } else {
       MOZ_ASSERT(false, "audio codec not supported");
     }
     audiodata->SetTimeStamp(outTimeUs);
     audiodata->SwapInFrameData(frameData);
     aContainer.AppendEncodedFrame(audiodata);
   }
 
@@ -338,9 +341,49 @@ OmxAMRAudioTrackEncoder::GetMetadata()
   if (mCanceled || mEncodingComplete) {
     return nullptr;
   }
 
   RefPtr<AMRTrackMetadata> meta = new AMRTrackMetadata();
   return meta.forget();
 }
 
+nsresult
+OmxEVRCAudioTrackEncoder::Init(int aChannels, int aSamplingRate)
+{
+  mChannels = aChannels;
+  mSamplingRate = aSamplingRate;
+
+  mEncoder = OMXCodecWrapper::CreateEVRCEncoder();
+  NS_ENSURE_TRUE(mEncoder, NS_ERROR_FAILURE);
+
+  nsresult rv = mEncoder->Configure(mChannels, mSamplingRate, EVRC_SAMPLERATE);
+  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+  mInitialized = (rv == NS_OK);
+
+  mReentrantMonitor.NotifyAll();
+
+  return NS_OK;
 }
+
+already_AddRefed<TrackMetadataBase>
+OmxEVRCAudioTrackEncoder::GetMetadata()
+{
+  PROFILER_LABEL("OmxEVRCAudioTrackEncoder", "GetMetadata",
+    js::ProfileEntry::Category::OTHER);
+  {
+    // Wait if mEncoder is not initialized nor is being canceled.
+    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+    while (!mCanceled && !mInitialized) {
+      mReentrantMonitor.Wait();
+    }
+  }
+
+  if (mCanceled || mEncodingComplete) {
+    return nullptr;
+  }
+
+  RefPtr<EVRCTrackMetadata> meta = new EVRCTrackMetadata();
+  meta->mChannels = mChannels;
+  return meta.forget();
+}
+
+}
--- a/dom/media/encoder/OmxTrackEncoder.h
+++ b/dom/media/encoder/OmxTrackEncoder.h
@@ -85,10 +85,26 @@ public:
     AMR_NB_SAMPLERATE = 8000,
   };
   already_AddRefed<TrackMetadataBase> GetMetadata() override;
 
 protected:
   nsresult Init(int aChannels, int aSamplingRate) override;
 };
 
+class OmxEVRCAudioTrackEncoder final : public OmxAudioTrackEncoder
+{
+public:
+  OmxEVRCAudioTrackEncoder()
+    : OmxAudioTrackEncoder()
+  {}
+
+  enum {
+    EVRC_SAMPLERATE = 8000,
+  };
+  already_AddRefed<TrackMetadataBase> GetMetadata() override;
+
+protected:
+  nsresult Init(int aChannels, int aSamplingRate) override;
+};
+
 }
 #endif
--- a/dom/media/encoder/TrackMetadataBase.h
+++ b/dom/media/encoder/TrackMetadataBase.h
@@ -17,16 +17,17 @@ public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TrackMetadataBase)
   enum MetadataKind {
     METADATA_OPUS,    // Represent the Opus metadata
     METADATA_VP8,
     METADATA_VORBIS,
     METADATA_AVC,
     METADATA_AAC,
     METADATA_AMR,
+    METADATA_EVRC,
     METADATA_UNKNOWN  // Metadata Kind not set
   };
   // Return the specific metadata kind
   virtual MetadataKind GetKind() const = 0;
 
 protected:
   // Protected destructor, to discourage deletion outside of Release():
   virtual ~TrackMetadataBase() {}
new file mode 100644
--- /dev/null
+++ b/dom/media/encoder/fmp4_muxer/EVRCBox.cpp
@@ -0,0 +1,84 @@
+/* -*- 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 "ISOControl.h"
+#include "ISOMediaBoxes.h"
+#include "EVRCBox.h"
+#include "ISOTrackMetadata.h"
+
+namespace mozilla {
+
+nsresult
+EVRCSampleEntry::Generate(uint32_t* aBoxSize)
+{
+  uint32_t box_size;
+  nsresult rv = evrc_special_box->Generate(&box_size);
+  NS_ENSURE_SUCCESS(rv, rv);
+  size += box_size;
+
+  *aBoxSize = size;
+  return NS_OK;
+}
+
+nsresult
+EVRCSampleEntry::Write()
+{
+  BoxSizeChecker checker(mControl, size);
+  nsresult rv;
+  rv = AudioSampleEntry::Write();
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = evrc_special_box->Write();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+EVRCSampleEntry::EVRCSampleEntry(ISOControl* aControl)
+  : AudioSampleEntry(NS_LITERAL_CSTRING("sevc"), aControl)
+{
+  evrc_special_box = new EVRCSpecificBox(aControl);
+  MOZ_COUNT_CTOR(EVRCSampleEntry);
+}
+
+EVRCSampleEntry::~EVRCSampleEntry()
+{
+  MOZ_COUNT_DTOR(EVRCSampleEntry);
+}
+
+nsresult
+EVRCSpecificBox::Generate(uint32_t* aBoxSize)
+{
+  nsresult rv;
+  FragmentBuffer* frag = mControl->GetFragment(Audio_Track);
+  rv = frag->GetCSD(evrcDecSpecInfo);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  size += evrcDecSpecInfo.Length();
+  *aBoxSize = size;
+
+  return NS_OK;
+}
+
+nsresult
+EVRCSpecificBox::Write()
+{
+  BoxSizeChecker checker(mControl, size);
+  Box::Write();
+  mControl->Write(evrcDecSpecInfo.Elements(), evrcDecSpecInfo.Length());
+  return NS_OK;
+}
+
+EVRCSpecificBox::EVRCSpecificBox(ISOControl* aControl)
+  : Box(NS_LITERAL_CSTRING("devc"), aControl)
+{
+  MOZ_COUNT_CTOR(EVRCSpecificBox);
+}
+
+EVRCSpecificBox::~EVRCSpecificBox()
+{
+  MOZ_COUNT_DTOR(EVRCSpecificBox);
+}
+
+}
new file mode 100644
--- /dev/null
+++ b/dom/media/encoder/fmp4_muxer/EVRCBox.h
@@ -0,0 +1,50 @@
+/* -*- 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 EVRCBOX_h_
+#define EVRCBOX_h_
+
+#include "nsTArray.h"
+#include "MuxerOperation.h"
+
+namespace mozilla {
+
+class ISOControl;
+
+// 3GPP TS 26.244 6.7 'EVRCSpecificBox field for EVRCSampleEntry box'
+// Box type: 'devc'
+class EVRCSpecificBox : public Box {
+public:
+  // 3GPP members
+  nsTArray<uint8_t> evrcDecSpecInfo;
+
+  // MuxerOperation methods
+  nsresult Generate(uint32_t* aBoxSize) override;
+  nsresult Write() override;
+
+  // EVRCSpecificBox methods
+  EVRCSpecificBox(ISOControl* aControl);
+  ~EVRCSpecificBox();
+};
+
+// 3GPP TS 26.244 6.5 'EVRCSampleEntry box'
+// Box type: 'sevc'
+class EVRCSampleEntry : public AudioSampleEntry {
+public:
+  // 3GPP members
+  RefPtr<EVRCSpecificBox> evrc_special_box;
+
+  // MuxerOperation methods
+  nsresult Generate(uint32_t* aBoxSize) override;
+  nsresult Write() override;
+
+  // EVRCSampleEntry methods
+  EVRCSampleEntry(ISOControl* aControl);
+  ~EVRCSampleEntry();
+};
+
+}
+
+#endif // EVRCBOX_h_
--- a/dom/media/encoder/fmp4_muxer/ISOControl.cpp
+++ b/dom/media/encoder/fmp4_muxer/ISOControl.cpp
@@ -57,17 +57,17 @@ FragmentBuffer::AddFrame(EncodedFrame* a
   // already EOS, it rejects all new data.
   if (mEOS) {
     MOZ_ASSERT(0);
     return NS_OK;
   }
 
   EncodedFrame::FrameType type = aFrame->GetFrameType();
   if (type == EncodedFrame::AAC_CSD || type == EncodedFrame::AVC_CSD ||
-      type == EncodedFrame::AMR_AUDIO_CSD) {
+      type == EncodedFrame::AMR_AUDIO_CSD || type == EncodedFrame::EVRC_AUDIO_CSD) {
     mCSDFrame = aFrame;
     // Use CSD's timestamp as the start time. Encoder should send CSD frame first
     // and then data frames.
     mMediaStartTime = aFrame->GetTimeStamp();
     mFragmentNumber = 1;
     return NS_OK;
   }
 
@@ -163,29 +163,31 @@ ISOControl::GetTrackID(TrackMetadataBase
   return 0;
 }
 
 nsresult
 ISOControl::SetMetadata(TrackMetadataBase* aTrackMeta)
 {
   if (aTrackMeta->GetKind() == TrackMetadataBase::METADATA_AAC ||
       aTrackMeta->GetKind() == TrackMetadataBase::METADATA_AMR ||
-      aTrackMeta->GetKind() == TrackMetadataBase::METADATA_AVC) {
+      aTrackMeta->GetKind() == TrackMetadataBase::METADATA_AVC ||
+      aTrackMeta->GetKind() == TrackMetadataBase::METADATA_EVRC) {
     mMetaArray.AppendElement(aTrackMeta);
     return NS_OK;
   }
   return NS_ERROR_FAILURE;
 }
 
 nsresult
 ISOControl::GetAudioMetadata(RefPtr<AudioTrackMetadata>& aAudMeta)
 {
   for (uint32_t i = 0; i < mMetaArray.Length() ; i++) {
     if (mMetaArray[i]->GetKind() == TrackMetadataBase::METADATA_AAC ||
-        mMetaArray[i]->GetKind() == TrackMetadataBase::METADATA_AMR) {
+        mMetaArray[i]->GetKind() == TrackMetadataBase::METADATA_AMR ||
+        mMetaArray[i]->GetKind() == TrackMetadataBase::METADATA_EVRC) {
       aAudMeta = static_cast<AudioTrackMetadata*>(mMetaArray[i].get());
       return NS_OK;
     }
   }
   return NS_ERROR_FAILURE;
 }
 
 nsresult
--- a/dom/media/encoder/fmp4_muxer/ISOMediaBoxes.cpp
+++ b/dom/media/encoder/fmp4_muxer/ISOMediaBoxes.cpp
@@ -8,16 +8,17 @@
 #include "ISOMediaBoxes.h"
 #include "ISOControl.h"
 #include "ISOMediaWriter.h"
 #include "EncodedFrameContainer.h"
 #include "ISOTrackMetadata.h"
 #include "MP4ESDS.h"
 #include "AMRBox.h"
 #include "AVCBox.h"
+#include "EVRCBox.h"
 #include "VideoUtils.h"
 
 namespace mozilla {
 
 // 14496-12 6.2.2 'Data Types and fields'
 const uint32_t iso_matrix[] = { 0x00010000, 0,          0,
                                 0,          0x00010000, 0,
                                 0,          0,          0x40000000 };
@@ -661,16 +662,18 @@ SampleDescriptionBox::SampleDescriptionB
 
 nsresult
 SampleDescriptionBox::CreateAudioSampleEntry(RefPtr<SampleEntryBox>& aSampleEntry)
 {
   if (mAudioMeta->GetKind() == TrackMetadataBase::METADATA_AMR) {
     aSampleEntry = new AMRSampleEntry(mControl);
   } else if (mAudioMeta->GetKind() == TrackMetadataBase::METADATA_AAC) {
     aSampleEntry = new MP4AudioSampleEntry(mControl);
+  } else if (mAudioMeta->GetKind() == TrackMetadataBase::METADATA_EVRC) {
+    aSampleEntry = new EVRCSampleEntry(mControl);
   } else {
     MOZ_ASSERT(0);
   }
   return NS_OK;
 }
 
 nsresult
 SampleDescriptionBox::CreateVideoSampleEntry(RefPtr<SampleEntryBox>& aSampleEntry)
@@ -1245,16 +1248,27 @@ FileTypeBox::Generate(uint32_t* aBoxSize
     major_brand = "3gp9";
     // According to 3GPP TS 26.244 V12.2.0, section 5.3.4, it's recommended to
     // list all compatible brands here. 3GP spec supports fragment from '3gp6'.
     compatible_brands.AppendElement("3gp9");
     compatible_brands.AppendElement("3gp8");
     compatible_brands.AppendElement("3gp7");
     compatible_brands.AppendElement("3gp6");
     compatible_brands.AppendElement("isom");
+  } else if (mControl->GetMuxingType() == ISOMediaWriter::TYPE_FRAG_3G2) {
+    major_brand = "3g2a";
+    // 3GPP2 Release 0 and A and 3GPP Release 6 allow movie fragmentation
+    compatible_brands.AppendElement("3gp9");
+    compatible_brands.AppendElement("3gp8");
+    compatible_brands.AppendElement("3gp7");
+    compatible_brands.AppendElement("3gp6");
+    compatible_brands.AppendElement("isom");
+    compatible_brands.AppendElement("3g2c");
+    compatible_brands.AppendElement("3g2b");
+    compatible_brands.AppendElement("3g2a");
   } else {
     MOZ_ASSERT(0);
   }
 
   size += major_brand.Length() +
           sizeof(minor_version) +
           compatible_brands.Length() * 4;
 
--- a/dom/media/encoder/fmp4_muxer/ISOMediaWriter.cpp
+++ b/dom/media/encoder/fmp4_muxer/ISOMediaWriter.cpp
@@ -99,17 +99,19 @@ ISOMediaWriter::WriteEncodedTrack(const 
     return NS_OK;
   }
   for (uint32_t i = 0; i < len; i++) {
     RefPtr<EncodedFrame> frame(aData.GetEncodedFrames()[i]);
     EncodedFrame::FrameType type = frame->GetFrameType();
     if (type == EncodedFrame::AAC_AUDIO_FRAME ||
         type == EncodedFrame::AAC_CSD ||
         type == EncodedFrame::AMR_AUDIO_FRAME ||
-        type == EncodedFrame::AMR_AUDIO_CSD) {
+        type == EncodedFrame::AMR_AUDIO_CSD ||
+        type == EncodedFrame::EVRC_AUDIO_FRAME ||
+        type == EncodedFrame::EVRC_AUDIO_CSD) {
       frag = mAudioFragmentBuffer;
     } else if (type == EncodedFrame::AVC_I_FRAME ||
                type == EncodedFrame::AVC_P_FRAME ||
                type == EncodedFrame::AVC_B_FRAME ||
                type == EncodedFrame::AVC_CSD) {
       frag = mVideoFragmentBuffer;
     } else {
       MOZ_ASSERT(0);
@@ -207,17 +209,18 @@ ISOMediaWriter::GetContainerData(nsTArra
 }
 
 nsresult
 ISOMediaWriter::SetMetadata(TrackMetadataBase* aMetadata)
 {
   PROFILER_LABEL("ISOMediaWriter", "SetMetadata",
     js::ProfileEntry::Category::OTHER);
   if (aMetadata->GetKind() == TrackMetadataBase::METADATA_AAC ||
-      aMetadata->GetKind() == TrackMetadataBase::METADATA_AMR) {
+      aMetadata->GetKind() == TrackMetadataBase::METADATA_AMR ||
+      aMetadata->GetKind() == TrackMetadataBase::METADATA_EVRC) {
     mControl->SetMetadata(aMetadata);
     mAudioFragmentBuffer = new FragmentBuffer(Audio_Track, FRAG_DURATION);
     mControl->SetFragment(mAudioFragmentBuffer);
     return NS_OK;
   }
   if (aMetadata->GetKind() == TrackMetadataBase::METADATA_AVC) {
     mControl->SetMetadata(aMetadata);
     mVideoFragmentBuffer = new FragmentBuffer(Video_Track, FRAG_DURATION);
--- a/dom/media/encoder/fmp4_muxer/ISOMediaWriter.h
+++ b/dom/media/encoder/fmp4_muxer/ISOMediaWriter.h
@@ -21,16 +21,20 @@ public:
   // Brand names in 'ftyp' box are 'isom' and 'mp42'.
   const static uint32_t TYPE_FRAG_MP4 = 1 << 0;
 
   // Generate an fragmented 3GP stream, 3GPP TS 26.244,
   // '5.4.3 Basic profile'.
   // Brand names in 'ftyp' box are '3gp9' and 'isom'.
   const static uint32_t TYPE_FRAG_3GP = 1 << 1;
 
+  // Generate an fragmented 3G2 stream, 3GPP2 C.S0050-B
+  // Brand names in 'ftyp' box are '3g2c' and 'isom'
+  const static uint32_t TYPE_FRAG_3G2 = 1 << 2;
+
   // aType is the combination of CREATE_AUDIO_TRACK and CREATE_VIDEO_TRACK.
   // It is a hint to muxer that the output streaming contains audio, video
   // or both.
   //
   // aHint is one of the value in TYPE_XXXXXXXX. It is a hint to muxer what kind
   // of ISO format should be generated.
   ISOMediaWriter(uint32_t aType, uint32_t aHint = TYPE_FRAG_MP4);
   ~ISOMediaWriter();
--- a/dom/media/encoder/fmp4_muxer/ISOTrackMetadata.h
+++ b/dom/media/encoder/fmp4_muxer/ISOTrackMetadata.h
@@ -94,11 +94,38 @@ public:
   // TrackMetadataBase member
   MetadataKind GetKind() const override { return METADATA_AMR; }
 
   // AMRTrackMetadata members
   AMRTrackMetadata() { MOZ_COUNT_CTOR(AMRTrackMetadata); }
   ~AMRTrackMetadata() { MOZ_COUNT_DTOR(AMRTrackMetadata); }
 };
 
+// EVRC sample rate is 8000 samples/s.
+#define EVRC_SAMPLE_RATE 8000
+
+class EVRCTrackMetadata : public AudioTrackMetadata {
+public:
+  // AudioTrackMetadata members
+  //
+  // The number of sample sets generates by encoder is variant. So the
+  // frame duration and frame size are both 0.
+  uint32_t GetAudioFrameDuration() override { return 0; }
+  uint32_t GetAudioFrameSize() override { return 0; }
+  uint32_t GetAudioSampleRate() override { return EVRC_SAMPLE_RATE; }
+  uint32_t GetAudioChannels() override { return mChannels; }
+
+  // TrackMetadataBase member
+  MetadataKind GetKind() const override { return METADATA_EVRC; }
+
+  // EVRCTrackMetadata members
+  EVRCTrackMetadata()
+    : mChannels(0) {
+    MOZ_COUNT_CTOR(EVRCTrackMetadata);
+  }
+  ~EVRCTrackMetadata() { MOZ_COUNT_DTOR(EVRCTrackMetadata); }
+
+  uint32_t mChannels;       // Channel number, it should be 1 or 2.
+};
+
 }
 
 #endif // ISOTrackMetadata_h_
--- a/dom/media/encoder/fmp4_muxer/moz.build
+++ b/dom/media/encoder/fmp4_muxer/moz.build
@@ -7,15 +7,16 @@
 EXPORTS += [
     'ISOMediaWriter.h',
     'ISOTrackMetadata.h',
 ]
 
 UNIFIED_SOURCES += [
     'AMRBox.cpp',
     'AVCBox.cpp',
+    'EVRCBox.cpp',
     'ISOControl.cpp',
     'ISOMediaBoxes.cpp',
     'ISOMediaWriter.cpp',
     'MP4ESDS.cpp',
 ]
 
 FINAL_LIBRARY = 'xul'
--- a/dom/media/omx/OMXCodecWrapper.cpp
+++ b/dom/media/omx/OMXCodecWrapper.cpp
@@ -25,24 +25,27 @@
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 using namespace mozilla::layers;
 
 #define INPUT_BUFFER_TIMEOUT_US (5 * 1000ll)
 // AMR NB kbps
 #define AMRNB_BITRATE 12200
+#define EVRC_BITRATE 8755
 
 #define CODEC_ERROR(args...)                                                   \
   do {                                                                         \
     __android_log_print(ANDROID_LOG_ERROR, "OMXCodecWrapper", ##args);         \
   } while (0)
 
 namespace android {
 
+const char *MEDIA_MIMETYPE_AUDIO_EVRC = "audio/evrc";
+
 enum BufferState
 {
   BUFFER_OK,
   BUFFER_FAIL,
   WAIT_FOR_NEW_BUFFER
 };
 
 bool
@@ -80,42 +83,55 @@ OMXCodecWrapper::CreateAMRNBEncoder()
 {
   nsAutoPtr<OMXAudioEncoder> amr(new OMXAudioEncoder(CodecType::AMR_NB_ENC));
   // Return the object only when media codec is valid.
   NS_ENSURE_TRUE(amr->IsValid(), nullptr);
 
   return amr.forget();
 }
 
+OMXAudioEncoder*
+OMXCodecWrapper::CreateEVRCEncoder()
+{
+  nsAutoPtr<OMXAudioEncoder> evrc(new OMXAudioEncoder(CodecType::EVRC_ENC));
+  // Return the object only when media codec is valid.
+  NS_ENSURE_TRUE(evrc->IsValid(), nullptr);
+
+  return evrc.forget();
+}
+
 OMXVideoEncoder*
 OMXCodecWrapper::CreateAVCEncoder()
 {
   nsAutoPtr<OMXVideoEncoder> avc(new OMXVideoEncoder(CodecType::AVC_ENC));
   // Return the object only when media codec is valid.
   NS_ENSURE_TRUE(avc->IsValid(), nullptr);
 
   return avc.forget();
 }
 
 OMXCodecWrapper::OMXCodecWrapper(CodecType aCodecType)
   : mCodecType(aCodecType)
   , mStarted(false)
   , mAMRCSDProvided(false)
+  , mEVRCCSDProvided(false)
 {
   ProcessState::self()->startThreadPool();
 
   mLooper = new ALooper();
   mLooper->start();
 
   if (aCodecType == CodecType::AVC_ENC) {
     mCodec = MediaCodec::CreateByType(mLooper, MEDIA_MIMETYPE_VIDEO_AVC, true);
   } else if (aCodecType == CodecType::AMR_NB_ENC) {
     mCodec = MediaCodec::CreateByType(mLooper, MEDIA_MIMETYPE_AUDIO_AMR_NB, true);
   } else if (aCodecType == CodecType::AAC_ENC) {
     mCodec = MediaCodec::CreateByType(mLooper, MEDIA_MIMETYPE_AUDIO_AAC, true);
+  } else if (aCodecType == CodecType::EVRC_ENC) {
+    mCodec = MediaCodec::CreateByType(mLooper, MEDIA_MIMETYPE_AUDIO_EVRC, true);
   } else {
     NS_ERROR("Unknown codec type.");
   }
 }
 
 OMXCodecWrapper::~OMXCodecWrapper()
 {
   if (mCodec.get()) {
@@ -628,16 +644,20 @@ OMXAudioEncoder::Configure(int aChannels
     format->setString("mime", MEDIA_MIMETYPE_AUDIO_AAC);
     format->setInt32("aac-profile", OMX_AUDIO_AACObjectLC);
     format->setInt32("bitrate", kAACBitrate);
     format->setInt32("sample-rate", aInputSampleRate);
   } else if (mCodecType == AMR_NB_ENC) {
     format->setString("mime", MEDIA_MIMETYPE_AUDIO_AMR_NB);
     format->setInt32("bitrate", AMRNB_BITRATE);
     format->setInt32("sample-rate", aEncodedSampleRate);
+  } else if (mCodecType == EVRC_ENC) {
+    format->setString("mime", MEDIA_MIMETYPE_AUDIO_EVRC);
+    format->setInt32("bitrate", EVRC_BITRATE);
+    format->setInt32("sample-rate", aEncodedSampleRate);
   } else {
     MOZ_ASSERT(false, "Can't support this codec type!!");
   }
   // Input values.
   format->setInt32("channel-count", aChannels);
 
   status_t result = mCodec->configure(format, nullptr, nullptr,
                                       MediaCodec::CONFIGURE_FLAG_ENCODE);
@@ -1065,16 +1085,28 @@ OMXCodecWrapper::GetNextEncodedFrame(nsT
         0x0,                // decoder version
         0x83, 0xFF,         // mode set: all enabled
         0x00,               // mode change period
         0x01,               // frames per sample
       };
       aOutputBuf->AppendElements(decConfig, sizeof(decConfig));
       outFlags |= MediaCodec::BUFFER_FLAG_CODECCONFIG;
       mAMRCSDProvided = true;
+    } else if ((mCodecType == EVRC_ENC) && !mEVRCCSDProvided){
+      // OMX EVRC codec won't provide csd data, need to generate a fake one.
+      RefPtr<EncodedFrame> audiodata = new EncodedFrame();
+      // Decoder config descriptor
+      const uint8_t decConfig[] = {
+        0x0, 0x0, 0x0, 0x0, // vendor: 4 bytes
+        0x0,                // decoder version
+        0x01,               // frames per sample
+      };
+      aOutputBuf->AppendElements(decConfig, sizeof(decConfig));
+      outFlags |= MediaCodec::BUFFER_FLAG_CODECCONFIG;
+      mEVRCCSDProvided = true;
     } else {
       AppendFrame(aOutputBuf, omxBuf->data(), omxBuf->size());
     }
   }
   mCodec->releaseOutputBuffer(index);
 
   if (aOutputTimestamp) {
     *aOutputTimestamp = outTimeUs;
--- a/dom/media/omx/OMXCodecWrapper.h
+++ b/dom/media/omx/OMXCodecWrapper.h
@@ -83,16 +83,17 @@ class OMXVideoEncoder;
 class OMXCodecWrapper
 {
 public:
   // Codec types.
   enum CodecType {
     AAC_ENC, // AAC encoder.
     AMR_NB_ENC, // AMR_NB encoder.
     AVC_ENC, // AVC/H.264 encoder.
+    EVRC_ENC, // EVRC encoder
     TYPE_COUNT
   };
 
   // Input and output flags.
   enum {
     // For Encode() and Encode, it indicates the end of input stream;
     // For GetNextEncodedFrame(), it indicates the end of output
     // stream.
@@ -115,16 +116,19 @@ public:
   };
 
   /** Create a AAC audio encoder. Returns nullptr when failed. */
   static OMXAudioEncoder* CreateAACEncoder();
 
   /** Create a AMR audio encoder. Returns nullptr when failed. */
   static OMXAudioEncoder* CreateAMRNBEncoder();
 
+  /** Create a EVRC audio encoder. Returns nullptr when failed. */
+  static OMXAudioEncoder* CreateEVRCEncoder();
+
   /** Create a AVC/H.264 video encoder. Returns nullptr when failed. */
   static OMXVideoEncoder* CreateAVCEncoder();
 
   virtual ~OMXCodecWrapper();
 
   /**
    * Get the next available encoded data from MediaCodec. The data will be
    * copied into aOutputBuf array, with its timestamp (in microseconds) in
@@ -199,16 +203,17 @@ private:
   sp<ALooper> mLooper;
 
   Vector<sp<ABuffer> > mInputBufs;  // MediaCodec buffers to hold input data.
   Vector<sp<ABuffer> > mOutputBufs; // MediaCodec buffers to hold output data.
 
   int mCodecType;
   bool mStarted; // Has MediaCodec been started?
   bool mAMRCSDProvided;
+  bool mEVRCCSDProvided;
 };
 
 /**
  * Audio encoder.
  */
 class OMXAudioEncoder final : public OMXCodecWrapper
 {
 public:
--- a/netwerk/mime/nsMimeTypes.h
+++ b/netwerk/mime/nsMimeTypes.h
@@ -79,16 +79,17 @@
 #define AUDIO_OGG                           "audio/ogg"
 #define AUDIO_WAV                           "audio/x-wav"
 #define AUDIO_WEBM                          "audio/webm"
 #define AUDIO_MP3                           "audio/mpeg"
 #define AUDIO_MP4                           "audio/mp4"
 #define AUDIO_AMR                           "audio/amr"
 #define AUDIO_FLAC                          "audio/flac"
 #define AUDIO_3GPP                          "audio/3gpp"
+#define AUDIO_3GPP2                         "audio/3gpp2"
 #define AUDIO_MIDI                          "audio/x-midi"
 #define AUDIO_MATROSKA                      "audio/x-matroska"
 
 #define BINARY_OCTET_STREAM                 "binary/octet-stream"
 
 #define IMAGE_GIF                           "image/gif"
 #define IMAGE_JPEG                          "image/jpeg"
 #define IMAGE_JPG                           "image/jpg"
--- a/uriloader/exthandler/nsExternalHelperAppService.cpp
+++ b/uriloader/exthandler/nsExternalHelperAppService.cpp
@@ -613,16 +613,17 @@ static nsExtraMimeTypeEntry extraMimeEnt
   { AUDIO_WAV, "wav", "Waveform Audio" },
   { VIDEO_3GPP, "3gpp,3gp", "3GPP Video" },
   { VIDEO_3GPP2,"3g2", "3GPP2 Video" },
 #ifdef MOZ_WIDGET_GONK
   // The AUDIO_3GPP has to come after the VIDEO_3GPP entry because the Gallery
   // app on Firefox OS depends on the "3gp" extension mapping to the
   // "video/3gpp" MIME type.
   { AUDIO_3GPP, "3gpp,3gp", "3GPP Audio" },
+  { AUDIO_3GPP2, "3g2", "3GPP2 Audio" },
 #endif
   { AUDIO_MIDI, "mid", "Standard MIDI Audio" }
 };
 
 #undef MAC_TYPE
 
 /**
  * File extensions for which decoding should be disabled.