Bug 910498 - Camera changes to use CreateFd. r=mikeh
☠☠ backed out by 971e06b74dd1 ☠ ☠
authorDave Hylands <dhylands@mozilla.com>
Thu, 16 Jan 2014 17:01:43 -0800
changeset 163933 64647f71727f1a1d7493daf61c85d54ede5d2a86
parent 163932 7ffefbeff08c759687893105f7f205702206f48c
child 163934 971e06b74dd1eada432f3caa2e9748f910a1fdca
push id38584
push usercbook@mozilla.com
push dateFri, 17 Jan 2014 10:04:30 +0000
treeherdermozilla-inbound@28a9d7e2416f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmikeh
bugs910498
milestone29.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 910498 - Camera changes to use CreateFd. r=mikeh
dom/camera/CameraControlImpl.cpp
dom/camera/CameraControlImpl.h
dom/camera/DOMCameraControl.cpp
dom/camera/DOMCameraControl.h
dom/camera/GonkCameraControl.cpp
dom/camera/ICameraControl.h
--- a/dom/camera/CameraControlImpl.cpp
+++ b/dom/camera/CameraControlImpl.cpp
@@ -445,22 +445,19 @@ CameraControlImpl::TakePicture(const Cam
     cancel = true;
   }
 
   nsCOMPtr<nsIRunnable> takePictureTask = new TakePictureTask(this, cancel, aSize, aRotation, aFileFormat, aPosition, aDateTime, onSuccess, onError);
   return mCameraThread->Dispatch(takePictureTask, NS_DISPATCH_NORMAL);
 }
 
 nsresult
