Bug 842715: add prefs for default getUserMedia resolution r=derf
authorRandell Jesup <rjesup@jesup.org>
Wed, 20 Feb 2013 10:18:54 -0500
changeset 122461 fa3150c558e0eebbc87d53caa7418330574a78db
parent 122460 f04919e7789fc6b7977aec2d3fbd5519dbd9360f
child 122462 4780b6fe2826e6eb2a91b0d3bd70fa116cc3e11e
push id24342
push userryanvm@gmail.com
push dateThu, 21 Feb 2013 13:05:06 +0000
treeherdermozilla-central@702d2814efbf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersderf
bugs842715
milestone22.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 842715: add prefs for default getUserMedia resolution r=derf
content/media/webrtc/MediaEngineDefault.cpp
content/media/webrtc/MediaEngineDefault.h
content/media/webrtc/MediaEngineWebRTC.cpp
content/media/webrtc/MediaEngineWebRTC.h
content/media/webrtc/MediaEngineWebRTCVideo.cpp
--- a/content/media/webrtc/MediaEngineDefault.cpp
+++ b/content/media/webrtc/MediaEngineDefault.cpp
@@ -7,42 +7,42 @@
 #include "nsCOMPtr.h"
 #include "nsDOMFile.h"
 #include "nsILocalFile.h"
 #include "Layers.h"
 #include "ImageContainer.h"
 #include "ImageTypes.h"
 #include "prmem.h"
 
