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 "CameraControlImpl.h"
6 #include "base/basictypes.h"
7 #include "mozilla/Assertions.h"
8 #include "mozilla/unused.h"
9 #include "nsPrintfCString.h"
10 #include "nsIWeakReferenceUtils.h"
11 #include "CameraCommon.h"
12 #include "nsGlobalWindow.h"
13 #include "DeviceStorageFileDescriptor.h"
14 #include "CameraControlListener.h"
16 using namespace mozilla
;
18 /* static */ StaticRefPtr
<nsIThread
> CameraControlImpl::sCameraThread
;
20 CameraControlImpl::CameraControlImpl()
21 : mListenerLock(PR_NewRWLock(PR_RWLOCK_RANK_NONE
, "CameraControlImpl.Listeners.Lock"))
22 , mPreviewState(CameraControlListener::kPreviewStopped
)
23 , mHardwareState(CameraControlListener::kHardwareUninitialized
)
24 , mHardwareStateChangeReason(NS_OK
)
26 DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__
, __LINE__
, this);
27 mCurrentConfiguration
.mMode
= ICameraControl::kUnspecifiedMode
;
29 // reuse the same camera thread to conserve resources
30 nsCOMPtr
<nsIThread
> ct
= do_QueryInterface(sCameraThread
);
32 mCameraThread
= ct
.forget();
34 nsresult rv
= NS_NewNamedThread("CameraThread", getter_AddRefs(mCameraThread
));
36 MOZ_CRASH("Failed to create new Camera Thread");
38 sCameraThread
= mCameraThread
;
41 // Care must be taken with the mListenerLock read-write lock to prevent
42 // deadlocks. Currently this is handled by ensuring that any attempts to
43 // acquire the lock for writing (as in Add/RemoveListener()) happen in a
44 // runnable dispatched to the Camera Thread--even if the method is being
45 // called from that thread. This ensures that if a registered listener
46 // (which is invoked with a read-lock) tries to call Add/RemoveListener(),
47 // the lock-for-writing attempt won't happen until the listener has
50 // Multiple parallel listeners being invoked are not a problem because
51 // the read-write lock allows multiple simultaneous read-locks.
53 MOZ_CRASH("Out of memory getting new PRRWLock");
57 CameraControlImpl::~CameraControlImpl()
59 DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__
, __LINE__
, this);
61 MOZ_ASSERT(mListenerLock
, "mListenerLock missing in ~CameraControlImpl()");
63 PR_DestroyRWLock(mListenerLock
);
64 mListenerLock
= nullptr;
69 CameraControlImpl::OnHardwareStateChange(CameraControlListener::HardwareState aNewState
,
72 // This callback can run on threads other than the Main Thread and
73 // the Camera Thread. On Gonk, it may be called from the camera's
74 // local binder thread, should the mediaserver process die.
75 RwLockAutoEnterRead
lock(mListenerLock
);
77 if (aNewState
== mHardwareState
) {
78 DOM_CAMERA_LOGI("OnHardwareStateChange: state did not change from %d\n", mHardwareState
);
83 const char* state
[] = { "uninitialized", "closed", "open", "failed" };
84 MOZ_ASSERT(aNewState
>= 0);
85 if (static_cast<unsigned int>(aNewState
) < sizeof(state
) / sizeof(state
[0])) {
86 DOM_CAMERA_LOGI("New hardware state is '%s' (reason=0x%x)\n",
87 state
[aNewState
], aReason
);
89 DOM_CAMERA_LOGE("OnHardwareStateChange: got invalid HardwareState value %d\n", aNewState
);
93 mHardwareState
= aNewState
;
94 mHardwareStateChangeReason
= aReason
;
96 for (uint32_t i
= 0; i
< mListeners
.Length(); ++i
) {
97 CameraControlListener
* l
= mListeners
[i
];
98 l
->OnHardwareStateChange(mHardwareState
, mHardwareStateChangeReason
);
103 CameraControlImpl::OnConfigurationChange()
105 MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread
);
106 RwLockAutoEnterRead
lock(mListenerLock
);
108 DOM_CAMERA_LOGI("OnConfigurationChange : %zu listeners\n", mListeners
.Length());
110 for (uint32_t i
= 0; i
< mListeners
.Length(); ++i
) {
111 CameraControlListener
* l
= mListeners
[i
];
112 l
->OnConfigurationChange(mCurrentConfiguration
);
117 CameraControlImpl::OnAutoFocusComplete(bool aAutoFocusSucceeded
)
119 // This callback can run on threads other than the Main Thread and
120 // the Camera Thread. On Gonk, it is called from the camera
121 // library's auto focus thread.
122 RwLockAutoEnterRead
lock(mListenerLock
);
124 for (uint32_t i
= 0; i
< mListeners
.Length(); ++i
) {
125 CameraControlListener
* l
= mListeners
[i
];
126 l
->OnAutoFocusComplete(aAutoFocusSucceeded
);
131 CameraControlImpl::OnAutoFocusMoving(bool aIsMoving
)
133 RwLockAutoEnterRead
lock(mListenerLock
);
135 for (uint32_t i
= 0; i
< mListeners
.Length(); ++i
) {
136 CameraControlListener
* l
= mListeners
[i
];
137 l
->OnAutoFocusMoving(aIsMoving
);
142 CameraControlImpl::OnFacesDetected(const nsTArray
<Face
>& aFaces
)
144 // This callback can run on threads other than the Main Thread and
145 // the Camera Thread. On Gonk, it is called from the camera
146 // library's face detection thread.
147 RwLockAutoEnterRead
lock(mListenerLock
);
149 for (uint32_t i
= 0; i
< mListeners
.Length(); ++i
) {
150 CameraControlListener
* l
= mListeners
[i
];
151 l
->OnFacesDetected(aFaces
);
156 CameraControlImpl::OnTakePictureComplete(uint8_t* aData
, uint32_t aLength
, const nsAString
& aMimeType
)
158 // This callback can run on threads other than the Main Thread and
159 // the Camera Thread. On Gonk, it is called from the camera
160 // library's snapshot thread.
161 RwLockAutoEnterRead
lock(mListenerLock
);
163 for (uint32_t i
= 0; i
< mListeners
.Length(); ++i
) {
164 CameraControlListener
* l
= mListeners
[i
];
165 l
->OnTakePictureComplete(aData
, aLength
, aMimeType
);
170 CameraControlImpl::OnShutter()
172 // This callback can run on threads other than the Main Thread and
173 // the Camera Thread. On Gonk, it is called from the camera driver's
175 RwLockAutoEnterRead
lock(mListenerLock
);
177 for (uint32_t i
= 0; i
< mListeners
.Length(); ++i
) {
178 CameraControlListener
* l
= mListeners
[i
];
184 CameraControlImpl::OnRecorderStateChange(CameraControlListener::RecorderState aState
,
185 int32_t aStatus
, int32_t aTrackNumber
)
187 // This callback can run on threads other than the Main Thread and
188 // the Camera Thread. On Gonk, it is called from the media encoder
190 RwLockAutoEnterRead
lock(mListenerLock
);
192 for (uint32_t i
= 0; i
< mListeners
.Length(); ++i
) {
193 CameraControlListener
* l
= mListeners
[i
];
194 l
->OnRecorderStateChange(aState
, aStatus
, aTrackNumber
);
199 CameraControlImpl::OnPreviewStateChange(CameraControlListener::PreviewState aNewState
)
201 // This callback runs on the Main Thread and the Camera Thread, and
202 // may run on the local binder thread, should the mediaserver
204 RwLockAutoEnterRead
lock(mListenerLock
);
206 if (aNewState
== mPreviewState
) {
207 DOM_CAMERA_LOGI("OnPreviewStateChange: state did not change from %d\n", mPreviewState
);
212 const char* state
[] = { "stopped", "paused", "started" };
213 MOZ_ASSERT(aNewState
>= 0);
214 if (static_cast<unsigned int>(aNewState
) < sizeof(state
) / sizeof(state
[0])) {
215 DOM_CAMERA_LOGI("New preview state is '%s'\n", state
[aNewState
]);
217 DOM_CAMERA_LOGE("OnPreviewStateChange: got unknown PreviewState value %d\n", aNewState
);
221 mPreviewState
= aNewState
;
223 for (uint32_t i
= 0; i
< mListeners
.Length(); ++i
) {
224 CameraControlListener
* l
= mListeners
[i
];
225 l
->OnPreviewStateChange(mPreviewState
);
230 CameraControlImpl::OnRateLimitPreview(bool aLimit
)
232 // This function runs on neither the Main Thread nor the Camera Thread.
233 RwLockAutoEnterRead
lock(mListenerLock
);
235 DOM_CAMERA_LOGI("OnRateLimitPreview: %d\n", aLimit
);
237 for (uint32_t i
= 0; i
< mListeners
.Length(); ++i
) {
238 CameraControlListener
* l
= mListeners
[i
];
239 l
->OnRateLimitPreview(aLimit
);
244 CameraControlImpl::OnNewPreviewFrame(layers::Image
* aImage
, uint32_t aWidth
, uint32_t aHeight
)
246 // This function runs on neither the Main Thread nor the Camera Thread.
247 // On Gonk, it is called from the camera driver's preview thread.
248 RwLockAutoEnterRead
lock(mListenerLock
);
250 DOM_CAMERA_LOGI("OnNewPreviewFrame: we have %zu preview frame listener(s)\n",
251 mListeners
.Length());
253 bool consumed
= false;
255 for (uint32_t i
= 0; i
< mListeners
.Length(); ++i
) {
256 CameraControlListener
* l
= mListeners
[i
];
257 consumed
= l
->OnNewPreviewFrame(aImage
, aWidth
, aHeight
) || consumed
;
263 CameraControlImpl::OnUserError(CameraControlListener::UserContext aContext
,
266 // This callback can run on threads other than the Main Thread and
267 // the Camera Thread.
268 RwLockAutoEnterRead
lock(mListenerLock
);
271 const char* context
[] = {
275 "StartFaceDetection",
285 "ResumeContinuousFocus",
288 if (static_cast<size_t>(aContext
) < sizeof(context
) / sizeof(context
[0])) {
289 DOM_CAMERA_LOGW("CameraControlImpl::OnUserError : aContext='%s' (%d), aError=0x%x\n",
290 context
[aContext
], aContext
, aError
);
292 DOM_CAMERA_LOGE("CameraControlImpl::OnUserError : aContext=%d, aError=0x%x\n",
297 for (uint32_t i
= 0; i
< mListeners
.Length(); ++i
) {
298 CameraControlListener
* l
= mListeners
[i
];
299 l
->OnUserError(aContext
, aError
);
304 CameraControlImpl::OnSystemError(CameraControlListener::SystemContext aContext
,
307 // This callback can run on threads other than the Main Thread and
308 // the Camera Thread.
309 RwLockAutoEnterRead
lock(mListenerLock
);
312 const char* context
[] = {
315 if (static_cast<size_t>(aContext
) < sizeof(context
) / sizeof(context
[0])) {
316 DOM_CAMERA_LOGW("CameraControlImpl::OnSystemError : aContext='%s' (%d), aError=0x%x\n",
317 context
[aContext
], aContext
, aError
);
319 DOM_CAMERA_LOGE("CameraControlImpl::OnSystemError : aContext=%d, aError=0x%x\n",
324 for (uint32_t i
= 0; i
< mListeners
.Length(); ++i
) {
325 CameraControlListener
* l
= mListeners
[i
];
326 l
->OnSystemError(aContext
, aError
);
330 // Camera control asynchronous message; these are dispatched from
331 // the Main Thread to the Camera Thread, where they are consumed.
333 class CameraControlImpl::ControlMessage
: public nsRunnable
336 ControlMessage(CameraControlImpl
* aCameraControl
,
337 CameraControlListener::UserContext aContext
)
338 : mCameraControl(aCameraControl
)
342 virtual nsresult
RunImpl() = 0;
347 MOZ_ASSERT(mCameraControl
);
348 MOZ_ASSERT(NS_GetCurrentThread() == mCameraControl
->mCameraThread
);
350 nsresult rv
= RunImpl();
352 nsPrintfCString
msg("Camera control API(%d) failed with 0x%x", mContext
, rv
);
353 NS_WARNING(msg
.get());
354 mCameraControl
->OnUserError(mContext
, rv
);
361 virtual ~ControlMessage() { }
363 nsRefPtr
<CameraControlImpl
> mCameraControl
;
364 CameraControlListener::UserContext mContext
;
368 CameraControlImpl::Dispatch(ControlMessage
* aMessage
)
370 nsresult rv
= mCameraThread
->Dispatch(aMessage
, NS_DISPATCH_NORMAL
);
371 if (NS_SUCCEEDED(rv
)) {
375 nsPrintfCString
msg("Failed to dispatch camera control message (0x%x)", rv
);
376 NS_WARNING(msg
.get());
377 return NS_ERROR_FAILURE
;
381 CameraControlImpl::Start(const Configuration
* aConfig
)
383 class Message
: public ControlMessage
386 Message(CameraControlImpl
* aCameraControl
,
387 CameraControlListener::UserContext aContext
,
388 const Configuration
* aConfig
)
389 : ControlMessage(aCameraControl
, aContext
)
390 , mHaveInitialConfig(false)
394 mHaveInitialConfig
= true;
399 RunImpl() MOZ_OVERRIDE
401 if (mHaveInitialConfig
) {
402 return mCameraControl
->StartImpl(&mConfig
);
404 return mCameraControl
->StartImpl();
408 bool mHaveInitialConfig
;
409 Configuration mConfig
;
412 return Dispatch(new Message(this, CameraControlListener::kInStartCamera
, aConfig
));
416 CameraControlImpl::SetConfiguration(const Configuration
& aConfig
)
418 class Message
: public ControlMessage
421 Message(CameraControlImpl
* aCameraControl
,
422 CameraControlListener::UserContext aContext
,
423 const Configuration
& aConfig
)
424 : ControlMessage(aCameraControl
, aContext
)
429 RunImpl() MOZ_OVERRIDE
431 return mCameraControl
->SetConfigurationImpl(mConfig
);
435 Configuration mConfig
;
438 return Dispatch(new Message(this, CameraControlListener::kInSetConfiguration
, aConfig
));
442 CameraControlImpl::AutoFocus()
444 class Message
: public ControlMessage
447 Message(CameraControlImpl
* aCameraControl
,
448 CameraControlListener::UserContext aContext
)
449 : ControlMessage(aCameraControl
, aContext
)
453 RunImpl() MOZ_OVERRIDE
455 return mCameraControl
->AutoFocusImpl();
459 return Dispatch(new Message(this, CameraControlListener::kInAutoFocus
));
463 CameraControlImpl::StartFaceDetection()
465 class Message
: public ControlMessage
468 Message(CameraControlImpl
* aCameraControl
,
469 CameraControlListener::UserContext aContext
)
470 : ControlMessage(aCameraControl
, aContext
)
474 RunImpl() MOZ_OVERRIDE
476 return mCameraControl
->StartFaceDetectionImpl();
480 return Dispatch(new Message(this, CameraControlListener::kInStartFaceDetection
));
484 CameraControlImpl::StopFaceDetection()
486 class Message
: public ControlMessage
489 Message(CameraControlImpl
* aCameraControl
,
490 CameraControlListener::UserContext aContext
)
491 : ControlMessage(aCameraControl
, aContext
)
495 RunImpl() MOZ_OVERRIDE
497 return mCameraControl
->StopFaceDetectionImpl();
501 return Dispatch(new Message(this, CameraControlListener::kInStopFaceDetection
));
505 CameraControlImpl::TakePicture()
507 class Message
: public ControlMessage
510 Message(CameraControlImpl
* aCameraControl
,
511 CameraControlListener::UserContext aContext
)
512 : ControlMessage(aCameraControl
, aContext
)
516 RunImpl() MOZ_OVERRIDE
518 return mCameraControl
->TakePictureImpl();
522 return Dispatch(new Message(this, CameraControlListener::kInTakePicture
));
526 CameraControlImpl::StartRecording(DeviceStorageFileDescriptor
* aFileDescriptor
,
527 const StartRecordingOptions
* aOptions
)
529 class Message
: public ControlMessage
532 Message(CameraControlImpl
* aCameraControl
,
533 CameraControlListener::UserContext aContext
,
534 const StartRecordingOptions
* aOptions
,
535 DeviceStorageFileDescriptor
* aFileDescriptor
)
536 : ControlMessage(aCameraControl
, aContext
)
537 , mOptionsPassed(false)
538 , mFileDescriptor(aFileDescriptor
)
541 mOptions
= *aOptions
;
542 mOptionsPassed
= true;
547 RunImpl() MOZ_OVERRIDE
549 return mCameraControl
->StartRecordingImpl(mFileDescriptor
,
550 mOptionsPassed
? &mOptions
: nullptr);
554 StartRecordingOptions mOptions
;
556 nsRefPtr
<DeviceStorageFileDescriptor
> mFileDescriptor
;
559 if (!aFileDescriptor
) {
560 return NS_ERROR_INVALID_ARG
;
562 return Dispatch(new Message(this, CameraControlListener::kInStartRecording
,
563 aOptions
, aFileDescriptor
));
567 CameraControlImpl::StopRecording()
569 class Message
: public ControlMessage
572 Message(CameraControlImpl
* aCameraControl
,
573 CameraControlListener::UserContext aContext
)
574 : ControlMessage(aCameraControl
, aContext
)
578 RunImpl() MOZ_OVERRIDE
580 return mCameraControl
->StopRecordingImpl();
584 return Dispatch(new Message(this, CameraControlListener::kInStopRecording
));
588 CameraControlImpl::StartPreview()
590 class Message
: public ControlMessage
593 Message(CameraControlImpl
* aCameraControl
,
594 CameraControlListener::UserContext aContext
)
595 : ControlMessage(aCameraControl
, aContext
)
599 RunImpl() MOZ_OVERRIDE
601 return mCameraControl
->StartPreviewImpl();
605 return Dispatch(new Message(this, CameraControlListener::kInStartPreview
));
609 CameraControlImpl::StopPreview()
611 class Message
: public ControlMessage
614 Message(CameraControlImpl
* aCameraControl
,
615 CameraControlListener::UserContext aContext
)
616 : ControlMessage(aCameraControl
, aContext
)
620 RunImpl() MOZ_OVERRIDE
622 return mCameraControl
->StopPreviewImpl();
626 return Dispatch(new Message(this, CameraControlListener::kInStopPreview
));
630 CameraControlImpl::ResumeContinuousFocus()
632 class Message
: public ControlMessage
635 Message(CameraControlImpl
* aCameraControl
,
636 CameraControlListener::UserContext aContext
)
637 : ControlMessage(aCameraControl
, aContext
)
641 RunImpl() MOZ_OVERRIDE
643 return mCameraControl
->ResumeContinuousFocusImpl();
647 return Dispatch(new Message(this, CameraControlListener::kInResumeContinuousFocus
));
651 CameraControlImpl::Stop()
653 class Message
: public ControlMessage
656 Message(CameraControlImpl
* aCameraControl
,
657 CameraControlListener::UserContext aContext
)
658 : ControlMessage(aCameraControl
, aContext
)
662 RunImpl() MOZ_OVERRIDE
664 return mCameraControl
->StopImpl();
668 return Dispatch(new Message(this, CameraControlListener::kInStopCamera
));
671 class CameraControlImpl::ListenerMessage
: public CameraControlImpl::ControlMessage
674 ListenerMessage(CameraControlImpl
* aCameraControl
,
675 CameraControlListener
* aListener
)
676 : ControlMessage(aCameraControl
, CameraControlListener::kInUnspecified
)
677 , mListener(aListener
)
681 nsRefPtr
<CameraControlListener
> mListener
;
685 CameraControlImpl::AddListenerImpl(already_AddRefed
<CameraControlListener
> aListener
)
687 RwLockAutoEnterWrite
lock(mListenerLock
);
689 CameraControlListener
* l
= *mListeners
.AppendElement() = aListener
;
690 DOM_CAMERA_LOGI("Added camera control listener %p\n", l
);
692 // Update the newly-added listener's state
693 l
->OnConfigurationChange(mCurrentConfiguration
);
694 l
->OnHardwareStateChange(mHardwareState
, mHardwareStateChangeReason
);
695 l
->OnPreviewStateChange(mPreviewState
);
699 CameraControlImpl::AddListener(CameraControlListener
* aListener
)
701 class Message
: public ListenerMessage
704 Message(CameraControlImpl
* aCameraControl
,
705 CameraControlListener
* aListener
)
706 : ListenerMessage(aCameraControl
, aListener
)
710 RunImpl() MOZ_OVERRIDE
712 mCameraControl
->AddListenerImpl(mListener
.forget());
718 Dispatch(new Message(this, aListener
));
723 CameraControlImpl::RemoveListenerImpl(CameraControlListener
* aListener
)
725 RwLockAutoEnterWrite
lock(mListenerLock
);
727 nsRefPtr
<CameraControlListener
> l(aListener
);
728 mListeners
.RemoveElement(l
);
729 DOM_CAMERA_LOGI("Removed camera control listener %p\n", l
.get());
730 // XXXmikeh - do we want to notify the listener that it has been removed?
734 CameraControlImpl::RemoveListener(CameraControlListener
* aListener
)
736 class Message
: public ListenerMessage
739 Message(CameraControlImpl
* aCameraControl
, CameraControlListener
* aListener
)
740 : ListenerMessage(aCameraControl
, aListener
)
744 RunImpl() MOZ_OVERRIDE
746 mCameraControl
->RemoveListenerImpl(mListener
);
752 Dispatch(new Message(this, aListener
));