Bug 1161276: part 2 - pass bitrates to track encoders for MediaRecorder r=roc
☠☠ backed out by 9442eb6fda7e ☠ ☠
authorMaire Reavy <mreavy@mozilla.com>
Mon, 08 Jun 2015 11:47:28 +0800
changeset 295666 4e8f28b173005afe52592738e8848d56f6a3a54d
parent 295665 cf05881e89ed313051a69a3e18e5c06c8bd909b5
child 295667 9187f4d2518fcba32aaa124ea1c0abff397f0a64
push id5245
push userraliiev@mozilla.com
push dateThu, 29 Oct 2015 11:30:51 +0000
treeherdermozilla-beta@dac831dc1bd0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs1161276
milestone43.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 1161276: part 2 - pass bitrates to track encoders for MediaRecorder r=roc
dom/media/MediaRecorder.cpp
dom/media/MediaRecorder.h
dom/media/encoder/MediaEncoder.cpp
dom/media/encoder/MediaEncoder.h
dom/media/encoder/OpusTrackEncoder.cpp
dom/media/encoder/TrackEncoder.h
dom/media/encoder/VP8TrackEncoder.cpp
dom/media/encoder/VorbisTrackEncoder.cpp
--- a/dom/media/MediaRecorder.cpp
+++ b/dom/media/MediaRecorder.cpp
@@ -590,19 +590,27 @@ private:
       LOG(LogLevel::Debug, ("Session.InitEncoder failure, mRecorder is null %p", this));
       return;
     }
     // Allocate encoder and bind with union stream.
     // At this stage, the API doesn't allow UA to choose the output mimeType format.
 
     // 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), aTrackTypes);
+      mEncoder = MediaEncoder::CreateEncoder(NS_LITERAL_STRING(AUDIO_3GPP),
+                                             mRecorder->GetAudioBitrate(),
+                                             mRecorder->GetVideoBitrate(),
+                                             mRecorder->GetBitrate(),
+                                             aTrackTypes);
     } else {
-      mEncoder = MediaEncoder::CreateEncoder(NS_LITERAL_STRING(""), aTrackTypes);
+      mEncoder = MediaEncoder::CreateEncoder(NS_LITERAL_STRING(""),
+                                             mRecorder->GetAudioBitrate(),
+                                             mRecorder->GetVideoBitrate(),
+                                             mRecorder->GetBitrate(),
+                                             aTrackTypes);
     }
 
     if (!mEncoder) {
       LOG(LogLevel::Debug, ("Session.InitEncoder !mEncoder %p", this));
       DoSessionEndTask(NS_ERROR_ABORT);
       return;
     }
 