-CameraControlImpl::StartRecording(CameraStartRecordingOptions* aOptions, nsIFile* aFolder, const nsAString& aFilename, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError)
+CameraControlImpl::StartRecording(CameraStartRecordingOptions* aOptions, DeviceStorageFileDescriptor* aFileDescriptor, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError)
 {
-  nsCOMPtr<nsIFile> clone;
-  aFolder->Clone(getter_AddRefs(clone));
-
-  nsCOMPtr<nsIRunnable> startRecordingTask = new StartRecordingTask(this, *aOptions, clone, aFilename, onSuccess, onError, mWindowId);
+  nsCOMPtr<nsIRunnable> startRecordingTask = new StartRecordingTask(this, *aOptions, aFileDescriptor, 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);
--- a/dom/camera/CameraControlImpl.h
+++ b/dom/camera/CameraControlImpl.h
@@ -9,16 +9,19 @@
 #include "nsDOMFile.h"
 #include "nsProxyRelease.h"
 #include "DictionaryHelpers.h"
 #include "nsIDOMDeviceStorage.h"
 #include "DOMCameraManager.h"
 #include "DOMCameraPreview.h"
 #include "ICameraControl.h"
 #include "CameraCommon.h"
+#include "DeviceStorage.h"
+
+class DeviceStorageFileDescriptor;
 
 namespace mozilla {
 
 class GetPreviewStreamTask;
 class StartPreviewTask;
 class StopPreviewTask;
 class AutoFocusTask;
 class TakePictureTask;
@@ -49,17 +52,17 @@ class CameraControlImpl : public ICamera
 public:
   CameraControlImpl(uint32_t aCameraId, nsIThread* aCameraThread, uint64_t aWindowId);
 
   nsresult GetPreviewStream(idl::CameraSize aSize, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError);
   nsresult StartPreview(DOMCameraPreview* aDOMPreview);
   void StopPreview();
   nsresult AutoFocus(nsICameraAutoFocusCallback* onSuccess, nsICameraErrorCallback* onError);
   nsresult TakePicture(const idl::CameraSize& aSize, int32_t aRotation, const nsAString& aFileFormat, idl::CameraPosition aPosition, uint64_t aDateTime, nsICameraTakePictureCallback* onSuccess, nsICameraErrorCallback* onError);
-  nsresult StartRecording(idl::CameraStartRecordingOptions* aOptions, nsIFile* aFolder, const nsAString& aFilename, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError);
+  nsresult StartRecording(idl::CameraStartRecordingOptions* aOptions, DeviceStorageFileDescriptor *aDSFileDescriptor, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError);
   nsresult StopRecording();
   nsresult GetPreviewStreamVideoMode(idl::CameraRecorderOptions* aOptions, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError);
   nsresult ReleaseHardware(nsICameraReleaseCallback* 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);
@@ -447,21 +450,25 @@ protected:
   nsMainThreadPtrHandle<nsICameraStartRecordingCallback> mOnSuccessCb;
   uint64_t mWindowId;
 };
 
 // Start video recording.
 class StartRecordingTask : public nsRunnable
 {
 public:
-  StartRecordingTask(CameraControlImpl* aCameraControl, idl::CameraStartRecordingOptions aOptions, nsIFile* aFolder, const nsAString& aFilename, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError, uint64_t aWindowId)
+  StartRecordingTask(CameraControlImpl* aCameraControl,
+                     idl::CameraStartRecordingOptions aOptions,
+                     DeviceStorageFileDescriptor *aDSFileDescriptor,
+                     nsICameraStartRecordingCallback* onSuccess,
+                     nsICameraErrorCallback* onError,
+                     uint64_t aWindowId)
     : mCameraControl(aCameraControl)
     , mOptions(aOptions)
-    , mFolder(aFolder)
-    , mFilename(aFilename)
+    , mDSFileDescriptor(aDSFileDescriptor)
     , mOnSuccessCb(new nsMainThreadPtrHolder<nsICameraStartRecordingCallback>(onSuccess))
     , mOnErrorCb(new nsMainThreadPtrHolder<nsICameraErrorCallback>(onError))
     , mWindowId(aWindowId)
   {
     DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
   }
 
   virtual ~StartRecordingTask()
@@ -486,18 +493,17 @@ public:
     if (NS_FAILED(rv)) {
       DOM_CAMERA_LOGE("Failed to dispatch start recording result to main thread (%d)!", rv);
     }
     return rv;
   }
 
   nsRefPtr<CameraControlImpl> mCameraControl;
   idl::CameraStartRecordingOptions mOptions;
-  nsCOMPtr<nsIFile> mFolder;
-  nsString mFilename;
+  nsRefPtr<DeviceStorageFileDescriptor> mDSFileDescriptor;
   nsMainThreadPtrHandle<nsICameraStartRecordingCallback> mOnSuccessCb;
   nsMainThreadPtrHandle<nsICameraErrorCallback> mOnErrorCb;
   uint64_t mWindowId;
 };
 
 // Stop video recording.
 class StopRecordingTask : public nsRunnable
 {
--- a/dom/camera/DOMCameraControl.cpp
+++ b/dom/camera/DOMCameraControl.cpp
@@ -344,23 +344,22 @@ nsDOMCameraControl::StartRecording(JSCon
                                    JS::Handle<JS::Value> aOptions,
                                    nsDOMDeviceStorage& storageArea,
                                    const nsAString& filename,
                                    nsICameraStartRecordingCallback* onSuccess,
                                    const Optional<nsICameraErrorCallback*>& onError,
                                    ErrorResult& aRv)
 {
   MOZ_ASSERT(onSuccess, "no onSuccess handler passed");
-  mozilla::idl::CameraStartRecordingOptions options;
 
   // Default values, until the dictionary parser can handle them.
-  options.rotation = 0;
-  options.maxFileSizeBytes = 0;
-  options.maxVideoLengthMs = 0;
-  aRv = options.Init(aCx, aOptions.address());
+  mOptions.rotation = 0;
+  mOptions.maxFileSizeBytes = 0;
+  mOptions.maxVideoLengthMs = 0;
+  aRv = mOptions.Init(aCx, aOptions.address());
   if (aRv.Failed()) {
     return;
   }
 
   aRv = NotifyRecordingStatusChange(NS_LITERAL_STRING("starting"));
 
   #ifdef MOZ_B2G
   if (!mAudioChannelAgent) {
@@ -370,23 +369,63 @@ nsDOMCameraControl::StartRecording(JSCon
       mAudioChannelAgent->Init(AUDIO_CHANNEL_CONTENT, nullptr);
       // Video recording doesn't output any sound, so it's not necessary to check canPlay.
       int32_t canPlay;
       mAudioChannelAgent->StartPlaying(&canPlay);
     }
   }
   #endif
 
-  nsCOMPtr<nsIFile> folder;
-  aRv = storageArea.GetRootDirectoryForFile(filename, getter_AddRefs(folder));
+  nsCOMPtr<nsIDOMDOMRequest> request;
+  mDSFileDescriptor = new DeviceStorageFileDescriptor();
+  aRv = storageArea.CreateFileDescriptor(filename, mDSFileDescriptor.get(),
+                                         getter_AddRefs(request));
   if (aRv.Failed()) {
     return;
   }
-  aRv = mCameraControl->StartRecording(&options, folder, filename, onSuccess,
-                                       onError.WasPassed() ? onError.Value() : nullptr);
+
+  mOnSuccessCb = onSuccess;
+  mOnErrorCb = onError.WasPassed() ? onError.Value() : nullptr;
+
+  request->AddEventListener(NS_LITERAL_STRING("success"), this, false);
+  request->AddEventListener(NS_LITERAL_STRING("error"), this, false);
+}
+
+NS_IMETHODIMP
+nsDOMCameraControl::HandleEvent(nsIDOMEvent* aEvent)
+{
+  nsString  eventType;
+  aEvent->GetType(eventType);
+  ErrorResult rv;
+
+  if ((eventType.EqualsLiteral("success")) &&
+      mDSFileDescriptor->mFileDescriptor.IsValid()) {
+
+    rv = mCameraControl->StartRecording(&mOptions,
+                                        mDSFileDescriptor.get(),
+                                        mOnSuccessCb.get(),
+                                        mOnErrorCb.get());
+    if (!rv.Failed()) {
+      return rv.ErrorCode();
+    }
+
+    // An error happened. Fall through and call the error callback.
+  }
+
+  // We're already be on the main thread, so go ahead and call the
+  // error callback directly.
+
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (mOnErrorCb &&
+      nsDOMCameraManager::IsWindowStillActive(mWindow->WindowID())) {
+    mOnErrorCb->HandleEvent(NS_LITERAL_STRING("FAILURE"));
+  }
+
+  return NS_OK;
 }
 
 void
 nsDOMCameraControl::StopRecording(ErrorResult& aRv)
 {
   aRv = NotifyRecordingStatusChange(NS_LITERAL_STRING("shutdown"));
 
   #ifdef MOZ_B2G
--- a/dom/camera/DOMCameraControl.h
+++ b/dom/camera/DOMCameraControl.h
@@ -3,41 +3,44 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef DOM_CAMERA_DOMCAMERACONTROL_H
 #define DOM_CAMERA_DOMCAMERACONTROL_H
 
 #include "nsCOMPtr.h"
 #include "nsAutoPtr.h"
 #include "nsCycleCollectionParticipant.h"
+#include "nsIDOMEventListener.h"
 #include "DictionaryHelpers.h"
 #include "ICameraControl.h"
 #include "DOMCameraPreview.h"
 #include "nsIDOMCameraManager.h"
 #include "CameraCommon.h"
 #include "AudioChannelAgent.h"
 #include "nsProxyRelease.h"
 #include "nsHashPropertyBag.h"
+#include "DeviceStorage.h"
 
 class nsDOMDeviceStorage;
 class nsPIDOMWindow;
 
 namespace mozilla {
 namespace dom {
 class CameraPictureOptions;
 template<typename T> class Optional;
 }
 class ErrorResult;
 
 // Main camera control.
-class nsDOMCameraControl MOZ_FINAL : public nsISupports,
+class nsDOMCameraControl MOZ_FINAL : public nsIDOMEventListener,
                                      public nsWrapperCache
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_NSIDOMEVENTLISTENER
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsDOMCameraControl)
 
   nsDOMCameraControl(uint32_t aCameraId, nsIThread* aCameraThread,
                      nsICameraGetCameraCallback* onSuccess,
                      nsICameraErrorCallback* onError, nsPIDOMWindow* aWindow);
   nsresult Result(nsresult aResult,
                   const nsMainThreadPtrHandle<nsICameraGetCameraCallback>& onSuccess,
                   const nsMainThreadPtrHandle<nsICameraErrorCallback>& onError,
@@ -102,16 +105,21 @@ protected:
 
 private:
   nsDOMCameraControl(const nsDOMCameraControl&) MOZ_DELETE;
   nsDOMCameraControl& operator=(const nsDOMCameraControl&) MOZ_DELETE;
 
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
   nsresult NotifyRecordingStatusChange(const nsString& aMsg);
 
+  mozilla::idl::CameraStartRecordingOptions mOptions;
+  nsRefPtr<DeviceStorageFileDescriptor> mDSFileDescriptor;
+  nsCOMPtr<nsICameraStartRecordingCallback> mOnSuccessCb;
+  nsCOMPtr<nsICameraErrorCallback> mOnErrorCb;
+
 protected:
   /* additional members */
   nsRefPtr<ICameraControl>        mCameraControl; // non-DOM camera control
   nsCOMPtr<nsICameraCapabilities> mDOMCapabilities;
   // An agent used to join audio channel service.
   nsCOMPtr<nsIAudioChannelAgent>  mAudioChannelAgent;
   nsCOMPtr<nsPIDOMWindow> mWindow;
 };
--- a/dom/camera/GonkCameraControl.cpp
+++ b/dom/camera/GonkCameraControl.cpp
@@ -1018,17 +1018,17 @@ nsGonkCameraControl::TakePictureImpl(Tak
     SetParameter(CameraParameters::KEY_GPS_TIMESTAMP, nsPrintfCString("%lf", aTakePicture->mPosition.timestamp).get());
   }
 
   // Add the non-GPS timestamp.  The EXIF date/time field is formatted as
   // "YYYY:MM:DD HH:MM:SS", without room for a time-zone; as such, the time
   // is meant to be stored as a local time.  Since we are given seconds from
   // Epoch GMT, we use localtime_r() to handle the conversion.
   time_t time = aTakePicture->mDateTime;
-  if (time != aTakePicture->mDateTime) {
+  if ((uint64_t)time != aTakePicture->mDateTime) {
     DOM_CAMERA_LOGE("picture date/time '%llu' is too far in the future\n", aTakePicture->mDateTime);
   } else {
     struct tm t;
     if (localtime_r(&time, &t)) {
       char dateTime[20];
       if (strftime(dateTime, sizeof(dateTime), "%Y:%m:%d %T", &t)) {
         DOM_CAMERA_LOGI("setting picture date/time to %s\n", dateTime);
         // Not every platform defines a CameraParameters::KEY_EXIF_DATETIME;
@@ -1092,52 +1092,34 @@ 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->mFolder;
-  filename->AppendRelativePath(aStartRecording->mFilename);
-
-  nsString fullpath;
-  filename->GetPath(fullpath);
-
-  nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID);
-  NS_ENSURE_TRUE(vs, NS_ERROR_FAILURE);
-
-  nsCOMPtr<nsIVolume> vol;
-  nsresult rv = vs->GetVolumeByPath(fullpath, getter_AddRefs(vol));
-  NS_ENSURE_SUCCESS(rv, NS_ERROR_INVALID_ARG);
-
-  nsString volName;
-  vol->GetName(volName);
-
-  mVideoFile = new DeviceStorageFile(NS_LITERAL_STRING("videos"),
-                                     volName,
-                                     aStartRecording->mFilename);
-
-  nsAutoCString nativeFilename;
-  filename->GetNativePath(nativeFilename);
-  DOM_CAMERA_LOGI("Video filename is '%s'\n", nativeFilename.get());
+  nsRefPtr<DeviceStorageFileDescriptor> dsfd = aStartRecording->mDSFileDescriptor;
+  NS_ENSURE_TRUE(dsfd, NS_ERROR_FAILURE);
+  nsAutoString fullPath;
+  mVideoFile = dsfd->mDSFile;
+  mVideoFile->GetFullPath(fullPath);
+  DOM_CAMERA_LOGI("Video filename is '%s'\n",
+                  NS_LossyConvertUTF16toASCII(fullPath).get());
 
   if (!mVideoFile->IsSafePath()) {
     DOM_CAMERA_LOGE("Invalid video file name\n");
     return NS_ERROR_INVALID_ARG;
   }
 
-  ScopedClose fd(open(nativeFilename.get(), O_RDWR | O_CREAT, 0644));
-  if (fd < 0) {
-    DOM_CAMERA_LOGE("Couldn't create file '%s': (%d) %s\n", nativeFilename.get(), errno, strerror(errno));
-    return NS_ERROR_FAILURE;
-  }
-
-  rv = SetupRecording(fd, aStartRecording->mOptions.rotation, aStartRecording->mOptions.maxFileSizeBytes, aStartRecording->mOptions.maxVideoLengthMs);
+  nsresult rv;
+  rv = SetupRecording(dsfd->mFileDescriptor.PlatformHandle(),
+                      aStartRecording->mOptions.rotation,
+                      aStartRecording->mOptions.maxFileSizeBytes,
+                      aStartRecording->mOptions.maxVideoLengthMs);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (mRecorder->start() != OK) {
     DOM_CAMERA_LOGE("mRecorder->start() failed\n");
     // important: we MUST destroy the recorder if start() fails!
     mRecorder = nullptr;
     return NS_ERROR_FAILURE;
   }
--- a/dom/camera/ICameraControl.h
+++ b/dom/camera/ICameraControl.h
@@ -5,32 +5,34 @@
 #ifndef DOM_CAMERA_ICAMERACONTROL_H
 #define DOM_CAMERA_ICAMERACONTROL_H
 
 #include "nsIFile.h"
 #include "nsIDOMCameraManager.h"
 #include "DictionaryHelpers.h"
 #include "CameraCommon.h"
 
+class DeviceStorageFileDescriptor;
+
 namespace mozilla {
 
 class DOMCameraPreview;
 class RecorderProfileManager;
 
 class ICameraControl
 {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ICameraControl)
 
   virtual nsresult GetPreviewStream(idl::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(const idl::CameraSize& aSize, int32_t aRotation, const nsAString& aFileFormat, idl::CameraPosition aPosition, uint64_t aDateTime, nsICameraTakePictureCallback* onSuccess, nsICameraErrorCallback* onError) = 0;
-  virtual nsresult StartRecording(idl::CameraStartRecordingOptions* aOptions, nsIFile* aFolder, const nsAString& aFilename, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError) = 0;
+  virtual nsresult StartRecording(idl::CameraStartRecordingOptions* aOptions, DeviceStorageFileDescriptor *aFileDescriptor, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError) = 0;
   virtual nsresult StopRecording() = 0;
   virtual nsresult GetPreviewStreamVideoMode(idl::CameraRecorderOptions* aOptions, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError) = 0;
   virtual nsresult ReleaseHardware(nsICameraReleaseCallback* 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;