Bug 818933 - Provide a hardware release API so that we don't have to rely on CC of CameraControl objects. r=mhabicher
authorMike Habicher <mikeh@mozilla.com>
Sun, 23 Dec 2012 10:54:54 -0500
changeset 126062 606f13fe356fac14f7e06e9817d70a1ae28cbc6d
parent 126061 63f38c1cf053010a07f0f9f7e4c8610515f5ab3c
child 126063 8099630f363d122a13c5cfc62fdb6b16cc894b97
push id2151
push userlsblakk@mozilla.com
push dateTue, 19 Feb 2013 18:06:57 +0000
treeherdermozilla-beta@4952e88741ec [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmhabicher
bugs818933
milestone20.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 818933 - Provide a hardware release API so that we don't have to rely on CC of CameraControl objects. r=mhabicher
dom/camera/CameraControlImpl.cpp
dom/camera/CameraControlImpl.h
dom/camera/DOMCameraControl.cpp
dom/camera/DOMCameraPreview.cpp
dom/camera/DOMCameraPreview.h
dom/camera/GonkCameraControl.cpp
dom/camera/GonkCameraControl.h
dom/camera/ICameraControl.h
dom/camera/nsIDOMCameraManager.idl
--- a/dom/camera/CameraControlImpl.cpp
+++ b/dom/camera/CameraControlImpl.cpp
@@ -362,16 +362,23 @@ CameraControlImpl::StopPreview()
 
 nsresult
 CameraControlImpl::GetPreviewStreamVideoMode(CameraRecorderOptions* aOptions, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError)
 {
   nsCOMPtr<nsIRunnable> getPreviewStreamVideoModeTask = new GetPreviewStreamVideoModeTask(this, *aOptions, onSuccess, onError);
   return mCameraThread->Dispatch(getPreviewStreamVideoModeTask, NS_DISPATCH_NORMAL);
 }
 
+nsresult
+CameraControlImpl::ReleaseHardware(nsICameraReleaseCallback* onSuccess, nsICameraErrorCallback* onError)
+{
+  nsCOMPtr<nsIRunnable> releaseHardwareTask = new ReleaseHardwareTask(this, onSuccess, onError);
+  return mCameraThread->Dispatch(releaseHardwareTask, NS_DISPATCH_NORMAL);
+}
+
 bool
 CameraControlImpl::ReceiveFrame(void* aBuffer, ImageFormat aFormat, FrameBuilder aBuilder)
 {
   if (!mDOMPreview) {
     return false;
   }
 
   return mDOMPreview->ReceiveFrame(aBuffer, aFormat, aBuilder);
--- a/dom/camera/CameraControlImpl.h
+++ b/dom/camera/CameraControlImpl.h
@@ -22,44 +22,47 @@ class StartPreviewTask;
 class StopPreviewTask;
 class AutoFocusTask;
 class TakePictureTask;
 class StartRecordingTask;
 class StopRecordingTask;
 class SetParameterTask;
 class GetParameterTask;
 class GetPreviewStreamVideoModeTask;
+class ReleaseHardwareTask;
 
 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;
+  friend class ReleaseHardwareTask;
 
 public:
   CameraControlImpl(uint32_t aCameraId, nsIThread* aCameraThread, uint64_t aWindowId);
 
   nsresult GetPreviewStream(dom::CameraSize aSize, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError);
   nsresult StartPreview(DOMCameraPreview* aDOMPreview);
   void StopPreview();
   nsresult AutoFocus(nsICameraAutoFocusCallback* onSuccess, nsICameraErrorCallback* onError);
   nsresult TakePicture(dom::CameraSize aSize, int32_t aRotation, const nsAString& aFileFormat, dom::CameraPosition aPosition, nsICameraTakePictureCallback* onSuccess, nsICameraErrorCallback* onError);
   nsresult StartRecording(dom::CameraStartRecordingOptions* aOptions, nsIFile* aFolder, const nsAString& aFilename, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError);
   nsresult StopRecording();
   nsresult GetPreviewStreamVideoMode(dom::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);
   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);
@@ -112,16 +115,17 @@ protected:
   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 nsresult ReleaseHardwareImpl(ReleaseHardwareTask* aReleaseHardware) = 0;
   virtual already_AddRefed<RecorderProfileManager> GetRecorderProfileManagerImpl() = 0;
 
   void OnShutterInternal();
   void OnClosedInternal();
 
   uint32_t            mCameraId;
   nsCOMPtr<nsIThread> mCameraThread;
   uint64_t            mWindowId;
@@ -605,17 +609,83 @@ public:
   }
 
   nsRefPtr<CameraControlImpl> mCameraControl;
   dom::CameraRecorderOptions mOptions;
   nsCOMPtr<nsICameraPreviewStreamCallback> mOnSuccessCb;
   nsCOMPtr<nsICameraErrorCallback> mOnErrorCb;
 };
 