@@ -1010,16 +1018,27 @@ MediaRecorder::SetOptions(const MediaRec
 {
   SetMimeType(aInitDict.mMimeType);
   mAudioBitsPerSecond = aInitDict.mAudioBitsPerSecond.WasPassed() ?
                         aInitDict.mAudioBitsPerSecond.Value() : 0;
   mVideoBitsPerSecond = aInitDict.mVideoBitsPerSecond.WasPassed() ?
                         aInitDict.mVideoBitsPerSecond.Value() : 0;
   mBitsPerSecond = aInitDict.mBitsPerSecond.WasPassed() ?
                    aInitDict.mBitsPerSecond.Value() : 0;
+  // We're not handling dynamic changes yet. Eventually we'll handle
+  // setting audio, video and/or total -- and anything that isn't set,
+  // we'll derive. Calculated versions require querying bitrates after
+  // the encoder is Init()ed. This happens only after data is
+  // available and thus requires dynamic changes.
+  //
+  // Until dynamic changes are supported, I prefer to be safe and err
+  // slightly high
+  if (aInitDict.mBitsPerSecond.WasPassed() && !aInitDict.mVideoBitsPerSecond.WasPassed()) {
+    mVideoBitsPerSecond = mBitsPerSecond;
+  }
 }
 
 nsresult
 MediaRecorder::CreateAndDispatchBlobEvent(already_AddRefed<nsIDOMBlob>&& aBlob)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Not running on main thread");
   if (!CheckPrincipal()) {
     // Media is not same-origin, don't allow the data out.
--- a/dom/media/MediaRecorder.h
+++ b/dom/media/MediaRecorder.h
@@ -99,16 +99,19 @@ public:
   IMPL_EVENT_HANDLER(dataavailable)
   IMPL_EVENT_HANDLER(error)
   IMPL_EVENT_HANDLER(start)
   IMPL_EVENT_HANDLER(stop)
   IMPL_EVENT_HANDLER(warning)
 
   NS_DECL_NSIDOCUMENTACTIVITY
 
+  uint32_t GetAudioBitrate() { return mAudioBitsPerSecond; }
+  uint32_t GetVideoBitrate() { return mVideoBitsPerSecond; }
+  uint32_t GetBitrate() { return mBitsPerSecond; }
 protected:
   virtual ~MediaRecorder();
 
   MediaRecorder& operator = (const MediaRecorder& x) = delete;
   // Create dataavailable event with Blob data and it runs in main thread
   nsresult CreateAndDispatchBlobEvent(already_AddRefed<nsIDOMBlob>&& aBlob);
   // Creating a simple event to notify UA simple event.
   void DispatchSimpleEvent(const nsAString & aStr);
--- a/dom/media/encoder/MediaEncoder.cpp
+++ b/dom/media/encoder/MediaEncoder.cpp
@@ -68,17 +68,19 @@ MediaEncoder::NotifyEvent(MediaStreamGra
   }
   if (mVideoEncoder) {
     mVideoEncoder->NotifyEvent(aGraph, event);
   }
 }
 
 /* static */
 already_AddRefed<MediaEncoder>
-MediaEncoder::CreateEncoder(const nsAString& aMIMEType, uint8_t aTrackTypes)
+MediaEncoder::CreateEncoder(const nsAString& aMIMEType, uint32_t aAudioBitrate,
+                            uint32_t aVideoBitrate, uint32_t aBitrate,
+                            uint8_t aTrackTypes)
 {
   if (!gMediaEncoderLog) {
     gMediaEncoderLog = PR_NewLogModule("MediaEncoder");
   }
   PROFILER_LABEL("MediaEncoder", "CreateEncoder",
     js::ProfileEntry::Category::OTHER);
 
   nsAutoPtr<ContainerWriter> writer;
@@ -139,18 +141,25 @@ MediaEncoder::CreateEncoder(const nsAStr
   }
   else {
     LOG(LogLevel::Error, ("Can not find any encoder to record this media stream"));
     return nullptr;
   }
   LOG(LogLevel::Debug, ("Create encoder result:a[%d] v[%d] w[%d] mimeType = %s.",
                       audioEncoder != nullptr, videoEncoder != nullptr,
                       writer != nullptr, mimeType.get()));
+  if (videoEncoder && aVideoBitrate != 0) {
+    videoEncoder->SetBitrate(aVideoBitrate);
+  }
+  if (audioEncoder && aAudioBitrate != 0) {
+    audioEncoder->SetBitrate(aAudioBitrate);
+  }
   encoder = new MediaEncoder(writer.forget(), audioEncoder.forget(),
-                             videoEncoder.forget(), mimeType);
+                             videoEncoder.forget(), mimeType, aAudioBitrate,
+                             aVideoBitrate, aBitrate);
   return encoder.forget();
 }
 
 /**
  * GetEncodedData() runs as a state machine, starting with mState set to
  * GET_METADDATA, the procedure should be as follow:
  *
  * While non-stop
--- a/dom/media/encoder/MediaEncoder.h
+++ b/dom/media/encoder/MediaEncoder.h
@@ -57,17 +57,20 @@ public :
     ENCODE_TRACK,
     ENCODE_DONE,
     ENCODE_ERROR,
   };
 
   MediaEncoder(ContainerWriter* aWriter,
                AudioTrackEncoder* aAudioEncoder,
                VideoTrackEncoder* aVideoEncoder,
-               const nsAString& aMIMEType)
+               const nsAString& aMIMEType,
+               uint32_t aAudioBitrate,
+               uint32_t aVideoBitrate,
+               uint32_t aBitrate)
     : mWriter(aWriter)
     , mAudioEncoder(aAudioEncoder)
     , mVideoEncoder(aVideoEncoder)
     , mStartTime(TimeStamp::Now())
     , mMIMEType(aMIMEType)
     , mSizeOfBuffer(0)
     , mState(MediaEncoder::ENCODE_METADDATA)
     , mShutdown(false)
@@ -91,16 +94,18 @@ public :
                            MediaStreamListener::MediaStreamGraphEvent event) override;
 
   /**
    * Creates an encoder with a given MIME type. Returns null if we are unable
    * to create the encoder. For now, default aMIMEType to "audio/ogg" and use
    * Ogg+Opus if it is empty.
    */
   static already_AddRefed<MediaEncoder> CreateEncoder(const nsAString& aMIMEType,
+                                                      uint32_t aAudioBitrate, uint32_t aVideoBitrate,
+                                                      uint32_t aBitrate,
                                                       uint8_t aTrackTypes = ContainerWriter::CREATE_AUDIO_TRACK);
   /**
    * Encodes the raw track data and returns the final container data. Assuming
    * it is called on a single worker thread. The buffer of container data is
    * allocated in ContainerWriter::GetContainerData(), and is appended to
    * aOutputBufs. aMIMEType is the valid mime-type of this returned container
    * data.
    */
