2 * Copyright (C) 2012-2015 Mozilla Foundation
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #include "GonkCameraControl.h"
24 #include "base/basictypes.h"
25 #include "camera/CameraParameters.h"
30 #include <media/MediaProfiles.h>
31 #include "mozilla/FileUtils.h"
32 #include "mozilla/Services.h"
33 #include "mozilla/unused.h"
34 #include "mozilla/ipc/FileDescriptorUtils.h"
35 #include "nsAlgorithm.h"
36 #include <media/mediaplayer.h>
37 #include "nsPrintfCString.h"
38 #include "nsIObserverService.h"
39 #include "nsIVolume.h"
40 #include "nsIVolumeService.h"
41 #include "AutoRwLock.h"
42 #include "GonkCameraHwMgr.h"
43 #include "GonkRecorderProfiles.h"
44 #include "GrallocImages.h"
45 #include "CameraCommon.h"
46 #include "GonkCameraParameters.h"
47 #include "DeviceStorageFileDescriptor.h"
49 using namespace mozilla
;
50 using namespace mozilla::layers
;
51 using namespace mozilla::gfx
;
52 using namespace mozilla::ipc
;
53 using namespace android
;
55 #define RETURN_IF_NO_CAMERA_HW() \
57 if (!mCameraHw.get()) { \
58 NS_WARNING("Camera hardware is not initialized"); \
59 DOM_CAMERA_LOGE("%s:%d : mCameraHw is null\n", __func__, __LINE__); \
60 return NS_ERROR_NOT_INITIALIZED; \
64 static const unsigned long kAutoFocusCompleteTimeoutMs
= 1000;
65 static const int32_t kAutoFocusCompleteTimeoutLimit
= 3;
67 // Construct nsGonkCameraControl on the main thread.
68 nsGonkCameraControl::nsGonkCameraControl(uint32_t aCameraId
)
69 : mCameraId(aCameraId
)
70 , mLastThumbnailSize({0, 0})
72 , mResumePreviewAfterTakingPicture(false) // XXXmikeh - see bug 950102
73 , mFlashSupported(false)
74 , mLuminanceSupported(false)
75 , mAutoFlashModeOverridden(false)
76 , mSeparateVideoAndPreviewSizesSupported(false)
77 , mDeferConfigUpdate(0)
79 , mRecorderMonitor("GonkCameraControl::mRecorder.Monitor")
81 , mAutoFocusPending(false)
82 , mAutoFocusCompleteExpired(0)
83 , mReentrantMonitor("GonkCameraControl::OnTakePicture.Monitor")
85 // Constructor runs on the main thread...
86 DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__
, __LINE__
, this);
87 mImageContainer
= LayerManager::CreateImageContainer();
89 mAutoFocusCompleteTimer
= do_CreateInstance(NS_TIMER_CONTRACTID
);
90 if (NS_WARN_IF(!mAutoFocusCompleteTimer
)) {
91 mAutoFocusCompleteExpired
= kAutoFocusCompleteTimeoutLimit
;
96 nsGonkCameraControl::StartImpl(const Configuration
* aInitialConfig
)
98 MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread
);
100 nsresult rv
= StartInternal(aInitialConfig
);
101 if (NS_WARN_IF(NS_FAILED(rv
))) {
102 OnHardwareStateChange(CameraControlListener::kHardwareOpenFailed
,
103 NS_ERROR_NOT_AVAILABLE
);
109 nsGonkCameraControl::StartInternal(const Configuration
* aInitialConfig
)
112 * For initialization, we try to return the camera control to the upper
113 * upper layer (i.e. the DOM) as quickly as possible. To do this, the
114 * camera is initialized in the following stages:
116 * 0. Initialize() initializes the hardware;
117 * 1. SetConfigurationInternal() does the minimal configuration
118 * required so that we can start the preview -and- report a valid
119 * configuration to the upper layer;
120 * 2. OnHardwareStateChange() reports that the hardware is ready,
121 * which the upper (e.g. DOM) layer can (and does) use to return
122 * the camera control object;
123 * 3. StartPreviewImpl() starts the flow of preview frames from the
126 * The intent of the above flow is to let the Main Thread do as much work
127 * up-front as possible without waiting for blocking Camera Thread calls
130 nsresult rv
= Initialize();
132 case NS_ERROR_ALREADY_INITIALIZED
:
140 if (aInitialConfig
) {
141 rv
= SetConfigurationInternal(*aInitialConfig
);
142 if (NS_WARN_IF(NS_FAILED(rv
))) {
143 // The initial configuration failed, close up the hardware
149 OnHardwareStateChange(CameraControlListener::kHardwareOpen
, NS_OK
);
151 if (aInitialConfig
) {
152 rv
= StartPreviewInternal();
153 if (NS_WARN_IF(NS_FAILED(rv
))) {
157 OnConfigurationChange();
158 OnPreviewStateChange(CameraControlListener::kPreviewStarted
);
164 nsGonkCameraControl::Initialize()
166 if (mCameraHw
.get()) {
167 DOM_CAMERA_LOGI("Camera %d already connected (this=%p)\n", mCameraId
, this);
168 return NS_ERROR_ALREADY_INITIALIZED
;
171 mCameraHw
= GonkCameraHardware::Connect(this, mCameraId
);
172 if (!mCameraHw
.get()) {
173 DOM_CAMERA_LOGE("Failed to connect to camera %d (this=%p)\n", mCameraId
, this);
174 return NS_ERROR_NOT_INITIALIZED
;
177 DOM_CAMERA_LOGI("Initializing camera %d (this=%p, mCameraHw=%p)\n", mCameraId
, this, mCameraHw
.get());
178 mCurrentConfiguration
.mRecorderProfile
.Truncate();
180 // Initialize our camera configuration database.
181 PullParametersImpl();
183 // Set preferred preview frame format.
184 mParams
.Set(CAMERA_PARAM_PREVIEWFORMAT
, NS_LITERAL_STRING("yuv420sp"));
185 // Turn off any normal pictures returned by the HDR scene mode
186 mParams
.Set(CAMERA_PARAM_SCENEMODE_HDR_RETURNNORMALPICTURE
, false);
187 PushParametersImpl();
189 // Grab any other settings we'll need later.
190 mParams
.Get(CAMERA_PARAM_PICTURE_FILEFORMAT
, mFileFormat
);
191 mParams
.Get(CAMERA_PARAM_THUMBNAILSIZE
, mLastThumbnailSize
);
193 // The emulator's camera returns -1 for these values; bump them up to 0
195 mParams
.Get(CAMERA_PARAM_SUPPORTED_MAXMETERINGAREAS
, areas
);
196 mCurrentConfiguration
.mMaxMeteringAreas
= areas
!= -1 ? areas
: 0;
197 mParams
.Get(CAMERA_PARAM_SUPPORTED_MAXFOCUSAREAS
, areas
);
198 mCurrentConfiguration
.mMaxFocusAreas
= areas
!= -1 ? areas
: 0;
200 mParams
.Get(CAMERA_PARAM_PICTURE_SIZE
, mCurrentConfiguration
.mPictureSize
);
201 mParams
.Get(CAMERA_PARAM_PREVIEWSIZE
, mCurrentConfiguration
.mPreviewSize
);
203 nsString luminance
; // check for support
204 mParams
.Get(CAMERA_PARAM_LUMINANCE
, luminance
);
205 mLuminanceSupported
= !luminance
.IsEmpty();
208 mParams
.Get(CAMERA_PARAM_FLASHMODE
, flashMode
);
209 mFlashSupported
= !flashMode
.IsEmpty();
211 double quality
; // informational only
212 mParams
.Get(CAMERA_PARAM_PICTURE_QUALITY
, quality
);
214 DOM_CAMERA_LOGI(" - maximum metering areas: %u\n", mCurrentConfiguration
.mMaxMeteringAreas
);
215 DOM_CAMERA_LOGI(" - maximum focus areas: %u\n", mCurrentConfiguration
.mMaxFocusAreas
);
216 DOM_CAMERA_LOGI(" - default picture size: %u x %u\n",
217 mCurrentConfiguration
.mPictureSize
.width
, mCurrentConfiguration
.mPictureSize
.height
);
218 DOM_CAMERA_LOGI(" - default picture file format: %s\n",
219 NS_ConvertUTF16toUTF8(mFileFormat
).get());
220 DOM_CAMERA_LOGI(" - default picture quality: %f\n", quality
);
221 DOM_CAMERA_LOGI(" - default thumbnail size: %u x %u\n",
222 mLastThumbnailSize
.width
, mLastThumbnailSize
.height
);
223 DOM_CAMERA_LOGI(" - default preview size: %u x %u\n",
224 mCurrentConfiguration
.mPreviewSize
.width
, mCurrentConfiguration
.mPreviewSize
.height
);
225 DOM_CAMERA_LOGI(" - luminance reporting: %ssupported\n",
226 mLuminanceSupported
? "" : "NOT ");
227 if (mFlashSupported
) {
228 DOM_CAMERA_LOGI(" - flash: supported, default mode '%s'\n",
229 NS_ConvertUTF16toUTF8(flashMode
).get());
231 DOM_CAMERA_LOGI(" - flash: NOT supported\n");
234 nsAutoTArray
<Size
, 16> sizes
;
235 mParams
.Get(CAMERA_PARAM_SUPPORTED_VIDEOSIZES
, sizes
);
236 if (sizes
.Length() > 0) {
237 mSeparateVideoAndPreviewSizesSupported
= true;
238 DOM_CAMERA_LOGI(" - support for separate preview and video sizes\n");
239 mParams
.Get(CAMERA_PARAM_VIDEOSIZE
, mLastRecorderSize
);
240 DOM_CAMERA_LOGI(" - default video recorder size: %u x %u\n",
241 mLastRecorderSize
.width
, mLastRecorderSize
.height
);
244 mParams
.Get(CAMERA_PARAM_PREFERRED_PREVIEWSIZE_FOR_VIDEO
, preferred
);
245 DOM_CAMERA_LOGI(" - preferred video preview size: %u x %u\n",
246 preferred
.width
, preferred
.height
);
248 mLastRecorderSize
= mCurrentConfiguration
.mPreviewSize
;
251 nsAutoTArray
<nsString
, 8> modes
;
252 mParams
.Get(CAMERA_PARAM_SUPPORTED_METERINGMODES
, modes
);
253 if (!modes
.IsEmpty()) {
255 const char* kCenterWeighted
= "center-weighted";
257 mParams
.Get(CAMERA_PARAM_METERINGMODE
, mode
);
258 if (!mode
.EqualsASCII(kCenterWeighted
)) {
259 nsTArray
<nsString
>::index_type i
= modes
.Length();
262 if (modes
[i
].EqualsASCII(kCenterWeighted
)) {
263 mParams
.Set(CAMERA_PARAM_METERINGMODE
, modes
[i
]);
264 PushParametersImpl();
270 mParams
.Get(CAMERA_PARAM_METERINGMODE
, mode
);
271 DOM_CAMERA_LOGI(" - metering mode: '%s'\n",
272 NS_ConvertUTF16toUTF8(mode
).get());
278 nsGonkCameraControl::~nsGonkCameraControl()
280 DOM_CAMERA_LOGT("%s:%d : this=%p, mCameraHw = %p\n", __func__
, __LINE__
, this, mCameraHw
.get());
283 DOM_CAMERA_LOGT("%s:%d\n", __func__
, __LINE__
);
287 nsGonkCameraControl::ValidateConfiguration(const Configuration
& aConfig
, Configuration
& aValidatedConfig
)
289 nsAutoTArray
<Size
, 16> supportedSizes
;
290 Get(CAMERA_PARAM_SUPPORTED_PICTURESIZES
, supportedSizes
);
292 nsresult rv
= GetSupportedSize(aConfig
.mPictureSize
, supportedSizes
,
293 aValidatedConfig
.mPictureSize
);
295 DOM_CAMERA_LOGW("Unable to find a picture size close to %ux%u\n",
296 aConfig
.mPictureSize
.width
, aConfig
.mPictureSize
.height
);
297 return NS_ERROR_INVALID_ARG
;
300 rv
= LoadRecorderProfiles();
301 if (NS_WARN_IF(NS_FAILED(rv
))) {
305 nsString profileName
= aConfig
.mRecorderProfile
;
306 if (profileName
.IsEmpty()) {
307 profileName
.AssignASCII("default");
310 RecorderProfile
* profile
;
311 if (!mRecorderProfiles
.Get(profileName
, &profile
)) {
312 DOM_CAMERA_LOGE("Recorder profile '%s' is not supported\n",
313 NS_ConvertUTF16toUTF8(aConfig
.mRecorderProfile
).get());
314 return NS_ERROR_INVALID_ARG
;
317 aValidatedConfig
.mMode
= aConfig
.mMode
;
318 aValidatedConfig
.mPreviewSize
= aConfig
.mPreviewSize
;
319 aValidatedConfig
.mRecorderProfile
= profile
->GetName();
324 nsGonkCameraControl::SetConfigurationInternal(const Configuration
& aConfig
)
326 DOM_CAMERA_LOGT("%s:%d\n", __func__
, __LINE__
);
328 // Ensure sanity of all provided parameters and determine defaults if
329 // none are provided when given a new configuration
330 Configuration config
;
331 nsresult rv
= ValidateConfiguration(aConfig
, config
);
332 if (NS_WARN_IF(NS_FAILED(rv
))) {
337 ICameraControlParameterSetAutoEnter
set(this);
339 switch (config
.mMode
) {
341 rv
= SetPictureConfiguration(config
);
345 rv
= SetVideoConfiguration(config
);
349 MOZ_ASSERT_UNREACHABLE("Unanticipated camera mode in SetConfigurationInternal()");
350 rv
= NS_ERROR_FAILURE
;
354 DOM_CAMERA_LOGT("%s:%d\n", __func__
, __LINE__
);
355 if (NS_WARN_IF(NS_FAILED(rv
))) {
359 rv
= Set(CAMERA_PARAM_RECORDINGHINT
, config
.mMode
== kVideoMode
);
361 DOM_CAMERA_LOGE("Failed to set recording hint (0x%x)\n", rv
);
364 mCurrentConfiguration
.mMode
= config
.mMode
;
365 mCurrentConfiguration
.mRecorderProfile
= config
.mRecorderProfile
;
367 if (config
.mMode
== kPictureMode
) {
368 mCurrentConfiguration
.mPictureSize
= config
.mPictureSize
;
369 } else /* if config.mMode == kVideoMode */ {
370 // The following is best-effort; we don't currently support taking
371 // pictures while in video mode, but we should at least return
372 // sane values to OnConfigurationChange() handlers...
373 SetPictureSizeImpl(config
.mPictureSize
);
380 nsGonkCameraControl::SetConfigurationImpl(const Configuration
& aConfig
)
382 DOM_CAMERA_LOGT("%s:%d\n", __func__
, __LINE__
);
383 MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread
);
385 if (aConfig
.mMode
== kUnspecifiedMode
) {
386 DOM_CAMERA_LOGW("Can't set camera mode to 'unspecified', ignoring\n");
387 return NS_ERROR_INVALID_ARG
;
390 // Stop any currently running preview
391 nsresult rv
= PausePreview();
393 DOM_CAMERA_LOGW("PausePreview() in SetConfigurationImpl() failed (0x%x)\n", rv
);
394 if (rv
== NS_ERROR_NOT_INITIALIZED
) {
395 // If there no hardware available, nothing else we try will work,
401 DOM_CAMERA_LOGT("%s:%d\n", __func__
, __LINE__
);
402 rv
= SetConfigurationInternal(aConfig
);
403 if (NS_WARN_IF(NS_FAILED(rv
))) {
408 // Restart the preview
409 DOM_CAMERA_LOGT("%s:%d\n", __func__
, __LINE__
);
410 rv
= StartPreviewInternal();
411 if (NS_WARN_IF(NS_FAILED(rv
))) {
416 // OnConfigurationChange() indicates the success case of this operation.
417 // It must not be fired until all intermediate steps, including starting
418 // the preview, have completed successfully.
419 OnConfigurationChange();
420 OnPreviewStateChange(CameraControlListener::kPreviewStarted
);
425 nsGonkCameraControl::MaybeAdjustVideoSize()
427 MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread
);
428 MOZ_ASSERT(mSeparateVideoAndPreviewSizesSupported
);
430 const Size
& preview
= mCurrentConfiguration
.mPreviewSize
;
432 // Some camera drivers will ignore our preview size if it's larger
433 // than the currently set video recording size, so in picture mode, we
434 // give preview size priority, and bump up the video size just in case.
435 // This is done on a best-effort basis.
437 if (preview
.width
<= mLastRecorderSize
.width
&&
438 preview
.height
<= mLastRecorderSize
.height
) {
439 DOM_CAMERA_LOGI("Video size %ux%u is suitable for preview size %ux%u\n",
440 mLastRecorderSize
.width
, mLastRecorderSize
.height
,
441 preview
.width
, preview
.height
);
445 nsTArray
<Size
> sizes
;
446 nsresult rv
= Get(CAMERA_PARAM_SUPPORTED_VIDEOSIZES
, sizes
);
447 if (NS_WARN_IF(NS_FAILED(rv
))) {
451 const uint32_t previewArea
= preview
.width
* preview
.height
;
452 uint32_t bestDelta
= UINT32_MAX
;
453 bool foundBest
= false;
456 for (SizeIndex i
= 0; i
< sizes
.Length(); ++i
) {
457 const Size
& s
= sizes
[i
];
458 if (s
.width
< preview
.width
|| s
.height
< preview
.height
) {
462 const uint32_t area
= s
.width
* s
.height
;
463 const uint32_t delta
= area
- previewArea
;
464 if (delta
< bestDelta
) {
472 // If no candidate was found, the driver will be fine with a video size
473 // smaller than the chosen preview size.
474 DOM_CAMERA_LOGI("No video size candidate for preview size %ux%u (0x%x)\n",
475 preview
.width
, preview
.height
, rv
);
479 DOM_CAMERA_LOGI("Adjusting video size upwards to %ux%u\n",
480 sizes
[best
].width
, sizes
[best
].height
);
481 rv
= Set(CAMERA_PARAM_VIDEOSIZE
, sizes
[best
]);
483 DOM_CAMERA_LOGW("Failed to adjust video size for preview size %ux%u (0x%x)\n",
484 preview
.width
, preview
.height
, rv
);
488 mLastRecorderSize
= preview
;
493 nsGonkCameraControl::SetPictureConfiguration(const Configuration
& aConfig
)
495 DOM_CAMERA_LOGT("%s:%d\n", __func__
, __LINE__
);
496 MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread
);
499 nsresult rv
= SelectCaptureAndPreviewSize(aConfig
.mPreviewSize
,
500 aConfig
.mPictureSize
, max
,
501 CAMERA_PARAM_PICTURE_SIZE
);
502 if (NS_WARN_IF(NS_FAILED(rv
))) {
506 if (mSeparateVideoAndPreviewSizesSupported
) {
507 MaybeAdjustVideoSize();
510 rv
= UpdateThumbnailSize();
511 if (NS_WARN_IF(NS_FAILED(rv
))) {
515 mParams
.Get(CAMERA_PARAM_PREVIEWFRAMERATE
, mPreviewFps
);
517 DOM_CAMERA_LOGI("picture mode preview: wanted %ux%u, got %ux%u (%u fps)\n",
518 aConfig
.mPreviewSize
.width
, aConfig
.mPreviewSize
.height
,
519 mCurrentConfiguration
.mPreviewSize
.width
,
520 mCurrentConfiguration
.mPreviewSize
.height
,
526 // Parameter management.
528 nsGonkCameraControl::PushParameters()
530 uint32_t dcu
= mDeferConfigUpdate
;
532 DOM_CAMERA_LOGI("Defering config update (nest level %u)\n", dcu
);
537 * If we're already on the camera thread, call PushParametersImpl()
538 * directly, so that it executes synchronously. Some callers
539 * require this so that changes take effect immediately before
542 if (NS_GetCurrentThread() != mCameraThread
) {
543 DOM_CAMERA_LOGT("%s:%d - dispatching to Camera Thread\n", __func__
, __LINE__
);
544 nsCOMPtr
<nsIRunnable
> pushParametersTask
=
545 NS_NewRunnableMethod(this, &nsGonkCameraControl::PushParametersImpl
);
546 return mCameraThread
->Dispatch(pushParametersTask
, NS_DISPATCH_NORMAL
);
549 DOM_CAMERA_LOGT("%s:%d\n", __func__
, __LINE__
);
550 return PushParametersImpl();
554 nsGonkCameraControl::BeginBatchParameterSet()
556 uint32_t dcu
= ++mDeferConfigUpdate
;
558 NS_WARNING("Overflow weirdness incrementing mDeferConfigUpdate!");
561 DOM_CAMERA_LOGI("Begin deferring camera configuration updates (nest level %u)\n", dcu
);
565 nsGonkCameraControl::EndBatchParameterSet()
567 uint32_t dcu
= mDeferConfigUpdate
--;
569 NS_WARNING("Underflow badness decrementing mDeferConfigUpdate!");
572 DOM_CAMERA_LOGI("End deferring camera configuration updates (nest level %u)\n", dcu
);
579 template<class T
> nsresult
580 nsGonkCameraControl::SetAndPush(uint32_t aKey
, const T
& aValue
)
582 nsresult rv
= mParams
.Set(aKey
, aValue
);
584 DOM_CAMERA_LOGE("Camera parameter aKey=%d failed to set (0x%x)\n", aKey
, rv
);
587 return PushParameters();
590 // Array-of-Size parameter accessor.
592 nsGonkCameraControl::Get(uint32_t aKey
, nsTArray
<Size
>& aSizes
)
594 if (aKey
== CAMERA_PARAM_SUPPORTED_VIDEOSIZES
&&
595 !mSeparateVideoAndPreviewSizesSupported
) {
596 aKey
= CAMERA_PARAM_SUPPORTED_PREVIEWSIZES
;
599 return mParams
.Get(aKey
, aSizes
);
602 // Array-of-doubles parameter accessor.
604 nsGonkCameraControl::Get(uint32_t aKey
, nsTArray
<double>& aValues
)
606 return mParams
.Get(aKey
, aValues
);
609 // Array-of-nsString parameter accessor.
611 nsGonkCameraControl::Get(uint32_t aKey
, nsTArray
<nsString
>& aValues
)
613 return mParams
.Get(aKey
, aValues
);
616 // nsString-valued parameter accessors
618 nsGonkCameraControl::Set(uint32_t aKey
, const nsAString
& aValue
)
620 nsresult rv
= mParams
.Set(aKey
, aValue
);
626 case CAMERA_PARAM_PICTURE_FILEFORMAT
:
627 // Picture format -- need to keep it for the TakePicture() callback.
628 mFileFormat
= aValue
;
631 case CAMERA_PARAM_FLASHMODE
:
632 // Explicit flash mode changes always win and stick.
633 mAutoFlashModeOverridden
= false;
636 case CAMERA_PARAM_SCENEMODE
:
637 // Reset disabling normal pictures in HDR mode in conjunction with setting
638 // scene mode because some drivers require they be changed together.
639 mParams
.Set(CAMERA_PARAM_SCENEMODE_HDR_RETURNNORMALPICTURE
, false);
643 return PushParameters();
647 nsGonkCameraControl::Get(uint32_t aKey
, nsAString
& aRet
)
649 return mParams
.Get(aKey
, aRet
);
652 // Double-valued parameter accessors
654 nsGonkCameraControl::Set(uint32_t aKey
, double aValue
)
656 return SetAndPush(aKey
, aValue
);
660 nsGonkCameraControl::Get(uint32_t aKey
, double& aRet
)
662 return mParams
.Get(aKey
, aRet
);
665 // Signed-64-bit parameter accessors.
667 nsGonkCameraControl::Set(uint32_t aKey
, int64_t aValue
)
669 return SetAndPush(aKey
, aValue
);
673 nsGonkCameraControl::Get(uint32_t aKey
, int64_t& aRet
)
675 return mParams
.Get(aKey
, aRet
);
678 // Boolean parameter accessors.
680 nsGonkCameraControl::Set(uint32_t aKey
, bool aValue
)
682 return SetAndPush(aKey
, aValue
);
686 nsGonkCameraControl::Get(uint32_t aKey
, bool& aRet
)
688 return mParams
.Get(aKey
, aRet
);
691 // Weighted-region parameter accessors.
693 nsGonkCameraControl::Set(uint32_t aKey
, const nsTArray
<Region
>& aRegions
)
695 return SetAndPush(aKey
, aRegions
);
699 nsGonkCameraControl::Get(uint32_t aKey
, nsTArray
<Region
>& aRegions
)
701 return mParams
.Get(aKey
, aRegions
);
704 // Singleton-size parameter accessors.
706 nsGonkCameraControl::Set(uint32_t aKey
, const Size
& aSize
)
709 case CAMERA_PARAM_PICTURE_SIZE
:
710 DOM_CAMERA_LOGI("setting picture size to %ux%u\n", aSize
.width
, aSize
.height
);
711 return SetPictureSize(aSize
);
713 case CAMERA_PARAM_THUMBNAILSIZE
:
714 DOM_CAMERA_LOGI("setting thumbnail size to %ux%u\n", aSize
.width
, aSize
.height
);
715 return SetThumbnailSize(aSize
);
718 return SetAndPush(aKey
, aSize
);
723 nsGonkCameraControl::Get(uint32_t aKey
, Size
& aSize
)
725 return mParams
.Get(aKey
, aSize
);
728 // Signed int parameter accessors.
730 nsGonkCameraControl::Set(uint32_t aKey
, int aValue
)
732 if (aKey
== CAMERA_PARAM_PICTURE_ROTATION
) {
733 RETURN_IF_NO_CAMERA_HW();
734 aValue
= RationalizeRotation(aValue
+ mCameraHw
->GetSensorOrientation());
736 return SetAndPush(aKey
, aValue
);
740 nsGonkCameraControl::Get(uint32_t aKey
, int& aRet
)
742 if (aKey
== CAMERA_PARAM_SENSORANGLE
) {
743 RETURN_IF_NO_CAMERA_HW();
744 aRet
= mCameraHw
->GetSensorOrientation();
748 return mParams
.Get(aKey
, aRet
);
751 // GPS location parameter accessors.
753 nsGonkCameraControl::SetLocation(const Position
& aLocation
)
755 return SetAndPush(CAMERA_PARAM_PICTURE_LOCATION
, aLocation
);
759 nsGonkCameraControl::StartPreviewInternal()
761 MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread
);
762 RETURN_IF_NO_CAMERA_HW();
764 ReentrantMonitorAutoEnter
mon(mReentrantMonitor
);
766 if (mPreviewState
== CameraControlListener::kPreviewStarted
) {
767 DOM_CAMERA_LOGW("Camera preview already started, nothing to do\n");
771 DOM_CAMERA_LOGI("Starting preview (this=%p)\n", this);
773 if (mCameraHw
->StartPreview() != OK
) {
774 DOM_CAMERA_LOGE("Failed to start camera preview\n");
775 return NS_ERROR_FAILURE
;
782 nsGonkCameraControl::StartPreviewImpl()
784 nsresult rv
= StartPreviewInternal();
785 if (NS_SUCCEEDED(rv
)) {
786 OnPreviewStateChange(CameraControlListener::kPreviewStarted
);
792 nsGonkCameraControl::StopPreviewImpl()
794 RETURN_IF_NO_CAMERA_HW();
796 DOM_CAMERA_LOGI("Stopping preview (this=%p)\n", this);
798 mCameraHw
->StopPreview();
799 OnPreviewStateChange(CameraControlListener::kPreviewStopped
);
804 nsGonkCameraControl::PausePreview()
806 RETURN_IF_NO_CAMERA_HW();
808 DOM_CAMERA_LOGI("Pausing preview (this=%p)\n", this);
810 mCameraHw
->StopPreview();
811 OnPreviewStateChange(CameraControlListener::kPreviewPaused
);
816 nsGonkCameraControl::AutoFocusImpl()
818 MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread
);
819 RETURN_IF_NO_CAMERA_HW();
821 DOM_CAMERA_LOGI("Starting auto focus\n");
823 if (mCameraHw
->AutoFocus() != OK
) {
824 return NS_ERROR_FAILURE
;
827 ReentrantMonitorAutoEnter
mon(mReentrantMonitor
);
828 mAutoFocusPending
= true;
829 if (mAutoFocusCompleteTimer
) {
830 mAutoFocusCompleteTimer
->Cancel();
836 nsGonkCameraControl::StartFaceDetectionImpl()
838 MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread
);
839 RETURN_IF_NO_CAMERA_HW();
841 DOM_CAMERA_LOGI("Starting face detection\n");
843 if (mCameraHw
->StartFaceDetection() != OK
) {
844 return NS_ERROR_FAILURE
;
850 nsGonkCameraControl::StopFaceDetectionImpl()
852 MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread
);
853 RETURN_IF_NO_CAMERA_HW();
855 DOM_CAMERA_LOGI("Stopping face detection\n");
857 if (mCameraHw
->StopFaceDetection() != OK
) {
858 return NS_ERROR_FAILURE
;
864 nsGonkCameraControl::SetThumbnailSizeImpl(const Size
& aSize
)
866 MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread
);
869 * We keep a copy of the specified size so that if the picture size
870 * changes, we can choose a new thumbnail size close to what was asked for
873 mLastThumbnailSize
= aSize
;
876 * If either of width or height is zero, set the other to zero as well.
877 * This should disable inclusion of a thumbnail in the final picture.
879 if (!aSize
.width
|| !aSize
.height
) {
880 DOM_CAMERA_LOGW("Requested thumbnail size %ux%u, disabling thumbnail\n",
881 aSize
.width
, aSize
.height
);
882 Size size
= { 0, 0 };
883 return SetAndPush(CAMERA_PARAM_THUMBNAILSIZE
, size
);
887 * Choose the supported thumbnail size that is closest to the specified size.
888 * Some drivers will fail to take a picture if the thumbnail does not have
889 * the same aspect ratio as the set picture size, so we need to enforce that
892 int smallestDelta
= INT_MAX
;
893 uint32_t smallestDeltaIndex
= UINT32_MAX
;
894 int targetArea
= aSize
.width
* aSize
.height
;
896 nsAutoTArray
<Size
, 8> supportedSizes
;
897 Get(CAMERA_PARAM_SUPPORTED_JPEG_THUMBNAIL_SIZES
, supportedSizes
);
899 for (uint32_t i
= 0; i
< supportedSizes
.Length(); ++i
) {
900 int area
= supportedSizes
[i
].width
* supportedSizes
[i
].height
;
901 int delta
= abs(area
- targetArea
);
904 delta
< smallestDelta
&&
905 supportedSizes
[i
].width
* mCurrentConfiguration
.mPictureSize
.height
==
906 mCurrentConfiguration
.mPictureSize
.width
* supportedSizes
[i
].height
) {
907 smallestDelta
= delta
;
908 smallestDeltaIndex
= i
;
912 if (smallestDeltaIndex
== UINT32_MAX
) {
913 DOM_CAMERA_LOGW("Unable to find a thumbnail size close to %ux%u, disabling thumbnail\n",
914 aSize
.width
, aSize
.height
);
915 // If we are unable to find a thumbnail size with a suitable aspect ratio,
916 // just disable the thumbnail altogether.
917 Size size
= { 0, 0 };
918 return SetAndPush(CAMERA_PARAM_THUMBNAILSIZE
, size
);
921 Size size
= supportedSizes
[smallestDeltaIndex
];
922 DOM_CAMERA_LOGI("camera-param set thumbnail-size = %ux%u (requested %ux%u)\n",
923 size
.width
, size
.height
, aSize
.width
, aSize
.height
);
924 if (size
.width
> INT32_MAX
|| size
.height
> INT32_MAX
) {
925 DOM_CAMERA_LOGE("Supported thumbnail size is too big, no change\n");
926 return NS_ERROR_FAILURE
;
929 return SetAndPush(CAMERA_PARAM_THUMBNAILSIZE
, size
);
932 android::sp
<android::GonkCameraHardware
>
933 nsGonkCameraControl::GetCameraHw()
939 nsGonkCameraControl::SetThumbnailSize(const Size
& aSize
)
941 class SetThumbnailSize
: public nsRunnable
944 SetThumbnailSize(nsGonkCameraControl
* aCameraControl
, const Size
& aSize
)
945 : mCameraControl(aCameraControl
)
948 MOZ_COUNT_CTOR(SetThumbnailSize
);
950 ~SetThumbnailSize() { MOZ_COUNT_DTOR(SetThumbnailSize
); }
955 nsresult rv
= mCameraControl
->SetThumbnailSizeImpl(mSize
);
957 mCameraControl
->OnUserError(CameraControlListener::kInSetThumbnailSize
, rv
);
963 nsRefPtr
<nsGonkCameraControl
> mCameraControl
;
967 if (NS_GetCurrentThread() == mCameraThread
) {
968 return SetThumbnailSizeImpl(aSize
);
971 return mCameraThread
->Dispatch(new SetThumbnailSize(this, aSize
), NS_DISPATCH_NORMAL
);
975 nsGonkCameraControl::UpdateThumbnailSize()
977 return SetThumbnailSize(mLastThumbnailSize
);
981 nsGonkCameraControl::SetPictureSizeImpl(const Size
& aSize
)
983 MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread
);
986 * Some drivers are less friendly about getting one of these set to zero,
987 * so if either is not specified, ignore both and go with current or
990 if (!aSize
.width
|| !aSize
.height
) {
991 DOM_CAMERA_LOGW("Ignoring requested picture size of %ux%u\n", aSize
.width
, aSize
.height
);
992 return NS_ERROR_INVALID_ARG
;
995 if (aSize
.width
== mCurrentConfiguration
.mPictureSize
.width
&&
996 aSize
.height
== mCurrentConfiguration
.mPictureSize
.height
) {
997 DOM_CAMERA_LOGI("Requested picture size %ux%u unchanged\n", aSize
.width
, aSize
.height
);
1001 nsAutoTArray
<Size
, 8> supportedSizes
;
1002 Get(CAMERA_PARAM_SUPPORTED_PICTURESIZES
, supportedSizes
);
1005 nsresult rv
= GetSupportedSize(aSize
, supportedSizes
, best
);
1006 if (NS_FAILED(rv
)) {
1007 DOM_CAMERA_LOGW("Unable to find a picture size close to %ux%u\n",
1008 aSize
.width
, aSize
.height
);
1009 return NS_ERROR_INVALID_ARG
;
1012 DOM_CAMERA_LOGI("camera-param set picture-size = %ux%u (requested %ux%u)\n",
1013 best
.width
, best
.height
, aSize
.width
, aSize
.height
);
1014 if (best
.width
> INT32_MAX
|| best
.height
> INT32_MAX
) {
1015 DOM_CAMERA_LOGE("Supported picture size is too big, no change\n");
1016 return NS_ERROR_FAILURE
;
1019 rv
= mParams
.Set(CAMERA_PARAM_PICTURE_SIZE
, best
);
1020 if (NS_FAILED(rv
)) {
1024 mCurrentConfiguration
.mPictureSize
= best
;
1026 // Finally, update the thumbnail size in case the picture aspect ratio changed.
1027 // Some drivers will fail to take a picture if the thumbnail size is not the
1028 // same aspect ratio as the picture size.
1029 return UpdateThumbnailSize();
1033 nsGonkCameraControl::RationalizeRotation(int32_t aRotation
)
1035 int32_t r
= aRotation
;
1037 // The result of this operation is an angle from 0..270 degrees,
1038 // in steps of 90 degrees. Angles are rounded to the nearest
1039 // magnitude, so 45 will be rounded to 90, and -45 will be rounded
1057 nsGonkCameraControl::SetPictureSize(const Size
& aSize
)
1059 class SetPictureSize
: public nsRunnable
1062 SetPictureSize(nsGonkCameraControl
* aCameraControl
, const Size
& aSize
)
1063 : mCameraControl(aCameraControl
)
1066 MOZ_COUNT_CTOR(SetPictureSize
);
1068 ~SetPictureSize() { MOZ_COUNT_DTOR(SetPictureSize
); }
1073 nsresult rv
= mCameraControl
->SetPictureSizeImpl(mSize
);
1074 if (NS_FAILED(rv
)) {
1075 mCameraControl
->OnUserError(CameraControlListener::kInSetPictureSize
, rv
);
1081 nsRefPtr
<nsGonkCameraControl
> mCameraControl
;
1085 if (NS_GetCurrentThread() == mCameraThread
) {
1086 return SetPictureSizeImpl(aSize
);
1089 return mCameraThread
->Dispatch(new SetPictureSize(this, aSize
), NS_DISPATCH_NORMAL
);
1093 nsGonkCameraControl::TakePictureImpl()
1095 MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread
);
1096 RETURN_IF_NO_CAMERA_HW();
1098 if (mCameraHw
->TakePicture() != OK
) {
1099 return NS_ERROR_FAILURE
;
1102 // In Gonk, taking a picture implicitly stops the preview stream,
1103 // so we need to reflect that here.
1104 OnPreviewStateChange(CameraControlListener::kPreviewPaused
);
1109 nsGonkCameraControl::PushParametersImpl()
1111 MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread
);
1112 DOM_CAMERA_LOGI("Pushing camera parameters\n");
1113 RETURN_IF_NO_CAMERA_HW();
1115 if (mCameraHw
->PushParameters(mParams
) != OK
) {
1116 return NS_ERROR_FAILURE
;
1123 nsGonkCameraControl::PullParametersImpl()
1125 MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread
);
1126 DOM_CAMERA_LOGI("Pulling camera parameters\n");
1127 RETURN_IF_NO_CAMERA_HW();
1129 return mCameraHw
->PullParameters(mParams
);
1133 nsGonkCameraControl::SetupRecordingFlash(bool aAutoEnableLowLightTorch
)
1135 mAutoFlashModeOverridden
= false;
1137 if (!aAutoEnableLowLightTorch
|| !mLuminanceSupported
|| !mFlashSupported
) {
1141 DOM_CAMERA_LOGI("Luminance reporting and flash supported\n");
1143 nsresult rv
= PullParametersImpl();
1144 if (NS_WARN_IF(NS_FAILED(rv
))) {
1149 rv
= mParams
.Get(CAMERA_PARAM_LUMINANCE
, luminance
);
1150 if (NS_WARN_IF(NS_FAILED(rv
))) {
1151 // If we failed to get the luminance, assume it's "high"
1156 rv
= mParams
.Get(CAMERA_PARAM_FLASHMODE
, flashMode
);
1157 if (NS_WARN_IF(NS_FAILED(rv
))) {
1158 // If we failed to get the current flash mode, swallow the error
1162 if (luminance
.EqualsASCII("low") && flashMode
.EqualsASCII("auto")) {
1163 DOM_CAMERA_LOGI("Low luminance detected, turning on flash\n");
1164 rv
= SetAndPush(CAMERA_PARAM_FLASHMODE
, NS_LITERAL_STRING("torch"));
1165 if (NS_WARN_IF(NS_FAILED(rv
))) {
1166 // If we failed to turn on the flash, swallow the error
1170 mAutoFlashModeOverridden
= true;
1177 nsGonkCameraControl::StartRecordingImpl(DeviceStorageFileDescriptor
* aFileDescriptor
,
1178 const StartRecordingOptions
* aOptions
)
1180 MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread
);
1182 ReentrantMonitorAutoEnter
mon(mRecorderMonitor
);
1184 NS_ENSURE_TRUE(!mCurrentConfiguration
.mRecorderProfile
.IsEmpty(), NS_ERROR_NOT_INITIALIZED
);
1185 NS_ENSURE_FALSE(mRecorder
, NS_ERROR_FAILURE
);
1188 * Get the base path from device storage and append the app-specified
1189 * filename to it. The filename may include a relative subpath
1190 * (e.g.) "DCIM/IMG_0001.jpg".
1192 * The camera app needs to provide the file extension '.3gp' for now.
1195 if (NS_WARN_IF(!aFileDescriptor
)) {
1196 return NS_ERROR_INVALID_ARG
;
1198 nsAutoString fullPath
;
1199 mVideoFile
= aFileDescriptor
->mDSFile
;
1200 mVideoFile
->GetFullPath(fullPath
);
1201 DOM_CAMERA_LOGI("Video filename is '%s'\n",
1202 NS_LossyConvertUTF16toASCII(fullPath
).get());
1204 if (!mVideoFile
->IsSafePath()) {
1205 DOM_CAMERA_LOGE("Invalid video file name\n");
1206 return NS_ERROR_INVALID_ARG
;
1209 // SetupRecording creates a dup of the file descriptor, so we need to
1210 // close the file descriptor when we leave this function. Also note, that
1211 // since we're already off the main thread, we don't need to dispatch this.
1212 // We just let the CloseFileRunnable destructor do the work.
1213 nsRefPtr
<CloseFileRunnable
> closer
;
1214 if (aFileDescriptor
->mFileDescriptor
.IsValid()) {
1215 closer
= new CloseFileRunnable(aFileDescriptor
->mFileDescriptor
);
1218 int fd
= aFileDescriptor
->mFileDescriptor
.PlatformHandle();
1220 rv
= SetupRecording(fd
, aOptions
->rotation
, aOptions
->maxFileSizeBytes
,
1221 aOptions
->maxVideoLengthMs
);
1222 if (NS_SUCCEEDED(rv
)) {
1223 rv
= SetupRecordingFlash(aOptions
->autoEnableLowLightTorch
);
1226 rv
= SetupRecording(fd
, 0, 0, 0);
1228 if (NS_WARN_IF(NS_FAILED(rv
))) {
1232 if (mRecorder
->start() != OK
) {
1233 DOM_CAMERA_LOGE("mRecorder->start() failed\n");
1234 // important: we MUST destroy the recorder if start() fails!
1235 mRecorder
= nullptr;
1236 // put the flash back to the 'auto' state
1237 if (mAutoFlashModeOverridden
) {
1238 SetAndPush(CAMERA_PARAM_FLASHMODE
, NS_LITERAL_STRING("auto"));
1240 return NS_ERROR_FAILURE
;
1243 OnRecorderStateChange(CameraControlListener::kRecorderStarted
);
1248 nsGonkCameraControl::StopRecordingImpl()
1250 class RecordingComplete
: public nsRunnable
1253 RecordingComplete(DeviceStorageFile
* aFile
)
1257 ~RecordingComplete() { }
1262 MOZ_ASSERT(NS_IsMainThread());
1264 nsCOMPtr
<nsIObserverService
> obs
= mozilla::services::GetObserverService();
1265 obs
->NotifyObservers(mFile
, "file-watcher-notify", NS_LITERAL_STRING("modified").get());
1270 nsRefPtr
<DeviceStorageFile
> mFile
;
1273 ReentrantMonitorAutoEnter
mon(mRecorderMonitor
);
1275 // nothing to do if we have no mRecorder
1281 mRecorder
= nullptr;
1282 OnRecorderStateChange(CameraControlListener::kRecorderStopped
);
1285 ICameraControlParameterSetAutoEnter
set(this);
1287 if (mAutoFlashModeOverridden
) {
1288 nsresult rv
= Set(CAMERA_PARAM_FLASHMODE
, NS_LITERAL_STRING("auto"));
1289 if (NS_FAILED(rv
)) {
1290 DOM_CAMERA_LOGE("Failed to set flash mode (0x%x)\n", rv
);
1295 // notify DeviceStorage that the new video file is closed and ready
1296 return NS_DispatchToMainThread(new RecordingComplete(mVideoFile
));
1300 nsGonkCameraControl::PauseRecordingImpl()
1302 ReentrantMonitorAutoEnter
mon(mRecorderMonitor
);
1304 #ifdef MOZ_WIDGET_GONK
1306 return NS_ERROR_NOT_AVAILABLE
;
1309 int err
= mRecorder
->pause();
1313 case INVALID_OPERATION
:
1314 return NS_ERROR_NOT_IMPLEMENTED
;
1316 return NS_ERROR_FAILURE
;
1319 OnRecorderStateChange(CameraControlListener::kRecorderPaused
);
1324 nsGonkCameraControl::ResumeRecordingImpl()
1326 ReentrantMonitorAutoEnter
mon(mRecorderMonitor
);
1328 #ifdef MOZ_WIDGET_GONK
1330 return NS_ERROR_NOT_AVAILABLE
;
1333 if (mRecorder
->resume() != OK
) {
1334 return NS_ERROR_FAILURE
;
1337 OnRecorderStateChange(CameraControlListener::kRecorderResumed
);
1342 nsGonkCameraControl::ResumeContinuousFocusImpl()
1344 MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread
);
1345 RETURN_IF_NO_CAMERA_HW();
1347 DOM_CAMERA_LOGI("Resuming continuous autofocus\n");
1350 // http://developer.android.com/reference/android/hardware/Camera.Parameters.html#FOCUS_MODE_CONTINUOUS_PICTURE
1351 if (NS_WARN_IF(mCameraHw
->CancelAutoFocus() != OK
)) {
1352 return NS_ERROR_FAILURE
;
1358 class AutoFocusMovingTimerCallback
: public nsITimerCallback
1361 NS_DECL_THREADSAFE_ISUPPORTS
1363 AutoFocusMovingTimerCallback(nsGonkCameraControl
* aCameraControl
)
1364 : mCameraControl(aCameraControl
)
1368 Notify(nsITimer
* aTimer
)
1370 mCameraControl
->OnAutoFocusComplete(true, true);
1375 virtual ~AutoFocusMovingTimerCallback()
1378 nsRefPtr
<nsGonkCameraControl
> mCameraControl
;
1381 NS_IMPL_ISUPPORTS(AutoFocusMovingTimerCallback
, nsITimerCallback
);
1384 nsGonkCameraControl::OnAutoFocusMoving(bool aIsMoving
)
1386 CameraControlImpl::OnAutoFocusMoving(aIsMoving
);
1389 /* Some drivers do not signal us with the status of the continuous auto focus
1390 operation, only the moving signal which comes first. As a result we need to
1391 arm a timer to detect the driver behaviour and if necessary generate the
1392 signal ourselves to update the application state. */
1393 int32_t expiredCount
= 0;
1396 ReentrantMonitorAutoEnter
mon(mReentrantMonitor
);
1398 if (mAutoFocusCompleteTimer
) {
1399 mAutoFocusCompleteTimer
->Cancel();
1401 if (!mAutoFocusPending
) {
1402 nsRefPtr
<nsITimerCallback
> timerCb
= new AutoFocusMovingTimerCallback(this);
1403 nsresult rv
= mAutoFocusCompleteTimer
->InitWithCallback(timerCb
,
1404 kAutoFocusCompleteTimeoutMs
,
1405 nsITimer::TYPE_ONE_SHOT
);
1406 NS_WARN_IF(NS_FAILED(rv
));
1411 if (!mAutoFocusPending
) {
1412 expiredCount
= mAutoFocusCompleteExpired
;
1416 if (expiredCount
== kAutoFocusCompleteTimeoutLimit
) {
1417 OnAutoFocusComplete(true, true);
1423 nsGonkCameraControl::OnAutoFocusComplete(bool aSuccess
, bool aExpired
)
1425 class AutoFocusComplete
: public nsRunnable
1428 AutoFocusComplete(nsGonkCameraControl
* aCameraControl
, bool aSuccess
, bool aExpired
)
1429 : mCameraControl(aCameraControl
)
1430 , mSuccess(aSuccess
)
1431 , mExpired(aExpired
)
1437 mCameraControl
->OnAutoFocusComplete(mSuccess
, mExpired
);
1442 nsRefPtr
<nsGonkCameraControl
> mCameraControl
;
1447 if (NS_GetCurrentThread() == mCameraThread
) {
1449 ReentrantMonitorAutoEnter
mon(mReentrantMonitor
);
1451 if (mAutoFocusPending
) {
1452 mAutoFocusPending
= false;
1453 } else if (mAutoFocusCompleteTimer
) {
1455 NS_WARNING("Camera timed out waiting for OnAutoFocusComplete");
1456 ++mAutoFocusCompleteExpired
;
1458 mAutoFocusCompleteTimer
->Cancel();
1459 --mAutoFocusCompleteExpired
;
1462 if (mAutoFocusCompleteExpired
== kAutoFocusCompleteTimeoutLimit
||
1463 mAutoFocusCompleteExpired
== -kAutoFocusCompleteTimeoutLimit
)
1465 mAutoFocusCompleteTimer
= nullptr;
1471 * Auto focusing can change some of the camera's parameters, so
1472 * we need to pull a new set before notifying any clients.
1474 PullParametersImpl();
1475 CameraControlImpl::OnAutoFocusComplete(aSuccess
);
1480 * Because the callback needs to call PullParametersImpl(),
1481 * we need to dispatch this callback through the Camera Thread.
1483 mCameraThread
->Dispatch(new AutoFocusComplete(this, aSuccess
, aExpired
), NS_DISPATCH_NORMAL
);
1487 FeatureDetected(int32_t feature
[])
1490 * For information on what constitutes a valid feature, see:
1491 * http://androidxref.com/4.0.4/xref/system/core/include/system/camera.h#202
1493 * Although the comments explicitly state that undetected features are
1494 * indicated using the value -2000, we conservatively include anything
1495 * outside the explicitly valid range of [-1000, 1000] as undetected
1498 const int32_t kLowerFeatureBound
= -1000;
1499 const int32_t kUpperFeatureBound
= 1000;
1500 return (feature
[0] >= kLowerFeatureBound
&& feature
[0] <= kUpperFeatureBound
) ||
1501 (feature
[1] >= kLowerFeatureBound
&& feature
[1] <= kUpperFeatureBound
);
1505 nsGonkCameraControl::OnFacesDetected(camera_frame_metadata_t
* aMetaData
)
1507 NS_ENSURE_TRUE_VOID(aMetaData
);
1509 nsTArray
<Face
> faces
;
1510 uint32_t numFaces
= aMetaData
->number_of_faces
;
1511 DOM_CAMERA_LOGI("Camera detected %d face(s)", numFaces
);
1513 faces
.SetCapacity(numFaces
);
1515 for (uint32_t i
= 0; i
< numFaces
; ++i
) {
1516 Face
* f
= faces
.AppendElement();
1518 f
->id
= aMetaData
->faces
[i
].id
;
1519 f
->score
= aMetaData
->faces
[i
].score
;
1520 if (f
->score
> 100) {
1523 f
->bound
.left
= aMetaData
->faces
[i
].rect
[0];
1524 f
->bound
.top
= aMetaData
->faces
[i
].rect
[1];
1525 f
->bound
.right
= aMetaData
->faces
[i
].rect
[2];
1526 f
->bound
.bottom
= aMetaData
->faces
[i
].rect
[3];
1527 DOM_CAMERA_LOGI("Camera face[%u] appended: id=%d, score=%d, bound=(%d, %d)-(%d, %d)\n",
1528 i
, f
->id
, f
->score
, f
->bound
.left
, f
->bound
.top
, f
->bound
.right
, f
->bound
.bottom
);
1530 f
->hasLeftEye
= FeatureDetected(aMetaData
->faces
[i
].left_eye
);
1531 if (f
->hasLeftEye
) {
1532 f
->leftEye
.x
= aMetaData
->faces
[i
].left_eye
[0];
1533 f
->leftEye
.y
= aMetaData
->faces
[i
].left_eye
[1];
1534 DOM_CAMERA_LOGI(" Left eye detected at (%d, %d)\n",
1535 f
->leftEye
.x
, f
->leftEye
.y
);
1537 DOM_CAMERA_LOGI(" No left eye detected\n");
1540 f
->hasRightEye
= FeatureDetected(aMetaData
->faces
[i
].right_eye
);
1541 if (f
->hasRightEye
) {
1542 f
->rightEye
.x
= aMetaData
->faces
[i
].right_eye
[0];
1543 f
->rightEye
.y
= aMetaData
->faces
[i
].right_eye
[1];
1544 DOM_CAMERA_LOGI(" Right eye detected at (%d, %d)\n",
1545 f
->rightEye
.x
, f
->rightEye
.y
);
1547 DOM_CAMERA_LOGI(" No right eye detected\n");
1550 f
->hasMouth
= FeatureDetected(aMetaData
->faces
[i
].mouth
);
1552 f
->mouth
.x
= aMetaData
->faces
[i
].mouth
[0];
1553 f
->mouth
.y
= aMetaData
->faces
[i
].mouth
[1];
1554 DOM_CAMERA_LOGI(" Mouth detected at (%d, %d)\n", f
->mouth
.x
, f
->mouth
.y
);
1556 DOM_CAMERA_LOGI(" No mouth detected\n");
1560 CameraControlImpl::OnFacesDetected(faces
);
1564 nsGonkCameraControl::OnTakePictureComplete(uint8_t* aData
, uint32_t aLength
)
1566 ReentrantMonitorAutoEnter
mon(mReentrantMonitor
);
1568 uint8_t* data
= new uint8_t[aLength
];
1570 memcpy(data
, aData
, aLength
);
1572 nsString
s(NS_LITERAL_STRING("image/"));
1573 s
.Append(mFileFormat
);
1574 DOM_CAMERA_LOGI("Got picture, type '%s', %u bytes\n", NS_ConvertUTF16toUTF8(s
).get(), aLength
);
1575 OnTakePictureComplete(data
, aLength
, s
);
1577 if (mResumePreviewAfterTakingPicture
) {
1578 nsresult rv
= StartPreview();
1579 if (NS_FAILED(rv
)) {
1580 DOM_CAMERA_LOGE("Failed to restart camera preview (%x)\n", rv
);
1581 OnPreviewStateChange(CameraControlListener::kPreviewStopped
);
1585 DOM_CAMERA_LOGI("nsGonkCameraControl::OnTakePictureComplete() done\n");
1589 nsGonkCameraControl::OnTakePictureError()
1591 CameraControlImpl::OnUserError(CameraControlListener::kInTakePicture
,
1596 nsGonkCameraControl::GetSupportedSize(const Size
& aSize
,
1597 const nsTArray
<Size
>& aSupportedSizes
,
1600 nsresult rv
= NS_ERROR_INVALID_ARG
;
1602 uint32_t minSizeDelta
= UINT32_MAX
;
1605 if (aSupportedSizes
.IsEmpty()) {
1610 if (!aSize
.width
&& !aSize
.height
) {
1611 // no size specified, take the first supported size
1612 best
= aSupportedSizes
[0];
1614 } else if (aSize
.width
&& aSize
.height
) {
1615 // both height and width specified, find the supported size closest to
1616 // the requested size, looking for an exact match first
1617 for (SizeIndex i
= 0; i
< aSupportedSizes
.Length(); ++i
) {
1618 Size size
= aSupportedSizes
[i
];
1619 if (size
.width
== aSize
.width
&& size
.height
== aSize
.height
) {
1625 // no exact match on dimensions--look for a match closest in area
1626 const uint32_t targetArea
= aSize
.width
* aSize
.height
;
1627 for (SizeIndex i
= 0; i
< aSupportedSizes
.Length(); i
++) {
1628 Size size
= aSupportedSizes
[i
];
1630 abs(static_cast<long int>(size
.width
* size
.height
- targetArea
));
1631 if (delta
< minSizeDelta
) {
1632 minSizeDelta
= delta
;
1637 } else if (!aSize
.width
) {
1638 // width not specified, find closest height match
1639 for (SizeIndex i
= 0; i
< aSupportedSizes
.Length(); i
++) {
1640 Size size
= aSupportedSizes
[i
];
1641 delta
= abs(static_cast<long int>(size
.height
- aSize
.height
));
1642 if (delta
< minSizeDelta
) {
1643 minSizeDelta
= delta
;
1648 } else if (!aSize
.height
) {
1649 // height not specified, find closest width match
1650 for (SizeIndex i
= 0; i
< aSupportedSizes
.Length(); i
++) {
1651 Size size
= aSupportedSizes
[i
];
1652 delta
= abs(static_cast<long int>(size
.width
- aSize
.width
));
1653 if (delta
< minSizeDelta
) {
1654 minSizeDelta
= delta
;
1665 nsGonkCameraControl::SelectCaptureAndPreviewSize(const Size
& aPreviewSize
,
1666 const Size
& aCaptureSize
,
1667 const Size
& aMaxSize
,
1668 uint32_t aCaptureSizeKey
)
1670 MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread
);
1672 // At this point, we know the capture size has been validated and replaced
1673 // if necessary with the best matching supported value.
1674 DOM_CAMERA_LOGI("Select capture size %ux%u, preview size %ux%u, maximum size %ux%u\n",
1675 aCaptureSize
.width
, aCaptureSize
.height
,
1676 aPreviewSize
.width
, aPreviewSize
.height
,
1677 aMaxSize
.width
, aMaxSize
.height
);
1679 nsAutoTArray
<Size
, 16> sizes
;
1680 nsresult rv
= Get(CAMERA_PARAM_SUPPORTED_PREVIEWSIZES
, sizes
);
1681 if (NS_WARN_IF(NS_FAILED(rv
))) {
1685 // May optionally apply a ceiling to the preview size. Any supported preview
1686 // size with an area larger than the maximum will be ignored regardless of
1687 // aspect ratio or delta to requested preview size.
1688 uint32_t maxArea
= aMaxSize
.width
* aMaxSize
.height
;
1690 maxArea
= UINT32_MAX
;
1693 const uint32_t previewArea
= aPreviewSize
.width
* aPreviewSize
.height
;
1695 // We should select a preview size with the same aspect ratio as the capture
1696 // size and minimize the delta with the requested preview size. If we are
1697 // unable to find any supported preview sizes which match the aspect ratio
1698 // of the capture size, we fallback to only minimizing the delta with the
1699 // requested preview size.
1701 SizeIndex bestSizeMatch
= 0; // initializers to keep warnings away
1702 SizeIndex bestSizeMatchWithAspectRatio
= 0;
1703 bool foundSizeMatch
= false;
1704 bool foundSizeMatchWithAspectRatio
= false;
1706 uint32_t bestAreaDelta
= UINT32_MAX
;
1707 uint32_t bestAreaDeltaWithAspect
= UINT32_MAX
;
1709 for (SizeIndex i
= 0; i
< sizes
.Length(); ++i
) {
1710 const Size
& s
= sizes
[i
];
1712 // preview size must be smaller or equal to the capture size
1713 if (aCaptureSize
.width
< s
.width
|| aCaptureSize
.height
< s
.height
) {
1717 const uint32_t area
= s
.width
* s
.height
;
1718 if (area
> maxArea
) {
1722 const uint32_t delta
= abs(static_cast<long int>(previewArea
- area
));
1723 if (s
.width
* aCaptureSize
.height
== aCaptureSize
.width
* s
.height
) {
1725 // exact match, including aspect ratio--we can stop now
1726 bestSizeMatchWithAspectRatio
= i
;
1727 foundSizeMatchWithAspectRatio
= true;
1729 } else if (delta
< bestAreaDeltaWithAspect
) {
1730 // aspect ratio match
1731 bestAreaDeltaWithAspect
= delta
;
1732 bestSizeMatchWithAspectRatio
= i
;
1733 foundSizeMatchWithAspectRatio
= true;
1735 } else if (delta
< bestAreaDelta
) {
1736 bestAreaDelta
= delta
;
1738 foundSizeMatch
= true;
1743 if (foundSizeMatchWithAspectRatio
) {
1744 previewSize
= sizes
[bestSizeMatchWithAspectRatio
];
1745 } else if (foundSizeMatch
) {
1746 DOM_CAMERA_LOGW("Unable to match a preview size with aspect ratio of capture size %ux%u\n",
1747 aCaptureSize
.width
, aCaptureSize
.height
);
1748 previewSize
= sizes
[bestSizeMatch
];
1750 DOM_CAMERA_LOGE("Unable to find a preview size for capture size %ux%u\n",
1751 aCaptureSize
.width
, aCaptureSize
.height
);
1752 return NS_ERROR_INVALID_ARG
;
1755 DOM_CAMERA_LOGI("Setting capture size to %ux%u, preview size to %ux%u\n",
1756 aCaptureSize
.width
, aCaptureSize
.height
,
1757 previewSize
.width
, previewSize
.height
);
1760 rv
= Get(CAMERA_PARAM_PREVIEWSIZE
, oldSize
);
1761 if (NS_WARN_IF(NS_FAILED(rv
))) {
1765 rv
= Set(CAMERA_PARAM_PREVIEWSIZE
, previewSize
);
1766 if (NS_WARN_IF(NS_FAILED(rv
))) {
1769 rv
= Set(aCaptureSizeKey
, aCaptureSize
);
1770 if (NS_WARN_IF(NS_FAILED(rv
))) {
1771 Set(CAMERA_PARAM_PREVIEWSIZE
, oldSize
); // error, try to restore the original preview size
1775 mCurrentConfiguration
.mPreviewSize
= previewSize
;
1780 nsGonkCameraControl::SetVideoConfiguration(const Configuration
& aConfig
)
1782 DOM_CAMERA_LOGT("%s:%d\n", __func__
, __LINE__
);
1784 RecorderProfile
* profile
;
1785 if (!mRecorderProfiles
.Get(aConfig
.mRecorderProfile
, &profile
)) {
1786 DOM_CAMERA_LOGE("Recorder profile '%s' is not supported\n",
1787 NS_ConvertUTF16toUTF8(aConfig
.mRecorderProfile
).get());
1788 return NS_ERROR_INVALID_ARG
;
1791 const RecorderProfile::Video
& video(profile
->GetVideo());
1792 const Size
& size
= video
.GetSize();
1793 const uint32_t fps
= video
.GetFramesPerSecond();
1794 if (fps
== 0 || fps
> INT_MAX
|| size
.width
== 0 || size
.height
== 0) {
1795 DOM_CAMERA_LOGE("Can't configure video with fps=%u, width=%u, height=%u\n",
1796 fps
, size
.width
, size
.height
);
1797 return NS_ERROR_FAILURE
;
1800 PullParametersImpl();
1803 ICameraControlParameterSetAutoEnter
set(this);
1806 if (mSeparateVideoAndPreviewSizesSupported
) {
1807 // The camera supports two video streams: a low(er) resolution preview
1808 // stream and and a potentially high(er) resolution stream for encoding.
1810 rv
= Get(CAMERA_PARAM_PREFERRED_PREVIEWSIZE_FOR_VIDEO
, preferred
);
1811 if (NS_WARN_IF(NS_FAILED(rv
))) {
1815 rv
= SelectCaptureAndPreviewSize(aConfig
.mPreviewSize
, size
, preferred
,
1816 CAMERA_PARAM_VIDEOSIZE
);
1817 if (NS_FAILED(rv
)) {
1818 DOM_CAMERA_LOGE("Failed to set video and preview sizes (0x%x)\n", rv
);
1822 // The camera only supports a single video stream: in this case, we set
1823 // the preview size to be the desired video recording size, and ignore
1824 // the specified preview size.
1825 rv
= Set(CAMERA_PARAM_PREVIEWSIZE
, size
);
1826 if (NS_FAILED(rv
)) {
1827 DOM_CAMERA_LOGE("Failed to set video mode preview size (0x%x)\n", rv
);
1831 mCurrentConfiguration
.mPreviewSize
= size
;
1834 mLastRecorderSize
= size
;
1836 rv
= Set(CAMERA_PARAM_PREVIEWFRAMERATE
, static_cast<int>(fps
));
1837 if (NS_FAILED(rv
)) {
1838 DOM_CAMERA_LOGE("Failed to set video mode frame rate (0x%x)\n", rv
);
1847 class GonkRecorderListener
: public IMediaRecorderClient
1850 GonkRecorderListener(nsGonkCameraControl
* aCameraControl
)
1851 : mCameraControl(aCameraControl
)
1853 DOM_CAMERA_LOGT("%s:%d : this=%p, aCameraControl=%p\n",
1854 __func__
, __LINE__
, this, mCameraControl
.get());
1857 void notify(int msg
, int ext1
, int ext2
)
1859 if (mCameraControl
) {
1860 mCameraControl
->OnRecorderEvent(msg
, ext1
, ext2
);
1864 IBinder
* onAsBinder()
1866 DOM_CAMERA_LOGE("onAsBinder() called, should NEVER get called!\n");
1871 ~GonkRecorderListener() { }
1872 nsRefPtr
<nsGonkCameraControl
> mCameraControl
;
1876 nsGonkCameraControl::OnRecorderEvent(int msg
, int ext1
, int ext2
)
1879 * Refer to base/include/media/mediarecorder.h for a complete list
1880 * of error and info message codes. There are duplicate values
1881 * within the status/error code space, as determined by code inspection:
1887 * 1 MEDIA_RECORDER_EVENT_ERROR
1888 * 1 MEDIA_RECORDER_ERROR_UNKNOWN
1889 * [3] ERROR_MALFORMED
1890 * 100 mediaplayer.h::MEDIA_ERROR_SERVER_DIED
1892 * 2 MEDIA_RECORDER_EVENT_INFO
1893 * 800 MEDIA_RECORDER_INFO_MAX_DURATION_REACHED
1895 * 801 MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED
1897 * 1000 MEDIA_RECORDER_TRACK_INFO_COMPLETION_STATUS[1b]
1898 * [3] UNKNOWN_ERROR, etc.
1899 * 100 MEDIA_ERROR[4]
1900 * 100 mediaplayer.h::MEDIA_ERROR_SERVER_DIED
1902 * 100 MEDIA_RECORDER_TRACK_EVENT_ERROR
1903 * 100 MEDIA_RECORDER_TRACK_ERROR_GENERAL[1a]
1904 * [3] UNKNOWN_ERROR, etc.
1905 * 200 MEDIA_RECORDER_ERROR_VIDEO_NO_SYNC_FRAME[2]
1907 * 101 MEDIA_RECORDER_TRACK_EVENT_INFO
1908 * 1000 MEDIA_RECORDER_TRACK_INFO_COMPLETION_STATUS[1a]
1909 * [3] UNKNOWN_ERROR, etc.
1910 * N see mediarecorder.h::media_recorder_info_type[5]
1912 * 1. a) High 4 bits are the track number, the next 12 bits are reserved,
1913 * and the final 16 bits are the actual error code (above).
1914 * b) But not in this case.
1915 * 2. Never actually used in AOSP code?
1916 * 3. Specific error codes are from utils/Errors.h and/or
1917 * include/media/stagefright/MediaErrors.h.
1918 * 4. Only in frameworks/base/media/libmedia/mediaplayer.cpp.
1919 * 5. These are mostly informational and we can ignore them; note that
1920 * although the MEDIA_RECORDER_INFO_MAX_DURATION_REACHED and
1921 * MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED values are defined in this
1922 * enum, they are used with different ext1 codes. /o\
1924 int trackNum
= CameraControlListener::kNoTrackNumber
;
1927 // Recorder-related events
1928 case MEDIA_RECORDER_EVENT_INFO
:
1930 case MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED
:
1931 DOM_CAMERA_LOGI("recorder-event : info: maximum file size reached\n");
1932 OnRecorderStateChange(CameraControlListener::kFileSizeLimitReached
, ext2
, trackNum
);
1935 case MEDIA_RECORDER_INFO_MAX_DURATION_REACHED
:
1936 DOM_CAMERA_LOGI("recorder-event : info: maximum video duration reached\n");
1937 OnRecorderStateChange(CameraControlListener::kVideoLengthLimitReached
, ext2
, trackNum
);
1940 case MEDIA_RECORDER_TRACK_INFO_COMPLETION_STATUS
:
1941 DOM_CAMERA_LOGI("recorder-event : info: track completed\n");
1942 OnRecorderStateChange(CameraControlListener::kTrackCompleted
, ext2
, trackNum
);
1947 case MEDIA_RECORDER_EVENT_ERROR
:
1949 case MEDIA_RECORDER_ERROR_UNKNOWN
:
1950 DOM_CAMERA_LOGE("recorder-event : recorder-error: %d (0x%08x)\n", ext2
, ext2
);
1951 OnRecorderStateChange(CameraControlListener::kMediaRecorderFailed
, ext2
, trackNum
);
1954 case MEDIA_ERROR_SERVER_DIED
:
1955 DOM_CAMERA_LOGE("recorder-event : recorder-error: server died\n");
1956 OnRecorderStateChange(CameraControlListener::kMediaServerFailed
, ext2
, trackNum
);
1961 // Track-related events, see note 1(a) above.
1962 case MEDIA_RECORDER_TRACK_EVENT_INFO
:
1963 trackNum
= (ext1
& 0xF0000000) >> 28;
1966 case MEDIA_RECORDER_TRACK_INFO_COMPLETION_STATUS
:
1968 DOM_CAMERA_LOGI("recorder-event : track-complete: track %d, %d (0x%08x)\n", trackNum
, ext2
, ext2
);
1969 OnRecorderStateChange(CameraControlListener::kTrackCompleted
, ext2
, trackNum
);
1972 DOM_CAMERA_LOGE("recorder-event : track-error: track %d, %d (0x%08x)\n", trackNum
, ext2
, ext2
);
1973 OnRecorderStateChange(CameraControlListener::kTrackFailed
, ext2
, trackNum
);
1976 case MEDIA_RECORDER_TRACK_INFO_PROGRESS_IN_TIME
:
1977 DOM_CAMERA_LOGI("recorder-event : track-info: progress in time: %d ms\n", ext2
);
1982 case MEDIA_RECORDER_TRACK_EVENT_ERROR
:
1983 trackNum
= (ext1
& 0xF0000000) >> 28;
1985 DOM_CAMERA_LOGE("recorder-event : track-error: track %d, %d (0x%08x)\n", trackNum
, ext2
, ext2
);
1986 OnRecorderStateChange(CameraControlListener::kTrackFailed
, ext2
, trackNum
);
1990 // All unhandled cases wind up here
1991 DOM_CAMERA_LOGW("recorder-event : unhandled: msg=%d, ext1=%d, ext2=%d\n", msg
, ext1
, ext2
);
1995 nsGonkCameraControl::SetupRecording(int aFd
, int aRotation
,
1996 uint64_t aMaxFileSizeBytes
,
1997 uint64_t aMaxVideoLengthMs
)
1999 RETURN_IF_NO_CAMERA_HW();
2001 // choosing a size big enough to hold the params
2002 const size_t SIZE
= 256;
2005 ReentrantMonitorAutoEnter
mon(mRecorderMonitor
);
2007 mRecorder
= new GonkRecorder();
2008 CHECK_SETARG_RETURN(mRecorder
->init(), NS_ERROR_FAILURE
);
2011 GonkRecorderProfile::ConfigureRecorder(*mRecorder
, mCameraId
,
2012 mCurrentConfiguration
.mRecorderProfile
);
2013 if (NS_WARN_IF(NS_FAILED(rv
))) {
2017 CHECK_SETARG_RETURN(mRecorder
->setCamera(mCameraHw
), NS_ERROR_FAILURE
);
2019 DOM_CAMERA_LOGI("maxVideoLengthMs=%llu\n", aMaxVideoLengthMs
);
2020 const uint64_t kMaxVideoLengthMs
= INT64_MAX
/ 1000;
2021 if (aMaxVideoLengthMs
== 0) {
2022 aMaxVideoLengthMs
= -1;
2023 } else if (aMaxVideoLengthMs
> kMaxVideoLengthMs
) {
2024 // GonkRecorder parameters are internally limited to signed 64-bit values,
2025 // and the time length limit is converted from milliseconds to microseconds,
2026 // so we limit this value to prevent any unexpected overflow weirdness.
2027 DOM_CAMERA_LOGW("maxVideoLengthMs capped to %lld\n", kMaxVideoLengthMs
);
2028 aMaxVideoLengthMs
= kMaxVideoLengthMs
;
2030 snprintf(buffer
, SIZE
, "max-duration=%lld", aMaxVideoLengthMs
);
2031 CHECK_SETARG_RETURN(mRecorder
->setParameters(String8(buffer
)),
2032 NS_ERROR_INVALID_ARG
);
2034 DOM_CAMERA_LOGI("maxFileSizeBytes=%llu\n", aMaxFileSizeBytes
);
2035 if (aMaxFileSizeBytes
== 0) {
2036 aMaxFileSizeBytes
= -1;
2037 } else if (aMaxFileSizeBytes
> INT64_MAX
) {
2038 // GonkRecorder parameters are internally limited to signed 64-bit values
2039 DOM_CAMERA_LOGW("maxFileSizeBytes capped to INT64_MAX\n");
2040 aMaxFileSizeBytes
= INT64_MAX
;
2042 snprintf(buffer
, SIZE
, "max-filesize=%lld", aMaxFileSizeBytes
);
2043 CHECK_SETARG_RETURN(mRecorder
->setParameters(String8(buffer
)),
2044 NS_ERROR_INVALID_ARG
);
2046 // adjust rotation by camera sensor offset
2048 r
+= mCameraHw
->GetSensorOrientation();
2049 r
= RationalizeRotation(r
);
2050 DOM_CAMERA_LOGI("setting video rotation to %d degrees (mapped from %d)\n", r
, aRotation
);
2051 snprintf(buffer
, SIZE
, "video-param-rotation-angle-degrees=%d", r
);
2052 CHECK_SETARG_RETURN(mRecorder
->setParameters(String8(buffer
)),
2053 NS_ERROR_INVALID_ARG
);
2055 CHECK_SETARG_RETURN(mRecorder
->setListener(new GonkRecorderListener(this)),
2058 // recording API needs file descriptor of output file
2059 CHECK_SETARG_RETURN(mRecorder
->setOutputFile(aFd
, 0, 0), NS_ERROR_FAILURE
);
2060 CHECK_SETARG_RETURN(mRecorder
->prepare(), NS_ERROR_FAILURE
);
2066 nsGonkCameraControl::StopInternal()
2068 DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__
, __LINE__
, this);
2070 // if we're recording, stop recording
2071 StopRecordingImpl();
2074 nsresult rv
= StopPreviewImpl();
2076 // release the hardware handle
2077 if (mCameraHw
.get()){
2086 nsGonkCameraControl::StopImpl()
2088 DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__
, __LINE__
, this);
2090 nsresult rv
= StopInternal();
2091 if (rv
!= NS_ERROR_NOT_INITIALIZED
) {
2094 if (NS_SUCCEEDED(rv
)) {
2095 OnHardwareStateChange(CameraControlListener::kHardwareClosed
, NS_OK
);
2101 nsGonkCameraControl::LoadRecorderProfiles()
2103 if (mRecorderProfiles
.Count() == 0) {
2104 nsTArray
<nsRefPtr
<RecorderProfile
>> profiles
;
2105 nsresult rv
= GonkRecorderProfile::GetAll(mCameraId
, profiles
);
2106 if (NS_WARN_IF(NS_FAILED(rv
))) {
2110 nsTArray
<Size
> sizes
;
2111 rv
= Get(CAMERA_PARAM_SUPPORTED_VIDEOSIZES
, sizes
);
2112 if (NS_WARN_IF(NS_FAILED(rv
))) {
2113 return NS_ERROR_NOT_AVAILABLE
;
2116 nsTArray
<RecorderProfile
>::size_type bestIndexMatch
= 0;
2117 int bestAreaMatch
= 0;
2119 // Limit profiles to those video sizes supported by the camera hardware...
2120 for (nsTArray
<RecorderProfile
>::size_type i
= 0; i
< profiles
.Length(); ++i
) {
2121 int width
= profiles
[i
]->GetVideo().GetSize().width
;
2122 int height
= profiles
[i
]->GetVideo().GetSize().height
;
2123 if (width
< 0 || height
< 0) {
2124 DOM_CAMERA_LOGW("Ignoring weird profile '%s' with width and/or height < 0\n",
2125 NS_ConvertUTF16toUTF8(profiles
[i
]->GetName()).get());
2128 for (nsTArray
<Size
>::size_type n
= 0; n
< sizes
.Length(); ++n
) {
2129 if (static_cast<uint32_t>(width
) == sizes
[n
].width
&&
2130 static_cast<uint32_t>(height
) == sizes
[n
].height
) {
2131 mRecorderProfiles
.Put(profiles
[i
]->GetName(), profiles
[i
]);
2132 int area
= width
* height
;
2133 if (area
> bestAreaMatch
) {
2135 bestAreaMatch
= area
;
2142 // Default profile is the one with the largest area.
2143 if (bestAreaMatch
> 0) {
2145 name
.AssignASCII("default");
2146 mRecorderProfiles
.Put(name
, profiles
[bestIndexMatch
]);
2153 /* static */ PLDHashOperator
2154 nsGonkCameraControl::Enumerate(const nsAString
& aProfileName
,
2155 RecorderProfile
* aProfile
,
2158 nsTArray
<nsString
>* profiles
= static_cast<nsTArray
<nsString
>*>(aUserArg
);
2159 MOZ_ASSERT(profiles
);
2160 profiles
->AppendElement(aProfileName
);
2161 return PL_DHASH_NEXT
;
2165 nsGonkCameraControl::GetRecorderProfiles(nsTArray
<nsString
>& aProfiles
)
2167 nsresult rv
= LoadRecorderProfiles();
2168 if (NS_WARN_IF(NS_FAILED(rv
))) {
2173 mRecorderProfiles
.EnumerateRead(Enumerate
, static_cast<void*>(&aProfiles
));
2177 ICameraControl::RecorderProfile
*
2178 nsGonkCameraControl::GetProfileInfo(const nsAString
& aProfile
)
2180 RecorderProfile
* profile
;
2181 if (!mRecorderProfiles
.Get(aProfile
, &profile
)) {
2188 nsGonkCameraControl::OnRateLimitPreview(bool aLimit
)
2190 CameraControlImpl::OnRateLimitPreview(aLimit
);
2194 nsGonkCameraControl::OnNewPreviewFrame(layers::TextureClient
* aBuffer
)
2196 nsRefPtr
<Image
> frame
= mImageContainer
->CreateImage(ImageFormat::GRALLOC_PLANAR_YCBCR
);
2198 GrallocImage
* videoImage
= static_cast<GrallocImage
*>(frame
.get());
2200 GrallocImage::GrallocData data
;
2201 data
.mGraphicBuffer
= aBuffer
;
2202 data
.mPicSize
= IntSize(mCurrentConfiguration
.mPreviewSize
.width
,
2203 mCurrentConfiguration
.mPreviewSize
.height
);
2204 videoImage
->SetData(data
);
2206 OnNewPreviewFrame(frame
, mCurrentConfiguration
.mPreviewSize
.width
,
2207 mCurrentConfiguration
.mPreviewSize
.height
);
2211 nsGonkCameraControl::OnSystemError(CameraControlListener::SystemContext aWhere
,
2214 if (aWhere
== CameraControlListener::kSystemService
) {
2216 OnHardwareStateChange(CameraControlListener::kHardwareClosed
, NS_ERROR_FAILURE
);
2219 CameraControlImpl::OnSystemError(aWhere
, aError
);
2222 // Gonk callback handlers.
2226 OnTakePictureComplete(nsGonkCameraControl
* gc
, uint8_t* aData
, uint32_t aLength
)
2228 gc
->OnTakePictureComplete(aData
, aLength
);
2232 OnTakePictureError(nsGonkCameraControl
* gc
)
2234 gc
->OnTakePictureError();
2238 OnAutoFocusComplete(nsGonkCameraControl
* gc
, bool aSuccess
)
2240 gc
->OnAutoFocusComplete(aSuccess
, false);
2244 OnAutoFocusMoving(nsGonkCameraControl
* gc
, bool aIsMoving
)
2246 gc
->OnAutoFocusMoving(aIsMoving
);
2250 OnFacesDetected(nsGonkCameraControl
* gc
, camera_frame_metadata_t
* aMetaData
)
2252 gc
->OnFacesDetected(aMetaData
);
2256 OnRateLimitPreview(nsGonkCameraControl
* gc
, bool aLimit
)
2258 gc
->OnRateLimitPreview(aLimit
);
2262 OnNewPreviewFrame(nsGonkCameraControl
* gc
, layers::TextureClient
* aBuffer
)
2264 gc
->OnNewPreviewFrame(aBuffer
);
2268 OnShutter(nsGonkCameraControl
* gc
)
2274 OnSystemError(nsGonkCameraControl
* gc
,
2275 CameraControlListener::SystemContext aWhere
,
2276 int32_t aArg1
, int32_t aArg2
)
2279 DOM_CAMERA_LOGE("OnSystemError : aWhere=%d, aArg1=%d, aArg2=%d\n", aWhere
, aArg1
, aArg2
);
2284 gc
->OnSystemError(aWhere
, NS_ERROR_FAILURE
);
2287 } // namespace mozilla