+#include "nsIPrefService.h"
+#include "nsIPrefBranch.h"
+
 #ifdef MOZ_WIDGET_ANDROID
 #include "AndroidBridge.h"
 #include "nsISupportsUtils.h"
 #endif
 
 #define VIDEO_RATE USECS_PER_S
 #define AUDIO_RATE 16000
 
 namespace mozilla {
 
 NS_IMPL_THREADSAFE_ISUPPORTS1(MediaEngineDefaultVideoSource, nsITimerCallback)
 /**
  * Default video source.
  */
 
-// Cannot be initialized in the class definition
-const MediaEngineVideoOptions MediaEngineDefaultVideoSource::mOpts = {
-  DEFAULT_WIDTH,
-  DEFAULT_HEIGHT,
-  DEFAULT_FPS,
-  kVideoCodecI420
-};
-
-MediaEngineDefaultVideoSource::MediaEngineDefaultVideoSource()
+MediaEngineDefaultVideoSource::MediaEngineDefaultVideoSource(int32_t aWidth,
+                                                             int32_t aHeight,
+                                                             int32_t aFPS)
   : mTimer(nullptr)
 {
+  mOpts.mWidth = aWidth;
+  mOpts.mHeight = aHeight;
+  mOpts.mMaxFPS = aFPS;
   mState = kReleased;
 }
 
 MediaEngineDefaultVideoSource::~MediaEngineDefaultVideoSource()
 {}
 
 void
 MediaEngineDefaultVideoSource::GetName(nsAString& aName)
@@ -138,34 +138,35 @@ MediaEngineDefaultVideoSource::Start(Sou
 
   nsRefPtr<layers::Image> image = mImageContainer->CreateImage(&format, 1);
   mImage = static_cast<layers::PlanarYCbCrImage*>(image.get());
 
   layers::PlanarYCbCrImage::Data data;
   // Allocate a single blank Image
   mCb = 16;
   mCr = 16;
-  AllocateSolidColorFrame(data, DEFAULT_WIDTH, DEFAULT_HEIGHT, 0x80, mCb, mCr);
+  AllocateSolidColorFrame(data, mOpts.mWidth, mOpts.mHeight, 0x80, mCb, mCr);
   // SetData copies data, so we can free the frame
   mImage->SetData(data);
   ReleaseFrame(data);
 
   // AddTrack takes ownership of segment
   VideoSegment *segment = new VideoSegment();
-  segment->AppendFrame(image.forget(), USECS_PER_S / DEFAULT_FPS, gfxIntSize(DEFAULT_WIDTH, DEFAULT_HEIGHT));
+  segment->AppendFrame(image.forget(), USECS_PER_S / mOpts.mMaxFPS,
+                       gfxIntSize(mOpts.mWidth, mOpts.mHeight));
   mSource->AddTrack(aID, VIDEO_RATE, 0, segment);
 
   // We aren't going to add any more tracks
   mSource->AdvanceKnownTracksTime(STREAM_TIME_MAX);
 
   // Remember TrackID so we can end it later
   mTrackID = aID;
 
   // Start timer for subsequent frames
-  mTimer->InitWithCallback(this, 1000 / DEFAULT_FPS, nsITimer::TYPE_REPEATING_SLACK);
+  mTimer->InitWithCallback(this, 1000 / mOpts.mMaxFPS, nsITimer::TYPE_REPEATING_SLACK);
   mState = kStarted;
 
   return NS_OK;
 }
 
 nsresult
 MediaEngineDefaultVideoSource::Stop(SourceMediaStream *aSource, TrackID aID)
 {
@@ -237,24 +238,25 @@ MediaEngineDefaultVideoSource::Notify(ns
   }
 
   // Allocate a single solid color image
   ImageFormat format = PLANAR_YCBCR;
   nsRefPtr<layers::Image> image = mImageContainer->CreateImage(&format, 1);
   nsRefPtr<layers::PlanarYCbCrImage> ycbcr_image =
       static_cast<layers::PlanarYCbCrImage*>(image.get());
   layers::PlanarYCbCrImage::Data data;
-  AllocateSolidColorFrame(data, DEFAULT_WIDTH, DEFAULT_HEIGHT, 0x80, mCb, mCr);
+  AllocateSolidColorFrame(data, mOpts.mWidth, mOpts.mHeight, 0x80, mCb, mCr);
   ycbcr_image->SetData(data);
   // SetData copies data, so we can free the frame
   ReleaseFrame(data);
 
   // AddTrack takes ownership of segment
   VideoSegment segment;
-  segment.AppendFrame(ycbcr_image.forget(), USECS_PER_S / DEFAULT_FPS, gfxIntSize(DEFAULT_WIDTH, DEFAULT_HEIGHT));
+  segment.AppendFrame(ycbcr_image.forget(), USECS_PER_S / mOpts.mMaxFPS,
+                      gfxIntSize(mOpts.mWidth, mOpts.mHeight));
   mSource->AppendToTrack(mTrackID, &segment);
 
   return NS_OK;
 }
 
 void
 MediaEngineDefaultVideoSource::NotifyPull(MediaStreamGraph* aGraph,
                                           StreamTime aDesiredTime)
@@ -339,17 +341,18 @@ MediaEngineDefaultAudioSource::Start(Sou
 
   // We aren't going to add any more tracks
   mSource->AdvanceKnownTracksTime(STREAM_TIME_MAX);
 
   // Remember TrackID so we can finish later
   mTrackID = aID;
 
   // 1 Audio frame per Video frame
-  mTimer->InitWithCallback(this, 1000 / MediaEngineDefaultVideoSource::DEFAULT_FPS, nsITimer::TYPE_REPEATING_SLACK);
+  mTimer->InitWithCallback(this, 1000 / MediaEngineDefaultVideoSource::DEFAULT_VIDEO_FPS,
+                           nsITimer::TYPE_REPEATING_SLACK);
   mState = kStarted;
 
   return NS_OK;
 }
 
 nsresult
 MediaEngineDefaultAudioSource::Stop(SourceMediaStream *aSource, TrackID aID)
 {
@@ -388,28 +391,49 @@ MediaEngineDefaultAudioSource::Notify(ns
 }
 
 void
 MediaEngineDefault::EnumerateVideoDevices(nsTArray<nsRefPtr<MediaEngineVideoSource> >* aVSources) {
   MutexAutoLock lock(mMutex);
   int32_t found = false;
   int32_t len = mVSources.Length();
 
+  int32_t width  = MediaEngineDefaultVideoSource::DEFAULT_VIDEO_WIDTH;
+  int32_t height = MediaEngineDefaultVideoSource::DEFAULT_VIDEO_HEIGHT;
+  int32_t fps    = MediaEngineDefaultVideoSource::DEFAULT_VIDEO_FPS;
+
+  // FIX - these should be passed in originating in prefs and/or getUserMedia constraints
+  // Bug 778801
+  nsresult rv;
+  nsCOMPtr<nsIPrefService> prefs = do_GetService("@mozilla.org/preferences-service;1", &rv);
+  if (NS_SUCCEEDED(rv)) {
+    nsCOMPtr<nsIPrefBranch> branch = do_QueryInterface(prefs);
+
+    if (branch) {
+      // these very rarely change
+      branch->GetIntPref("media.navigator.video.default_width", &width);
+      branch->GetIntPref("media.navigator.video.default_height", &height);
+      branch->GetIntPref("media.navigator.video.default_fps", &fps);
+    }
+  }
+
   for (int32_t i = 0; i < len; i++) {
     nsRefPtr<MediaEngineVideoSource> source = mVSources.ElementAt(i);
     aVSources->AppendElement(source);
-    if (source->IsAvailable()) {
+    const MediaEngineVideoOptions *opts = source->GetOptions();
+    if (source->IsAvailable() &&
+        opts->mWidth == width && opts->mHeight == height && opts->mMaxFPS == fps) {
       found = true;
     }
   }
 
-  // All streams are currently busy, just make a new one.
+  // All streams are currently busy (or wrong resolution), just make a new one.
   if (!found) {
     nsRefPtr<MediaEngineVideoSource> newSource =
-      new MediaEngineDefaultVideoSource();
+      new MediaEngineDefaultVideoSource(width, height, fps);
     mVSources.AppendElement(newSource);
     aVSources->AppendElement(newSource);
   }
   return;
 }
 
 void
 MediaEngineDefault::EnumerateAudioDevices(nsTArray<nsRefPtr<MediaEngineAudioSource> >* aASources) {
--- a/content/media/webrtc/MediaEngineDefault.h
+++ b/content/media/webrtc/MediaEngineDefault.h
@@ -20,24 +20,26 @@
 
 namespace mozilla {
 
 namespace layers {
 class ImageContainer;
 class PlanarYCbCrImage;
 }
 
+class MediaEngineDefault;
+
 /**
  * The default implementation of the MediaEngine interface.
  */
 class MediaEngineDefaultVideoSource : public nsITimerCallback,
                                       public MediaEngineVideoSource
 {
 public:
-  MediaEngineDefaultVideoSource();
+  MediaEngineDefaultVideoSource(int aWidth, int aHeight, int aFPS);
   ~MediaEngineDefaultVideoSource();
 
   virtual void GetName(nsAString&);
   virtual void GetUUID(nsAString&);
 
   virtual const MediaEngineVideoOptions *GetOptions();
   virtual nsresult Allocate();
   virtual nsresult Deallocate();
@@ -52,29 +54,31 @@ public:
                           SourceMediaStream *aSource,
                           TrackID aId,
                           StreamTime aDesiredTime,
                           TrackTicks &aLastEndTime) {}
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSITIMERCALLBACK
 
-  // Need something better...
-  static const int DEFAULT_WIDTH=640;
-  static const int DEFAULT_HEIGHT=480;
-  static const int DEFAULT_FPS=30;
+  static const int DEFAULT_VIDEO_FPS = 60;
+  static const int DEFAULT_VIDEO_MIN_FPS = 10;
+  static const int DEFAULT_VIDEO_WIDTH = 640;
+  static const int DEFAULT_VIDEO_HEIGHT = 480;
 
 protected:
+  friend class MediaEngineDefault;
+
   TrackID mTrackID;
   nsCOMPtr<nsITimer> mTimer;
   nsRefPtr<layers::ImageContainer> mImageContainer;
 
   SourceMediaStream* mSource;
   layers::PlanarYCbCrImage* mImage;
-  static const MediaEngineVideoOptions mOpts;
+  MediaEngineVideoOptions mOpts;
   int mCb;
   int mCr;
 };
 
 class MediaEngineDefaultAudioSource : public nsITimerCallback,
                                       public MediaEngineAudioSource
 {
 public:
--- a/content/media/webrtc/MediaEngineWebRTC.cpp
+++ b/content/media/webrtc/MediaEngineWebRTC.cpp
@@ -5,16 +5,19 @@
 #ifdef MOZ_LOGGING
 #define FORCE_PR_LOG
 #endif
 
 #if defined(PR_LOG)
 #error "This file must be #included before any IPDL-generated files or other files that #include prlog.h"
 #endif
 
+#include "nsIPrefService.h"
+#include "nsIPrefBranch.h"
+
 #include "CSFLog.h"
 #include "prenv.h"
 
 #ifdef PR_LOGGING
 static PRLogModuleInfo*
 GetUserMediaLog()
 {
   static PRLogModuleInfo *sLog;
@@ -35,16 +38,46 @@ namespace mozilla {
 void
 MediaEngineWebRTC::EnumerateVideoDevices(nsTArray<nsRefPtr<MediaEngineVideoSource> >* aVSources)
 {
   webrtc::ViEBase* ptrViEBase;
   webrtc::ViECapture* ptrViECapture;
   // We spawn threads to handle gUM runnables, so we must protect the member vars
   MutexAutoLock lock(mMutex);
 
+  int32_t width  = MediaEngineWebRTCVideoSource::DEFAULT_VIDEO_WIDTH;
+  int32_t height = MediaEngineWebRTCVideoSource::DEFAULT_VIDEO_HEIGHT;
+  int32_t fps    = MediaEngineWebRTCVideoSource::DEFAULT_VIDEO_FPS;
+  int32_t minfps = MediaEngineWebRTCVideoSource::DEFAULT_VIDEO_MIN_FPS;
+
+  // FIX - these should be passed in originating in prefs and/or getUserMedia constraints
+  // Bug 778801
+  nsresult rv;
+  nsCOMPtr<nsIPrefService> prefs = do_GetService("@mozilla.org/preferences-service;1", &rv);
+  if (NS_SUCCEEDED(rv)) {
+    nsCOMPtr<nsIPrefBranch> branch = do_QueryInterface(prefs);
+
+    if (branch) {
+      branch->GetIntPref("media.navigator.video.default_width", &width);
+      branch->GetIntPref("media.navigator.video.default_height", &height);
+      branch->GetIntPref("media.navigator.video.default_fps", &fps);
+      branch->GetIntPref("media.navigator.video.default_min_fps", &minfps);
+    }
+  }
+  LOG(("%s: %dx%d @%dfps (min %d)", __FUNCTION__, width, height, fps, minfps));
+
+  bool changed = false;
+  if (width != mWidth || height != mHeight || fps != mFPS || minfps != mMinFPS) {
+    changed = true;
+  }
+  mWidth = width;
+  mHeight = height;
+  mFPS = fps;
+  mMinFPS = minfps;
+
   if (!mVideoEngine) {
     if (!(mVideoEngine = webrtc::VideoEngine::Create())) {
       return;
     }
   }
 
   PRLogModuleInfo *logs = GetWebRTCLogInfo();
   if (!gWebrtcTraceLoggingOn && logs && logs->level > 0) {
@@ -129,21 +162,22 @@ MediaEngineWebRTC::EnumerateVideoDevices
     if (uniqueId[0] == '\0') {
       // In case a device doesn't set uniqueId!
       strncpy(uniqueId, deviceName, sizeof(uniqueId));
 	  uniqueId[sizeof(uniqueId)-1] = '\0'; // strncpy isn't safe
     }
 
     nsRefPtr<MediaEngineWebRTCVideoSource> vSource;
     NS_ConvertUTF8toUTF16 uuid(uniqueId);
-    if (mVideoSources.Get(uuid, getter_AddRefs(vSource))) {
+    if (!changed && mVideoSources.Get(uuid, getter_AddRefs(vSource))) {
       // We've already seen this device, just append.
       aVSources->AppendElement(vSource.get());
     } else {
-      vSource = new MediaEngineWebRTCVideoSource(mVideoEngine, i);
+      vSource = new MediaEngineWebRTCVideoSource(mVideoEngine, i,
+                                                 width, height, fps, minfps);
       mVideoSources.Put(uuid, vSource); // Hashtable takes ownership.
       aVSources->AppendElement(vSource);
     }
   }
 
   ptrViEBase->Release();
   ptrViECapture->Release();
 
--- a/content/media/webrtc/MediaEngineWebRTC.h
+++ b/content/media/webrtc/MediaEngineWebRTC.h
@@ -52,32 +52,36 @@ namespace mozilla {
  * The WebRTC implementation of the MediaEngine interface.
  */
 class MediaEngineWebRTCVideoSource : public MediaEngineVideoSource,
                                      public webrtc::ExternalRenderer,
                                      public nsRunnable
 {
 public:
   static const int DEFAULT_VIDEO_FPS = 60;
-  static const int DEFAULT_MIN_VIDEO_FPS = 10;
+  static const int DEFAULT_VIDEO_MIN_FPS = 10;
+  static const int DEFAULT_VIDEO_WIDTH = 640;
+  static const int DEFAULT_VIDEO_HEIGHT = 480;
 
   // ViEExternalRenderer.
   virtual int FrameSizeChange(unsigned int, unsigned int, unsigned int);
   virtual int DeliverFrame(unsigned char*, int, uint32_t, int64_t);
 
   MediaEngineWebRTCVideoSource(webrtc::VideoEngine* aVideoEnginePtr,
-    int aIndex, int aMinFps = DEFAULT_MIN_VIDEO_FPS)
+    int aIndex,
+    int aWidth = DEFAULT_VIDEO_WIDTH, int aHeight = DEFAULT_VIDEO_HEIGHT,
+    int aFps = DEFAULT_VIDEO_FPS, int aMinFps = DEFAULT_VIDEO_MIN_FPS)
     : mVideoEngine(aVideoEnginePtr)
     , mCaptureIndex(aIndex)
     , mCapabilityChosen(false)
-    , mWidth(640)
-    , mHeight(480)
+    , mWidth(aWidth)
+    , mHeight(aHeight)
     , mLastEndTime(0)
     , mMonitor("WebRTCCamera.Monitor")
-    , mFps(DEFAULT_VIDEO_FPS)
+    , mFps(aFps)
     , mMinFps(aMinFps)
     , mInitDone(false)
     , mInSnapshotMode(false)
     , mSnapshotPath(NULL) {
     MOZ_ASSERT(aVideoEnginePtr);
     mState = kReleased;
     Init();
   }
@@ -282,16 +286,19 @@ private:
 
   webrtc::VideoEngine* mVideoEngine;
   webrtc::VoiceEngine* mVoiceEngine;
 
   // Need this to avoid unneccesary WebRTC calls while enumerating.
   bool mVideoEngineInit;
   bool mAudioEngineInit;
 
+  // the last set of selection vars for video sources
+  int mHeight, mWidth, mFPS, mMinFPS;
+
   // Store devices we've already seen in a hashtable for quick return.
   // Maps UUID to MediaEngineSource (one set for audio, one for video).
   nsRefPtrHashtable<nsStringHashKey, MediaEngineWebRTCVideoSource > mVideoSources;
   nsRefPtrHashtable<nsStringHashKey, MediaEngineWebRTCAudioSource > mAudioSources;
 };
 
 }
 
--- a/content/media/webrtc/MediaEngineWebRTCVideo.cpp
+++ b/content/media/webrtc/MediaEngineWebRTCVideo.cpp
@@ -26,16 +26,17 @@ NS_IMPL_THREADSAFE_ISUPPORTS1(MediaEngin
 
 // ViEExternalRenderer Callback.
 int
 MediaEngineWebRTCVideoSource::FrameSizeChange(
    unsigned int w, unsigned int h, unsigned int streams)
 {
   mWidth = w;
   mHeight = h;
+  LOG(("Video FrameSizeChange: %ux%u", w, h));
   return 0;
 }
 
 // ViEExternalRenderer Callback. Process every incoming frame here.
 int
 MediaEngineWebRTCVideoSource::DeliverFrame(
    unsigned char* buffer, int size, uint32_t time_stamp, int64_t render_time)
 {
@@ -77,17 +78,18 @@ MediaEngineWebRTCVideoSource::DeliverFra
   data.mPicY = 0;
   data.mPicSize = gfxIntSize(mWidth, mHeight);
   data.mStereoMode = STEREO_MODE_MONO;
 
   videoImage->SetData(data);
 
 #ifdef DEBUG
   static uint32_t frame_num = 0;
-  LOGFRAME(("frame %d; timestamp %u, render_time %lu", frame_num++, time_stamp, render_time));
+  LOGFRAME(("frame %d (%dx%d); timestamp %u, render_time %lu", frame_num++,
+            mWidth, mHeight, time_stamp, render_time));
 #endif
 
   // we don't touch anything in 'this' until here (except for snapshot,
   // which has it's own lock)
   ReentrantMonitorAutoEnter enter(mMonitor);
 
   // implicitly releases last image
   mImage = image.forget();
@@ -173,16 +175,17 @@ MediaEngineWebRTCVideoSource::ChooseCapa
         mOpts.mWidth = cap.width;
         mOpts.mHeight = cap.height;
         mOpts.mMaxFPS = cap.maxFPS;
         mCapability = cap;
         // FIXME: expose expected capture delay?
       }
     }
   }
+  LOG(("chose cap %dx%d @%dfps", mOpts.mWidth, mOpts.mHeight, mOpts.mMaxFPS));
   mCapabilityChosen = true;
 }
 
 void
 MediaEngineWebRTCVideoSource::GetName(nsAString& aName)
 {
   // mDeviceName is UTF8
   CopyUTF8toUTF16(mDeviceName, aName);