--- a/dom/media/encoder/OpusTrackEncoder.cpp
+++ b/dom/media/encoder/OpusTrackEncoder.cpp
@@ -182,18 +182,23 @@ OpusTrackEncoder::Init(int aChannels, in
   }
   mSamplingRate = aSamplingRate;
   NS_ENSURE_TRUE(mSamplingRate > 0, NS_ERROR_FAILURE);
 
   int error = 0;
   mEncoder = opus_encoder_create(GetOutputSampleRate(), mChannels,
                                  OPUS_APPLICATION_AUDIO, &error);
 
+
   mInitialized = (error == OPUS_OK);
 
+  if (mAudioBitrate) {
+    opus_encoder_ctl(mEncoder, OPUS_SET_BITRATE(static_cast<int>(mAudioBitrate)));
+  }
+
   mReentrantMonitor.NotifyAll();
 
   return error == OPUS_OK ? NS_OK : NS_ERROR_FAILURE;
 }
 
 int
 OpusTrackEncoder::GetOutputSampleRate()
 {
--- a/dom/media/encoder/TrackEncoder.h
+++ b/dom/media/encoder/TrackEncoder.h
@@ -79,16 +79,18 @@ public:
    */
   void NotifyCancel()
   {
     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     mCanceled = true;
     mReentrantMonitor.NotifyAll();
   }
 
+  virtual void SetBitrate(const uint32_t aBitrate) {}
+
 protected:
   /**
    * Notifies track encoder that we have reached the end of source stream, and
    * wakes up mReentrantMonitor if encoder is waiting for any source data.
    */
   virtual void NotifyEndOfStream() = 0;
 
   /**
@@ -136,16 +138,17 @@ protected:
 
 class AudioTrackEncoder : public TrackEncoder
 {
 public:
   AudioTrackEncoder()
     : TrackEncoder()
     , mChannels(0)
     , mSamplingRate(0)
+    , mAudioBitrate(0)
   {}
 
   virtual void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
                                         StreamTime aTrackOffset,
                                         uint32_t aTrackEvents,
                                         const MediaSegment& aQueuedMedia) override;
 
   template<typename T>
@@ -186,16 +189,20 @@ public:
    */
   static void DeInterleaveTrackData(AudioDataValue* aInput, int32_t aDuration,
                                     int32_t aChannels, AudioDataValue* aOutput);
   /**
   * Measure size of mRawSegment
   */
   size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
 
+  virtual void SetBitrate(const uint32_t aBitrate) override
+  {
+    mAudioBitrate = aBitrate;
+  }
 protected:
   /**
    * Number of samples per channel in a pcm buffer. This is also the value of
    * frame size required by audio encoder, and mReentrantMonitor will be
    * notified when at least this much data has been added to mRawSegment.
    */
   virtual int GetPacketDuration() { return 0; }
 
@@ -234,44 +241,51 @@ protected:
    * The sampling rate of source audio data.
    */
   int mSamplingRate;
 
   /**
    * A segment queue of audio track data, protected by mReentrantMonitor.
    */
   AudioSegment mRawSegment;
+
+  uint32_t mAudioBitrate;
 };
 
 class VideoTrackEncoder : public TrackEncoder
 {
 public:
   VideoTrackEncoder()
     : TrackEncoder()
     , mFrameWidth(0)
     , mFrameHeight(0)
     , mDisplayWidth(0)
     , mDisplayHeight(0)
     , mTrackRate(0)
     , mTotalFrameDuration(0)
+    , mVideoBitrate(0)
   {}
 
   /**
    * Notified by the same callback of MediaEncoder when it has received a track
    * change from MediaStreamGraph. Called on the MediaStreamGraph thread.
    */
   virtual void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
                                         StreamTime aTrackOffset,
                                         uint32_t aTrackEvents,
                                         const MediaSegment& aQueuedMedia) override;
   /**
   * Measure size of mRawSegment
   */
   size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
 