-// Error result runnable
+// Return the result of releasing the camera hardware.  Runs on the main thread.
+class ReleaseHardwareResult : public nsRunnable
+{
+public:
+  ReleaseHardwareResult(nsICameraReleaseCallback* onSuccess, uint64_t aWindowId)
+    : mOnSuccessCb(onSuccess)
+    , mWindowId(aWindowId)
+  {
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  }
+
+  virtual ~ReleaseHardwareResult()
+  {
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  }
+
+  NS_IMETHOD Run()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    if (mOnSuccessCb && nsDOMCameraManager::IsWindowStillActive(mWindowId)) {
+      mOnSuccessCb->HandleEvent();
+    }
+    return NS_OK;
+  }
+
+protected:
+  nsCOMPtr<nsICameraReleaseCallback> mOnSuccessCb;
+  uint64_t mWindowId;
+};
+
+// Release the camera hardware.
+class ReleaseHardwareTask : public nsRunnable
+{
+public:
+  ReleaseHardwareTask(CameraControlImpl* aCameraControl, nsICameraReleaseCallback* onSuccess, nsICameraErrorCallback* onError)
+    : mCameraControl(aCameraControl)
+    , mOnSuccessCb(onSuccess)
+    , mOnErrorCb(onError)
+  {
+    DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+  }
+
+  virtual ~ReleaseHardwareTask()
+  {
+    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->ReleaseHardwareImpl(this);
+    DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
+
+    if (NS_FAILED(rv) && mOnErrorCb) {
+      rv = NS_DispatchToMainThread(new CameraErrorResult(mOnErrorCb, NS_LITERAL_STRING("FAILURE"), mCameraControl->GetWindowId()));
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+    return rv;
+  }
+
+  nsRefPtr<CameraControlImpl> mCameraControl;
+  nsCOMPtr<nsICameraReleaseCallback> mOnSuccessCb;
+  nsCOMPtr<nsICameraErrorCallback> mOnErrorCb;
+};
+
+// Report that the video recorder state has changed.
 class CameraRecorderStateChange : public nsRunnable
 {
 public:
   CameraRecorderStateChange(nsICameraRecorderStateChange* onStateChange, const nsString& aStateMsg, int32_t aStatus, int32_t aTrackNumber, uint64_t aWindowId)
     : mOnStateChangeCb(onStateChange)
     , mStateMsg(aStateMsg)
     , mStatus(aStatus)
     , mTrackNumber(aTrackNumber)
--- a/dom/camera/DOMCameraControl.cpp
+++ b/dom/camera/DOMCameraControl.cpp
@@ -368,16 +368,22 @@ nsDOMCameraControl::GetPreviewStreamVide
 
   CameraRecorderOptions options;
   nsresult rv = options.Init(cx, &aOptions);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return mCameraControl->GetPreviewStreamVideoMode(&options, onSuccess, onError);
 }
 
+NS_IMETHODIMP
+nsDOMCameraControl::ReleaseHardware(nsICameraReleaseCallback* onSuccess, nsICameraErrorCallback* onError)
+{
+  return mCameraControl->ReleaseHardware(onSuccess, onError);
+}
+
 class GetCameraResult : public nsRunnable
 {
 public:
   GetCameraResult(nsDOMCameraControl* aDOMCameraControl, nsresult aResult, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError, uint64_t aWindowId)
     : mDOMCameraControl(aDOMCameraControl)
     , mResult(aResult)
     , mOnSuccessCb(onSuccess)
     , mOnErrorCb(onError)
--- a/dom/camera/DOMCameraPreview.cpp
+++ b/dom/camera/DOMCameraPreview.cpp
@@ -207,26 +207,26 @@ DOMCameraPreview::Start()
   /**
    * Add a reference to ourselves to make sure we stay alive while
    * the preview is running, as the CameraControlImpl object holds a
    * weak reference to us.
    *
    * This reference is removed in SetStateStopped().
    */
   NS_ADDREF_THIS();
-  mState = STARTING;
+  DOM_CAMERA_SETSTATE(STARTING);
   mCameraControl->StartPreview(this);
 }
 
 void
 DOMCameraPreview::SetStateStarted()
 {
   NS_ASSERTION(NS_IsMainThread(), "SetStateStarted() not called from main thread");
 
-  mState = STARTED;
+  DOM_CAMERA_SETSTATE(STARTED);
   DOM_CAMERA_LOGI("Preview stream started\n");
 }
 
 void
 DOMCameraPreview::Started()
 {
   if (mState != STARTING) {
     return;
@@ -244,33 +244,33 @@ void
 DOMCameraPreview::StopPreview()
 {
   NS_ASSERTION(NS_IsMainThread(), "StopPreview() not called from main thread");
   if (mState != STARTED) {
     return;
   }
 
   DOM_CAMERA_LOGI("Stopping preview stream\n");
-  mState = STOPPING;
+  DOM_CAMERA_SETSTATE(STOPPING);
   mCameraControl->StopPreview();
   mInput->EndTrack(TRACK_VIDEO);
   mInput->Finish();
 }
 
 void
 DOMCameraPreview::SetStateStopped()
 {
   NS_ASSERTION(NS_IsMainThread(), "SetStateStopped() not called from main thread");
 
   // see bug 809259 and bug 817367.
   if (mState != STOPPING) {
     mInput->EndTrack(TRACK_VIDEO);
     mInput->Finish();
   }
-  mState = STOPPED;
+  DOM_CAMERA_SETSTATE(STOPPED);
   DOM_CAMERA_LOGI("Preview stream stopped\n");
 
   /**
    * Only remove the reference added in Start() once the preview
    * has stopped completely.
    */
   NS_RELEASE_THIS();
 }
--- a/dom/camera/DOMCameraPreview.h
+++ b/dom/camera/DOMCameraPreview.h
@@ -7,17 +7,16 @@
 
 #include "nsCycleCollectionParticipant.h"
 #include "MediaStreamGraph.h"
 #include "StreamBuffer.h"
 #include "ICameraControl.h"
 #include "nsDOMMediaStream.h"
 #include "CameraCommon.h"
 
-
 namespace mozilla {
 
 typedef void (*FrameBuilder)(mozilla::layers::Image* aImage, void* aBuffer, uint32_t aWidth, uint32_t aHeight);
 
 /**
  * DOMCameraPreview is only exposed to the DOM as an nsDOMMediaStream,
  * which is a cycle-collection participant already.
  */
@@ -33,17 +32,17 @@ public:
 
   NS_IMETHODIMP
   GetCurrentTime(double* aCurrentTime) {
     return nsDOMMediaStream::GetCurrentTime(aCurrentTime);
   }
 
   void Start();   // called by the MediaStreamListener to start preview
   void Started(); // called by the CameraControl when preview is started
-  void StopPreview();    // called by the MediaStreamListener to stop preview
+  void StopPreview(); // called by the MediaStreamListener to stop preview
   void Stopped(bool aForced = false);
                   // called by the CameraControl when preview is stopped
   void Error();   // something went wrong, NS_RELEASE needed
 
   void SetStateStarted();
   void SetStateStopped();
 
 protected:
@@ -52,16 +51,33 @@ protected:
   enum {
     STOPPED,
     STARTING,
     STARTED,
     STOPPING
   };
   uint32_t mState;
 
+  // Helper function, used in conjunction with the macro below, to make
+  //  it easy to track state changes, which must happen only on the main
+  //  thread.
+  void
+  SetState(uint32_t aNewState, const char* aFileOrFunc, int aLine)
+  {
+#ifdef PR_LOGGING
+    const char* states[] = { "stopped", "starting", "started", "stopping" };
+    MOZ_ASSERT(mState < sizeof(states) / sizeof(states[0]));
+    MOZ_ASSERT(aNewState < sizeof(states) / sizeof(states[0]));
+    DOM_CAMERA_LOGI("SetState: (this=%p) '%s' --> '%s' : %s:%d\n", this, states[mState], states[aNewState], aFileOrFunc, aLine);
+#endif
+
+    NS_ASSERTION(NS_IsMainThread(), "Preview state set OFF OF main thread!");
+    mState = aNewState;
+  }
+
   uint32_t mWidth;
   uint32_t mHeight;
   uint32_t mFramesPerSecond;
   SourceMediaStream* mInput;
   nsRefPtr<mozilla::layers::ImageContainer> mImageContainer;
   VideoSegment mVideoSegment;
   uint32_t mFrameCount;
   nsRefPtr<ICameraControl> mCameraControl;
@@ -71,9 +87,11 @@ protected:
 
 private:
   DOMCameraPreview(const DOMCameraPreview&) MOZ_DELETE;
   DOMCameraPreview& operator=(const DOMCameraPreview&) MOZ_DELETE;
 };
 
 } // namespace mozilla
 
+#define DOM_CAMERA_SETSTATE(newState)   SetState((newState), __func__, __LINE__)
+
 #endif // DOM_CAMERA_DOMCAMERAPREVIEW_H
--- a/dom/camera/GonkCameraControl.cpp
+++ b/dom/camera/GonkCameraControl.cpp
@@ -277,17 +277,17 @@ nsGonkCameraControl::Init()
   DOM_CAMERA_LOGI(" - maximum focus areas:           %d\n", mMaxFocusAreas);
 
   return mHwHandle != 0 ? NS_OK : NS_ERROR_FAILURE;
 }
 
 nsGonkCameraControl::~nsGonkCameraControl()
 {
   DOM_CAMERA_LOGT("%s:%d : this=%p, mHwHandle = %d\n", __func__, __LINE__, this, mHwHandle);
-  GonkCameraHardware::ReleaseHandle(mHwHandle);
+  ReleaseHardwareImpl(nullptr);
   if (mRwLock) {
     PRRWLock* lock = mRwLock;
     mRwLock = nullptr;
     PR_DestroyRWLock(lock);
   }
 
   DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
 }
@@ -1304,16 +1304,42 @@ nsGonkCameraControl::GetPreviewStreamVid
   if (NS_FAILED(rv)) {
     NS_WARNING("Failed to dispatch GetPreviewStreamVideoMode() onSuccess callback to main thread!");
     return rv;
   }
 
   return NS_OK;
 }
 
