Bug 910498 - Camera changes to use CreateFd. r=mikeh
authorDave Hylands <dhylands@mozilla.com>
Thu, 16 Jan 2014 17:01:43 -0800
changeset 174732 2b74b0de04a4c7244ea1b53afe92bbb269a3b59c
parent 174731 4a1217f771c1646969eea35f7aa346972d9d37e4
child 174733 585ff8eb2be77d01dc6d8a7a01fd5ca66ffcda25
push id5166
push userlsblakk@mozilla.com
push dateTue, 04 Feb 2014 01:47:54 +0000
treeherdermozilla-aurora@977eb2548b2d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmikeh
bugs910498
milestone29.0a1
Bug 910498 - Camera changes to use CreateFd. r=mikeh * * * Fixes for camera patch
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
@@ -4,16 +4,17 @@
 
 #include "base/basictypes.h"
 #include "mozilla/Assertions.h"
 #include "DOMCameraPreview.h"
 #include "CameraRecorderProfiles.h"
 #include "CameraControlImpl.h"
 #include "CameraCommon.h"
 #include "nsGlobalWindow.h"
+#include "DeviceStorageFileDescriptor.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::idl;
 
 CameraControlImpl::CameraControlImpl(uint32_t aCameraId, nsIThread* aCameraThread, uint64_t aWindowId)
   : mCameraId(aCameraId)
   , mCameraThread(aCameraThread)
@@ -445,22 +446,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,20 @@
 #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"
+#include "DeviceStorageFileDescriptor.h"
+
+class DeviceStorageFileDescriptor;
 
 namespace mozilla {
 
 class GetPreviewStreamTask;
 class StartPreviewTask;
 class StopPreviewTask;
 class AutoFocusTask;
 class TakePictureTask;
@@ -49,17 +53,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 +451,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 +494,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
@@ -3,16 +3,17 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "base/basictypes.h"
 #include "nsCOMPtr.h"
 #include "nsDOMClassInfo.h"
 #include "nsHashPropertyBag.h"
 #include "nsThread.h"
 #include "DeviceStorage.h"
+#include "DeviceStorageFileDescriptor.h"
 #include "mozilla/dom/CameraControlBinding.h"
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/MediaManager.h"
 #include "mozilla/Services.h"
 #include "mozilla/unused.h"
 #include "nsIAppsService.h"
 #include "nsIObserverService.h"
 #include "nsIDOMDeviceStorage.h"
@@ -344,23 +345,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 +370,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
@@ -37,16 +37,17 @@
 #include "nsIVolumeService.h"
 #include "DOMCameraManager.h"
 #include "GonkCameraHwMgr.h"
 #include "DOMCameraCapabilities.h"
 #include "DOMCameraControl.h"
 #include "GonkRecorderProfiles.h"
 #include "GonkCameraControl.h"
 #include "CameraCommon.h"
+#include "DeviceStorageFileDescriptor.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::layers;
 using namespace android;
 using mozilla::gfx::IntSize;
 
 /**
@@ -1018,17 +1019,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 +1093,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;