2 * Copyright (C) 2012-2014 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"
29 #include <media/MediaProfiles.h>
30 #include "mozilla/FileUtils.h"
31 #include "mozilla/Services.h"
32 #include "mozilla/unused.h"
33 #include "mozilla/ipc/FileDescriptorUtils.h"
34 #include "nsAlgorithm.h"
35 #include <media/mediaplayer.h>
36 #include "nsPrintfCString.h"
37 #include "nsIObserverService.h"
38 #include "nsIVolume.h"
39 #include "nsIVolumeService.h"
40 #include "AutoRwLock.h"
41 #include "GonkCameraHwMgr.h"
42 #include "GonkRecorderProfiles.h"
43 #include "CameraCommon.h"
44 #include "GonkCameraParameters.h"
45 #include "DeviceStorageFileDescriptor.h"
47 using namespace mozilla
;
48 using namespace mozilla::layers
;
49 using namespace mozilla::gfx
;
50 using namespace mozilla::ipc
;
51 using namespace android
;
53 #define RETURN_IF_NO_CAMERA_HW() \
55 if (!mCameraHw.get()) { \
56 NS_WARNING("Camera hardware is not initialized"); \
57 DOM_CAMERA_LOGE("%s:%d : mCameraHw is null\n", __func__, __LINE__); \
58 return NS_ERROR_NOT_INITIALIZED; \
62 // Construct nsGonkCameraControl on the main thread.
63 nsGonkCameraControl::nsGonkCameraControl(uint32_t aCameraId
)
65 , mCameraId(aCameraId
)
66 , mLastPictureSize({0, 0})
67 , mLastThumbnailSize({0, 0})
69 , mResumePreviewAfterTakingPicture(false) // XXXmikeh - see bug 950102
70 , mFlashSupported(false)
71 , mLuminanceSupported(false)
72 , mAutoFlashModeOverridden(false)
73 , mSeparateVideoAndPreviewSizesSupported(false)
74 , mDeferConfigUpdate(0)
75 , mMediaProfiles(nullptr)
77 , mRecorderMonitor("GonkCameraControl::mRecorder.Monitor")
78 , mProfileManager(nullptr)
79 , mRecorderProfile(nullptr)
81 , mReentrantMonitor("GonkCameraControl::OnTakePicture.Monitor")
83 // Constructor runs on the main thread...
84 DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__
, __LINE__
, this);
85 mImageContainer
= LayerManager::CreateImageContainer();
89 nsGonkCameraControl::StartImpl(const Configuration
* aInitialConfig
)
92 * For initialization, we try to return the camera control to the upper
93 * upper layer (i.e. the DOM) as quickly as possible. To do this, the
94 * camera is initialized in the following stages:
96 * 0. Initialize() initializes the hardware;
97 * 1. SetConfigurationInternal() does the minimal configuration
98 * required so that we can start the preview -and- report a valid
99 * configuration to the upper layer;
100 * 2. OnHardwareStateChange() reports that the hardware is ready,
101 * which the upper (e.g. DOM) layer can (and does) use to return
102 * the camera control object;
103 * 3. StartPreviewImpl() starts the flow of preview frames from the
106 * The intent of the above flow is to let the Main Thread do as much work
107 * up-front as possible without waiting for blocking Camera Thread calls
110 MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread
);
112 nsresult rv
= Initialize();
113 if (NS_WARN_IF(NS_FAILED(rv
))) {
114 return NS_ERROR_NOT_INITIALIZED
;
117 if (aInitialConfig
) {
118 rv
= SetConfigurationInternal(*aInitialConfig
);
119 if (NS_WARN_IF(NS_FAILED(rv
))) {
120 // The initial configuration failed, close up the hardware
126 OnHardwareStateChange(CameraControlListener::kHardwareOpen
);
127 if (aInitialConfig
) {
128 return StartPreviewImpl();
135 nsGonkCameraControl::Initialize()
137 if (mCameraHw
.get()) {
138 DOM_CAMERA_LOGI("Camera %d already connected (this=%p)\n", mCameraId
, this);
139 return NS_ERROR_ALREADY_INITIALIZED
;
142 mCameraHw
= GonkCameraHardware::Connect(this, mCameraId
);
143 if (!mCameraHw
.get()) {
144 DOM_CAMERA_LOGE("Failed to connect to camera %d (this=%p)\n", mCameraId
, this);
145 return NS_ERROR_NOT_INITIALIZED
;
148 DOM_CAMERA_LOGI("Initializing camera %d (this=%p, mCameraHw=%p)\n", mCameraId
, this, mCameraHw
.get());
150 // Initialize our camera configuration database.
151 PullParametersImpl();
153 // Set preferred preview frame format.
154 mParams
.Set(CAMERA_PARAM_PREVIEWFORMAT
, NS_LITERAL_STRING("yuv420sp"));
155 // Turn off any normal pictures returned by the HDR scene mode
156 mParams
.Set(CAMERA_PARAM_SCENEMODE_HDR_RETURNNORMALPICTURE
, false);
157 PushParametersImpl();
159 // Grab any other settings we'll need later.
160 mParams
.Get(CAMERA_PARAM_PICTURE_FILEFORMAT
, mFileFormat
);
161 mParams
.Get(CAMERA_PARAM_THUMBNAILSIZE
, mLastThumbnailSize
);
163 // The emulator's camera returns -1 for these values; bump them up to 0
165 mParams
.Get(CAMERA_PARAM_SUPPORTED_MAXMETERINGAREAS
, areas
);
166 mCurrentConfiguration
.mMaxMeteringAreas
= areas
!= -1 ? areas
: 0;
167 mParams
.Get(CAMERA_PARAM_SUPPORTED_MAXFOCUSAREAS
, areas
);
168 mCurrentConfiguration
.mMaxFocusAreas
= areas
!= -1 ? areas
: 0;
170 mParams
.Get(CAMERA_PARAM_PICTURE_SIZE
, mLastPictureSize
);
171 mParams
.Get(CAMERA_PARAM_PREVIEWSIZE
, mCurrentConfiguration
.mPreviewSize
);
173 nsString luminance
; // check for support
174 mParams
.Get(CAMERA_PARAM_LUMINANCE
, luminance
);
175 mLuminanceSupported
= !luminance
.IsEmpty();
178 mParams
.Get(CAMERA_PARAM_FLASHMODE
, flashMode
);
179 mFlashSupported
= !flashMode
.IsEmpty();
181 double quality
; // informational only
182 mParams
.Get(CAMERA_PARAM_PICTURE_QUALITY
, quality
);
184 DOM_CAMERA_LOGI(" - maximum metering areas: %u\n", mCurrentConfiguration
.mMaxMeteringAreas
);
185 DOM_CAMERA_LOGI(" - maximum focus areas: %u\n", mCurrentConfiguration
.mMaxFocusAreas
);
186 DOM_CAMERA_LOGI(" - default picture size: %u x %u\n",
187 mLastPictureSize
.width
, mLastPictureSize
.height
);
188 DOM_CAMERA_LOGI(" - default picture file format: %s\n",
189 NS_ConvertUTF16toUTF8(mFileFormat
).get());
190 DOM_CAMERA_LOGI(" - default picture quality: %f\n", quality
);
191 DOM_CAMERA_LOGI(" - default thumbnail size: %u x %u\n",
192 mLastThumbnailSize
.width
, mLastThumbnailSize
.height
);
193 DOM_CAMERA_LOGI(" - default preview size: %u x %u\n",
194 mCurrentConfiguration
.mPreviewSize
.width
, mCurrentConfiguration
.mPreviewSize
.height
);
195 DOM_CAMERA_LOGI(" - luminance reporting: %ssupported\n",
196 mLuminanceSupported
? "" : "NOT ");
197 if (mFlashSupported
) {
198 DOM_CAMERA_LOGI(" - flash: supported, default mode '%s'\n",
199 NS_ConvertUTF16toUTF8(flashMode
).get());
201 DOM_CAMERA_LOGI(" - flash: NOT supported\n");
204 nsAutoTArray
<Size
, 16> sizes
;
205 mParams
.Get(CAMERA_PARAM_SUPPORTED_VIDEOSIZES
, sizes
);
206 if (sizes
.Length() > 0) {
207 mSeparateVideoAndPreviewSizesSupported
= true;
208 DOM_CAMERA_LOGI(" - support for separate preview and video sizes\n");
209 mParams
.Get(CAMERA_PARAM_VIDEOSIZE
, mLastRecorderSize
);
210 DOM_CAMERA_LOGI(" - default video recorder size: %u x %u\n",
211 mLastRecorderSize
.width
, mLastRecorderSize
.height
);
213 mLastRecorderSize
= mCurrentConfiguration
.mPreviewSize
;
219 nsGonkCameraControl::~nsGonkCameraControl()
221 DOM_CAMERA_LOGT("%s:%d : this=%p, mCameraHw = %p\n", __func__
, __LINE__
, this, mCameraHw
.get());
224 DOM_CAMERA_LOGT("%s:%d\n", __func__
, __LINE__
);
228 nsGonkCameraControl::SetConfigurationInternal(const Configuration
& aConfig
)
230 DOM_CAMERA_LOGT("%s:%d\n", __func__
, __LINE__
);
235 ICameraControlParameterSetAutoEnter
set(this);
237 switch (aConfig
.mMode
) {
239 rv
= SetPictureConfiguration(aConfig
);
243 rv
= SetVideoConfiguration(aConfig
);
247 MOZ_ASSERT_UNREACHABLE("Unanticipated camera mode in SetConfigurationInternal()");
248 rv
= NS_ERROR_FAILURE
;
252 DOM_CAMERA_LOGT("%s:%d\n", __func__
, __LINE__
);
253 if (NS_WARN_IF(NS_FAILED(rv
))) {
257 rv
= Set(CAMERA_PARAM_RECORDINGHINT
, aConfig
.mMode
== kVideoMode
);
259 DOM_CAMERA_LOGE("Failed to set recording hint (0x%x)\n", rv
);
263 mCurrentConfiguration
.mMode
= aConfig
.mMode
;
264 mCurrentConfiguration
.mRecorderProfile
= aConfig
.mRecorderProfile
;
266 OnConfigurationChange();
271 nsGonkCameraControl::SetConfigurationImpl(const Configuration
& aConfig
)
273 DOM_CAMERA_LOGT("%s:%d\n", __func__
, __LINE__
);
274 MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread
);
276 if (aConfig
.mMode
== kUnspecifiedMode
) {
277 DOM_CAMERA_LOGW("Can't set camera mode to 'unspecified', ignoring\n");
278 return NS_ERROR_INVALID_ARG
;
281 // Stop any currently running preview
282 nsresult rv
= PausePreview();
284 DOM_CAMERA_LOGW("PausePreview() in SetConfigurationImpl() failed (0x%x)\n", rv
);
285 if (rv
== NS_ERROR_NOT_INITIALIZED
) {
286 // If there no hardware available, nothing else we try will work,
292 DOM_CAMERA_LOGT("%s:%d\n", __func__
, __LINE__
);
293 rv
= SetConfigurationInternal(aConfig
);
294 if (NS_WARN_IF(NS_FAILED(rv
))) {
299 // Restart the preview
300 DOM_CAMERA_LOGT("%s:%d\n", __func__
, __LINE__
);
301 return StartPreviewImpl();
305 nsGonkCameraControl::SetPictureConfiguration(const Configuration
& aConfig
)
307 DOM_CAMERA_LOGT("%s:%d\n", __func__
, __LINE__
);
309 // remove any existing recorder profile
310 mRecorderProfile
= nullptr;
312 nsresult rv
= SetPreviewSize(aConfig
.mPreviewSize
);
313 if (NS_WARN_IF(NS_FAILED(rv
))) {
317 mParams
.Get(CAMERA_PARAM_PREVIEWFRAMERATE
, mPreviewFps
);
319 DOM_CAMERA_LOGI("picture mode preview: wanted %ux%u, got %ux%u (%u fps)\n",
320 aConfig
.mPreviewSize
.width
, aConfig
.mPreviewSize
.height
,
321 mCurrentConfiguration
.mPreviewSize
.width
, mCurrentConfiguration
.mPreviewSize
.height
,
327 // Parameter management.
329 nsGonkCameraControl::PushParameters()
331 uint32_t dcu
= mDeferConfigUpdate
;
333 DOM_CAMERA_LOGI("Defering config update (nest level %u)\n", dcu
);
338 * If we're already on the camera thread, call PushParametersImpl()
339 * directly, so that it executes synchronously. Some callers
340 * require this so that changes take effect immediately before
343 if (NS_GetCurrentThread() != mCameraThread
) {
344 DOM_CAMERA_LOGT("%s:%d - dispatching to Camera Thread\n", __func__
, __LINE__
);
345 nsCOMPtr
<nsIRunnable
> pushParametersTask
=
346 NS_NewRunnableMethod(this, &nsGonkCameraControl::PushParametersImpl
);
347 return mCameraThread
->Dispatch(pushParametersTask
, NS_DISPATCH_NORMAL
);
350 DOM_CAMERA_LOGT("%s:%d\n", __func__
, __LINE__
);
351 return PushParametersImpl();
355 nsGonkCameraControl::BeginBatchParameterSet()
357 uint32_t dcu
= ++mDeferConfigUpdate
;
359 NS_WARNING("Overflow weirdness incrementing mDeferConfigUpdate!");
362 DOM_CAMERA_LOGI("Begin deferring camera configuration updates (nest level %u)\n", dcu
);
366 nsGonkCameraControl::EndBatchParameterSet()
368 uint32_t dcu
= mDeferConfigUpdate
--;
370 NS_WARNING("Underflow badness decrementing mDeferConfigUpdate!");
373 DOM_CAMERA_LOGI("End deferring camera configuration updates (nest level %u)\n", dcu
);
380 template<class T
> nsresult
381 nsGonkCameraControl::SetAndPush(uint32_t aKey
, const T
& aValue
)
383 nsresult rv
= mParams
.Set(aKey
, aValue
);
385 DOM_CAMERA_LOGE("Camera parameter aKey=%d failed to set (0x%x)\n", aKey
, rv
);
388 return PushParameters();
391 // Array-of-Size parameter accessor.
393 nsGonkCameraControl::Get(uint32_t aKey
, nsTArray
<Size
>& aSizes
)
395 if (aKey
== CAMERA_PARAM_SUPPORTED_VIDEOSIZES
&&
396 !mSeparateVideoAndPreviewSizesSupported
) {
397 aKey
= CAMERA_PARAM_SUPPORTED_PREVIEWSIZES
;
400 return mParams
.Get(aKey
, aSizes
);
403 // Array-of-doubles parameter accessor.
405 nsGonkCameraControl::Get(uint32_t aKey
, nsTArray
<double>& aValues
)
407 return mParams
.Get(aKey
, aValues
);
410 // Array-of-nsString parameter accessor.
412 nsGonkCameraControl::Get(uint32_t aKey
, nsTArray
<nsString
>& aValues
)
414 return mParams
.Get(aKey
, aValues
);
417 // nsString-valued parameter accessors
419 nsGonkCameraControl::Set(uint32_t aKey
, const nsAString
& aValue
)
421 nsresult rv
= mParams
.Set(aKey
, aValue
);
427 case CAMERA_PARAM_PICTURE_FILEFORMAT
:
428 // Picture format -- need to keep it for the TakePicture() callback.
429 mFileFormat
= aValue
;
432 case CAMERA_PARAM_FLASHMODE
:
433 // Explicit flash mode changes always win and stick.
434 mAutoFlashModeOverridden
= false;
437 case CAMERA_PARAM_SCENEMODE
:
438 // Reset disabling normal pictures in HDR mode in conjunction with setting
439 // scene mode because some drivers require they be changed together.
440 mParams
.Set(CAMERA_PARAM_SCENEMODE_HDR_RETURNNORMALPICTURE
, false);
444 return PushParameters();
448 nsGonkCameraControl::Get(uint32_t aKey
, nsAString
& aRet
)
450 return mParams
.Get(aKey
, aRet
);
453 // Double-valued parameter accessors
455 nsGonkCameraControl::Set(uint32_t aKey
, double aValue
)
457 return SetAndPush(aKey
, aValue
);
461 nsGonkCameraControl::Get(uint32_t aKey
, double& aRet
)
463 return mParams
.Get(aKey
, aRet
);
466 // Signed-64-bit parameter accessors.
468 nsGonkCameraControl::Set(uint32_t aKey
, int64_t aValue
)
470 return SetAndPush(aKey
, aValue
);
474 nsGonkCameraControl::Get(uint32_t aKey
, int64_t& aRet
)
476 return mParams
.Get(aKey
, aRet
);
479 // Boolean parameter accessors.
481 nsGonkCameraControl::Set(uint32_t aKey
, bool aValue
)
483 return SetAndPush(aKey
, aValue
);
487 nsGonkCameraControl::Get(uint32_t aKey
, bool& aRet
)
489 return mParams
.Get(aKey
, aRet
);
492 // Weighted-region parameter accessors.
494 nsGonkCameraControl::Set(uint32_t aKey
, const nsTArray
<Region
>& aRegions
)
496 return SetAndPush(aKey
, aRegions
);
500 nsGonkCameraControl::Get(uint32_t aKey
, nsTArray
<Region
>& aRegions
)
502 return mParams
.Get(aKey
, aRegions
);
505 // Singleton-size parameter accessors.
507 nsGonkCameraControl::Set(uint32_t aKey
, const Size
& aSize
)
510 case CAMERA_PARAM_PICTURE_SIZE
:
511 DOM_CAMERA_LOGI("setting picture size to %ux%u\n", aSize
.width
, aSize
.height
);
512 return SetPictureSize(aSize
);
514 case CAMERA_PARAM_THUMBNAILSIZE
:
515 DOM_CAMERA_LOGI("setting thumbnail size to %ux%u\n", aSize
.width
, aSize
.height
);
516 return SetThumbnailSize(aSize
);
519 return SetAndPush(aKey
, aSize
);
524 nsGonkCameraControl::Get(uint32_t aKey
, Size
& aSize
)
526 return mParams
.Get(aKey
, aSize
);
529 // Signed int parameter accessors.
531 nsGonkCameraControl::Set(uint32_t aKey
, int aValue
)
533 if (aKey
== CAMERA_PARAM_PICTURE_ROTATION
) {
534 RETURN_IF_NO_CAMERA_HW();
535 aValue
= RationalizeRotation(aValue
+ mCameraHw
->GetSensorOrientation());
537 return SetAndPush(aKey
, aValue
);
541 nsGonkCameraControl::Get(uint32_t aKey
, int& aRet
)
543 if (aKey
== CAMERA_PARAM_SENSORANGLE
) {
544 RETURN_IF_NO_CAMERA_HW();
545 aRet
= mCameraHw
->GetSensorOrientation();
549 return mParams
.Get(aKey
, aRet
);
552 // GPS location parameter accessors.
554 nsGonkCameraControl::SetLocation(const Position
& aLocation
)
556 return SetAndPush(CAMERA_PARAM_PICTURE_LOCATION
, aLocation
);
560 nsGonkCameraControl::StartPreviewImpl()
562 MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread
);
563 RETURN_IF_NO_CAMERA_HW();
565 ReentrantMonitorAutoEnter
mon(mReentrantMonitor
);
567 if (mPreviewState
== CameraControlListener::kPreviewStarted
) {
568 DOM_CAMERA_LOGW("Camera preview already started, nothing to do\n");
572 DOM_CAMERA_LOGI("Starting preview (this=%p)\n", this);
574 if (mCameraHw
->StartPreview() != OK
) {
575 DOM_CAMERA_LOGE("Failed to start camera preview\n");
576 return NS_ERROR_FAILURE
;
579 OnPreviewStateChange(CameraControlListener::kPreviewStarted
);
584 nsGonkCameraControl::StopPreviewImpl()
586 RETURN_IF_NO_CAMERA_HW();
588 DOM_CAMERA_LOGI("Stopping preview (this=%p)\n", this);
590 mCameraHw
->StopPreview();
591 OnPreviewStateChange(CameraControlListener::kPreviewStopped
);
596 nsGonkCameraControl::PausePreview()
598 RETURN_IF_NO_CAMERA_HW();
600 DOM_CAMERA_LOGI("Pausing preview (this=%p)\n", this);
602 mCameraHw
->StopPreview();
603 OnPreviewStateChange(CameraControlListener::kPreviewPaused
);
608 nsGonkCameraControl::AutoFocusImpl()
610 MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread
);
611 RETURN_IF_NO_CAMERA_HW();
613 DOM_CAMERA_LOGI("Starting auto focus\n");
615 if (mCameraHw
->AutoFocus() != OK
) {
616 return NS_ERROR_FAILURE
;
622 nsGonkCameraControl::StartFaceDetectionImpl()
624 MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread
);
625 RETURN_IF_NO_CAMERA_HW();
627 DOM_CAMERA_LOGI("Starting face detection\n");
629 if (mCameraHw
->StartFaceDetection() != OK
) {
630 return NS_ERROR_FAILURE
;
636 nsGonkCameraControl::StopFaceDetectionImpl()
638 MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread
);
639 RETURN_IF_NO_CAMERA_HW();
641 DOM_CAMERA_LOGI("Stopping face detection\n");
643 if (mCameraHw
->StopFaceDetection() != OK
) {
644 return NS_ERROR_FAILURE
;
650 nsGonkCameraControl::SetThumbnailSizeImpl(const Size
& aSize
)
652 MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread
);
655 * We keep a copy of the specified size so that if the picture size
656 * changes, we can choose a new thumbnail size close to what was asked for
659 mLastThumbnailSize
= aSize
;
662 * If either of width or height is zero, set the other to zero as well.
663 * This should disable inclusion of a thumbnail in the final picture.
665 if (!aSize
.width
|| !aSize
.height
) {
666 DOM_CAMERA_LOGW("Requested thumbnail size %ux%u, disabling thumbnail\n",
667 aSize
.width
, aSize
.height
);
668 Size size
= { 0, 0 };
669 return SetAndPush(CAMERA_PARAM_THUMBNAILSIZE
, size
);
673 * Choose the supported thumbnail size that is closest to the specified size.
674 * Some drivers will fail to take a picture if the thumbnail does not have
675 * the same aspect ratio as the set picture size, so we need to enforce that
678 int smallestDelta
= INT_MAX
;
679 uint32_t smallestDeltaIndex
= UINT32_MAX
;
680 int targetArea
= aSize
.width
* aSize
.height
;
682 nsAutoTArray
<Size
, 8> supportedSizes
;
683 Get(CAMERA_PARAM_SUPPORTED_JPEG_THUMBNAIL_SIZES
, supportedSizes
);
685 for (uint32_t i
= 0; i
< supportedSizes
.Length(); ++i
) {
686 int area
= supportedSizes
[i
].width
* supportedSizes
[i
].height
;
687 int delta
= abs(area
- targetArea
);
690 && delta
< smallestDelta
691 && supportedSizes
[i
].width
* mLastPictureSize
.height
/
692 supportedSizes
[i
].height
== mLastPictureSize
.width
694 smallestDelta
= delta
;
695 smallestDeltaIndex
= i
;
699 if (smallestDeltaIndex
== UINT32_MAX
) {
700 DOM_CAMERA_LOGW("Unable to find a thumbnail size close to %ux%u, disabling thumbnail\n",
701 aSize
.width
, aSize
.height
);
702 // If we are unable to find a thumbnail size with a suitable aspect ratio,
703 // just disable the thumbnail altogether.
704 Size size
= { 0, 0 };
705 return SetAndPush(CAMERA_PARAM_THUMBNAILSIZE
, size
);
708 Size size
= supportedSizes
[smallestDeltaIndex
];
709 DOM_CAMERA_LOGI("camera-param set thumbnail-size = %ux%u (requested %ux%u)\n",
710 size
.width
, size
.height
, aSize
.width
, aSize
.height
);
711 if (size
.width
> INT32_MAX
|| size
.height
> INT32_MAX
) {
712 DOM_CAMERA_LOGE("Supported thumbnail size is too big, no change\n");
713 return NS_ERROR_FAILURE
;
716 return SetAndPush(CAMERA_PARAM_THUMBNAILSIZE
, size
);
720 nsGonkCameraControl::SetThumbnailSize(const Size
& aSize
)
722 class SetThumbnailSize
: public nsRunnable
725 SetThumbnailSize(nsGonkCameraControl
* aCameraControl
, const Size
& aSize
)
726 : mCameraControl(aCameraControl
)
729 MOZ_COUNT_CTOR(SetThumbnailSize
);
731 ~SetThumbnailSize() { MOZ_COUNT_DTOR(SetThumbnailSize
); }
736 nsresult rv
= mCameraControl
->SetThumbnailSizeImpl(mSize
);
738 mCameraControl
->OnUserError(CameraControlListener::kInSetThumbnailSize
, rv
);
744 nsRefPtr
<nsGonkCameraControl
> mCameraControl
;
748 if (NS_GetCurrentThread() == mCameraThread
) {
749 return SetThumbnailSizeImpl(aSize
);
752 return mCameraThread
->Dispatch(new SetThumbnailSize(this, aSize
), NS_DISPATCH_NORMAL
);
756 nsGonkCameraControl::UpdateThumbnailSize()
758 return SetThumbnailSize(mLastThumbnailSize
);
762 nsGonkCameraControl::SetPictureSizeImpl(const Size
& aSize
)
764 MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread
);
767 * Some drivers are less friendly about getting one of these set to zero,
768 * so if either is not specified, ignore both and go with current or
771 if (!aSize
.width
|| !aSize
.height
) {
772 DOM_CAMERA_LOGW("Ignoring requested picture size of %ux%u\n", aSize
.width
, aSize
.height
);
773 return NS_ERROR_INVALID_ARG
;
776 if (aSize
.width
== mLastPictureSize
.width
&& aSize
.height
== mLastPictureSize
.height
) {
777 DOM_CAMERA_LOGI("Requested picture size %ux%u unchanged\n", aSize
.width
, aSize
.height
);
782 * Choose the supported picture size that is closest in area to the
783 * specified size. Some drivers will fail to take a picture if the
784 * thumbnail size is not the same aspect ratio, so we update that
785 * as well to a size closest to the last user-requested one.
787 int smallestDelta
= INT_MAX
;
788 uint32_t smallestDeltaIndex
= UINT32_MAX
;
789 int targetArea
= aSize
.width
* aSize
.height
;
791 nsAutoTArray
<Size
, 8> supportedSizes
;
792 Get(CAMERA_PARAM_SUPPORTED_PICTURESIZES
, supportedSizes
);
794 for (uint32_t i
= 0; i
< supportedSizes
.Length(); ++i
) {
795 int area
= supportedSizes
[i
].width
* supportedSizes
[i
].height
;
796 int delta
= abs(area
- targetArea
);
798 if (area
!= 0 && delta
< smallestDelta
) {
799 smallestDelta
= delta
;
800 smallestDeltaIndex
= i
;
804 if (smallestDeltaIndex
== UINT32_MAX
) {
805 DOM_CAMERA_LOGW("Unable to find a picture size close to %ux%u\n",
806 aSize
.width
, aSize
.height
);
807 return NS_ERROR_INVALID_ARG
;
810 Size size
= supportedSizes
[smallestDeltaIndex
];
811 DOM_CAMERA_LOGI("camera-param set picture-size = %ux%u (requested %ux%u)\n",
812 size
.width
, size
.height
, aSize
.width
, aSize
.height
);
813 if (size
.width
> INT32_MAX
|| size
.height
> INT32_MAX
) {
814 DOM_CAMERA_LOGE("Supported picture size is too big, no change\n");
815 return NS_ERROR_FAILURE
;
818 nsresult rv
= mParams
.Set(CAMERA_PARAM_PICTURE_SIZE
, size
);
823 mLastPictureSize
= size
;
825 // Finally, update the thumbnail size in case the picture
826 // aspect ratio changed.
827 return UpdateThumbnailSize();
831 nsGonkCameraControl::RationalizeRotation(int32_t aRotation
)
833 int32_t r
= aRotation
;
835 // The result of this operation is an angle from 0..270 degrees,
836 // in steps of 90 degrees. Angles are rounded to the nearest
837 // magnitude, so 45 will be rounded to 90, and -45 will be rounded
855 nsGonkCameraControl::SetPictureSize(const Size
& aSize
)
857 class SetPictureSize
: public nsRunnable
860 SetPictureSize(nsGonkCameraControl
* aCameraControl
, const Size
& aSize
)
861 : mCameraControl(aCameraControl
)
864 MOZ_COUNT_CTOR(SetPictureSize
);
866 ~SetPictureSize() { MOZ_COUNT_DTOR(SetPictureSize
); }
871 nsresult rv
= mCameraControl
->SetPictureSizeImpl(mSize
);
873 mCameraControl
->OnUserError(CameraControlListener::kInSetPictureSize
, rv
);
879 nsRefPtr
<nsGonkCameraControl
> mCameraControl
;
883 if (NS_GetCurrentThread() == mCameraThread
) {
884 return SetPictureSizeImpl(aSize
);
887 return mCameraThread
->Dispatch(new SetPictureSize(this, aSize
), NS_DISPATCH_NORMAL
);
891 nsGonkCameraControl::TakePictureImpl()
893 MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread
);
894 RETURN_IF_NO_CAMERA_HW();
896 if (mCameraHw
->TakePicture() != OK
) {
897 return NS_ERROR_FAILURE
;
900 // In Gonk, taking a picture implicitly stops the preview stream,
901 // so we need to reflect that here.
902 OnPreviewStateChange(CameraControlListener::kPreviewPaused
);
907 nsGonkCameraControl::PushParametersImpl()
909 MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread
);
910 DOM_CAMERA_LOGI("Pushing camera parameters\n");
911 RETURN_IF_NO_CAMERA_HW();
913 if (mCameraHw
->PushParameters(mParams
) != OK
) {
914 return NS_ERROR_FAILURE
;
921 nsGonkCameraControl::PullParametersImpl()
923 MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread
);
924 DOM_CAMERA_LOGI("Pulling camera parameters\n");
925 RETURN_IF_NO_CAMERA_HW();
927 return mCameraHw
->PullParameters(mParams
);
931 nsGonkCameraControl::SetupRecordingFlash(bool aAutoEnableLowLightTorch
)
933 mAutoFlashModeOverridden
= false;
935 if (!aAutoEnableLowLightTorch
|| !mLuminanceSupported
|| !mFlashSupported
) {
939 DOM_CAMERA_LOGI("Luminance reporting and flash supported\n");
941 nsresult rv
= PullParametersImpl();
942 if (NS_WARN_IF(NS_FAILED(rv
))) {
947 rv
= mParams
.Get(CAMERA_PARAM_LUMINANCE
, luminance
);
948 if (NS_WARN_IF(NS_FAILED(rv
))) {
949 // If we failed to get the luminance, assume it's "high"
954 rv
= mParams
.Get(CAMERA_PARAM_FLASHMODE
, flashMode
);
955 if (NS_WARN_IF(NS_FAILED(rv
))) {
956 // If we failed to get the current flash mode, swallow the error
960 if (luminance
.EqualsASCII("low") && flashMode
.EqualsASCII("auto")) {
961 DOM_CAMERA_LOGI("Low luminance detected, turning on flash\n");
962 rv
= SetAndPush(CAMERA_PARAM_FLASHMODE
, NS_LITERAL_STRING("torch"));
963 if (NS_WARN_IF(NS_FAILED(rv
))) {
964 // If we failed to turn on the flash, swallow the error
968 mAutoFlashModeOverridden
= true;
975 nsGonkCameraControl::StartRecordingImpl(DeviceStorageFileDescriptor
* aFileDescriptor
,
976 const StartRecordingOptions
* aOptions
)
978 MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread
);
980 ReentrantMonitorAutoEnter
mon(mRecorderMonitor
);
982 NS_ENSURE_TRUE(mRecorderProfile
, NS_ERROR_NOT_INITIALIZED
);
983 NS_ENSURE_FALSE(mRecorder
, NS_ERROR_FAILURE
);
986 * Get the base path from device storage and append the app-specified
987 * filename to it. The filename may include a relative subpath
988 * (e.g.) "DCIM/IMG_0001.jpg".
990 * The camera app needs to provide the file extension '.3gp' for now.
993 if (NS_WARN_IF(!aFileDescriptor
)) {
994 return NS_ERROR_INVALID_ARG
;
996 nsAutoString fullPath
;
997 mVideoFile
= aFileDescriptor
->mDSFile
;
998 mVideoFile
->GetFullPath(fullPath
);
999 DOM_CAMERA_LOGI("Video filename is '%s'\n",
1000 NS_LossyConvertUTF16toASCII(fullPath
).get());
1002 if (!mVideoFile
->IsSafePath()) {
1003 DOM_CAMERA_LOGE("Invalid video file name\n");
1004 return NS_ERROR_INVALID_ARG
;
1007 // SetupRecording creates a dup of the file descriptor, so we need to
1008 // close the file descriptor when we leave this function. Also note, that
1009 // since we're already off the main thread, we don't need to dispatch this.
1010 // We just let the CloseFileRunnable destructor do the work.
1011 nsRefPtr
<CloseFileRunnable
> closer
;
1012 if (aFileDescriptor
->mFileDescriptor
.IsValid()) {
1013 closer
= new CloseFileRunnable(aFileDescriptor
->mFileDescriptor
);
1016 int fd
= aFileDescriptor
->mFileDescriptor
.PlatformHandle();
1018 rv
= SetupRecording(fd
, aOptions
->rotation
, aOptions
->maxFileSizeBytes
,
1019 aOptions
->maxVideoLengthMs
);
1020 if (NS_SUCCEEDED(rv
)) {
1021 rv
= SetupRecordingFlash(aOptions
->autoEnableLowLightTorch
);
1024 rv
= SetupRecording(fd
, 0, 0, 0);
1026 if (NS_WARN_IF(NS_FAILED(rv
))) {
1030 if (mRecorder
->start() != OK
) {
1031 DOM_CAMERA_LOGE("mRecorder->start() failed\n");
1032 // important: we MUST destroy the recorder if start() fails!
1033 mRecorder
= nullptr;
1034 // put the flash back to the 'auto' state
1035 if (mAutoFlashModeOverridden
) {
1036 SetAndPush(CAMERA_PARAM_FLASHMODE
, NS_LITERAL_STRING("auto"));
1038 return NS_ERROR_FAILURE
;
1041 OnRecorderStateChange(CameraControlListener::kRecorderStarted
);
1046 nsGonkCameraControl::StopRecordingImpl()
1048 class RecordingComplete
: public nsRunnable
1051 RecordingComplete(DeviceStorageFile
* aFile
)
1055 ~RecordingComplete() { }
1060 MOZ_ASSERT(NS_IsMainThread());
1062 nsCOMPtr
<nsIObserverService
> obs
= mozilla::services::GetObserverService();
1063 obs
->NotifyObservers(mFile
, "file-watcher-notify", NS_LITERAL_STRING("modified").get());
1068 nsRefPtr
<DeviceStorageFile
> mFile
;
1071 ReentrantMonitorAutoEnter
mon(mRecorderMonitor
);
1073 // nothing to do if we have no mRecorder
1074 NS_ENSURE_TRUE(mRecorder
, NS_OK
);
1077 mRecorder
= nullptr;
1078 OnRecorderStateChange(CameraControlListener::kRecorderStopped
);
1081 ICameraControlParameterSetAutoEnter
set(this);
1083 if (mAutoFlashModeOverridden
) {
1084 nsresult rv
= Set(CAMERA_PARAM_FLASHMODE
, NS_LITERAL_STRING("auto"));
1085 if (NS_FAILED(rv
)) {
1086 DOM_CAMERA_LOGE("Failed to set flash mode (0x%x)\n", rv
);
1091 // notify DeviceStorage that the new video file is closed and ready
1092 return NS_DispatchToMainThread(new RecordingComplete(mVideoFile
));
1096 nsGonkCameraControl::ResumeContinuousFocusImpl()
1098 MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread
);
1099 RETURN_IF_NO_CAMERA_HW();
1101 DOM_CAMERA_LOGI("Resuming continuous autofocus\n");
1104 // http://developer.android.com/reference/android/hardware/Camera.Parameters.html#FOCUS_MODE_CONTINUOUS_PICTURE
1105 if (NS_WARN_IF(mCameraHw
->CancelAutoFocus() != OK
)) {
1106 return NS_ERROR_FAILURE
;
1113 nsGonkCameraControl::OnAutoFocusComplete(bool aSuccess
)
1115 class AutoFocusComplete
: public nsRunnable
1118 AutoFocusComplete(nsGonkCameraControl
* aCameraControl
, bool aSuccess
)
1119 : mCameraControl(aCameraControl
)
1120 , mSuccess(aSuccess
)
1126 mCameraControl
->OnAutoFocusComplete(mSuccess
);
1131 nsRefPtr
<nsGonkCameraControl
> mCameraControl
;
1135 if (NS_GetCurrentThread() == mCameraThread
) {
1137 * Auto focusing can change some of the camera's parameters, so
1138 * we need to pull a new set before notifying any clients.
1140 PullParametersImpl();
1141 CameraControlImpl::OnAutoFocusComplete(aSuccess
);
1146 * Because the callback needs to call PullParametersImpl(),
1147 * we need to dispatch this callback through the Camera Thread.
1149 mCameraThread
->Dispatch(new AutoFocusComplete(this, aSuccess
), NS_DISPATCH_NORMAL
);
1153 FeatureDetected(int32_t feature
[])
1156 * For information on what constitutes a valid feature, see:
1157 * http://androidxref.com/4.0.4/xref/system/core/include/system/camera.h#202
1159 * Although the comments explicitly state that undetected features are
1160 * indicated using the value -2000, we conservatively include anything
1161 * outside the explicitly valid range of [-1000, 1000] as undetected
1164 const int32_t kLowerFeatureBound
= -1000;
1165 const int32_t kUpperFeatureBound
= 1000;
1166 return (feature
[0] >= kLowerFeatureBound
&& feature
[0] <= kUpperFeatureBound
) ||
1167 (feature
[1] >= kLowerFeatureBound
&& feature
[1] <= kUpperFeatureBound
);
1171 nsGonkCameraControl::OnFacesDetected(camera_frame_metadata_t
* aMetaData
)
1173 NS_ENSURE_TRUE_VOID(aMetaData
);
1175 nsTArray
<Face
> faces
;
1176 uint32_t numFaces
= aMetaData
->number_of_faces
;
1177 DOM_CAMERA_LOGI("Camera detected %d face(s)", numFaces
);
1179 faces
.SetCapacity(numFaces
);
1181 for (uint32_t i
= 0; i
< numFaces
; ++i
) {
1182 Face
* f
= faces
.AppendElement();
1184 f
->id
= aMetaData
->faces
[i
].id
;
1185 f
->score
= aMetaData
->faces
[i
].score
;
1186 if (f
->score
> 100) {
1189 f
->bound
.left
= aMetaData
->faces
[i
].rect
[0];
1190 f
->bound
.top
= aMetaData
->faces
[i
].rect
[1];
1191 f
->bound
.right
= aMetaData
->faces
[i
].rect
[2];
1192 f
->bound
.bottom
= aMetaData
->faces
[i
].rect
[3];
1193 DOM_CAMERA_LOGI("Camera face[%u] appended: id=%d, score=%d, bound=(%d, %d)-(%d, %d)\n",
1194 i
, f
->id
, f
->score
, f
->bound
.left
, f
->bound
.top
, f
->bound
.right
, f
->bound
.bottom
);
1196 f
->hasLeftEye
= FeatureDetected(aMetaData
->faces
[i
].left_eye
);
1197 if (f
->hasLeftEye
) {
1198 f
->leftEye
.x
= aMetaData
->faces
[i
].left_eye
[0];
1199 f
->leftEye
.y
= aMetaData
->faces
[i
].left_eye
[1];
1200 DOM_CAMERA_LOGI(" Left eye detected at (%d, %d)\n",
1201 f
->leftEye
.x
, f
->leftEye
.y
);
1203 DOM_CAMERA_LOGI(" No left eye detected\n");
1206 f
->hasRightEye
= FeatureDetected(aMetaData
->faces
[i
].right_eye
);
1207 if (f
->hasRightEye
) {
1208 f
->rightEye
.x
= aMetaData
->faces
[i
].right_eye
[0];
1209 f
->rightEye
.y
= aMetaData
->faces
[i
].right_eye
[1];
1210 DOM_CAMERA_LOGI(" Right eye detected at (%d, %d)\n",
1211 f
->rightEye
.x
, f
->rightEye
.y
);
1213 DOM_CAMERA_LOGI(" No right eye detected\n");
1216 f
->hasMouth
= FeatureDetected(aMetaData
->faces
[i
].mouth
);
1218 f
->mouth
.x
= aMetaData
->faces
[i
].mouth
[0];
1219 f
->mouth
.y
= aMetaData
->faces
[i
].mouth
[1];
1220 DOM_CAMERA_LOGI(" Mouth detected at (%d, %d)\n", f
->mouth
.x
, f
->mouth
.y
);
1222 DOM_CAMERA_LOGI(" No mouth detected\n");
1226 CameraControlImpl::OnFacesDetected(faces
);
1230 nsGonkCameraControl::OnTakePictureComplete(uint8_t* aData
, uint32_t aLength
)
1232 ReentrantMonitorAutoEnter
mon(mReentrantMonitor
);
1234 uint8_t* data
= new uint8_t[aLength
];
1236 memcpy(data
, aData
, aLength
);
1238 nsString
s(NS_LITERAL_STRING("image/"));
1239 s
.Append(mFileFormat
);
1240 DOM_CAMERA_LOGI("Got picture, type '%s', %u bytes\n", NS_ConvertUTF16toUTF8(s
).get(), aLength
);
1241 OnTakePictureComplete(data
, aLength
, s
);
1243 if (mResumePreviewAfterTakingPicture
) {
1244 nsresult rv
= StartPreview();
1245 if (NS_FAILED(rv
)) {
1246 DOM_CAMERA_LOGE("Failed to restart camera preview (%x)\n", rv
);
1247 OnPreviewStateChange(CameraControlListener::kPreviewStopped
);
1251 DOM_CAMERA_LOGI("nsGonkCameraControl::OnTakePictureComplete() done\n");
1255 nsGonkCameraControl::OnTakePictureError()
1257 CameraControlImpl::OnUserError(CameraControlListener::kInTakePicture
,
1262 nsGonkCameraControl::SetPreviewSize(const Size
& aSize
)
1264 MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread
);
1266 nsTArray
<Size
> previewSizes
;
1267 nsresult rv
= Get(CAMERA_PARAM_SUPPORTED_PREVIEWSIZES
, previewSizes
);
1268 if (NS_FAILED(rv
)) {
1269 DOM_CAMERA_LOGE("Camera failed to return any preview sizes (0x%x)\n", rv
);
1274 rv
= GetSupportedSize(aSize
, previewSizes
, best
);
1275 if (NS_FAILED(rv
)) {
1276 DOM_CAMERA_LOGE("Failed to find a supported preview size, requested size %dx%d",
1277 aSize
.width
, aSize
.height
);
1281 if (mSeparateVideoAndPreviewSizesSupported
) {
1282 // Some camera drivers will ignore our preview size if it's larger
1283 // than the currently set video recording size, so we need to set
1284 // the video size here as well, just in case.
1285 if (best
.width
> mLastRecorderSize
.width
|| best
.height
> mLastRecorderSize
.height
) {
1289 mLastRecorderSize
= best
;
1291 mCurrentConfiguration
.mPreviewSize
= best
;
1292 return Set(CAMERA_PARAM_PREVIEWSIZE
, best
);
1296 nsGonkCameraControl::SetVideoSize(const Size
& aSize
)
1298 MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread
);
1300 if (!mSeparateVideoAndPreviewSizesSupported
) {
1301 DOM_CAMERA_LOGE("Camera does not support setting separate video size\n");
1302 return NS_ERROR_NOT_AVAILABLE
;
1305 nsTArray
<Size
> videoSizes
;
1306 nsresult rv
= Get(CAMERA_PARAM_SUPPORTED_VIDEOSIZES
, videoSizes
);
1307 if (NS_FAILED(rv
)) {
1308 DOM_CAMERA_LOGE("Camera failed to return any video sizes (0x%x)\n", rv
);
1313 rv
= GetSupportedSize(aSize
, videoSizes
, best
);
1314 if (NS_FAILED(rv
)) {
1315 DOM_CAMERA_LOGE("Failed to find a supported video size, requested size %dx%d",
1316 aSize
.width
, aSize
.height
);
1319 mLastRecorderSize
= best
;
1320 return Set(CAMERA_PARAM_VIDEOSIZE
, best
);
1324 nsGonkCameraControl::GetSupportedSize(const Size
& aSize
,
1325 const nsTArray
<Size
>& supportedSizes
,
1328 nsresult rv
= NS_ERROR_INVALID_ARG
;
1330 uint32_t minSizeDelta
= UINT32_MAX
;
1333 if (!aSize
.width
&& !aSize
.height
) {
1334 // no size specified, take the first supported size
1335 best
= supportedSizes
[0];
1337 } else if (aSize
.width
&& aSize
.height
) {
1338 // both height and width specified, find the supported size closest to requested size
1339 uint32_t targetArea
= aSize
.width
* aSize
.height
;
1340 for (nsTArray
<Size
>::index_type i
= 0; i
< supportedSizes
.Length(); i
++) {
1341 Size size
= supportedSizes
[i
];
1342 uint32_t delta
= abs((long int)(size
.width
* size
.height
- targetArea
));
1343 if (delta
< minSizeDelta
) {
1344 minSizeDelta
= delta
;
1349 } else if (!aSize
.width
) {
1350 // width not specified, find closest height match
1351 for (nsTArray
<Size
>::index_type i
= 0; i
< supportedSizes
.Length(); i
++) {
1352 Size size
= supportedSizes
[i
];
1353 delta
= abs((long int)(size
.height
- aSize
.height
));
1354 if (delta
< minSizeDelta
) {
1355 minSizeDelta
= delta
;
1360 } else if (!aSize
.height
) {
1361 // height not specified, find closest width match
1362 for (nsTArray
<Size
>::index_type i
= 0; i
< supportedSizes
.Length(); i
++) {
1363 Size size
= supportedSizes
[i
];
1364 delta
= abs((long int)(size
.width
- aSize
.width
));
1365 if (delta
< minSizeDelta
) {
1366 minSizeDelta
= delta
;
1376 nsGonkCameraControl::SetVideoConfiguration(const Configuration
& aConfig
)
1378 DOM_CAMERA_LOGT("%s:%d\n", __func__
, __LINE__
);
1380 // read preferences for camcorder
1381 mMediaProfiles
= MediaProfiles::getInstance();
1383 nsAutoCString profile
= NS_ConvertUTF16toUTF8(aConfig
.mRecorderProfile
);
1384 mRecorderProfile
= GetGonkRecorderProfileManager().take()->Get(profile
.get());
1385 if (!mRecorderProfile
) {
1386 DOM_CAMERA_LOGE("Recorder profile '%s' is not supported\n", profile
.get());
1387 return NS_ERROR_INVALID_ARG
;
1390 const GonkRecorderVideoProfile
* video
= mRecorderProfile
->GetGonkVideoProfile();
1391 int width
= video
->GetWidth();
1392 int height
= video
->GetHeight();
1393 int fps
= video
->GetFramerate();
1394 if (fps
== -1 || width
< 0 || height
< 0) {
1395 DOM_CAMERA_LOGE("Can't configure preview with fps=%d, width=%d, height=%d\n",
1396 fps
, width
, height
);
1397 return NS_ERROR_FAILURE
;
1400 PullParametersImpl();
1403 size
.width
= static_cast<uint32_t>(width
);
1404 size
.height
= static_cast<uint32_t>(height
);
1407 ICameraControlParameterSetAutoEnter
set(this);
1410 if (mSeparateVideoAndPreviewSizesSupported
) {
1411 // The camera supports two video streams: a low(er) resolution preview
1412 // stream and and a potentially high(er) resolution stream for encoding.
1413 rv
= SetVideoSize(size
);
1414 if (NS_FAILED(rv
)) {
1415 DOM_CAMERA_LOGE("Failed to set video mode video size (0x%x)\n", rv
);
1419 // The video size must be set first, before the preview size, because
1420 // some platforms have a dependency between the two.
1421 rv
= SetPreviewSize(aConfig
.mPreviewSize
);
1422 if (NS_FAILED(rv
)) {
1423 DOM_CAMERA_LOGE("Failed to set video mode preview size (0x%x)\n", rv
);
1427 // The camera only supports a single video stream: in this case, we set
1428 // the preview size to be the desired video recording size, and ignore
1429 // the specified preview size.
1430 rv
= SetPreviewSize(size
);
1431 if (NS_FAILED(rv
)) {
1432 DOM_CAMERA_LOGE("Failed to set video mode preview size (0x%x)\n", rv
);
1437 rv
= Set(CAMERA_PARAM_PREVIEWFRAMERATE
, fps
);
1438 if (NS_FAILED(rv
)) {
1439 DOM_CAMERA_LOGE("Failed to set video mode frame rate (0x%x)\n", rv
);
1448 class GonkRecorderListener
: public IMediaRecorderClient
1451 GonkRecorderListener(nsGonkCameraControl
* aCameraControl
)
1452 : mCameraControl(aCameraControl
)
1454 DOM_CAMERA_LOGT("%s:%d : this=%p, aCameraControl=%p\n",
1455 __func__
, __LINE__
, this, mCameraControl
.get());
1458 void notify(int msg
, int ext1
, int ext2
)
1460 if (mCameraControl
) {
1461 mCameraControl
->OnRecorderEvent(msg
, ext1
, ext2
);
1465 IBinder
* onAsBinder()
1467 DOM_CAMERA_LOGE("onAsBinder() called, should NEVER get called!\n");
1472 ~GonkRecorderListener() { }
1473 nsRefPtr
<nsGonkCameraControl
> mCameraControl
;
1477 nsGonkCameraControl::OnRecorderEvent(int msg
, int ext1
, int ext2
)
1480 * Refer to base/include/media/mediarecorder.h for a complete list
1481 * of error and info message codes. There are duplicate values
1482 * within the status/error code space, as determined by code inspection:
1488 * 1 MEDIA_RECORDER_EVENT_ERROR
1489 * 1 MEDIA_RECORDER_ERROR_UNKNOWN
1490 * [3] ERROR_MALFORMED
1491 * 100 mediaplayer.h::MEDIA_ERROR_SERVER_DIED
1493 * 2 MEDIA_RECORDER_EVENT_INFO
1494 * 800 MEDIA_RECORDER_INFO_MAX_DURATION_REACHED
1496 * 801 MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED
1498 * 1000 MEDIA_RECORDER_TRACK_INFO_COMPLETION_STATUS[1b]
1499 * [3] UNKNOWN_ERROR, etc.
1500 * 100 MEDIA_ERROR[4]
1501 * 100 mediaplayer.h::MEDIA_ERROR_SERVER_DIED
1503 * 100 MEDIA_RECORDER_TRACK_EVENT_ERROR
1504 * 100 MEDIA_RECORDER_TRACK_ERROR_GENERAL[1a]
1505 * [3] UNKNOWN_ERROR, etc.
1506 * 200 MEDIA_RECORDER_ERROR_VIDEO_NO_SYNC_FRAME[2]
1508 * 101 MEDIA_RECORDER_TRACK_EVENT_INFO
1509 * 1000 MEDIA_RECORDER_TRACK_INFO_COMPLETION_STATUS[1a]
1510 * [3] UNKNOWN_ERROR, etc.
1511 * N see mediarecorder.h::media_recorder_info_type[5]
1513 * 1. a) High 4 bits are the track number, the next 12 bits are reserved,
1514 * and the final 16 bits are the actual error code (above).
1515 * b) But not in this case.
1516 * 2. Never actually used in AOSP code?
1517 * 3. Specific error codes are from utils/Errors.h and/or
1518 * include/media/stagefright/MediaErrors.h.
1519 * 4. Only in frameworks/base/media/libmedia/mediaplayer.cpp.
1520 * 5. These are mostly informational and we can ignore them; note that
1521 * although the MEDIA_RECORDER_INFO_MAX_DURATION_REACHED and
1522 * MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED values are defined in this
1523 * enum, they are used with different ext1 codes. /o\
1525 int trackNum
= CameraControlListener::kNoTrackNumber
;
1528 // Recorder-related events
1529 case MEDIA_RECORDER_EVENT_INFO
:
1531 case MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED
:
1532 DOM_CAMERA_LOGI("recorder-event : info: maximum file size reached\n");
1533 OnRecorderStateChange(CameraControlListener::kFileSizeLimitReached
, ext2
, trackNum
);
1536 case MEDIA_RECORDER_INFO_MAX_DURATION_REACHED
:
1537 DOM_CAMERA_LOGI("recorder-event : info: maximum video duration reached\n");
1538 OnRecorderStateChange(CameraControlListener::kVideoLengthLimitReached
, ext2
, trackNum
);
1541 case MEDIA_RECORDER_TRACK_INFO_COMPLETION_STATUS
:
1542 DOM_CAMERA_LOGI("recorder-event : info: track completed\n");
1543 OnRecorderStateChange(CameraControlListener::kTrackCompleted
, ext2
, trackNum
);
1548 case MEDIA_RECORDER_EVENT_ERROR
:
1550 case MEDIA_RECORDER_ERROR_UNKNOWN
:
1551 DOM_CAMERA_LOGE("recorder-event : recorder-error: %d (0x%08x)\n", ext2
, ext2
);
1552 OnRecorderStateChange(CameraControlListener::kMediaRecorderFailed
, ext2
, trackNum
);
1555 case MEDIA_ERROR_SERVER_DIED
:
1556 DOM_CAMERA_LOGE("recorder-event : recorder-error: server died\n");
1557 OnRecorderStateChange(CameraControlListener::kMediaServerFailed
, ext2
, trackNum
);
1562 // Track-related events, see note 1(a) above.
1563 case MEDIA_RECORDER_TRACK_EVENT_INFO
:
1564 trackNum
= (ext1
& 0xF0000000) >> 28;
1567 case MEDIA_RECORDER_TRACK_INFO_COMPLETION_STATUS
:
1569 DOM_CAMERA_LOGI("recorder-event : track-complete: track %d, %d (0x%08x)\n", trackNum
, ext2
, ext2
);
1570 OnRecorderStateChange(CameraControlListener::kTrackCompleted
, ext2
, trackNum
);
1573 DOM_CAMERA_LOGE("recorder-event : track-error: track %d, %d (0x%08x)\n", trackNum
, ext2
, ext2
);
1574 OnRecorderStateChange(CameraControlListener::kTrackFailed
, ext2
, trackNum
);
1577 case MEDIA_RECORDER_TRACK_INFO_PROGRESS_IN_TIME
:
1578 DOM_CAMERA_LOGI("recorder-event : track-info: progress in time: %d ms\n", ext2
);
1583 case MEDIA_RECORDER_TRACK_EVENT_ERROR
:
1584 trackNum
= (ext1
& 0xF0000000) >> 28;
1586 DOM_CAMERA_LOGE("recorder-event : track-error: track %d, %d (0x%08x)\n", trackNum
, ext2
, ext2
);
1587 OnRecorderStateChange(CameraControlListener::kTrackFailed
, ext2
, trackNum
);
1591 // All unhandled cases wind up here
1592 DOM_CAMERA_LOGW("recorder-event : unhandled: msg=%d, ext1=%d, ext2=%d\n", msg
, ext1
, ext2
);
1596 nsGonkCameraControl::SetupRecording(int aFd
, int aRotation
,
1597 uint64_t aMaxFileSizeBytes
,
1598 uint64_t aMaxVideoLengthMs
)
1600 RETURN_IF_NO_CAMERA_HW();
1602 // choosing a size big enough to hold the params
1603 const size_t SIZE
= 256;
1606 ReentrantMonitorAutoEnter
mon(mRecorderMonitor
);
1608 mRecorder
= new GonkRecorder();
1609 CHECK_SETARG_RETURN(mRecorder
->init(), NS_ERROR_FAILURE
);
1611 nsresult rv
= mRecorderProfile
->ConfigureRecorder(mRecorder
);
1612 NS_ENSURE_SUCCESS(rv
, rv
);
1614 CHECK_SETARG_RETURN(mRecorder
->setCamera(mCameraHw
), NS_ERROR_FAILURE
);
1616 DOM_CAMERA_LOGI("maxVideoLengthMs=%llu\n", aMaxVideoLengthMs
);
1617 const uint64_t kMaxVideoLengthMs
= INT64_MAX
/ 1000;
1618 if (aMaxVideoLengthMs
== 0) {
1619 aMaxVideoLengthMs
= -1;
1620 } else if (aMaxVideoLengthMs
> kMaxVideoLengthMs
) {
1621 // GonkRecorder parameters are internally limited to signed 64-bit values,
1622 // and the time length limit is converted from milliseconds to microseconds,
1623 // so we limit this value to prevent any unexpected overflow weirdness.
1624 DOM_CAMERA_LOGW("maxVideoLengthMs capped to %lld\n", kMaxVideoLengthMs
);
1625 aMaxVideoLengthMs
= kMaxVideoLengthMs
;
1627 snprintf(buffer
, SIZE
, "max-duration=%lld", aMaxVideoLengthMs
);
1628 CHECK_SETARG_RETURN(mRecorder
->setParameters(String8(buffer
)),
1629 NS_ERROR_INVALID_ARG
);
1631 DOM_CAMERA_LOGI("maxFileSizeBytes=%llu\n", aMaxFileSizeBytes
);
1632 if (aMaxFileSizeBytes
== 0) {
1633 aMaxFileSizeBytes
= -1;
1634 } else if (aMaxFileSizeBytes
> INT64_MAX
) {
1635 // GonkRecorder parameters are internally limited to signed 64-bit values
1636 DOM_CAMERA_LOGW("maxFileSizeBytes capped to INT64_MAX\n");
1637 aMaxFileSizeBytes
= INT64_MAX
;
1639 snprintf(buffer
, SIZE
, "max-filesize=%lld", aMaxFileSizeBytes
);
1640 CHECK_SETARG_RETURN(mRecorder
->setParameters(String8(buffer
)),
1641 NS_ERROR_INVALID_ARG
);
1643 // adjust rotation by camera sensor offset
1645 r
+= mCameraHw
->GetSensorOrientation();
1646 r
= RationalizeRotation(r
);
1647 DOM_CAMERA_LOGI("setting video rotation to %d degrees (mapped from %d)\n", r
, aRotation
);
1648 snprintf(buffer
, SIZE
, "video-param-rotation-angle-degrees=%d", r
);
1649 CHECK_SETARG_RETURN(mRecorder
->setParameters(String8(buffer
)),
1650 NS_ERROR_INVALID_ARG
);
1652 CHECK_SETARG_RETURN(mRecorder
->setListener(new GonkRecorderListener(this)),
1655 // recording API needs file descriptor of output file
1656 CHECK_SETARG_RETURN(mRecorder
->setOutputFile(aFd
, 0, 0), NS_ERROR_FAILURE
);
1657 CHECK_SETARG_RETURN(mRecorder
->prepare(), NS_ERROR_FAILURE
);
1663 nsGonkCameraControl::StopImpl()
1665 DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__
, __LINE__
, this);
1667 // if we're recording, stop recording
1668 StopRecordingImpl();
1673 // release the hardware handle
1674 if (mCameraHw
.get()){
1679 OnHardwareStateChange(CameraControlListener::kHardwareClosed
);
1683 already_AddRefed
<GonkRecorderProfileManager
>
1684 nsGonkCameraControl::GetGonkRecorderProfileManager()
1686 if (!mProfileManager
) {
1687 nsTArray
<Size
> sizes
;
1688 nsresult rv
= Get(CAMERA_PARAM_SUPPORTED_VIDEOSIZES
, sizes
);
1689 NS_ENSURE_SUCCESS(rv
, nullptr);
1691 mProfileManager
= new GonkRecorderProfileManager(mCameraId
);
1692 mProfileManager
->SetSupportedResolutions(sizes
);
1695 nsRefPtr
<GonkRecorderProfileManager
> profileMgr
= mProfileManager
;
1696 return profileMgr
.forget();
1699 already_AddRefed
<RecorderProfileManager
>
1700 nsGonkCameraControl::GetRecorderProfileManagerImpl()
1702 nsRefPtr
<RecorderProfileManager
> profileMgr
= GetGonkRecorderProfileManager();
1703 return profileMgr
.forget();
1707 nsGonkCameraControl::OnRateLimitPreview(bool aLimit
)
1709 CameraControlImpl::OnRateLimitPreview(aLimit
);
1713 nsGonkCameraControl::OnNewPreviewFrame(layers::TextureClient
* aBuffer
)
1715 nsRefPtr
<Image
> frame
= mImageContainer
->CreateImage(ImageFormat::GRALLOC_PLANAR_YCBCR
);
1717 GrallocImage
* videoImage
= static_cast<GrallocImage
*>(frame
.get());
1719 GrallocImage::GrallocData data
;
1720 data
.mGraphicBuffer
= aBuffer
;
1721 data
.mPicSize
= IntSize(mCurrentConfiguration
.mPreviewSize
.width
,
1722 mCurrentConfiguration
.mPreviewSize
.height
);
1723 videoImage
->SetData(data
);
1725 OnNewPreviewFrame(frame
, mCurrentConfiguration
.mPreviewSize
.width
,
1726 mCurrentConfiguration
.mPreviewSize
.height
);
1730 nsGonkCameraControl::OnSystemError(CameraControlListener::SystemContext aWhere
,
1733 if (aWhere
== CameraControlListener::kSystemService
) {
1734 OnPreviewStateChange(CameraControlListener::kPreviewStopped
);
1735 OnHardwareStateChange(CameraControlListener::kHardwareClosed
);
1738 CameraControlImpl::OnSystemError(aWhere
, aError
);
1741 // Gonk callback handlers.
1745 OnTakePictureComplete(nsGonkCameraControl
* gc
, uint8_t* aData
, uint32_t aLength
)
1747 gc
->OnTakePictureComplete(aData
, aLength
);
1751 OnTakePictureError(nsGonkCameraControl
* gc
)
1753 gc
->OnTakePictureError();
1757 OnAutoFocusComplete(nsGonkCameraControl
* gc
, bool aSuccess
)
1759 gc
->OnAutoFocusComplete(aSuccess
);
1763 OnAutoFocusMoving(nsGonkCameraControl
* gc
, bool aIsMoving
)
1765 gc
->OnAutoFocusMoving(aIsMoving
);
1769 OnFacesDetected(nsGonkCameraControl
* gc
, camera_frame_metadata_t
* aMetaData
)
1771 gc
->OnFacesDetected(aMetaData
);
1775 OnRateLimitPreview(nsGonkCameraControl
* gc
, bool aLimit
)
1777 gc
->OnRateLimitPreview(aLimit
);
1781 OnNewPreviewFrame(nsGonkCameraControl
* gc
, layers::TextureClient
* aBuffer
)
1783 gc
->OnNewPreviewFrame(aBuffer
);
1787 OnShutter(nsGonkCameraControl
* gc
)
1793 OnClosed(nsGonkCameraControl
* gc
)
1799 OnSystemError(nsGonkCameraControl
* gc
,
1800 CameraControlListener::SystemContext aWhere
,
1801 int32_t aArg1
, int32_t aArg2
)
1804 DOM_CAMERA_LOGE("OnSystemError : aWhere=%d, aArg1=%d, aArg2=%d\n", aWhere
, aArg1
, aArg2
);
1809 gc
->OnSystemError(aWhere
, NS_ERROR_FAILURE
);
1812 } // namespace mozilla