Bug 795379 - Add support for getting and setting video recording profiles. r=jst, a=blocking-basecamp
authorMike Habicher <mikeh@mozilla.com>
Tue, 23 Oct 2012 18:30:28 -0400
changeset 113615 c6d650bf29adb92131aab7106b68ed1822247ccd
parent 113614 13a355eeeff88f52d075781d19f30e2b9a6d11f7
child 113616 578577f79163868785bb0f491ce015cf5cab3c45
push id2427
push userryanvm@gmail.com
push dateWed, 24 Oct 2012 20:28:34 +0000
treeherdermozilla-aurora@c6d650bf29ad [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjst, blocking-basecamp
bugs795379
milestone18.0a2
Bug 795379 - Add support for getting and setting video recording profiles. r=jst, a=blocking-basecamp
dom/camera/CameraControlImpl.cpp
dom/camera/CameraControlImpl.h
dom/camera/CameraRecorderProfiles.cpp
dom/camera/CameraRecorderProfiles.h
dom/camera/DOMCameraCapabilities.cpp
dom/camera/DOMCameraControl.cpp
dom/camera/FallbackCameraControl.cpp
dom/camera/GonkCameraControl.cpp
dom/camera/GonkCameraControl.h
dom/camera/GonkRecorderProfiles.cpp
dom/camera/GonkRecorderProfiles.def
dom/camera/GonkRecorderProfiles.h
dom/camera/ICameraControl.h
dom/camera/Makefile.in
dom/camera/nsIDOMCameraManager.idl
--- a/dom/camera/CameraControlImpl.cpp
+++ b/dom/camera/CameraControlImpl.cpp
@@ -1,19 +1,45 @@
 /* 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 "base/basictypes.h"
 #include "DOMCameraPreview.h"
+#include "CameraRecorderProfiles.h"
 #include "CameraControlImpl.h"
 #include "CameraCommon.h"
 
 using namespace mozilla;
 
+CameraControlImpl::CameraControlImpl(uint32_t aCameraId, nsIThread* aCameraThread, uint64_t aWindowId)
+  : mCameraId(aCameraId)
+  , mCameraThread(aCameraThread)
+  , mWindowId(aWindowId)
+  , mFileFormat()
+  , mMaxMeteringAreas(0)
+  , mMaxFocusAreas(0)
+  , mDOMPreview(nullptr)
+  , mAutoFocusOnSuccessCb(nullptr)
+  , mAutoFocusOnErrorCb(nullptr)
+  , mTakePictureOnSuccessCb(nullptr)
+  , mTakePictureOnErrorCb(nullptr)
+  , mStartRecordingOnSuccessCb(nullptr)
+  , mStartRecordingOnErrorCb(nullptr)
+  , mOnShutterCb(nullptr)
+  , mOnClosedCb(nullptr)
+{
+  DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+}
+
+CameraControlImpl::~CameraControlImpl()
+{
+  DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+}
+
 // Helpers for string properties.
 nsresult
 CameraControlImpl::Set(uint32_t aKey, const nsAString& aValue)
 {
   SetParameter(aKey, NS_ConvertUTF16toUTF8(aValue).get());
   return NS_OK;
 }
 
@@ -190,16 +216,22 @@ CameraControlImpl::Set(nsICameraClosedCa
 
 nsresult
 CameraControlImpl::Get(nsICameraClosedCallback** aOnClosed)
 {
   *aOnClosed = mOnClosedCb;
   return NS_OK;
 }
 
+already_AddRefed<RecorderProfileManager>
+CameraControlImpl::GetRecorderProfileManager()
+{
+  return GetRecorderProfileManagerImpl();
+}
+
 void
 CameraControlImpl::Shutdown()
 {
   DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
   mAutoFocusOnSuccessCb = nullptr;
   mAutoFocusOnErrorCb = nullptr;
   mTakePictureOnSuccessCb = nullptr;
   mTakePictureOnErrorCb = nullptr;
@@ -271,19 +303,22 @@ CameraControlImpl::AutoFocus(nsICameraAu
 nsresult
 CameraControlImpl::TakePicture(CameraSize aSize, int32_t aRotation, const nsAString& aFileFormat, CameraPosition aPosition, nsICameraTakePictureCallback* onSuccess, nsICameraErrorCallback* onError)
 {
   nsCOMPtr<nsIRunnable> takePictureTask = new TakePictureTask(this, aSize, aRotation, aFileFormat, aPosition, onSuccess, onError);
   return mCameraThread->Dispatch(takePictureTask, NS_DISPATCH_NORMAL);
 }
 
 nsresult
-CameraControlImpl::StartRecording(nsIDOMDeviceStorage* aStorageArea, const nsAString& aFilename, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError)
+CameraControlImpl::StartRecording(CameraStartRecordingOptions* aOptions, nsIFile* aFolder, const nsAString& aFilename, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError)
 {
-  nsCOMPtr<nsIRunnable> startRecordingTask = new StartRecordingTask(this, aStorageArea, aFilename, onSuccess, onError);
+  nsCOMPtr<nsIFile> clone;
+  aFolder->Clone(getter_AddRefs(clone));
+
+  nsCOMPtr<nsIRunnable> startRecordingTask = new StartRecordingTask(this, *aOptions, clone, aFilename, onSuccess, onError, mWindowId);
   return mCameraThread->Dispatch(startRecordingTask, NS_DISPATCH_NORMAL);
 }
 
 nsresult
 CameraControlImpl::StopRecording()
 {
   nsCOMPtr<nsIRunnable> stopRecordingTask = new StopRecordingTask(this);
   return mCameraThread->Dispatch(stopRecordingTask, NS_DISPATCH_NORMAL);
@@ -299,17 +334,17 @@ CameraControlImpl::StartPreview(DOMCamer
 void
 CameraControlImpl::StopPreview()
 {
   nsCOMPtr<nsIRunnable> stopPreviewTask = new StopPreviewTask(this);
   mCameraThread->Dispatch(stopPreviewTask, NS_DISPATCH_NORMAL);
 }
 
 nsresult
-CameraControlImpl::GetPreviewStreamVideoMode(CameraRecordingOptions* aOptions, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError)
+CameraControlImpl::GetPreviewStreamVideoMode(CameraRecorderOptions* aOptions, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError)
 {
   nsCOMPtr<nsIRunnable> getPreviewStreamVideoModeTask = new GetPreviewStreamVideoModeTask(this, *aOptions, onSuccess, onError);
   return mCameraThread->Dispatch(getPreviewStreamVideoModeTask, NS_DISPATCH_NORMAL);
 }
 
 bool
 CameraControlImpl::ReceiveFrame(void* aBuffer, ImageFormat aFormat, FrameBuilder aBuilder)
 {
--- a/dom/camera/CameraControlImpl.h
+++ b/dom/camera/CameraControlImpl.h
@@ -5,16 +5,17 @@
 #ifndef DOM_CAMERA_CAMERACONTROLIMPL_H
 #define DOM_CAMERA_CAMERACONTROLIMPL_H
 
 #include "nsCOMPtr.h"
 #include "nsDOMFile.h"
 #include "DictionaryHelpers.h"
 #include "nsIDOMDeviceStorage.h"
 #include "DOMCameraManager.h"
+#include "DOMCameraPreview.h"
 #include "ICameraControl.h"
 #include "CameraCommon.h"
 
 namespace mozilla {
 
 using namespace dom;
 
 class GetPreviewStreamTask;
@@ -24,59 +25,42 @@ class AutoFocusTask;
 class TakePictureTask;
 class StartRecordingTask;
 class StopRecordingTask;
 class SetParameterTask;
 class GetParameterTask;
 class GetPreviewStreamVideoModeTask;
 
 class DOMCameraPreview;
+class RecorderProfileManager;
 
 class CameraControlImpl : public ICameraControl
 {
   friend class GetPreviewStreamTask;
   friend class StartPreviewTask;
   friend class StopPreviewTask;
   friend class AutoFocusTask;
   friend class TakePictureTask;
   friend class StartRecordingTask;
   friend class StopRecordingTask;
   friend class SetParameterTask;
   friend class GetParameterTask;
   friend class GetPreviewStreamVideoModeTask;
 
 public:
-  CameraControlImpl(uint32_t aCameraId, nsIThread* aCameraThread, uint64_t aWindowId)
-    : mCameraId(aCameraId)
-    , mCameraThread(aCameraThread)
-    , mWindowId(aWindowId)
-    , mFileFormat()
-    , mMaxMeteringAreas(0)
-    , mMaxFocusAreas(0)
-    , mDOMPreview(nullptr)
-    , mAutoFocusOnSuccessCb(nullptr)
-    , mAutoFocusOnErrorCb(nullptr)
-    , mTakePictureOnSuccessCb(nullptr)
-    , mTakePictureOnErrorCb(nullptr)
-    , mStartRecordingOnSuccessCb(nullptr)
-    , mStartRecordingOnErrorCb(nullptr)
-    , mOnShutterCb(nullptr)
-    , mOnClosedCb(nullptr)
-  {
-    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
-  }
+  CameraControlImpl(uint32_t aCameraId, nsIThread* aCameraThread, uint64_t aWindowId);
 
   nsresult GetPreviewStream(CameraSize aSize, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError);
   nsresult StartPreview(DOMCameraPreview* aDOMPreview);
   void StopPreview();
   nsresult AutoFocus(nsICameraAutoFocusCallback* onSuccess, nsICameraErrorCallback* onError);
   nsresult TakePicture(CameraSize aSize, int32_t aRotation, const nsAString& aFileFormat, CameraPosition aPosition, nsICameraTakePictureCallback* onSuccess, nsICameraErrorCallback* onError);
-  nsresult StartRecording(nsIDOMDeviceStorage* aStorageArea, const nsAString& aFilename, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError);
+  nsresult StartRecording(CameraStartRecordingOptions* aOptions, nsIFile* aFolder, const nsAString& aFilename, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError);
   nsresult StopRecording();
-  nsresult GetPreviewStreamVideoMode(CameraRecordingOptions* aOptions, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError);
+  nsresult GetPreviewStreamVideoMode(CameraRecorderOptions* aOptions, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError);
 
   nsresult Set(uint32_t aKey, const nsAString& aValue);
   nsresult Get(uint32_t aKey, nsAString& aValue);
   nsresult Set(uint32_t aKey, double aValue);
   nsresult Get(uint32_t aKey, double* aValue);
   nsresult Set(JSContext* aCx, uint32_t aKey, const JS::Value& aValue, uint32_t aLimit);
   nsresult Get(JSContext* aCx, uint32_t aKey, JS::Value* aValue);
   nsresult Set(nsICameraShutterCallback* aOnShutter);
@@ -89,52 +73,54 @@ public:
     return Set(aCx, CAMERA_PARAM_FOCUSAREAS, aValue, mMaxFocusAreas);
   }
 
   nsresult SetMeteringAreas(JSContext* aCx, const JS::Value& aValue)
   {
     return Set(aCx, CAMERA_PARAM_METERINGAREAS, aValue, mMaxMeteringAreas);
   }
 
+  already_AddRefed<RecorderProfileManager> GetRecorderProfileManager();
+  uint32_t GetCameraId() { return mCameraId; }
+
   virtual const char* GetParameter(const char* aKey) = 0;
   virtual const char* GetParameterConstChar(uint32_t aKey) = 0;
   virtual double GetParameterDouble(uint32_t aKey) = 0;
   virtual void GetParameter(uint32_t aKey, nsTArray<CameraRegion>& aRegions) = 0;
   virtual void SetParameter(const char* aKey, const char* aValue) = 0;
   virtual void SetParameter(uint32_t aKey, const char* aValue) = 0;
   virtual void SetParameter(uint32_t aKey, double aValue) = 0;
   virtual void SetParameter(uint32_t aKey, const nsTArray<CameraRegion>& aRegions) = 0;
+  virtual nsresult GetVideoSizes(nsTArray<CameraSize>& aVideoSizes) = 0;
   virtual nsresult PushParameters() = 0;
   virtual void Shutdown();
 
   bool ReceiveFrame(void* aBuffer, ImageFormat aFormat, FrameBuilder aBuilder);
   void OnShutter();
   void OnClosed();
 
   uint64_t GetWindowId()
   {
     return mWindowId;
   }
 
 protected:
-  virtual ~CameraControlImpl()
-  {
-    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
-  }
+  virtual ~CameraControlImpl();
 
   virtual nsresult GetPreviewStreamImpl(GetPreviewStreamTask* aGetPreviewStream) = 0;
   virtual nsresult StartPreviewImpl(StartPreviewTask* aStartPreview) = 0;
   virtual nsresult StopPreviewImpl(StopPreviewTask* aStopPreview) = 0;
   virtual nsresult AutoFocusImpl(AutoFocusTask* aAutoFocus) = 0;
   virtual nsresult TakePictureImpl(TakePictureTask* aTakePicture) = 0;
   virtual nsresult StartRecordingImpl(StartRecordingTask* aStartRecording) = 0;
   virtual nsresult StopRecordingImpl(StopRecordingTask* aStopRecording) = 0;
   virtual nsresult PushParametersImpl() = 0;
   virtual nsresult PullParametersImpl() = 0;
   virtual nsresult GetPreviewStreamVideoModeImpl(GetPreviewStreamVideoModeTask* aGetPreviewStreamVideoMode) = 0;
+  virtual already_AddRefed<RecorderProfileManager> GetRecorderProfileManagerImpl() = 0;
 
   void OnShutterInternal();
   void OnClosedInternal();
 
   uint32_t            mCameraId;
   nsCOMPtr<nsIThread> mCameraThread;
   uint64_t            mWindowId;
   nsString            mFileFormat;
@@ -394,17 +380,17 @@ public:
   CameraSize mSize;
   int32_t mRotation;
   nsString mFileFormat;
   CameraPosition mPosition;
   nsCOMPtr<nsICameraTakePictureCallback> mOnSuccessCb;
   nsCOMPtr<nsICameraErrorCallback> mOnErrorCb;
 };
 
-// Return the captured video to JS.  Runs on the main thread.
+// Return the result of starting recording.  Runs on the main thread.
 class StartRecordingResult : public nsRunnable
 {
 public:
   StartRecordingResult(nsICameraStartRecordingCallback* onSuccess, uint64_t aWindowId)
     : mOnSuccessCb(onSuccess)
     , mWindowId(aWindowId)
   { }
 
@@ -424,48 +410,60 @@ protected:
   nsCOMPtr<nsICameraStartRecordingCallback> mOnSuccessCb;
   uint64_t mWindowId;
 };
 
 // Start video recording.
 class StartRecordingTask : public nsRunnable
 {
 public:
-  StartRecordingTask(CameraControlImpl* aCameraControl, nsIDOMDeviceStorage* aStorageArea, const nsAString& aFilename, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError)
+  StartRecordingTask(CameraControlImpl* aCameraControl, CameraStartRecordingOptions aOptions, nsIFile* aFolder, const nsAString& aFilename, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError, uint64_t aWindowId)
     : mCameraControl(aCameraControl)
-    , mStorageArea(aStorageArea)
+    , mOptions(aOptions)
+    , mFolder(aFolder)
     , mFilename(aFilename)
     , mOnSuccessCb(onSuccess)
     , mOnErrorCb(onError)
+    , mWindowId(aWindowId)
   {
     DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
   }
 
   virtual ~StartRecordingTask()
   {
     DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
   }
 
   NS_IMETHOD Run()
   {
     DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
     nsresult rv = mCameraControl->StartRecordingImpl(this);
     DOM_CAMERA_LOGT("%s:%d : result %d\n", __func__, __LINE__, rv);
 
-    if (NS_FAILED(rv) && mOnErrorCb) {
-      rv = NS_DispatchToMainThread(new CameraErrorResult(mOnErrorCb, NS_LITERAL_STRING("FAILURE"), mCameraControl->GetWindowId()));
+    // dispatch the callback
+    nsCOMPtr<nsIRunnable> startRecordingResult;
+    if (NS_SUCCEEDED(rv)) {
+      startRecordingResult = new StartRecordingResult(mOnSuccessCb, mWindowId);
+    } else {
+      startRecordingResult = new CameraErrorResult(mOnErrorCb, NS_LITERAL_STRING("FAILURE"), mWindowId);
+    }
+    rv = NS_DispatchToMainThread(startRecordingResult);
+    if (NS_FAILED(rv)) {
+      DOM_CAMERA_LOGE("Failed to dispatch start recording result to main thread (%d)!", rv);
     }
     return rv;
   }
 
   nsRefPtr<CameraControlImpl> mCameraControl;
-  nsCOMPtr<nsIDOMDeviceStorage> mStorageArea;
+  CameraStartRecordingOptions mOptions;
+  nsCOMPtr<nsIFile> mFolder;
   nsString mFilename;
   nsCOMPtr<nsICameraStartRecordingCallback> mOnSuccessCb;
   nsCOMPtr<nsICameraErrorCallback> mOnErrorCb;
+  uint64_t mWindowId;
 };
 
 // Stop video recording.
 class StopRecordingTask : public nsRunnable
 {
 public:
   StopRecordingTask(CameraControlImpl* aCameraControl)
     : mCameraControl(aCameraControl)
@@ -578,17 +576,17 @@ protected:
   nsCOMPtr<nsIDOMMediaStream> mStream;
   nsCOMPtr<nsICameraPreviewStreamCallback> mOnSuccessCb;
 };
 
 // Get the video mode preview stream.
 class GetPreviewStreamVideoModeTask : public nsRunnable
 {
 public:
-  GetPreviewStreamVideoModeTask(CameraControlImpl* aCameraControl, CameraRecordingOptions aOptions,  nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError)
+  GetPreviewStreamVideoModeTask(CameraControlImpl* aCameraControl, CameraRecorderOptions aOptions,  nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError)
     : mCameraControl(aCameraControl)
     , mOptions(aOptions)
     , mOnSuccessCb(onSuccess)
     , mOnErrorCb(onError)
   { }
 
   NS_IMETHOD Run()
   {
@@ -599,16 +597,16 @@ public:
     if (NS_FAILED(rv) && mOnErrorCb) {
       rv = NS_DispatchToMainThread(new CameraErrorResult(mOnErrorCb, NS_LITERAL_STRING("FAILURE"), mCameraControl->GetWindowId()));
       NS_ENSURE_SUCCESS(rv, rv);
     }
     return NS_OK;
   }
 
   nsRefPtr<CameraControlImpl> mCameraControl;
-  CameraRecordingOptions mOptions;
+  CameraRecorderOptions mOptions;
   nsCOMPtr<nsICameraPreviewStreamCallback> mOnSuccessCb;
   nsCOMPtr<nsICameraErrorCallback> mOnErrorCb;
 };
 
 } // namespace mozilla
 
 #endif // DOM_CAMERA_CAMERACONTROLIMPL_H
new file mode 100644
--- /dev/null
+++ b/dom/camera/CameraRecorderProfiles.cpp
@@ -0,0 +1,195 @@
+/* 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 "jsapi.h"
+#include "CameraRecorderProfiles.h"
+#include "CameraCommon.h"
+
+using namespace mozilla;
+
+/**
+ * Video profile implementation.
+ */
+RecorderVideoProfile::RecorderVideoProfile(uint32_t aCameraId, uint32_t aQualityIndex)
+  : mCameraId(aCameraId)
+  , mQualityIndex(aQualityIndex)
+{
+  DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+}
+
+RecorderVideoProfile::~RecorderVideoProfile()
+{
+  DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+}
+
+nsresult
+RecorderVideoProfile::GetJsObject(JSContext* aCx, JSObject** aObject)
+{
+  NS_ENSURE_TRUE(aObject, NS_ERROR_INVALID_ARG);
+
+  JSObject* o = JS_NewObject(aCx, nullptr, nullptr, nullptr);
+  NS_ENSURE_TRUE(o, NS_ERROR_OUT_OF_MEMORY);
+
+  const char* codec = GetCodecName();
+  NS_ENSURE_TRUE(codec, NS_ERROR_FAILURE);
+
+  JSString* s = JS_NewStringCopyZ(aCx, codec);
+  jsval v = STRING_TO_JSVAL(s);
+  if (!JS_SetProperty(aCx, o, "codec", &v)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  if (mBitrate != -1) {
+    v = INT_TO_JSVAL(mBitrate);
+    if (!JS_SetProperty(aCx, o, "bitrate", &v)) {
+      return NS_ERROR_FAILURE;
+    }
+  }
+  if (mFramerate != -1) {
+    v = INT_TO_JSVAL(mFramerate);
+    if (!JS_SetProperty(aCx, o, "framerate", &v)) {
+      return NS_ERROR_FAILURE;
+    }
+  }
+  if (mWidth != -1) {
+    v = INT_TO_JSVAL(mWidth);
+    if (!JS_SetProperty(aCx, o, "width", &v)) {
+      return NS_ERROR_FAILURE;
+    }
+  }
+  if (mHeight != -1) {
+    v = INT_TO_JSVAL(mHeight);
+    if (!JS_SetProperty(aCx, o, "height", &v)) {
+      return NS_ERROR_FAILURE;
+    }
+  }
+
+  *aObject = o;
+  return NS_OK;
+}
+
+/**
+ * Audio profile implementation.
+ */
+RecorderAudioProfile::RecorderAudioProfile(uint32_t aCameraId, uint32_t aQualityIndex)
+  : mCameraId(aCameraId)
+  , mQualityIndex(aQualityIndex)
+{
+  DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+}
+
+RecorderAudioProfile::~RecorderAudioProfile()
+{
+  DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+}
+
+nsresult
+RecorderAudioProfile::GetJsObject(JSContext* aCx, JSObject** aObject)
+{
+  NS_ENSURE_TRUE(aObject, NS_ERROR_INVALID_ARG);
+
+  JSObject* o = JS_NewObject(aCx, nullptr, nullptr, nullptr);
+  NS_ENSURE_TRUE(o, NS_ERROR_OUT_OF_MEMORY);
+
+  const char* codec = GetCodecName();
+  NS_ENSURE_TRUE(codec, NS_ERROR_FAILURE);
+
+  JSString* s = JS_NewStringCopyZ(aCx, codec);
+  jsval v = STRING_TO_JSVAL(s);
+  if (!JS_SetProperty(aCx, o, "codec", &v)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  if (mBitrate != -1) {
+    v = INT_TO_JSVAL(mBitrate);
+    if (!JS_SetProperty(aCx, o, "bitrate", &v)) {
+      return NS_ERROR_FAILURE;
+    }
+  }
+  if (mSamplerate != -1) {
+    v = INT_TO_JSVAL(mSamplerate);
+    if (!JS_SetProperty(aCx, o, "samplerate", &v)) {
+      return NS_ERROR_FAILURE;
+    }
+  }
+  if (mChannels != -1) {
+    v = INT_TO_JSVAL(mChannels);
+    if (!JS_SetProperty(aCx, o, "channels", &v)) {
+      return NS_ERROR_FAILURE;
+    }
+  }
+
+  *aObject = o;
+  return NS_OK;
+}
+
+/**
+ * Recorder Profile
+ */
+RecorderProfile::RecorderProfile(uint32_t aCameraId, uint32_t aQualityIndex)
+  : mCameraId(aCameraId)
+  , mQualityIndex(aQualityIndex)
+  , mName(nullptr)
+{
+  DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+}
+
+RecorderProfile::~RecorderProfile()
+{
+  DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+}
+
+/**
+ * Recorder profile manager implementation.
+ */
+RecorderProfileManager::RecorderProfileManager(uint32_t aCameraId)
+  : mCameraId(aCameraId)
+{
+  DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+}
+
+RecorderProfileManager::~RecorderProfileManager()
+{
+  DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+}
+
+nsresult
+RecorderProfileManager::GetJsObject(JSContext* aCx, JSObject** aObject) const
+{
+  NS_ENSURE_TRUE(aObject, NS_ERROR_INVALID_ARG);
+
+  JSObject* o = JS_NewObject(aCx, nullptr, nullptr, nullptr);
+  if (!o) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  for (uint32_t q = 0; q < GetMaxQualityIndex(); ++q) {
+    if (!IsSupported(q)) {
+      continue;
+    }
+
+    nsRefPtr<RecorderProfile> profile = Get(q);
+    if (!profile) {
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+
+    const char* profileName = profile->GetName();
+    if (!profileName) {
+      // don't allow anonymous recorder profiles
+      continue;
+    }
+
+    JSObject* p;
+    nsresult rv = profile->GetJsObject(aCx, &p);
+    NS_ENSURE_SUCCESS(rv, rv);
+    jsval v = OBJECT_TO_JSVAL(p);
+
+    if (!JS_SetProperty(aCx, o, profileName, &v)) {
+      return NS_ERROR_FAILURE;
+    }
+  }
+
+  *aObject = o;
+  return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/dom/camera/CameraRecorderProfiles.h
@@ -0,0 +1,226 @@
+/* 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 DOM_CAMERA_CAMERA_RECORDER_PROFILES_H
+#define DOM_CAMERA_CAMERA_RECORDER_PROFILES_H
+
+#include "nsISupportsImpl.h"
+#include "nsAutoPtr.h"
+#include "nsTArray.h"
+#include "jsapi.h"
+#include "DictionaryHelpers.h"
+#include "CameraCommon.h"
+
+using namespace mozilla;
+using namespace dom;
+
+namespace mozilla {
+
+class CameraControlImpl;
+
+class RecorderVideoProfile
+{
+public:
+  RecorderVideoProfile(uint32_t aCameraId, uint32_t aQualityIndex);
+  virtual ~RecorderVideoProfile();
+
+  int GetBitrate() const    { return mBitrate; }
+  int GetFramerate() const  { return mFramerate; }
+  int GetWidth() const      { return mWidth; }
+  int GetHeight() const     { return mHeight; }
+
+  enum Codec {
+    H263,
+    H264,
+    MPEG4SP,
+    UNKNOWN
+  };
+  Codec GetCodec() const    { return mCodec; }
+  const char* GetCodecName() const
+  {
+    switch (mCodec) {
+      case H263:    return "h263";
+      case H264:    return "h264";
+      case MPEG4SP: return "mpeg4sp";
+      default:      return nullptr;
+    }
+  }
+
+  nsresult GetJsObject(JSContext* aCx, JSObject** aObject);
+
+protected:
+  uint32_t mCameraId;
+  uint32_t mQualityIndex;
+  Codec mCodec;
+  int mBitrate;
+  int mFramerate;
+  int mWidth;
+  int mHeight;
+};
+
+class RecorderAudioProfile
+{
+public:
+  RecorderAudioProfile(uint32_t aCameraId, uint32_t aQualityIndex);
+  virtual ~RecorderAudioProfile();
+
+  int GetBitrate() const    { return mBitrate; }
+  int GetSamplerate() const { return mSamplerate; }
+  int GetChannels() const   { return mChannels; }
+
+  enum Codec {
+    AMRNB,
+    AMRWB,
+    AAC,
+    UNKNOWN
+  };
+
+public:
+  Codec GetCodec() const    { return mCodec; }
+  const char* GetCodecName() const
+  {
+    switch (mCodec) {
+      case AMRNB: return "amrnb";
+      case AMRWB: return "amrwb";
+      case AAC:   return "aac";
+      default:    return nullptr;
+    }
+  }
+
+  nsresult GetJsObject(JSContext* aCx, JSObject** aObject);
+
+protected:
+  uint32_t mCameraId;
+  uint32_t mQualityIndex;
+  Codec mCodec;
+  int mBitrate;
+  int mSamplerate;
+  int mChannels;
+};
+
+class RecorderProfile
+{
+public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RecorderProfile)
+
+  RecorderProfile(uint32_t aCameraId, uint32_t aQualityIndex);
+
+  virtual const RecorderVideoProfile* GetVideoProfile() const = 0;
+  virtual const RecorderAudioProfile* GetAudioProfile() const = 0;
+  const char* GetName() const { return mName; }
+
+  enum FileFormat {
+    THREE_GPP,
+    MPEG4,
+    UNKNOWN
+  };
+  FileFormat GetFileFormat() const { return mFileFormat; }
+  const char* GetFileFormatName() const
+  {
+    switch (mFileFormat) {
+      case THREE_GPP: return "3gp";
+      case MPEG4:     return "mp4";
+      default:        return nullptr;
+    }
+  }
+
+  virtual nsresult GetJsObject(JSContext* aCx, JSObject** aObject) = 0;
+
+protected:
+  virtual ~RecorderProfile();
+
+  uint32_t mCameraId;
+  uint32_t mQualityIndex;
+  const char* mName;
+  FileFormat mFileFormat;
+};
+
+template <class Audio, class Video>
+class RecorderProfileBase : public RecorderProfile
+{
+public:
+  RecorderProfileBase(uint32_t aCameraId, uint32_t aQualityIndex)
+    : RecorderProfile(aCameraId, aQualityIndex)
+    , mVideo(aCameraId, aQualityIndex)
+    , mAudio(aCameraId, aQualityIndex)
+  {
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  }
+
+  virtual ~RecorderProfileBase()
+  {
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  }
+
+  const RecorderVideoProfile* GetVideoProfile() const { return &mVideo; }
+  const RecorderAudioProfile* GetAudioProfile() const { return &mAudio; }
+
+  nsresult GetJsObject(JSContext* aCx, JSObject** aObject)
+  {
+    NS_ENSURE_TRUE(aObject, NS_ERROR_INVALID_ARG);
+
+    const char* format = GetFileFormatName();
+    if (!format) {
+      // the profile must have a file format
+      return NS_ERROR_FAILURE;
+    }
+
+    JSObject* o = JS_NewObject(aCx, nullptr, nullptr, nullptr);
+    if (!o) {
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+
+    JSString* s = JS_NewStringCopyZ(aCx, format);
+    jsval v = STRING_TO_JSVAL(s);
+    if (!JS_SetProperty(aCx, o, "format", &v)) {
+      return NS_ERROR_FAILURE;
+    }
+
+    JSObject* video;
+    nsresult rv = mVideo.GetJsObject(aCx, &video);
+    NS_ENSURE_SUCCESS(rv, rv);
+    v = OBJECT_TO_JSVAL(video);
+    if (!JS_SetProperty(aCx, o, "video", &v)) {
+      return NS_ERROR_FAILURE;
+    }
+
+    JSObject* audio;
+    rv = mAudio.GetJsObject(aCx, &audio);
+    NS_ENSURE_SUCCESS(rv, rv);
+    v = OBJECT_TO_JSVAL(audio);
+    if (!JS_SetProperty(aCx, o, "audio", &v)) {
+      return NS_ERROR_FAILURE;
+    }
+
+    *aObject = o;
+    return NS_OK;
+  }
+
+protected:
+  Video mVideo;
+  Audio mAudio;
+};
+
+class RecorderProfileManager
+{
+public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RecorderProfileManager)
+
+  virtual bool IsSupported(uint32_t aQualityIndex) const { return true; }
+  virtual already_AddRefed<RecorderProfile> Get(uint32_t aQualityIndex) const = 0;
+
+  uint32_t GetMaxQualityIndex() const { return mMaxQualityIndex; }
+  nsresult GetJsObject(JSContext* aCx, JSObject** aObject) const;
+
+protected:
+  RecorderProfileManager(uint32_t aCameraId);
+  virtual ~RecorderProfileManager();
+
+  uint32_t mCameraId;
+  uint32_t mMaxQualityIndex;
+};
+
+}; // namespace mozilla
+
+#endif // DOM_CAMERA_CAMERA_RECORDER_PROFILES_H
--- a/dom/camera/DOMCameraCapabilities.cpp
+++ b/dom/camera/DOMCameraCapabilities.cpp
@@ -2,16 +2,17 @@
  * 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 <cstring>
 #include <cstdlib>
 #include "base/basictypes.h"
 #include "nsDOMClassInfo.h"
 #include "jsapi.h"
+#include "CameraRecorderProfiles.h"
 #include "DOMCameraControl.h"
 #include "DOMCameraCapabilities.h"
 #include "CameraCommon.h"
 
 using namespace mozilla;
 
 DOMCI_DATA(CameraCapabilities, nsICameraCapabilities)
 
@@ -31,17 +32,17 @@ ParseZoomRatioItemAndAdd(JSContext* aCx,
 {
   if (!*aEnd) {
     // make 'aEnd' follow the same semantics as strchr().
     aEnd = nullptr;
   }
 
   /**
    * The by-100 divisor is Gonk-specific.  For now, assume other platforms
-   * return actual fractoinal multipliers.
+   * return actual fractional multipliers.
    */
   double d = strtod(aStart, aEnd);
 #if MOZ_WIDGET_GONK
   d /= 100;
 #endif
 
   jsval v = JS_NumberValue(d);
 
@@ -341,10 +342,64 @@ DOMCameraCapabilities::GetZoomRatios(JSC
   *aZoomRatios = OBJECT_TO_JSVAL(array);
   return NS_OK;
 }
 
 /* readonly attribute jsval videoSizes; */
 NS_IMETHODIMP
 DOMCameraCapabilities::GetVideoSizes(JSContext* cx, JS::Value* aVideoSizes)
 {
-  return DimensionListToNewObject(cx, aVideoSizes, CAMERA_PARAM_SUPPORTED_VIDEOSIZES);
+  NS_ENSURE_TRUE(mCamera, NS_ERROR_NOT_AVAILABLE);
+
+  nsTArray<CameraSize> sizes;
+  nsresult rv = mCamera->GetVideoSizes(sizes);
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (sizes.Length() == 0) {
+    // video recording not supported, return a null object
+    *aVideoSizes = JSVAL_NULL;
+    return NS_OK;
+  }
+
+  JSObject* array = JS_NewArrayObject(cx, 0, nullptr);
+  if (!array) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  for (uint32_t i = 0; i < sizes.Length(); ++i) {
+    JSObject* o = JS_NewObject(cx, nullptr, nullptr, nullptr);
+    jsval v = INT_TO_JSVAL(sizes[i].width);
+    if (!JS_SetProperty(cx, o, "width", &v)) {
+      return NS_ERROR_FAILURE;
+    }
+    v = INT_TO_JSVAL(sizes[i].height);
+    if (!JS_SetProperty(cx, o, "height", &v)) {
+      return NS_ERROR_FAILURE;
+    }
+
+    v = OBJECT_TO_JSVAL(o);
+    if (!JS_SetElement(cx, array, i, &v)) {
+      return NS_ERROR_FAILURE;
+    }
+  }
+
+  *aVideoSizes = OBJECT_TO_JSVAL(array);
+  return NS_OK;
 }
+
+/* readonly attribute jsval recorderProfiles; */
+NS_IMETHODIMP
+DOMCameraCapabilities::GetRecorderProfiles(JSContext* cx, JS::Value* aRecorderProfiles)
+{
+  NS_ENSURE_TRUE(mCamera, NS_ERROR_NOT_AVAILABLE);
+
+  nsRefPtr<RecorderProfileManager> profileMgr = mCamera->GetRecorderProfileManager();
+  if (!profileMgr) {
+    *aRecorderProfiles = JSVAL_NULL;
+    return NS_OK;
+  }
+
+  JSObject* o = nullptr;  // keeps the compiler from emitting a warning
+  nsresult rv = profileMgr->GetJsObject(cx, &o);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  *aRecorderProfiles = OBJECT_TO_JSVAL(o);
+  return NS_OK;
+}
--- a/dom/camera/DOMCameraControl.cpp
+++ b/dom/camera/DOMCameraControl.cpp
@@ -224,33 +224,40 @@ nsDOMCameraControl::GetOnClosed(nsICamer
   return mCameraControl->Get(aOnClosed);
 }
 NS_IMETHODIMP
 nsDOMCameraControl::SetOnClosed(nsICameraClosedCallback* aOnClosed)
 {
   return mCameraControl->Set(aOnClosed);
 }
 
-/* [implicit_jscontext] void startRecording (in nsIDOMDeviceStorage storageArea, in DOMString filename, in nsICameraStartRecordingCallback onSuccess, [optional] in nsICameraErrorCallback onError); */
+/* [implicit_jscontext] void startRecording (in jsval aOptions, in nsIDOMDeviceStorage storageArea, in DOMString filename, in nsICameraStartRecordingCallback onSuccess, [optional] in nsICameraErrorCallback onError); */
 NS_IMETHODIMP
-nsDOMCameraControl::StartRecording(nsIDOMDeviceStorage* storageArea, const nsAString& filename, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError, JSContext* cx)
+nsDOMCameraControl::StartRecording(const JS::Value& aOptions, nsIDOMDeviceStorage* storageArea, const nsAString& filename, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError, JSContext* cx)
 {
   NS_ENSURE_TRUE(onSuccess, NS_ERROR_INVALID_ARG);
+  NS_ENSURE_TRUE(storageArea, NS_ERROR_INVALID_ARG);
+
+  CameraStartRecordingOptions options;
+  nsresult rv = options.Init(cx, &aOptions);
+  NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   if (!obs) {
     NS_WARNING("Could not get the Observer service for CameraControl::StartRecording.");
     return NS_ERROR_FAILURE;
   }
 
   obs->NotifyObservers(nullptr,
                        "recording-device-events",
                        NS_LITERAL_STRING("starting").get());
 
-  return mCameraControl->StartRecording(storageArea, filename, onSuccess, onError);
+  nsCOMPtr<nsIFile> folder;
+  storageArea->GetRootDirectory(getter_AddRefs(folder));
+  return mCameraControl->StartRecording(&options, folder, filename, onSuccess, onError);
 }
 
 /* void stopRecording (); */
 NS_IMETHODIMP
 nsDOMCameraControl::StopRecording()
 {
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   if (!obs) {
@@ -324,17 +331,17 @@ nsDOMCameraControl::TakePicture(const JS
 }
 
 /* [implicit_jscontext] void GetPreviewStreamVideoMode (in jsval aOptions, in nsICameraPreviewStreamCallback onSuccess, [optional] in nsICameraErrorCallback onError); */
 NS_IMETHODIMP
 nsDOMCameraControl::GetPreviewStreamVideoMode(const JS::Value& aOptions, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError, JSContext* cx)
 {
   NS_ENSURE_TRUE(onSuccess, NS_ERROR_INVALID_ARG);
 
-  CameraRecordingOptions options;
+  CameraRecorderOptions options;
   nsresult rv = options.Init(cx, &aOptions);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return mCameraControl->GetPreviewStreamVideoMode(&options, onSuccess, onError);
 }
 
 class GetCameraResult : public nsRunnable
 {
--- a/dom/camera/FallbackCameraControl.cpp
+++ b/dom/camera/FallbackCameraControl.cpp
@@ -2,16 +2,20 @@
  * 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 "DOMCameraControl.h"
 #include "CameraControlImpl.h"
 
 using namespace mozilla;
 
+namespace mozilla {
+class RecorderProfileManager;
+}
+
 /**
  * Fallback camera control subclass.  Can be used as a template for the
  * definition of new camera support classes.
  */
 class nsFallbackCameraControl : public CameraControlImpl
 {
 public:
   nsFallbackCameraControl(uint32_t aCameraId, nsIThread* aCameraThread, nsDOMCameraControl* aDOMCameraControl, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError, uint64_t aWindowId);
@@ -19,30 +23,32 @@ public:
   const char* GetParameter(const char* aKey);
   const char* GetParameterConstChar(uint32_t aKey);
   double GetParameterDouble(uint32_t aKey);
   void GetParameter(uint32_t aKey, nsTArray<dom::CameraRegion>& aRegions);
   void SetParameter(const char* aKey, const char* aValue);
   void SetParameter(uint32_t aKey, const char* aValue);
   void SetParameter(uint32_t aKey, double aValue);
   void SetParameter(uint32_t aKey, const nsTArray<dom::CameraRegion>& aRegions);
+  nsresult GetVideoSizes(nsTArray<CameraSize>& aVideoSizes);
   nsresult PushParameters();
 
 protected:
   ~nsFallbackCameraControl();
 
   nsresult GetPreviewStreamImpl(GetPreviewStreamTask* aGetPreviewStream);
   nsresult StartPreviewImpl(StartPreviewTask* aStartPreview);
   nsresult StopPreviewImpl(StopPreviewTask* aStopPreview);
   nsresult AutoFocusImpl(AutoFocusTask* aAutoFocus);
   nsresult TakePictureImpl(TakePictureTask* aTakePicture);
   nsresult StartRecordingImpl(StartRecordingTask* aStartRecording);
   nsresult StopRecordingImpl(StopRecordingTask* aStopRecording);
   nsresult PushParametersImpl();
   nsresult PullParametersImpl();
+  already_AddRefed<RecorderProfileManager> GetRecorderProfileManagerImpl();
 
 private:
   nsFallbackCameraControl(const nsFallbackCameraControl&) MOZ_DELETE;
   nsFallbackCameraControl& operator=(const nsFallbackCameraControl&) MOZ_DELETE;
 };
 
 /**
  * Stub implementation of the DOM-facing camera control constructor.
@@ -168,8 +174,20 @@ nsFallbackCameraControl::PushParametersI
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 nsresult
 nsFallbackCameraControl::PullParametersImpl()
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
+
+nsresult
+nsFallbackCameraControl::GetVideoSizes(nsTArray<CameraSize>& aVideoSizes)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+already_AddRefed<RecorderProfileManager> 
+nsFallbackCameraControl::GetRecorderProfileManagerImpl()
+{
+  return nullptr;
+}
--- a/dom/camera/GonkCameraControl.cpp
+++ b/dom/camera/GonkCameraControl.cpp
@@ -29,16 +29,17 @@
 #include "nsThread.h"
 #include <media/MediaProfiles.h>
 #include "nsDirectoryServiceDefs.h" // for NS_GetSpecialDirectory
 #include "nsPrintfCString.h"
 #include "DOMCameraManager.h"
 #include "GonkCameraHwMgr.h"
 #include "DOMCameraCapabilities.h"
 #include "DOMCameraControl.h"
+#include "GonkRecorderProfiles.h"
 #include "GonkCameraControl.h"
 #include "CameraCommon.h"
 
 using namespace mozilla;
 using namespace android;
 
 static const char* getKeyText(uint32_t aKey)
 {
@@ -167,17 +168,24 @@ nsGonkCameraControl::nsGonkCameraControl
   : CameraControlImpl(aCameraId, aCameraThread, aWindowId)
   , mHwHandle(0)
   , mExposureCompensationMin(0.0)
   , mExposureCompensationStep(0.0)
   , mDeferConfigUpdate(false)
   , mWidth(0)
   , mHeight(0)
   , mFormat(PREVIEW_FORMAT_UNKNOWN)
+  , mFps(30)
   , mDiscardedFrameCount(0)
+  , mMediaProfiles(nullptr)
+  , mRecorder(nullptr)
+  , mVideoRotation(0)
+  , mVideoFile()
+  , mProfileManager(nullptr)
+  , mRecorderProfile(nullptr)
 {
   // Constructor runs on the main thread...
   DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
   mRwLock = PR_NewRWLock(PR_RWLOCK_RANK_NONE, "GonkCameraControl.Parameters.Lock");
 
   // ...but initialization is carried out on the camera thread.
   nsCOMPtr<nsIRunnable> init = new InitGonkCameraControl(this, aDOMCameraControl, onSuccess, onError, aWindowId);
   mCameraThread->Dispatch(init, NS_DISPATCH_NORMAL);
@@ -713,18 +721,17 @@ nsGonkCameraControl::StartRecordingImpl(
   /**
    * Get the base path from device storage and append the app-specified
    * filename to it.  The filename may include a relative subpath
    * (e.g.) "DCIM/IMG_0001.jpg".
    *
    * The camera app needs to provide the file extension '.3gp' for now.
    * See bug 795202.
    */
-  nsCOMPtr<nsIFile> filename;
-  aStartRecording->mStorageArea->GetRootDirectory(getter_AddRefs(filename));
+  nsCOMPtr<nsIFile> filename = aStartRecording->mFolder;
   filename->AppendRelativePath(aStartRecording->mFilename);
 
   nsAutoCString nativeFilename;
   filename->GetNativePath(nativeFilename);
   DOM_CAMERA_LOGI("Video filename is '%s'\n", nativeFilename.get());
 
   int fd = open(nativeFilename.get(), O_RDWR | O_CREAT, 0644);
   if (fd < 0) {
@@ -738,22 +745,16 @@ nsGonkCameraControl::StartRecordingImpl(
     return NS_ERROR_FAILURE;
   }
   if (mRecorder->start() != OK) {
     DOM_CAMERA_LOGE("mRecorder->start() failed\n");
     close(fd);
     return NS_ERROR_FAILURE;
   }
 
-  // dispatch the callback
-  nsCOMPtr<nsIRunnable> startRecordingResult = new StartRecordingResult(mStartRecordingOnSuccessCb, mWindowId);
-  nsresult rv = NS_DispatchToMainThread(startRecordingResult);
-  if (NS_FAILED(rv)) {
-    DOM_CAMERA_LOGE("Failed to dispatch start recording result to main thread (%d)!", rv);
-  }
   return NS_OK;
 }
 
 nsresult
 nsGonkCameraControl::StopRecordingImpl(StopRecordingTask* aStopRecording)
 {
   mRecorder->stop();
   delete mRecorder;
@@ -866,161 +867,166 @@ nsGonkCameraControl::SetPreviewSize(uint
 
   mWidth = bestWidth;
   mHeight = bestHeight;
   mParams.setPreviewSize(mWidth, mHeight);
   PushParameters();
 }
 
 nsresult
-nsGonkCameraControl::SetupVideoMode()
+nsGonkCameraControl::SetupVideoMode(const nsAString& aProfile)
 {
   // read preferences for camcorder
   mMediaProfiles = MediaProfiles::getInstance();
 
-  /**
-   * Right now default to profile 3, which is 352x288 on Otoro.  In the
-   * future, allow the application to select a recording quality and
-   * configuration.
-   *
-   * See bug 795379.
-   */
-  int quality = 3;  // cif:352x288
-  camcorder_quality q = static_cast<camcorder_quality>(quality);
-  mDuration         = mMediaProfiles->getCamcorderProfileParamByName("duration",    (int)mCameraId, q);
-  mVideoFileFormat  = mMediaProfiles->getCamcorderProfileParamByName("file.format", (int)mCameraId, q);
-  mVideoCodec       = mMediaProfiles->getCamcorderProfileParamByName("vid.codec",   (int)mCameraId, q);
-  mVideoBitRate     = mMediaProfiles->getCamcorderProfileParamByName("vid.bps",     (int)mCameraId, q);
-  mVideoFrameRate   = mMediaProfiles->getCamcorderProfileParamByName("vid.fps",     (int)mCameraId, q);
-  mVideoFrameWidth  = mMediaProfiles->getCamcorderProfileParamByName("vid.width",   (int)mCameraId, q);
-  mVideoFrameHeight = mMediaProfiles->getCamcorderProfileParamByName("vid.height",  (int)mCameraId, q);
-  mAudioCodec       = mMediaProfiles->getCamcorderProfileParamByName("aud.codec",   (int)mCameraId, q);
-  mAudioBitRate     = mMediaProfiles->getCamcorderProfileParamByName("aud.bps",     (int)mCameraId, q);
-  mAudioSampleRate  = mMediaProfiles->getCamcorderProfileParamByName("aud.hz",      (int)mCameraId, q);
-  mAudioChannels    = mMediaProfiles->getCamcorderProfileParamByName("aud.ch",      (int)mCameraId, q);
+  nsAutoCString profile = NS_ConvertUTF16toUTF8(aProfile);
+  mRecorderProfile = GetGonkRecorderProfileManager().get()->Get(profile.get());
+  if (!mRecorderProfile) {
+    DOM_CAMERA_LOGE("Recorder profile '%s' is not supported\n", profile.get());
+    return NS_ERROR_INVALID_ARG;
+  }
 
-  if (mVideoFrameRate == -1) {
-    DOM_CAMERA_LOGE("Failed to get a valid frame rate!\n");
-    DOM_CAMERA_LOGE("Also got width=%d, height=%d\n", mVideoFrameWidth, mVideoFrameHeight);
+  const GonkRecorderVideoProfile* video = mRecorderProfile->GetGonkVideoProfile();
+  int width = video->GetWidth();
+  int height = video->GetHeight();
+  int fps = video->GetFramerate();
+  if (fps == -1 || width == -1 || height == -1) {
+    DOM_CAMERA_LOGE("Can't configure preview with fps=%d, width=%d, height=%d\n", fps, width, height);
     return NS_ERROR_FAILURE;
   }
 
   PullParametersImpl();
 
-  // Configure camera video recording parameters.
+  // configure camera video recording parameters
   const size_t SIZE = 256;
   char buffer[SIZE];
 
-  /**
-   * Ignore the width and height settings from app, just use the one in profile.
-   * Eventually, will try to choose a profile which respects the settings from app.
-   * See bug 795330.
-   */
-  mParams.setPreviewSize(mVideoFrameWidth, mVideoFrameHeight);
-  mParams.setPreviewFrameRate(mVideoFrameRate);
-  snprintf(buffer, SIZE, "%dx%d", mVideoFrameWidth, mVideoFrameHeight);
+  mParams.setPreviewSize(width, height);
+  mParams.setPreviewFrameRate(fps);
 
   /**
    * "record-size" is probably deprecated in later ICS;
    * might need to set "video-size" instead of "record-size".
    * See bug 795332.
    */
+  snprintf(buffer, SIZE, "%dx%d", width, height);
   mParams.set("record-size", buffer);
 
-  /**
-   * If we want to enable picture-taking _while_ recording video, this sets the
-   * size of the captured picture.  For now, just set it to the same dimensions
-   * as the video we're recording; ideally, we should probably make sure it
-   * matches one of the supported picture sizes.
-   */
-  mParams.setPictureSize(mVideoFrameWidth, mVideoFrameHeight);
-
-  PushParametersImpl();
+  // push the updated camera configuration immediately
+  PushParameters();
   return NS_OK;
 }
 
-#ifndef CHECK_SETARG
-#define CHECK_SETARG(x)                 \
-  do {                                  \
-    if (x) {                            \
-      DOM_CAMERA_LOGE(#x " failed\n");  \
-      return NS_ERROR_INVALID_ARG;      \
-    }                                   \
-  } while(0)
-#endif
-
 nsresult
-nsGonkCameraControl::SetupRecording(int aFd)
+nsGonkCameraControl::SetupRecording(int aFd, int aMaxFileSizeBytes, int aMaxVideoLengthMs)
 {
   // choosing a size big enough to hold the params
   const size_t SIZE = 256;
   char buffer[SIZE];
 
   mRecorder = new GonkRecorder();
   CHECK_SETARG(mRecorder->init());
 
-  // set all the params
+  nsresult rv = mRecorderProfile->ConfigureRecorder(mRecorder);
+  NS_ENSURE_SUCCESS(rv, rv);
+
   CHECK_SETARG(mRecorder->setCameraHandle((int32_t)mHwHandle));
-  CHECK_SETARG(mRecorder->setAudioSource(AUDIO_SOURCE_CAMCORDER));
-  CHECK_SETARG(mRecorder->setVideoSource(VIDEO_SOURCE_CAMERA));
-  CHECK_SETARG(mRecorder->setOutputFormat((output_format)mVideoFileFormat));
-  CHECK_SETARG(mRecorder->setVideoFrameRate(mVideoFrameRate));
-  CHECK_SETARG(mRecorder->setVideoSize(mVideoFrameWidth, mVideoFrameHeight));
-  snprintf(buffer, SIZE, "video-param-encoding-bitrate=%d", mVideoBitRate);
+
+  snprintf(buffer, SIZE, "max-duration=%d", aMaxVideoLengthMs);
   CHECK_SETARG(mRecorder->setParameters(String8(buffer)));
-  CHECK_SETARG(mRecorder->setVideoEncoder((video_encoder)mVideoCodec));
-  snprintf(buffer, SIZE, "audio-param-encoding-bitrate=%d", mAudioBitRate);
-  CHECK_SETARG(mRecorder->setParameters(String8(buffer)));
-  snprintf(buffer, SIZE, "audio-param-number-of-channels=%d", mAudioChannels);
+
+  snprintf(buffer, SIZE, "max-duration=%d", aMaxFileSizeBytes);
   CHECK_SETARG(mRecorder->setParameters(String8(buffer)));
-  snprintf(buffer, SIZE, "audio-param-sampling-rate=%d", mAudioSampleRate);
-  CHECK_SETARG(mRecorder->setParameters(String8(buffer)));
-  CHECK_SETARG(mRecorder->setAudioEncoder((audio_encoder)mAudioCodec));
-  // TODO: For now there is no limit on recording duration (See bug 795090)
-  CHECK_SETARG(mRecorder->setParameters(String8("max-duration=-1")));
-  // TODO: For now there is no limit on file size (See bug 795090)
-  CHECK_SETARG(mRecorder->setParameters(String8("max-filesize=-1")));
+
   snprintf(buffer, SIZE, "video-param-rotation-angle-degrees=%d", mVideoRotation);
   CHECK_SETARG(mRecorder->setParameters(String8(buffer)));
 
   // recording API needs file descriptor of output file
   CHECK_SETARG(mRecorder->setOutputFile(aFd, 0, 0));
   CHECK_SETARG(mRecorder->prepare());
   return NS_OK;
 }
 
 nsresult
 nsGonkCameraControl::GetPreviewStreamVideoModeImpl(GetPreviewStreamVideoModeTask* aGetPreviewStreamVideoMode)
 {
-  nsCOMPtr<GetPreviewStreamResult> getPreviewStreamResult = nullptr;
-
   // stop any currently running preview
   StopPreviewInternal(true /* forced */);
 
-  // copy the recording preview options
+  // setup the video mode
   mVideoRotation = aGetPreviewStreamVideoMode->mOptions.rotation;
-  mVideoWidth = aGetPreviewStreamVideoMode->mOptions.width;
-  mVideoHeight = aGetPreviewStreamVideoMode->mOptions.height;
-  DOM_CAMERA_LOGI("recording preview format: %d x %d (w x h) (rotated %d degrees)\n", mVideoWidth, mVideoHeight, mVideoRotation);
-
-  // setup the video mode
-  nsresult rv = SetupVideoMode();
+  nsresult rv = SetupVideoMode(aGetPreviewStreamVideoMode->mOptions.profile);
   NS_ENSURE_SUCCESS(rv, rv);
+  
+  const RecorderVideoProfile* video = mRecorderProfile->GetVideoProfile();
+  int width = video->GetWidth();
+  int height = video->GetHeight();
+  int fps = video->GetFramerate();
+  DOM_CAMERA_LOGI("recording preview format: %d x %d (rotated %d degrees)\n", width, height, fps);
 
   // create and return new preview stream object
-  getPreviewStreamResult = new GetPreviewStreamResult(this, mVideoWidth, mVideoHeight, mVideoFrameRate, aGetPreviewStreamVideoMode->mOnSuccessCb, mWindowId);
+  nsCOMPtr<GetPreviewStreamResult> getPreviewStreamResult = new GetPreviewStreamResult(this, width, height, fps, aGetPreviewStreamVideoMode->mOnSuccessCb, mWindowId);
   rv = NS_DispatchToMainThread(getPreviewStreamResult);
   if (NS_FAILED(rv)) {
     NS_WARNING("Failed to dispatch GetPreviewStreamVideoMode() onSuccess callback to main thread!");
     return rv;
   }
 
   return NS_OK;
 }
 
+already_AddRefed<GonkRecorderProfileManager>
+nsGonkCameraControl::GetGonkRecorderProfileManager()
+{
+  if (!mProfileManager) {
+    nsTArray<CameraSize> sizes;
+    nsresult rv = GetVideoSizes(sizes);
+    NS_ENSURE_SUCCESS(rv, nullptr);
+
+    mProfileManager = new GonkRecorderProfileManager(mCameraId);
+    mProfileManager->SetSupportedResolutions(sizes);
+  }
+
+  nsRefPtr<GonkRecorderProfileManager> profileMgr = mProfileManager;
+  return profileMgr.forget();
+}
+
+already_AddRefed<RecorderProfileManager>
+nsGonkCameraControl::GetRecorderProfileManagerImpl()
+{
+  nsRefPtr<RecorderProfileManager> profileMgr = GetGonkRecorderProfileManager();
+  return profileMgr.forget();
+}
+
+nsresult
+nsGonkCameraControl::GetVideoSizes(nsTArray<CameraSize>& aVideoSizes)
+{
+  aVideoSizes.Clear();
+
+  Vector<Size> sizes;
+  mParams.getSupportedVideoSizes(sizes);
+  if (sizes.size() == 0) {
+    DOM_CAMERA_LOGI("Camera doesn't support video independent of the preview\n");
+    mParams.getSupportedPreviewSizes(sizes);
+  }
+
+  if (sizes.size() == 0) {
+    DOM_CAMERA_LOGW("Camera doesn't report any supported video sizes at all\n");
+    return NS_OK;
+  }
+
+  for (size_t i = 0; i < sizes.size(); ++i) {
+    CameraSize size;
+    size.width = sizes[i].width;
+    size.height = sizes[i].height;
+    aVideoSizes.AppendElement(size);
+  }
+  return NS_OK;
+}
+
 // Gonk callback handlers.
 namespace mozilla {
 
 void
 ReceiveImage(nsGonkCameraControl* gc, uint8_t* aData, uint32_t aLength)
 {
   gc->TakePictureComplete(aData, aLength);
 }
--- a/dom/camera/GonkCameraControl.h
+++ b/dom/camera/GonkCameraControl.h
@@ -21,40 +21,46 @@
 #include "prtypes.h"
 #include "prrwlock.h"
 #include "nsIDOMCameraManager.h"
 #include "DOMCameraControl.h"
 #include "CameraControlImpl.h"
 #include "CameraCommon.h"
 #include "GonkRecorder.h"
 
+using namespace android;
+
 namespace mozilla {
 
 namespace layers {
 class GraphicBufferLocked;
 }
 
+class GonkRecorderProfile;
+class GonkRecorderProfileManager;
+
 class nsGonkCameraControl : public CameraControlImpl
 {
 public:
   nsGonkCameraControl(uint32_t aCameraId, nsIThread* aCameraThread, nsDOMCameraControl* aDOMCameraControl, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError, uint64_t aWindowId);
   nsresult Init();
 
   const char* GetParameter(const char* aKey);
   const char* GetParameterConstChar(uint32_t aKey);
   double GetParameterDouble(uint32_t aKey);
   void GetParameter(uint32_t aKey, nsTArray<dom::CameraRegion>& aRegions);
   void SetParameter(const char* aKey, const char* aValue);
   void SetParameter(uint32_t aKey, const char* aValue);
   void SetParameter(uint32_t aKey, double aValue);
   void SetParameter(uint32_t aKey, const nsTArray<dom::CameraRegion>& aRegions);
+  nsresult GetVideoSizes(nsTArray<CameraSize>& aVideoSizes);
   nsresult PushParameters();
 
-  nsresult SetupRecording(int aFd);
-  nsresult SetupVideoMode();
+  nsresult SetupRecording(int aFd, int aMaxFileSizeBytes = -1, int aMaxVideoLengthMs = -1);
+  nsresult SetupVideoMode(const nsAString& aProfile);
 
   void AutoFocusComplete(bool aSuccess);
   void TakePictureComplete(uint8_t* aData, uint32_t aLength);
 
 protected:
   ~nsGonkCameraControl();
 
   nsresult GetPreviewStreamImpl(GetPreviewStreamTask* aGetPreviewStream);
@@ -63,16 +69,18 @@ protected:
   nsresult StopPreviewInternal(bool aForced = false);
   nsresult AutoFocusImpl(AutoFocusTask* aAutoFocus);
   nsresult TakePictureImpl(TakePictureTask* aTakePicture);
   nsresult StartRecordingImpl(StartRecordingTask* aStartRecording);
   nsresult StopRecordingImpl(StopRecordingTask* aStopRecording);
   nsresult PushParametersImpl();
   nsresult PullParametersImpl();
   nsresult GetPreviewStreamVideoModeImpl(GetPreviewStreamVideoModeTask* aGetPreviewStreamVideoMode);
+  already_AddRefed<RecorderProfileManager> GetRecorderProfileManagerImpl();
+  already_AddRefed<GonkRecorderProfileManager> GetGonkRecorderProfileManager();
 
   void SetPreviewSize(uint32_t aWidth, uint32_t aHeight);
 
   uint32_t                  mHwHandle;
   double                    mExposureCompensationMin;
   double                    mExposureCompensationStep;
   bool                      mDeferConfigUpdate;
   PRRWLock*                 mRwLock;
@@ -89,32 +97,21 @@ protected:
 
   uint32_t                  mFps;
   uint32_t                  mDiscardedFrameCount;
 
   android::MediaProfiles*   mMediaProfiles;
   android::GonkRecorder*    mRecorder;
 
   uint32_t                  mVideoRotation;
-  uint32_t                  mVideoWidth;
-  uint32_t                  mVideoHeight;
   nsString                  mVideoFile;
 
   // camcorder profile settings for the desired quality level
-  int mDuration;        // max recording duration (ignored)
-  int mVideoFileFormat; // output file format
-  int mVideoCodec;      // video encoder
-  int mVideoBitRate;    // video bit rate
-  int mVideoFrameRate;  // video frame rate
-  int mVideoFrameWidth; // video frame width
-  int mVideoFrameHeight;// video frame height
-  int mAudioCodec;      // audio encoder
-  int mAudioBitRate;    // audio bit rate
-  int mAudioSampleRate; // audio sample rate
-  int mAudioChannels;   // number of audio channels
+  nsRefPtr<GonkRecorderProfileManager> mProfileManager;
+  nsRefPtr<GonkRecorderProfile> mRecorderProfile;
 
 private:
   nsGonkCameraControl(const nsGonkCameraControl&) MOZ_DELETE;
   nsGonkCameraControl& operator=(const nsGonkCameraControl&) MOZ_DELETE;
 };
 
 // camera driver callbacks
 void ReceiveImage(nsGonkCameraControl* gc, uint8_t* aData, uint32_t aLength);
new file mode 100644
--- /dev/null
+++ b/dom/camera/GonkRecorderProfiles.cpp
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2012 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <media/MediaProfiles.h>
+#include "GonkRecorder.h"
+#include "CameraControlImpl.h"
+#include "GonkRecorderProfiles.h"
+#include "CameraCommon.h"
+
+using namespace mozilla;
+using namespace android;
+
+#define DEF_GONK_RECORDER_PROFILE(e, n) e##_INDEX,
+enum {
+  #include "GonkRecorderProfiles.def"
+  PROFILE_COUNT
+};
+
+#define DEF_GONK_RECORDER_PROFILE(e, n) { n, e },
+static struct {
+  const char* name;
+  int quality;
+} ProfileList[] = {
+  #include "GonkRecorderProfiles.def"
+  { nullptr, 0 }
+};
+
+static MediaProfiles* sMediaProfiles = nullptr;
+
+static bool
+IsQualitySupported(uint32_t aCameraId, uint32_t aQualityIndex)
+{
+  if (!sMediaProfiles) {
+    sMediaProfiles = MediaProfiles::getInstance();
+  }
+  camcorder_quality q = static_cast<camcorder_quality>(ProfileList[aQualityIndex].quality);
+  return sMediaProfiles->hasCamcorderProfile(static_cast<int>(aCameraId), q);
+}
+
+static int
+GetProfileParam(uint32_t aCameraId, uint32_t aQualityIndex, const char* aParam)
+{
+  if (!sMediaProfiles) {
+    sMediaProfiles = MediaProfiles::getInstance();
+  }
+  camcorder_quality q = static_cast<camcorder_quality>(ProfileList[aQualityIndex].quality);
+  return sMediaProfiles->getCamcorderProfileParamByName(aParam, static_cast<int>(aCameraId), q);
+}
+
+/**
+ * Recorder profile.
+ */
+static RecorderProfile::FileFormat
+TranslateFileFormat(output_format aFileFormat)
+{
+  switch (aFileFormat) {
+    case OUTPUT_FORMAT_THREE_GPP: return RecorderProfile::THREE_GPP;
+    case OUTPUT_FORMAT_MPEG_4:    return RecorderProfile::MPEG4;
+    default:                      return RecorderProfile::UNKNOWN;
+  }
+}
+
+GonkRecorderProfile::GonkRecorderProfile(uint32_t aCameraId, uint32_t aQualityIndex)
+  : RecorderProfileBase<GonkRecorderAudioProfile, GonkRecorderVideoProfile>(aCameraId, aQualityIndex)
+{
+  DOM_CAMERA_LOGT("%s:%d : this=%p, mCameraId=%d, mQualityIndex=%d\n", __func__, __LINE__, this, mCameraId, mQualityIndex);
+  mPlatformOutputFormat = static_cast<output_format>(GetProfileParam(mCameraId, mQualityIndex, "file.format"));
+  mFileFormat = TranslateFileFormat(mPlatformOutputFormat);
+  if (aQualityIndex < PROFILE_COUNT) {
+    mName = ProfileList[aQualityIndex].name;
+    DOM_CAMERA_LOGI("Created camera %d profile index %d: '%s'\n", mCameraId, mQualityIndex, mName);
+  }
+}
+
+GonkRecorderProfile::~GonkRecorderProfile()
+{
+  DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+}
+
+nsresult
+GonkRecorderProfile::ConfigureRecorder(GonkRecorder* aRecorder)
+{
+  static const size_t SIZE = 256;
+  char buffer[SIZE];
+
+  // set all the params
+  CHECK_SETARG(aRecorder->setAudioSource(AUDIO_SOURCE_CAMCORDER));
+  CHECK_SETARG(aRecorder->setVideoSource(VIDEO_SOURCE_CAMERA));
+  CHECK_SETARG(aRecorder->setOutputFormat(GetOutputFormat()));
+  CHECK_SETARG(aRecorder->setVideoFrameRate(mVideo.GetFramerate()));
+  CHECK_SETARG(aRecorder->setVideoSize(mVideo.GetWidth(), mVideo.GetHeight()));
+  CHECK_SETARG(aRecorder->setVideoEncoder(mVideo.GetPlatformCodec()));
+  CHECK_SETARG(aRecorder->setAudioEncoder(mAudio.GetPlatformCodec()));
+
+  snprintf(buffer, SIZE, "video-param-encoding-bitrate=%d", mVideo.GetBitrate());
+  CHECK_SETARG(aRecorder->setParameters(String8(buffer)));
+
+  snprintf(buffer, SIZE, "audio-param-encoding-bitrate=%d", mAudio.GetBitrate());
+  CHECK_SETARG(aRecorder->setParameters(String8(buffer)));
+
+  snprintf(buffer, SIZE, "audio-param-number-of-channels=%d", mAudio.GetChannels());
+  CHECK_SETARG(aRecorder->setParameters(String8(buffer)));
+
+  snprintf(buffer, SIZE, "audio-param-sampling-rate=%d", mAudio.GetSamplerate());
+  CHECK_SETARG(aRecorder->setParameters(String8(buffer)));
+
+  return NS_OK;
+}
+
+/**
+ * Recorder audio profile.
+ */
+static RecorderAudioProfile::Codec
+TranslateAudioCodec(audio_encoder aCodec)
+{
+  switch (aCodec) {
+    case AUDIO_ENCODER_AMR_NB:  return RecorderAudioProfile::AMRNB;
+    case AUDIO_ENCODER_AMR_WB:  return RecorderAudioProfile::AMRWB;
+    case AUDIO_ENCODER_AAC:     return RecorderAudioProfile::AAC;
+    default:                    return RecorderAudioProfile::UNKNOWN;
+  }
+}
+
+GonkRecorderAudioProfile::GonkRecorderAudioProfile(uint32_t aCameraId, uint32_t aQualityIndex)
+  : RecorderAudioProfile(aCameraId, aQualityIndex)
+{
+  DOM_CAMERA_LOGT("%s:%d : this=%p, mCameraId=%d, mQualityIndex=%d\n", __func__, __LINE__, this, mCameraId, mQualityIndex);
+  mPlatformCodec = static_cast<audio_encoder>(GetProfileParam(mCameraId, mQualityIndex, "aud.codec"));
+  mCodec = TranslateAudioCodec(mPlatformCodec);
+  mBitrate = GetProfileParam(mCameraId, mQualityIndex, "aud.bps");
+  mSamplerate = GetProfileParam(mCameraId, mQualityIndex, "aud.hz");
+  mChannels = GetProfileParam(mCameraId, mQualityIndex, "aud.ch");
+}
+
+GonkRecorderAudioProfile::~GonkRecorderAudioProfile()
+{
+  DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+}
+
+/**
+ * Recorder video profile.
+ */
+static RecorderVideoProfile::Codec
+TranslateVideoCodec(video_encoder aCodec)
+{
+  switch (aCodec) {
+    case VIDEO_ENCODER_H263:      return RecorderVideoProfile::H263;
+    case VIDEO_ENCODER_H264:      return RecorderVideoProfile::H264;
+    case VIDEO_ENCODER_MPEG_4_SP: return RecorderVideoProfile::MPEG4SP;
+    default:                      return RecorderVideoProfile::UNKNOWN;
+  }
+}
+
+GonkRecorderVideoProfile::GonkRecorderVideoProfile(uint32_t aCameraId, uint32_t aQualityIndex)
+  : RecorderVideoProfile(aCameraId, aQualityIndex)
+{
+  DOM_CAMERA_LOGT("%s:%d : this=%p, mCameraId=%d, mQualityIndex=%d\n", __func__, __LINE__, this, mCameraId, mQualityIndex);
+  mPlatformCodec = static_cast<video_encoder>(GetProfileParam(mCameraId, mQualityIndex, "vid.codec"));
+  mCodec = TranslateVideoCodec(mPlatformCodec);
+  mBitrate = GetProfileParam(mCameraId, mQualityIndex, "vid.bps");
+  mFramerate = GetProfileParam(mCameraId, mQualityIndex, "vid.fps");
+  mWidth = GetProfileParam(mCameraId, mQualityIndex, "vid.width");
+  mHeight = GetProfileParam(mCameraId, mQualityIndex, "vid.height");
+}
+
+GonkRecorderVideoProfile::~GonkRecorderVideoProfile()
+{
+  DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+}
+
+GonkRecorderProfileManager::GonkRecorderProfileManager(uint32_t aCameraId)
+  : RecorderProfileManager(aCameraId)
+{
+  DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  mMaxQualityIndex = CAMCORDER_QUALITY_LIST_END - CAMCORDER_QUALITY_LIST_START;
+}
+
+GonkRecorderProfileManager::~GonkRecorderProfileManager()
+{
+  DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+}
+
+bool
+GonkRecorderProfileManager::IsSupported(uint32_t aQualityIndex) const
+{
+  if (!IsQualitySupported(mCameraId, aQualityIndex)) {
+    // This profile is not supported
+    return false;
+  }
+
+  int width = GetProfileParam(mCameraId, aQualityIndex, "vid.width");
+  int height = GetProfileParam(mCameraId, aQualityIndex, "vid.height");
+  if (width == -1 || height == -1) {
+    // This would be unexpected, but we handle it just in case
+    DOM_CAMERA_LOGE("Camera %d recorder profile %d has width=%d, height=%d\n", mCameraId, aQualityIndex, width, height);
+    return false;
+  }
+
+  for (uint32_t i = 0; i < mSupportedSizes.Length(); ++i) {
+    if (static_cast<uint32_t>(width) == mSupportedSizes[i].width &&
+      static_cast<uint32_t>(height) == mSupportedSizes[i].height)
+    {
+      return true;
+    }
+  }
+  return false;
+}
+
+already_AddRefed<RecorderProfile>
+GonkRecorderProfileManager::Get(uint32_t aQualityIndex) const
+{
+  // This overrides virtual RecorderProfileManager::Get(...)
+  DOM_CAMERA_LOGT("%s:%d : aQualityIndex=%d\n", __func__, __LINE__, aQualityIndex);
+  nsRefPtr<RecorderProfile> profile = new GonkRecorderProfile(mCameraId, aQualityIndex);
+  return profile.forget();
+}
+
+already_AddRefed<GonkRecorderProfile>
+GonkRecorderProfileManager::Get(const char* aProfileName) const
+{
+  DOM_CAMERA_LOGT("%s:%d : aProfileName='%s'\n", __func__, __LINE__, aProfileName);
+  for (uint32_t i = 0; i < mMaxQualityIndex; ++i) {
+    if (strcmp(ProfileList[i].name, aProfileName) == 0) {
+      nsRefPtr<GonkRecorderProfile> profile = nullptr;
+      if (IsSupported(i)) {
+        profile = new GonkRecorderProfile(mCameraId, i);
+        return profile.forget();
+      }
+      return nullptr;
+    }
+  }
+
+  DOM_CAMERA_LOGW("Couldn't file recorder profile named '%s'\n", aProfileName);
+  return nullptr;
+}
new file mode 100644
--- /dev/null
+++ b/dom/camera/GonkRecorderProfiles.def
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2012 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * DO NOT PUT RE-INCLUSION GUARD MACROS AROUND THIS HEADER!!!
+ */
+
+DEF_GONK_RECORDER_PROFILE(CAMCORDER_QUALITY_LOW,   "low")
+DEF_GONK_RECORDER_PROFILE(CAMCORDER_QUALITY_HIGH,  "high")
+DEF_GONK_RECORDER_PROFILE(CAMCORDER_QUALITY_QCIF,  "qcif")
+DEF_GONK_RECORDER_PROFILE(CAMCORDER_QUALITY_CIF,   "cif")
+DEF_GONK_RECORDER_PROFILE(CAMCORDER_QUALITY_480P,  "480p")
+DEF_GONK_RECORDER_PROFILE(CAMCORDER_QUALITY_720P,  "720p")
+DEF_GONK_RECORDER_PROFILE(CAMCORDER_QUALITY_1080P, "1080p")
+/**
+ * This profile is disabled because the CAMCORDER_QUALITY_QVGA value
+ * conflicts across AOSP and vendor-specific versions of MediaProfiles.h.
+ *
+ * See bug 804359.
+ */
+// DEF_GONK_RECORDER_PROFILE(CAMCORDER_QUALITY_QVGA,  "qvga")
+/**
+ * The following profiles do not appear in the AOSP version of
+ * MediaProfiles.h and are disabled.
+ *
+ * See bug 804359.
+ */
+// DEF_GONK_RECORDER_PROFILE(CAMCORDER_QUALITY_FWVGA, "fwvga")
+// DEF_GONK_RECORDER_PROFILE(CAMCORDER_QUALITY_WVGA,  "wvga")
+// DEF_GONK_RECORDER_PROFILE(CAMCORDER_QUALITY_VGA,   "vga")
+// DEF_GONK_RECORDER_PROFILE(CAMCORDER_QUALITY_WQVGA, "wqvga")
+#undef DEF_GONK_RECORDER_PROFILE
new file mode 100644
--- /dev/null
+++ b/dom/camera/GonkRecorderProfiles.h
@@ -0,0 +1,113 @@
+/* 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 DOM_CAMERA_GONK_RECORDER_PROFILES_H
+#define DOM_CAMERA_GONK_RECORDER_PROFILES_H
+
+#include <media/MediaProfiles.h>
+#include "CameraRecorderProfiles.h"
+
+#ifndef CHECK_SETARG
+#define CHECK_SETARG(x)                 \
+  do {                                  \
+    if (x) {                            \
+      DOM_CAMERA_LOGE(#x " failed\n");  \
+      return NS_ERROR_INVALID_ARG;      \
+    }                                   \
+  } while(0)
+#endif
+
+using namespace android;
+
+namespace android {
+class GonkRecorder;
+};
+
+namespace mozilla {
+
+/**
+ * Gonk-specific video profile.
+ */
+class GonkRecorderVideoProfile : public RecorderVideoProfile
+{
+public:
+  GonkRecorderVideoProfile(uint32_t aCameraId, uint32_t aQualityIndex);
+  ~GonkRecorderVideoProfile();
+  video_encoder GetPlatformCodec() const { return mPlatformCodec; }
+
+protected:
+  video_encoder mPlatformCodec;
+};
+
+/**
+ * Gonk-specific audio profile.
+ */
+class GonkRecorderAudioProfile : public RecorderAudioProfile
+{
+public:
+  GonkRecorderAudioProfile(uint32_t aCameraId, uint32_t aQualityIndex);
+  ~GonkRecorderAudioProfile();
+  audio_encoder GetPlatformCodec() const { return mPlatformCodec; }
+
+protected:
+  audio_encoder mPlatformCodec;
+};
+
+/**
+ * Gonk-specific recorder profile.
+ */
+class GonkRecorderProfile : public RecorderProfileBase<GonkRecorderAudioProfile, GonkRecorderVideoProfile>
+{
+public:
+  GonkRecorderProfile(uint32_t aCameraId, uint32_t aQualityIndex);
+
+  GonkRecorderAudioProfile* GetGonkAudioProfile() { return &mAudio; }
+  GonkRecorderVideoProfile* GetGonkVideoProfile() { return &mVideo; }
+
+  output_format GetOutputFormat() const { return mPlatformOutputFormat; }
+  nsresult ConfigureRecorder(GonkRecorder* aRecorder);
+
+protected:
+  virtual ~GonkRecorderProfile();
+
+  output_format mPlatformOutputFormat;
+};
+
+/**
+ * Gonk-specific profile manager.
+ */
+class GonkRecorderProfileManager : public RecorderProfileManager
+{
+public:
+  GonkRecorderProfileManager(uint32_t aCameraId);
+
+  /**
+   * Call this function to indicate that the specified resolutions are in fact
+   * supported by the camera hardware.  (Just because it appears in a recorder
+   * profile doesn't mean the hardware can handle it.)
+   */
+  void SetSupportedResolutions(const nsTArray<CameraSize>& aSizes)
+    { mSupportedSizes = aSizes; }
+
+  /**
+   * Call this function to remove all resolutions set by calling
+   * SetSupportedResolutions().
+   */
+  void ClearSupportedResolutions() { mSupportedSizes.Clear(); }
+
+  bool IsSupported(uint32_t aQualityIndex) const;
+
+  already_AddRefed<RecorderProfile> Get(uint32_t aQualityIndex) const;
+  already_AddRefed<GonkRecorderProfile> Get(const char* aProfileName) const;
+  nsresult ConfigureRecorder(android::GonkRecorder* aRecorder);
+
+protected:
+  virtual ~GonkRecorderProfileManager();
+
+  nsTArray<CameraSize> mSupportedSizes;
+};
+
+}; // namespace mozilla
+
+#endif // DOM_CAMERA_GONK_RECORDER_PROFILES_H
--- a/dom/camera/ICameraControl.h
+++ b/dom/camera/ICameraControl.h
@@ -1,53 +1,57 @@
 /* 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 DOM_CAMERA_ICAMERACONTROL_H
 #define DOM_CAMERA_ICAMERACONTROL_H
 
 #include "jsapi.h"
-#include "nsIDOMDeviceStorage.h"
+#include "nsIFile.h"
 #include "nsIDOMCameraManager.h"
 #include "DictionaryHelpers.h"
 #include "CameraCommon.h"
 
 namespace mozilla {
 
 using namespace dom;
 
 class DOMCameraPreview;
+class RecorderProfileManager;
 
 class ICameraControl
 {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ICameraControl)
 
   virtual nsresult GetPreviewStream(CameraSize aSize, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError) = 0;
   virtual nsresult StartPreview(DOMCameraPreview* aDOMPreview) = 0;
   virtual void StopPreview() = 0;
   virtual nsresult AutoFocus(nsICameraAutoFocusCallback* onSuccess, nsICameraErrorCallback* onError) = 0;
   virtual nsresult TakePicture(CameraSize aSize, int32_t aRotation, const nsAString& aFileFormat, CameraPosition aPosition, nsICameraTakePictureCallback* onSuccess, nsICameraErrorCallback* onError) = 0;
-  virtual nsresult StartRecording(nsIDOMDeviceStorage* aStorageArea, const nsAString& aFilename, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError) = 0;
+  virtual nsresult StartRecording(CameraStartRecordingOptions* aOptions, nsIFile* aFolder, const nsAString& aFilename, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError) = 0;
   virtual nsresult StopRecording() = 0;
-  virtual nsresult GetPreviewStreamVideoMode(CameraRecordingOptions* aOptions, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError) = 0;
+  virtual nsresult GetPreviewStreamVideoMode(CameraRecorderOptions* aOptions, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError) = 0;
 
   virtual nsresult Set(uint32_t aKey, const nsAString& aValue) = 0;
   virtual nsresult Get(uint32_t aKey, nsAString& aValue) = 0;
   virtual nsresult Set(uint32_t aKey, double aValue) = 0;
   virtual nsresult Get(uint32_t aKey, double* aValue) = 0;
   virtual nsresult Set(JSContext* aCx, uint32_t aKey, const JS::Value& aValue, uint32_t aLimit) = 0;
   virtual nsresult Get(JSContext* aCx, uint32_t aKey, JS::Value* aValue) = 0;
   virtual nsresult Set(nsICameraShutterCallback* aOnShutter) = 0;
   virtual nsresult Get(nsICameraShutterCallback** aOnShutter) = 0;
   virtual nsresult Set(nsICameraClosedCallback* aOnClosed) = 0;
   virtual nsresult Get(nsICameraClosedCallback** aOnClosed) = 0;
   virtual nsresult SetFocusAreas(JSContext* aCx, const JS::Value& aValue) = 0;
   virtual nsresult SetMeteringAreas(JSContext* aCx, const JS::Value& aValue) = 0;
+  virtual nsresult GetVideoSizes(nsTArray<CameraSize>& aVideoSizes) = 0;
+  virtual already_AddRefed<RecorderProfileManager> GetRecorderProfileManager() = 0;
+  virtual uint32_t GetCameraId() = 0;
 
   virtual const char* GetParameter(const char* aKey) = 0;
   virtual const char* GetParameterConstChar(uint32_t aKey) = 0;
   virtual double GetParameterDouble(uint32_t aKey) = 0;
   virtual void GetParameter(uint32_t aKey, nsTArray<CameraRegion>& aRegions) = 0;
   virtual void SetParameter(const char* aKey, const char* aValue) = 0;
   virtual void SetParameter(uint32_t aKey, const char* aValue) = 0;
   virtual void SetParameter(uint32_t aKey, double aValue) = 0;
--- a/dom/camera/Makefile.in
+++ b/dom/camera/Makefile.in
@@ -19,27 +19,29 @@ FAIL_ON_WARNINGS := 1
 include $(topsrcdir)/dom/dom-config.mk
 
 CPPSRCS = \
   DOMCameraManager.cpp \
   DOMCameraControl.cpp \
   DOMCameraPreview.cpp \
   DOMCameraCapabilities.cpp \
   CameraControlImpl.cpp \
+  CameraRecorderProfiles.cpp \
   $(NULL)
 
 ifeq ($(MOZ_B2G_CAMERA),1)
 CPPSRCS += \
   GonkCameraManager.cpp \
   GonkCameraControl.cpp \
   GonkCameraHwMgr.cpp \
   GonkNativeWindow.cpp \
   GonkRecorder.cpp \
   GonkCameraSource.cpp \
   AudioParameter.cpp \
+  GonkRecorderProfiles.cpp \
   $(NULL)
 else ifeq (gonk,$(MOZ_WIDGET_TOOLKIT))
 CPPSRCS += \
   FallbackCameraManager.cpp \
   FallbackCameraControl.cpp \
   GonkNativeWindow.cpp \
   $(NULL)
 else
--- a/dom/camera/nsIDOMCameraManager.idl
+++ b/dom/camera/nsIDOMCameraManager.idl
@@ -102,16 +102,22 @@ interface nsICameraCapabilities : nsISup
        or null if zooming is not supported */
     [implicit_jscontext]
     readonly attribute jsval        zoomRatios;
 
     /* an array of objects with 'height' and 'width' properties
        supported for video recording */
     [implicit_jscontext]
     readonly attribute jsval        videoSizes;
+
+    /* an object with attributes for each of the supported recorder
+       profiles, e.g. recorderProfiles.cif, recorderProfiles.qvga,
+       etc. */
+    [implicit_jscontext]
+    readonly attribute jsval        recorderProfiles;
 };
 
 /* These properties only affect the captured image;
    invalid property settings are ignored. */
 dictionary CameraPictureOptions
 {
     /* an object with a combination of 'height' and 'width' properties
        chosen from nsICameraCapabilities.pictureSizes */
@@ -141,22 +147,61 @@ dictionary CameraPictureOptions
         available/desired.
 
         'altitude' is in metres; 'timestamp' is UTC, in seconds from
         January 1, 1970.
     */
     jsval     position;
 };
 
-/* These properties affect video recording. */
-dictionary CameraRecordingOptions
+/* These properties affect the video recording preview, e.g.
+      {
+         profile: "1080p",
+         rotation: 0
+      }
+
+   'profile' is one of the profiles returned by
+   nsICameraCapabilities.recorderProfiles'; if this profile is missing,
+   an arbitrary profile will be chosen.
+
+   'rotation' is the degrees clockwise to rotate the preview; if
+   this options is not supported, it will be ignored; if this option
+   is missing, the default is 0.
+*/
+dictionary CameraRecorderOptions
 {
-    long width;
-    long height;
-    long rotation;
+    DOMString profile;
+    long      rotation;
+};
+
+/* These properties affect the actual video recording, e.g.
+      {
+         rotation: 0,
+         maxFileSizeBytes: 1024 * 1024,
+         maxVideoLengthMs: 0
+      }
+
+   'rotation' is the degrees clockwise to rotate the recorded video; if
+   this options is not supported, it will be ignored; if this option is
+   missing, the default is 0.
+
+   'maxFileSizeBytes' is the maximum size in bytes to which the recorded
+   video file will be allowed to grow.
+
+   'maxVideoLengthMs' is the maximum length in milliseconds to which the
+   recorded video will be allowed to grow.
+
+   if either 'maxFileSizeBytes' or 'maxVideoLengthMs' is missing, zero,
+   or negative, that limit will be disabled.
+*/
+dictionary CameraStartRecordingOptions
+{
+    long      rotation;
+    long      maxFileSizeBytes;
+    long      maxVideoLengthMs;
 };
 
 [scriptable, function, uuid(0444a687-4bc9-462c-8246-5423f0fe46a4)]
 interface nsICameraPreviewStreamCallback : nsISupports
 {
     void handleEvent(in nsIDOMMediaStream stream);
 };
 
@@ -303,31 +348,25 @@ interface nsICameraControl : nsISupports
        if the camera supports it, this may be invoked while the camera is
        already recording video.
 
        invoking this function will stop the preview stream, which must be
        manually restarted (e.g. by calling .play() on it). */
     [implicit_jscontext]
     void takePicture(in jsval aOptions, in nsICameraTakePictureCallback onSuccess, [optional] in nsICameraErrorCallback onError);
 
-    /* get a media stream to be used as a camera viewfinder in video mode; 'aOptions' 
-       define the frame size of the video capture, chosen from capabilities.videoSizes, e.g.:
-        {
-            width: 640,
-            height: 480,
-            rotation: 90
-        }
-    */
+    /* get a media stream to be used as a camera viewfinder in video mode;
+       'aOptions' is an CameraRecorderOptions object. */
     [implicit_jscontext]
     void getPreviewStreamVideoMode(in jsval aOptions, in nsICameraPreviewStreamCallback onSuccess, [optional] in nsICameraErrorCallback onError);
 
-    /* start recording video; 
-    */
+    /* start recording video; 'aOptions' is a
+       CameraStartRecordingOptions object. */
     [implicit_jscontext]
-    void startRecording(in nsIDOMDeviceStorage storageArea, in DOMString filename, in nsICameraStartRecordingCallback onSuccess, [optional] in nsICameraErrorCallback onError);
+    void startRecording(in jsval aOptions, in nsIDOMDeviceStorage storageArea, in DOMString filename, in nsICameraStartRecordingCallback onSuccess, [optional] in nsICameraErrorCallback onError);
 
     /* stop precording video. */
     void stopRecording();
 
     /* get a media stream to be used as a camera viewfinder; the options
        define the desired frame size of the preview, chosen from
        capabilities.previewSizes, e.g.:
         {