1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "base/basictypes.h"
6 #include "mozilla/Assertions.h"
7 #include "DOMCameraPreview.h"
8 #include "CameraRecorderProfiles.h"
9 #include "CameraControlImpl.h"
10 #include "CameraCommon.h"
11 #include "nsGlobalWindow.h"
13 using namespace mozilla
;
14 using namespace mozilla::dom
;
15 using namespace mozilla::idl
;
17 CameraControlImpl::CameraControlImpl(uint32_t aCameraId
, nsIThread
* aCameraThread
, uint64_t aWindowId
)
18 : mCameraId(aCameraId
)
19 , mCameraThread(aCameraThread
)
20 , mWindowId(aWindowId
)
22 , mMaxMeteringAreas(0)
24 , mPreviewState(PREVIEW_STOPPED
)
25 , mDOMPreview(nullptr)
26 , mAutoFocusOnSuccessCb(nullptr)
27 , mAutoFocusOnErrorCb(nullptr)
28 , mTakePictureOnSuccessCb(nullptr)
29 , mTakePictureOnErrorCb(nullptr)
30 , mOnShutterCb(nullptr)
31 , mOnClosedCb(nullptr)
32 , mOnRecorderStateChangeCb(nullptr)
33 , mOnPreviewStateChangeCb(nullptr)
35 DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__
, __LINE__
, this);
38 CameraControlImpl::~CameraControlImpl()
40 DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__
, __LINE__
, this);
43 // Helpers for string properties.
45 CameraControlImpl::Set(uint32_t aKey
, const nsAString
& aValue
)
47 SetParameter(aKey
, NS_ConvertUTF16toUTF8(aValue
).get());
52 CameraControlImpl::Get(uint32_t aKey
, nsAString
& aValue
)
54 const char* value
= GetParameterConstChar(aKey
);
56 return NS_ERROR_FAILURE
;
59 aValue
.AssignASCII(value
);
63 // Helpers for doubles.
65 CameraControlImpl::Set(uint32_t aKey
, double aValue
)
67 SetParameter(aKey
, aValue
);
72 CameraControlImpl::Get(uint32_t aKey
, double* aValue
)
75 *aValue
= GetParameterDouble(aKey
);
79 // Helper for weighted regions.
81 CameraControlImpl::Set(JSContext
* aCx
, uint32_t aKey
, const JS::Value
& aValue
, uint32_t aLimit
)
84 DOM_CAMERA_LOGI("%s:%d : aLimit = 0, nothing to do\n", __func__
, __LINE__
);
88 if (!aValue
.isObject()) {
89 return NS_ERROR_INVALID_ARG
;
94 JS::Rooted
<JSObject
*> regions(aCx
, &aValue
.toObject());
95 if (!JS_GetArrayLength(aCx
, regions
, &length
)) {
96 return NS_ERROR_FAILURE
;
99 DOM_CAMERA_LOGI("%s:%d : got %d regions (limited to %d)\n", __func__
, __LINE__
, length
, aLimit
);
100 if (length
> aLimit
) {
104 nsTArray
<CameraRegion
> regionArray
;
105 regionArray
.SetCapacity(length
);
107 for (uint32_t i
= 0; i
< length
; ++i
) {
108 JS::Rooted
<JS::Value
> v(aCx
);
110 if (!JS_GetElement(aCx
, regions
, i
, &v
)) {
111 return NS_ERROR_FAILURE
;
114 CameraRegion
* r
= regionArray
.AppendElement();
116 * These are the default values. We can remove these when the xpidl
117 * dictionary parser gains the ability to grok default values.
125 nsresult rv
= r
->Init(aCx
, v
.address());
126 NS_ENSURE_SUCCESS(rv
, rv
);
128 DOM_CAMERA_LOGI("region %d: top=%d, left=%d, bottom=%d, right=%d, weight=%d\n",
137 SetParameter(aKey
, regionArray
);
142 CameraControlImpl::Get(JSContext
* aCx
, uint32_t aKey
, JS::Value
* aValue
)
144 nsTArray
<CameraRegion
> regionArray
;
146 GetParameter(aKey
, regionArray
);
148 JS::Rooted
<JSObject
*> array(aCx
, JS_NewArrayObject(aCx
, 0, nullptr));
150 return NS_ERROR_OUT_OF_MEMORY
;
153 uint32_t length
= regionArray
.Length();
154 DOM_CAMERA_LOGI("%s:%d : got %d regions\n", __func__
, __LINE__
, length
);
156 for (uint32_t i
= 0; i
< length
; ++i
) {
157 CameraRegion
* r
= ®ionArray
[i
];
158 JS::Rooted
<JS::Value
> v(aCx
);
160 JS::Rooted
<JSObject
*> o(aCx
, JS_NewObject(aCx
, nullptr, nullptr, nullptr));
162 return NS_ERROR_OUT_OF_MEMORY
;
165 DOM_CAMERA_LOGI("top=%d\n", r
->top
);
166 v
= INT_TO_JSVAL(r
->top
);
167 if (!JS_SetProperty(aCx
, o
, "top", v
)) {
168 return NS_ERROR_FAILURE
;
170 DOM_CAMERA_LOGI("left=%d\n", r
->left
);
171 v
= INT_TO_JSVAL(r
->left
);
172 if (!JS_SetProperty(aCx
, o
, "left", v
)) {
173 return NS_ERROR_FAILURE
;
175 DOM_CAMERA_LOGI("bottom=%d\n", r
->bottom
);
176 v
= INT_TO_JSVAL(r
->bottom
);
177 if (!JS_SetProperty(aCx
, o
, "bottom", v
)) {
178 return NS_ERROR_FAILURE
;
180 DOM_CAMERA_LOGI("right=%d\n", r
->right
);
181 v
= INT_TO_JSVAL(r
->right
);
182 if (!JS_SetProperty(aCx
, o
, "right", v
)) {
183 return NS_ERROR_FAILURE
;
185 DOM_CAMERA_LOGI("weight=%d\n", r
->weight
);
186 v
= INT_TO_JSVAL(r
->weight
);
187 if (!JS_SetProperty(aCx
, o
, "weight", v
)) {
188 return NS_ERROR_FAILURE
;
191 v
= OBJECT_TO_JSVAL(o
);
192 if (!JS_SetElement(aCx
, array
, i
, &v
)) {
193 return NS_ERROR_FAILURE
;
197 *aValue
= JS::ObjectValue(*array
);
202 CameraControlImpl::Set(nsICameraShutterCallback
* aOnShutter
)
204 mOnShutterCb
= new nsMainThreadPtrHolder
<nsICameraShutterCallback
>(aOnShutter
);
209 CameraControlImpl::Get(nsICameraShutterCallback
** aOnShutter
)
211 *aOnShutter
= mOnShutterCb
;
216 CameraControlImpl::Set(nsICameraClosedCallback
* aOnClosed
)
218 mOnClosedCb
= new nsMainThreadPtrHolder
<nsICameraClosedCallback
>(aOnClosed
);
223 CameraControlImpl::Get(nsICameraClosedCallback
** aOnClosed
)
225 *aOnClosed
= mOnClosedCb
;
230 CameraControlImpl::Set(nsICameraRecorderStateChange
* aOnRecorderStateChange
)
232 mOnRecorderStateChangeCb
= new nsMainThreadPtrHolder
<nsICameraRecorderStateChange
>(aOnRecorderStateChange
);
237 CameraControlImpl::Get(nsICameraRecorderStateChange
** aOnRecorderStateChange
)
239 *aOnRecorderStateChange
= mOnRecorderStateChangeCb
;
244 CameraControlImpl::Set(nsICameraPreviewStateChange
* aOnPreviewStateChange
)
246 mOnPreviewStateChangeCb
= new nsMainThreadPtrHolder
<nsICameraPreviewStateChange
>(aOnPreviewStateChange
);
251 CameraControlImpl::Get(nsICameraPreviewStateChange
** aOnPreviewStateChange
)
253 *aOnPreviewStateChange
= mOnPreviewStateChangeCb
;
258 CameraControlImpl::Set(uint32_t aKey
, const idl::CameraSize
& aSize
)
260 SetParameter(aKey
, aSize
);
265 CameraControlImpl::Get(uint32_t aKey
, idl::CameraSize
& aSize
)
267 GetParameter(aKey
, aSize
);
272 CameraControlImpl::Get(uint32_t aKey
, int32_t* aValue
)
275 *aValue
= GetParameterInt32(aKey
);
279 already_AddRefed
<RecorderProfileManager
>
280 CameraControlImpl::GetRecorderProfileManager()
282 return GetRecorderProfileManagerImpl();
286 CameraControlImpl::Shutdown()
288 DOM_CAMERA_LOGT("%s:%d\n", __func__
, __LINE__
);
289 mAutoFocusOnSuccessCb
= nullptr;
290 mAutoFocusOnErrorCb
= nullptr;
291 mTakePictureOnSuccessCb
= nullptr;
292 mTakePictureOnErrorCb
= nullptr;
293 mOnShutterCb
= nullptr;
294 mOnClosedCb
= nullptr;
295 mOnRecorderStateChangeCb
= nullptr;
296 mOnPreviewStateChangeCb
= nullptr;
300 CameraControlImpl::OnShutterInternal()
302 DOM_CAMERA_LOGI("** SNAP **\n");
303 if (mOnShutterCb
.get()) {
304 mOnShutterCb
->HandleEvent();
309 CameraControlImpl::OnShutter()
311 nsCOMPtr
<nsIRunnable
> onShutter
= NS_NewRunnableMethod(this, &CameraControlImpl::OnShutterInternal
);
312 nsresult rv
= NS_DispatchToMainThread(onShutter
);
314 DOM_CAMERA_LOGW("Failed to dispatch onShutter event to main thread (%d)\n", rv
);
318 class OnClosedTask
: public nsRunnable
321 OnClosedTask(nsMainThreadPtrHandle
<nsICameraClosedCallback
> onClosed
, uint64_t aWindowId
)
322 : mOnClosedCb(onClosed
)
323 , mWindowId(aWindowId
)
325 DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__
, __LINE__
, this);
328 virtual ~OnClosedTask()
330 DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__
, __LINE__
, this);
335 MOZ_ASSERT(NS_IsMainThread());
337 if (mOnClosedCb
.get() && nsDOMCameraManager::IsWindowStillActive(mWindowId
)) {
338 mOnClosedCb
->HandleEvent();
344 nsMainThreadPtrHandle
<nsICameraClosedCallback
> mOnClosedCb
;
349 CameraControlImpl::OnClosed()
351 nsCOMPtr
<nsIRunnable
> onClosed
= new OnClosedTask(mOnClosedCb
, mWindowId
);
352 nsresult rv
= NS_DispatchToMainThread(onClosed
);
354 DOM_CAMERA_LOGW("Failed to dispatch onClosed event to main thread (%d)\n", rv
);
359 CameraControlImpl::OnRecorderStateChange(const nsString
& aStateMsg
, int32_t aStatus
, int32_t aTrackNumber
)
361 DOM_CAMERA_LOGI("OnRecorderStateChange: '%s'\n", NS_ConvertUTF16toUTF8(aStateMsg
).get());
363 nsCOMPtr
<nsIRunnable
> onRecorderStateChange
= new CameraRecorderStateChange(mOnRecorderStateChangeCb
, aStateMsg
, aStatus
, aTrackNumber
, mWindowId
);
364 nsresult rv
= NS_DispatchToMainThread(onRecorderStateChange
);
366 DOM_CAMERA_LOGE("Failed to dispatch onRecorderStateChange event to main thread (%d)\n", rv
);
371 CameraControlImpl::OnPreviewStateChange(PreviewState aNewState
)
373 if (aNewState
== mPreviewState
) {
374 DOM_CAMERA_LOGI("OnPreviewStateChange: state did not change from %d\n", mPreviewState
);
380 case PREVIEW_STOPPED
:
381 msg
= NS_LITERAL_STRING("stopped");
384 case PREVIEW_STARTED
:
385 msg
= NS_LITERAL_STRING("started");
389 MOZ_ASSUME_UNREACHABLE("Preview state can only be PREVIEW_STOPPED or _STARTED!");
392 // const nsString& aStateMsg)
393 DOM_CAMERA_LOGI("OnPreviewStateChange: '%s'\n", NS_ConvertUTF16toUTF8(msg
).get());
394 mPreviewState
= aNewState
;
396 nsCOMPtr
<nsIRunnable
> onPreviewStateChange
= new CameraPreviewStateChange(mOnPreviewStateChangeCb
, msg
, mWindowId
);
397 nsresult rv
= NS_DispatchToMainThread(onPreviewStateChange
);
399 DOM_CAMERA_LOGE("Failed to dispatch onPreviewStateChange event to main thread (%d)\n", rv
);
404 CameraControlImpl::GetPreviewStream(CameraSize aSize
, nsICameraPreviewStreamCallback
* onSuccess
, nsICameraErrorCallback
* onError
)
406 nsCOMPtr
<nsIRunnable
> getPreviewStreamTask
= new GetPreviewStreamTask(this, aSize
, onSuccess
, onError
);
407 return mCameraThread
->Dispatch(getPreviewStreamTask
, NS_DISPATCH_NORMAL
);
411 CameraControlImpl::AutoFocus(nsICameraAutoFocusCallback
* onSuccess
, nsICameraErrorCallback
* onError
)
413 MOZ_ASSERT(NS_IsMainThread());
416 nsCOMPtr
<nsICameraAutoFocusCallback
> cb
= mAutoFocusOnSuccessCb
.get();
419 * We already have a callback, so someone has already
420 * called autoFocus() -- cancel it.
422 mAutoFocusOnSuccessCb
= nullptr;
423 mAutoFocusOnErrorCb
= nullptr;
427 nsCOMPtr
<nsIRunnable
> autoFocusTask
= new AutoFocusTask(this, cancel
, onSuccess
, onError
);
428 return mCameraThread
->Dispatch(autoFocusTask
, NS_DISPATCH_NORMAL
);
432 CameraControlImpl::TakePicture(const CameraSize
& aSize
, int32_t aRotation
, const nsAString
& aFileFormat
, CameraPosition aPosition
, uint64_t aDateTime
, nsICameraTakePictureCallback
* onSuccess
, nsICameraErrorCallback
* onError
)
434 MOZ_ASSERT(NS_IsMainThread());
437 nsCOMPtr
<nsICameraTakePictureCallback
> cb
= mTakePictureOnSuccessCb
.get();
440 * We already have a callback, so someone has already
441 * called takePicture() -- cancel it.
443 mTakePictureOnSuccessCb
= nullptr;
444 mTakePictureOnErrorCb
= nullptr;
448 nsCOMPtr
<nsIRunnable
> takePictureTask
= new TakePictureTask(this, cancel
, aSize
, aRotation
, aFileFormat
, aPosition
, aDateTime
, onSuccess
, onError
);
449 return mCameraThread
->Dispatch(takePictureTask
, NS_DISPATCH_NORMAL
);
453 CameraControlImpl::StartRecording(CameraStartRecordingOptions
* aOptions
, nsIFile
* aFolder
, const nsAString
& aFilename
, nsICameraStartRecordingCallback
* onSuccess
, nsICameraErrorCallback
* onError
)
455 nsCOMPtr
<nsIFile
> clone
;
456 aFolder
->Clone(getter_AddRefs(clone
));
458 nsCOMPtr
<nsIRunnable
> startRecordingTask
= new StartRecordingTask(this, *aOptions
, clone
, aFilename
, onSuccess
, onError
, mWindowId
);
459 return mCameraThread
->Dispatch(startRecordingTask
, NS_DISPATCH_NORMAL
);
463 CameraControlImpl::StopRecording()
465 nsCOMPtr
<nsIRunnable
> stopRecordingTask
= new StopRecordingTask(this);
466 return mCameraThread
->Dispatch(stopRecordingTask
, NS_DISPATCH_NORMAL
);
470 CameraControlImpl::StartPreview(DOMCameraPreview
* aDOMPreview
)
472 nsCOMPtr
<nsIRunnable
> startPreviewTask
= new StartPreviewTask(this, aDOMPreview
);
473 return mCameraThread
->Dispatch(startPreviewTask
, NS_DISPATCH_NORMAL
);
477 CameraControlImpl::StopPreview()
479 nsCOMPtr
<nsIRunnable
> stopPreviewTask
= new StopPreviewTask(this);
480 mCameraThread
->Dispatch(stopPreviewTask
, NS_DISPATCH_NORMAL
);
484 CameraControlImpl::GetPreviewStreamVideoMode(CameraRecorderOptions
* aOptions
, nsICameraPreviewStreamCallback
* onSuccess
, nsICameraErrorCallback
* onError
)
486 nsCOMPtr
<nsIRunnable
> getPreviewStreamVideoModeTask
= new GetPreviewStreamVideoModeTask(this, *aOptions
, onSuccess
, onError
);
487 return mCameraThread
->Dispatch(getPreviewStreamVideoModeTask
, NS_DISPATCH_NORMAL
);
491 CameraControlImpl::ReleaseHardware(nsICameraReleaseCallback
* onSuccess
, nsICameraErrorCallback
* onError
)
493 nsCOMPtr
<nsIRunnable
> releaseHardwareTask
= new ReleaseHardwareTask(this, onSuccess
, onError
);
494 return mCameraThread
->Dispatch(releaseHardwareTask
, NS_DISPATCH_NORMAL
);
498 CameraControlImpl::ReceiveFrame(void* aBuffer
, ImageFormat aFormat
, FrameBuilder aBuilder
)
504 return mDOMPreview
->ReceiveFrame(aBuffer
, aFormat
, aBuilder
);
508 GetPreviewStreamResult::Run()
511 * The camera preview stream object is DOM-facing, and as such
512 * must be a cycle-collection participant created on the main
515 MOZ_ASSERT(NS_IsMainThread());
517 nsCOMPtr
<nsICameraPreviewStreamCallback
> onSuccess
= mOnSuccessCb
.get();
518 nsGlobalWindow
* window
= nsGlobalWindow::GetInnerWindowWithId(mWindowId
);
519 if (onSuccess
&& nsDOMCameraManager::IsWindowStillActive(mWindowId
) && window
) {
520 nsCOMPtr
<nsIDOMMediaStream
> stream
=
521 new DOMCameraPreview(window
, mCameraControl
, mWidth
, mHeight
,
523 onSuccess
->HandleEvent(stream
);