+nsresult
+nsGonkCameraControl::ReleaseHardwareImpl(ReleaseHardwareTask* aReleaseHardware)
+{
+  DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
+
+  // if we're recording, stop recording
+  if (mRecorder) {
+    DOM_CAMERA_LOGI("shutting down existing video recorder\n");
+    mRecorder->stop();
+    mRecorder = nullptr;
+  }
+
+  // stop the preview
+  StopPreviewInternal(true /* forced */);
+
+  // release the hardware handle
+  GonkCameraHardware::ReleaseHandle(mHwHandle);
+
+  if (aReleaseHardware && aReleaseHardware->mOnSuccessCb) {
+    nsCOMPtr<nsIRunnable> releaseHardwareResult = new ReleaseHardwareResult(aReleaseHardware->mOnSuccessCb, mWindowId);
+    return NS_DispatchToMainThread(releaseHardwareResult);
+  }
+
+  return NS_OK;
+}
+
 already_AddRefed<GonkRecorderProfileManager>
 nsGonkCameraControl::GetGonkRecorderProfileManager()
 {
   if (!mProfileManager) {
     nsTArray<CameraSize> sizes;
     nsresult rv = GetVideoSizes(sizes);
     NS_ENSURE_SUCCESS(rv, nullptr);
 
--- a/dom/camera/GonkCameraControl.h
+++ b/dom/camera/GonkCameraControl.h
@@ -71,16 +71,17 @@ 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);
+  nsresult ReleaseHardwareImpl(ReleaseHardwareTask* aReleaseHardware);
   already_AddRefed<RecorderProfileManager> GetRecorderProfileManagerImpl();
   already_AddRefed<GonkRecorderProfileManager> GetGonkRecorderProfileManager();
 
   void SetPreviewSize(uint32_t aWidth, uint32_t aHeight);
   void SetupThumbnail(uint32_t aPictureWidth, uint32_t aPictureHeight, uint32_t aPercentQuality);
 
   uint32_t                  mHwHandle;
   double                    mExposureCompensationMin;
--- a/dom/camera/ICameraControl.h
+++ b/dom/camera/ICameraControl.h
@@ -25,16 +25,17 @@ public:
   virtual nsresult GetPreviewStream(dom::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(dom::CameraSize aSize, int32_t aRotation, const nsAString& aFileFormat, dom::CameraPosition aPosition, nsICameraTakePictureCallback* onSuccess, nsICameraErrorCallback* onError) = 0;
   virtual nsresult StartRecording(dom::CameraStartRecordingOptions* aOptions, nsIFile* aFolder, const nsAString& aFilename, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError) = 0;
   virtual nsresult StopRecording() = 0;
   virtual nsresult GetPreviewStreamVideoMode(dom::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;
   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;
--- a/dom/camera/nsIDOMCameraManager.idl
+++ b/dom/camera/nsIDOMCameraManager.idl
@@ -236,27 +236,33 @@ interface nsICameraClosedCallback : nsIS
 };
 
 [scriptable, function, uuid(550d675a-257d-4713-8b3d-0da53eba68fc)]
 interface nsICameraRecorderStateChange : nsISupports
 {
     void handleStateChange(in DOMString newState);
 };
 
+[scriptable, function, uuid(f84d607b-554c-413d-8810-cf848642765a)]
+interface nsICameraReleaseCallback : nsISupports
+{
+    void handleEvent();
+};
+
 [scriptable, function, uuid(a302c6c9-3776-4d1d-a395-f4105d47c3d3)]
 interface nsICameraErrorCallback : nsISupports
 {
     void handleEvent(in DOMString error);
 };
 
 /*
     attributes here affect the preview, any pictures taken, and/or
     any video recorded by the camera.
 */
-[scriptable, uuid(70f45209-b69b-4937-bbac-57d82600e2af)]
+[scriptable, uuid(c8e7418d-8913-4b66-bd9f-562fba627266)]
 interface nsICameraControl : nsISupports
 {
     readonly attribute nsICameraCapabilities capabilities;
 
     /* one of the vales chosen from capabilities.effects;
        default is "none" */
     attribute DOMString         effect;
 
@@ -386,16 +392,27 @@ interface nsICameraControl : nsISupports
          }
     */
     [implicit_jscontext]
     void getPreviewStream(in jsval aOptions, in nsICameraPreviewStreamCallback onSuccess, [optional] in nsICameraErrorCallback onError);
 
     /* call in or after the takePicture() onSuccess callback to
        resume the camera preview stream. */
     void resumePreview();
+
+    /* release the camera so that other applications can use it; you should
+       probably call this whenever the camera is not longer in the foreground
+       (depending on your usage model).
+
+       the callbacks are optional, unless you really need to know when
+       the hardware is ultimately released.
+
+       once this is called, the camera control object is to be considered
+       defunct; a new instance will need to be created to access the camera. */
+    [binaryname(ReleaseHardware)] void release([optional] in nsICameraReleaseCallback onSuccess, [optional] in nsICameraErrorCallback onError);
 };
 
 [scriptable, function, uuid(a267afbc-d91c-413a-8de5-0b94aecffa3e)]
 interface nsICameraGetCameraCallback : nsISupports
 {
     void handleEvent(in nsICameraControl camera);
 };