+  virtual void SetBitrate(const uint32_t aBitrate) override
+  {
+    mVideoBitrate = aBitrate;
+  }
 protected:
   /**
    * Initialized the video encoder. In order to collect the value of width and
    * height of source frames, this initialization is delayed until we have
    * received the first valid video frame from MediaStreamGraph;
    * mReentrantMonitor will be notified after it has successfully initialized,
    * and this method is called on the MediaStramGraph thread.
    */
@@ -327,13 +341,15 @@ protected:
    * subclasses.
    */
   VideoFrame mLastFrame;
 
   /**
    * A segment queue of audio track data, protected by mReentrantMonitor.
    */
   VideoSegment mRawSegment;
+
+  uint32_t mVideoBitrate;
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/encoder/VP8TrackEncoder.cpp
+++ b/dom/media/encoder/VP8TrackEncoder.cpp
@@ -15,17 +15,17 @@
 
 namespace mozilla {
 
 PRLogModuleInfo* gVP8TrackEncoderLog;
 #define VP8LOG(msg, ...) MOZ_LOG(gVP8TrackEncoderLog, mozilla::LogLevel::Debug, \
                                   (msg, ##__VA_ARGS__))
 // Debug logging macro with object pointer and class name.
 
-#define DEFAULT_BITRATE 2500 // in kbit/s
+#define DEFAULT_BITRATE_BPS 2500000
 #define DEFAULT_ENCODE_FRAMERATE 30
 
 using namespace mozilla::layers;
 
 VP8TrackEncoder::VP8TrackEncoder()
   : VideoTrackEncoder()
   , mEncodedFrameDuration(0)
   , mEncodedTimestamp(0)
@@ -82,17 +82,19 @@ VP8TrackEncoder::Init(int32_t aWidth, in
   // (actual memory is not allocated).
   vpx_img_wrap(mVPXImageWrapper, VPX_IMG_FMT_I420,
                mFrameWidth, mFrameHeight, 1, nullptr);
 
   config.g_w = mFrameWidth;
   config.g_h = mFrameHeight;
   // TODO: Maybe we should have various aFrameRate bitrate pair for each devices?
   // or for different platform
-  config.rc_target_bitrate = DEFAULT_BITRATE; // in kbit/s
+
+  // rc_target_bitrate needs kbit/s
+  config.rc_target_bitrate = (mVideoBitrate != 0 ? mVideoBitrate : DEFAULT_BITRATE_BPS)/1000;
 
   // Setting the time base of the codec
   config.g_timebase.num = 1;
   config.g_timebase.den = mTrackRate;
 
   config.g_error_resilient = 0;
 
   config.g_lag_in_frames = 0; // 0- no frame lagging
--- a/dom/media/encoder/VorbisTrackEncoder.cpp
+++ b/dom/media/encoder/VorbisTrackEncoder.cpp
@@ -51,19 +51,22 @@ VorbisTrackEncoder::Init(int aChannels, 
   // This monitor is used to wake up other methods that are waiting for encoder
   // to be completely initialized.
   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
   mChannels = aChannels;
   mSamplingRate = aSamplingRate;
 
   int ret = 0;
   vorbis_info_init(&mVorbisInfo);
+  double quality = mAudioBitrate ? (double)mAudioBitrate/aSamplingRate :
+                   BASE_QUALITY;
 
+  printf("quality %f \n", quality);
   ret = vorbis_encode_init_vbr(&mVorbisInfo, mChannels, mSamplingRate,
-                               BASE_QUALITY);
+                               quality);
 
   mInitialized = (ret == 0);
 
   if (mInitialized) {
     // Set up the analysis state and auxiliary encoding storage
     vorbis_analysis_init(&mVorbisDsp, &mVorbisInfo);
     vorbis_block_init(&mVorbisDsp, &mVorbisBlock);
   }