Bumping gaia.json for 2 gaia revision(s) a=gaia-bump
[gecko.git] / dom / camera / GonkCameraControl.cpp
blobb6b915a6dfc0ae4b01be4889b7058c874eedacbc
1 /*
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"
18 #include <time.h>
19 #include <string.h>
20 #include <sys/stat.h>
21 #include <fcntl.h>
22 #include <errno.h>
23 #include <libgen.h>
24 #include "base/basictypes.h"
25 #include "camera/CameraParameters.h"
26 #include "nsCOMPtr.h"
27 #include "nsMemory.h"
28 #include "nsThread.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() \
54 do { \
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; \
59 } \
60 } while(0)
62 // Construct nsGonkCameraControl on the main thread.
63 nsGonkCameraControl::nsGonkCameraControl(uint32_t aCameraId)
64 : CameraControlImpl()
65 , mCameraId(aCameraId)
66 , mLastPictureSize({0, 0})
67 , mLastThumbnailSize({0, 0})
68 , mPreviewFps(30)
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)
76 , mRecorder(nullptr)
77 , mRecorderMonitor("GonkCameraControl::mRecorder.Monitor")
78 , mProfileManager(nullptr)
79 , mRecorderProfile(nullptr)
80 , mVideoFile(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();
88 nsresult
89 nsGonkCameraControl::StartImpl(const Configuration* aInitialConfig)
91 /**
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
104 * camera hardware.
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
108 * to complete.
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
121 StopImpl();
122 return rv;
126 OnHardwareStateChange(CameraControlListener::kHardwareOpen);
127 if (aInitialConfig) {
128 return StartPreviewImpl();
131 return NS_OK;
134 nsresult
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
164 int areas;
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();
177 nsString flashMode;
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());
200 } else {
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);
212 } else {
213 mLastRecorderSize = mCurrentConfiguration.mPreviewSize;
216 return NS_OK;
219 nsGonkCameraControl::~nsGonkCameraControl()
221 DOM_CAMERA_LOGT("%s:%d : this=%p, mCameraHw = %p\n", __func__, __LINE__, this, mCameraHw.get());
223 StopImpl();
224 DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
227 nsresult
228 nsGonkCameraControl::SetConfigurationInternal(const Configuration& aConfig)
230 DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
232 nsresult rv;
235 ICameraControlParameterSetAutoEnter set(this);
237 switch (aConfig.mMode) {
238 case kPictureMode:
239 rv = SetPictureConfiguration(aConfig);
240 break;
242 case kVideoMode:
243 rv = SetVideoConfiguration(aConfig);
244 break;
246 default:
247 MOZ_ASSERT_UNREACHABLE("Unanticipated camera mode in SetConfigurationInternal()");
248 rv = NS_ERROR_FAILURE;
249 break;
252 DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
253 if (NS_WARN_IF(NS_FAILED(rv))) {
254 return rv;
257 rv = Set(CAMERA_PARAM_RECORDINGHINT, aConfig.mMode == kVideoMode);
258 if (NS_FAILED(rv)) {
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();
267 return NS_OK;
270 nsresult
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();
283 if (NS_FAILED(rv)) {
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,
287 // so bail out here.
288 return rv;
292 DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
293 rv = SetConfigurationInternal(aConfig);
294 if (NS_WARN_IF(NS_FAILED(rv))) {
295 StopPreviewImpl();
296 return rv;
299 // Restart the preview
300 DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
301 return StartPreviewImpl();
304 nsresult
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))) {
314 return 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,
322 mPreviewFps);
324 return NS_OK;
327 // Parameter management.
328 nsresult
329 nsGonkCameraControl::PushParameters()
331 uint32_t dcu = mDeferConfigUpdate;
332 if (dcu > 0) {
333 DOM_CAMERA_LOGI("Defering config update (nest level %u)\n", dcu);
334 return NS_OK;
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
341 * we can proceed.
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();
354 void
355 nsGonkCameraControl::BeginBatchParameterSet()
357 uint32_t dcu = ++mDeferConfigUpdate;
358 if (dcu == 0) {
359 NS_WARNING("Overflow weirdness incrementing mDeferConfigUpdate!");
360 MOZ_CRASH();
362 DOM_CAMERA_LOGI("Begin deferring camera configuration updates (nest level %u)\n", dcu);
365 void
366 nsGonkCameraControl::EndBatchParameterSet()
368 uint32_t dcu = mDeferConfigUpdate--;
369 if (dcu == 0) {
370 NS_WARNING("Underflow badness decrementing mDeferConfigUpdate!");
371 MOZ_CRASH();
373 DOM_CAMERA_LOGI("End deferring camera configuration updates (nest level %u)\n", dcu);
375 if (dcu == 1) {
376 PushParameters();
380 template<class T> nsresult
381 nsGonkCameraControl::SetAndPush(uint32_t aKey, const T& aValue)
383 nsresult rv = mParams.Set(aKey, aValue);
384 if (NS_FAILED(rv)) {
385 DOM_CAMERA_LOGE("Camera parameter aKey=%d failed to set (0x%x)\n", aKey, rv);
386 return rv;
388 return PushParameters();
391 // Array-of-Size parameter accessor.
392 nsresult
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.
404 nsresult
405 nsGonkCameraControl::Get(uint32_t aKey, nsTArray<double>& aValues)
407 return mParams.Get(aKey, aValues);
410 // Array-of-nsString parameter accessor.
411 nsresult
412 nsGonkCameraControl::Get(uint32_t aKey, nsTArray<nsString>& aValues)
414 return mParams.Get(aKey, aValues);
417 // nsString-valued parameter accessors
418 nsresult
419 nsGonkCameraControl::Set(uint32_t aKey, const nsAString& aValue)
421 nsresult rv = mParams.Set(aKey, aValue);
422 if (NS_FAILED(rv)) {
423 return rv;
426 switch (aKey) {
427 case CAMERA_PARAM_PICTURE_FILEFORMAT:
428 // Picture format -- need to keep it for the TakePicture() callback.
429 mFileFormat = aValue;
430 break;
432 case CAMERA_PARAM_FLASHMODE:
433 // Explicit flash mode changes always win and stick.
434 mAutoFlashModeOverridden = false;
435 break;
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);
441 break;
444 return PushParameters();
447 nsresult
448 nsGonkCameraControl::Get(uint32_t aKey, nsAString& aRet)
450 return mParams.Get(aKey, aRet);
453 // Double-valued parameter accessors
454 nsresult
455 nsGonkCameraControl::Set(uint32_t aKey, double aValue)
457 return SetAndPush(aKey, aValue);
460 nsresult
461 nsGonkCameraControl::Get(uint32_t aKey, double& aRet)
463 return mParams.Get(aKey, aRet);
466 // Signed-64-bit parameter accessors.
467 nsresult
468 nsGonkCameraControl::Set(uint32_t aKey, int64_t aValue)
470 return SetAndPush(aKey, aValue);
473 nsresult
474 nsGonkCameraControl::Get(uint32_t aKey, int64_t& aRet)
476 return mParams.Get(aKey, aRet);
479 // Boolean parameter accessors.
480 nsresult
481 nsGonkCameraControl::Set(uint32_t aKey, bool aValue)
483 return SetAndPush(aKey, aValue);
486 nsresult
487 nsGonkCameraControl::Get(uint32_t aKey, bool& aRet)
489 return mParams.Get(aKey, aRet);
492 // Weighted-region parameter accessors.
493 nsresult
494 nsGonkCameraControl::Set(uint32_t aKey, const nsTArray<Region>& aRegions)
496 return SetAndPush(aKey, aRegions);
499 nsresult
500 nsGonkCameraControl::Get(uint32_t aKey, nsTArray<Region>& aRegions)
502 return mParams.Get(aKey, aRegions);
505 // Singleton-size parameter accessors.
506 nsresult
507 nsGonkCameraControl::Set(uint32_t aKey, const Size& aSize)
509 switch (aKey) {
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);
518 default:
519 return SetAndPush(aKey, aSize);
523 nsresult
524 nsGonkCameraControl::Get(uint32_t aKey, Size& aSize)
526 return mParams.Get(aKey, aSize);
529 // Signed int parameter accessors.
530 nsresult
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);
540 nsresult
541 nsGonkCameraControl::Get(uint32_t aKey, int& aRet)
543 if (aKey == CAMERA_PARAM_SENSORANGLE) {
544 RETURN_IF_NO_CAMERA_HW();
545 aRet = mCameraHw->GetSensorOrientation();
546 return NS_OK;
549 return mParams.Get(aKey, aRet);
552 // GPS location parameter accessors.
553 nsresult
554 nsGonkCameraControl::SetLocation(const Position& aLocation)
556 return SetAndPush(CAMERA_PARAM_PICTURE_LOCATION, aLocation);
559 nsresult
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");
569 return NS_OK;
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);
580 return NS_OK;
583 nsresult
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);
592 return NS_OK;
595 nsresult
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);
604 return NS_OK;
607 nsresult
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;
618 return NS_OK;
621 nsresult
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;
632 return NS_OK;
635 nsresult
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;
646 return NS_OK;
649 nsresult
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
657 * last time.
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
676 * too.
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);
689 if (area != 0
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);
719 nsresult
720 nsGonkCameraControl::SetThumbnailSize(const Size& aSize)
722 class SetThumbnailSize : public nsRunnable
724 public:
725 SetThumbnailSize(nsGonkCameraControl* aCameraControl, const Size& aSize)
726 : mCameraControl(aCameraControl)
727 , mSize(aSize)
729 MOZ_COUNT_CTOR(SetThumbnailSize);
731 ~SetThumbnailSize() { MOZ_COUNT_DTOR(SetThumbnailSize); }
733 NS_IMETHODIMP
734 Run() MOZ_OVERRIDE
736 nsresult rv = mCameraControl->SetThumbnailSizeImpl(mSize);
737 if (NS_FAILED(rv)) {
738 mCameraControl->OnUserError(CameraControlListener::kInSetThumbnailSize, rv);
740 return NS_OK;
743 protected:
744 nsRefPtr<nsGonkCameraControl> mCameraControl;
745 Size mSize;
748 if (NS_GetCurrentThread() == mCameraThread) {
749 return SetThumbnailSizeImpl(aSize);
752 return mCameraThread->Dispatch(new SetThumbnailSize(this, aSize), NS_DISPATCH_NORMAL);
755 nsresult
756 nsGonkCameraControl::UpdateThumbnailSize()
758 return SetThumbnailSize(mLastThumbnailSize);
761 nsresult
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
769 * default settings.
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);
778 return NS_OK;
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);
819 if (NS_FAILED(rv)) {
820 return rv;
823 mLastPictureSize = size;
825 // Finally, update the thumbnail size in case the picture
826 // aspect ratio changed.
827 return UpdateThumbnailSize();
830 int32_t
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
838 // to -90 (not 0).
839 if (r >= 0) {
840 r += 45;
841 } else {
842 r -= 45;
844 r /= 90;
845 r %= 4;
846 r *= 90;
847 if (r < 0) {
848 r += 360;
851 return r;
854 nsresult
855 nsGonkCameraControl::SetPictureSize(const Size& aSize)
857 class SetPictureSize : public nsRunnable
859 public:
860 SetPictureSize(nsGonkCameraControl* aCameraControl, const Size& aSize)
861 : mCameraControl(aCameraControl)
862 , mSize(aSize)
864 MOZ_COUNT_CTOR(SetPictureSize);
866 ~SetPictureSize() { MOZ_COUNT_DTOR(SetPictureSize); }
868 NS_IMETHODIMP
869 Run() MOZ_OVERRIDE
871 nsresult rv = mCameraControl->SetPictureSizeImpl(mSize);
872 if (NS_FAILED(rv)) {
873 mCameraControl->OnUserError(CameraControlListener::kInSetPictureSize, rv);
875 return NS_OK;
878 protected:
879 nsRefPtr<nsGonkCameraControl> mCameraControl;
880 Size mSize;
883 if (NS_GetCurrentThread() == mCameraThread) {
884 return SetPictureSizeImpl(aSize);
887 return mCameraThread->Dispatch(new SetPictureSize(this, aSize), NS_DISPATCH_NORMAL);
890 nsresult
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);
903 return NS_OK;
906 nsresult
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;
917 return NS_OK;
920 nsresult
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);
930 nsresult
931 nsGonkCameraControl::SetupRecordingFlash(bool aAutoEnableLowLightTorch)
933 mAutoFlashModeOverridden = false;
935 if (!aAutoEnableLowLightTorch || !mLuminanceSupported || !mFlashSupported) {
936 return NS_OK;
939 DOM_CAMERA_LOGI("Luminance reporting and flash supported\n");
941 nsresult rv = PullParametersImpl();
942 if (NS_WARN_IF(NS_FAILED(rv))) {
943 return rv;
946 nsString luminance;
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"
950 return NS_OK;
953 nsString flashMode;
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
957 return NS_OK;
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
965 return NS_OK;
968 mAutoFlashModeOverridden = true;
971 return NS_OK;
974 nsresult
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.
991 * See bug 795202.
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);
1015 nsresult rv;
1016 int fd = aFileDescriptor->mFileDescriptor.PlatformHandle();
1017 if (aOptions) {
1018 rv = SetupRecording(fd, aOptions->rotation, aOptions->maxFileSizeBytes,
1019 aOptions->maxVideoLengthMs);
1020 if (NS_SUCCEEDED(rv)) {
1021 rv = SetupRecordingFlash(aOptions->autoEnableLowLightTorch);
1023 } else {
1024 rv = SetupRecording(fd, 0, 0, 0);
1026 if (NS_WARN_IF(NS_FAILED(rv))) {
1027 return 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);
1042 return NS_OK;
1045 nsresult
1046 nsGonkCameraControl::StopRecordingImpl()
1048 class RecordingComplete : public nsRunnable
1050 public:
1051 RecordingComplete(DeviceStorageFile* aFile)
1052 : mFile(aFile)
1055 ~RecordingComplete() { }
1057 NS_IMETHODIMP
1058 Run()
1060 MOZ_ASSERT(NS_IsMainThread());
1062 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
1063 obs->NotifyObservers(mFile, "file-watcher-notify", NS_LITERAL_STRING("modified").get());
1064 return NS_OK;
1067 private:
1068 nsRefPtr<DeviceStorageFile> mFile;
1071 ReentrantMonitorAutoEnter mon(mRecorderMonitor);
1073 // nothing to do if we have no mRecorder
1074 NS_ENSURE_TRUE(mRecorder, NS_OK);
1076 mRecorder->stop();
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));
1095 nsresult
1096 nsGonkCameraControl::ResumeContinuousFocusImpl()
1098 MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
1099 RETURN_IF_NO_CAMERA_HW();
1101 DOM_CAMERA_LOGI("Resuming continuous autofocus\n");
1103 // see
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;
1109 return NS_OK;
1112 void
1113 nsGonkCameraControl::OnAutoFocusComplete(bool aSuccess)
1115 class AutoFocusComplete : public nsRunnable
1117 public:
1118 AutoFocusComplete(nsGonkCameraControl* aCameraControl, bool aSuccess)
1119 : mCameraControl(aCameraControl)
1120 , mSuccess(aSuccess)
1123 NS_IMETHODIMP
1124 Run() MOZ_OVERRIDE
1126 mCameraControl->OnAutoFocusComplete(mSuccess);
1127 return NS_OK;
1130 protected:
1131 nsRefPtr<nsGonkCameraControl> mCameraControl;
1132 bool mSuccess;
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);
1142 return;
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);
1152 bool
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
1162 * as well.
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);
1170 void
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) {
1187 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);
1202 } else {
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);
1212 } else {
1213 DOM_CAMERA_LOGI(" No right eye detected\n");
1216 f->hasMouth = FeatureDetected(aMetaData->faces[i].mouth);
1217 if (f->hasMouth) {
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);
1221 } else {
1222 DOM_CAMERA_LOGI(" No mouth detected\n");
1226 CameraControlImpl::OnFacesDetected(faces);
1229 void
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");
1254 void
1255 nsGonkCameraControl::OnTakePictureError()
1257 CameraControlImpl::OnUserError(CameraControlListener::kInTakePicture,
1258 NS_ERROR_FAILURE);
1261 nsresult
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);
1270 return rv;
1273 Size best;
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);
1278 return rv;
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) {
1286 SetVideoSize(best);
1288 } else {
1289 mLastRecorderSize = best;
1291 mCurrentConfiguration.mPreviewSize = best;
1292 return Set(CAMERA_PARAM_PREVIEWSIZE, best);
1295 nsresult
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);
1309 return rv;
1312 Size best;
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);
1317 return rv;
1319 mLastRecorderSize = best;
1320 return Set(CAMERA_PARAM_VIDEOSIZE, best);
1323 nsresult
1324 nsGonkCameraControl::GetSupportedSize(const Size& aSize,
1325 const nsTArray<Size>& supportedSizes,
1326 Size& best)
1328 nsresult rv = NS_ERROR_INVALID_ARG;
1329 best = aSize;
1330 uint32_t minSizeDelta = UINT32_MAX;
1331 uint32_t delta;
1333 if (!aSize.width && !aSize.height) {
1334 // no size specified, take the first supported size
1335 best = supportedSizes[0];
1336 rv = NS_OK;
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;
1345 best = size;
1346 rv = NS_OK;
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;
1356 best = size;
1357 rv = NS_OK;
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;
1367 best = size;
1368 rv = NS_OK;
1372 return rv;
1375 nsresult
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();
1402 Size size;
1403 size.width = static_cast<uint32_t>(width);
1404 size.height = static_cast<uint32_t>(height);
1407 ICameraControlParameterSetAutoEnter set(this);
1408 nsresult rv;
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);
1416 return 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);
1424 return rv;
1426 } else {
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);
1433 return 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);
1440 return rv;
1444 mPreviewFps = fps;
1445 return NS_OK;
1448 class GonkRecorderListener : public IMediaRecorderClient
1450 public:
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");
1468 return nullptr;
1471 protected:
1472 ~GonkRecorderListener() { }
1473 nsRefPtr<nsGonkCameraControl> mCameraControl;
1476 void
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:
1484 * +------- msg
1485 * | +----- ext1
1486 * | | +--- ext2
1487 * V V V
1488 * 1 MEDIA_RECORDER_EVENT_ERROR
1489 * 1 MEDIA_RECORDER_ERROR_UNKNOWN
1490 * [3] ERROR_MALFORMED
1491 * 100 mediaplayer.h::MEDIA_ERROR_SERVER_DIED
1492 * 0 <always zero>
1493 * 2 MEDIA_RECORDER_EVENT_INFO
1494 * 800 MEDIA_RECORDER_INFO_MAX_DURATION_REACHED
1495 * 0 <always zero>
1496 * 801 MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED
1497 * 0 <always zero>
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
1502 * 0 <always zero>
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]
1507 * ? <unknown>
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;
1527 switch (msg) {
1528 // Recorder-related events
1529 case MEDIA_RECORDER_EVENT_INFO:
1530 switch (ext1) {
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);
1534 return;
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);
1539 return;
1541 case MEDIA_RECORDER_TRACK_INFO_COMPLETION_STATUS:
1542 DOM_CAMERA_LOGI("recorder-event : info: track completed\n");
1543 OnRecorderStateChange(CameraControlListener::kTrackCompleted, ext2, trackNum);
1544 return;
1546 break;
1548 case MEDIA_RECORDER_EVENT_ERROR:
1549 switch (ext1) {
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);
1553 return;
1555 case MEDIA_ERROR_SERVER_DIED:
1556 DOM_CAMERA_LOGE("recorder-event : recorder-error: server died\n");
1557 OnRecorderStateChange(CameraControlListener::kMediaServerFailed, ext2, trackNum);
1558 return;
1560 break;
1562 // Track-related events, see note 1(a) above.
1563 case MEDIA_RECORDER_TRACK_EVENT_INFO:
1564 trackNum = (ext1 & 0xF0000000) >> 28;
1565 ext1 &= 0xFFFF;
1566 switch (ext1) {
1567 case MEDIA_RECORDER_TRACK_INFO_COMPLETION_STATUS:
1568 if (ext2 == OK) {
1569 DOM_CAMERA_LOGI("recorder-event : track-complete: track %d, %d (0x%08x)\n", trackNum, ext2, ext2);
1570 OnRecorderStateChange(CameraControlListener::kTrackCompleted, ext2, trackNum);
1571 return;
1573 DOM_CAMERA_LOGE("recorder-event : track-error: track %d, %d (0x%08x)\n", trackNum, ext2, ext2);
1574 OnRecorderStateChange(CameraControlListener::kTrackFailed, ext2, trackNum);
1575 return;
1577 case MEDIA_RECORDER_TRACK_INFO_PROGRESS_IN_TIME:
1578 DOM_CAMERA_LOGI("recorder-event : track-info: progress in time: %d ms\n", ext2);
1579 return;
1581 break;
1583 case MEDIA_RECORDER_TRACK_EVENT_ERROR:
1584 trackNum = (ext1 & 0xF0000000) >> 28;
1585 ext1 &= 0xFFFF;
1586 DOM_CAMERA_LOGE("recorder-event : track-error: track %d, %d (0x%08x)\n", trackNum, ext2, ext2);
1587 OnRecorderStateChange(CameraControlListener::kTrackFailed, ext2, trackNum);
1588 return;
1591 // All unhandled cases wind up here
1592 DOM_CAMERA_LOGW("recorder-event : unhandled: msg=%d, ext1=%d, ext2=%d\n", msg, ext1, ext2);
1595 nsresult
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;
1604 char buffer[SIZE];
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
1644 int r = aRotation;
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)),
1653 NS_ERROR_FAILURE);
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);
1659 return NS_OK;
1662 nsresult
1663 nsGonkCameraControl::StopImpl()
1665 DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
1667 // if we're recording, stop recording
1668 StopRecordingImpl();
1670 // stop the preview
1671 StopPreviewImpl();
1673 // release the hardware handle
1674 if (mCameraHw.get()){
1675 mCameraHw->Close();
1676 mCameraHw.clear();
1679 OnHardwareStateChange(CameraControlListener::kHardwareClosed);
1680 return NS_OK;
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();
1706 void
1707 nsGonkCameraControl::OnRateLimitPreview(bool aLimit)
1709 CameraControlImpl::OnRateLimitPreview(aLimit);
1712 void
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);
1729 void
1730 nsGonkCameraControl::OnSystemError(CameraControlListener::SystemContext aWhere,
1731 nsresult aError)
1733 if (aWhere == CameraControlListener::kSystemService) {
1734 OnPreviewStateChange(CameraControlListener::kPreviewStopped);
1735 OnHardwareStateChange(CameraControlListener::kHardwareClosed);
1738 CameraControlImpl::OnSystemError(aWhere, aError);
1741 // Gonk callback handlers.
1742 namespace mozilla {
1744 void
1745 OnTakePictureComplete(nsGonkCameraControl* gc, uint8_t* aData, uint32_t aLength)
1747 gc->OnTakePictureComplete(aData, aLength);
1750 void
1751 OnTakePictureError(nsGonkCameraControl* gc)
1753 gc->OnTakePictureError();
1756 void
1757 OnAutoFocusComplete(nsGonkCameraControl* gc, bool aSuccess)
1759 gc->OnAutoFocusComplete(aSuccess);
1762 void
1763 OnAutoFocusMoving(nsGonkCameraControl* gc, bool aIsMoving)
1765 gc->OnAutoFocusMoving(aIsMoving);
1768 void
1769 OnFacesDetected(nsGonkCameraControl* gc, camera_frame_metadata_t* aMetaData)
1771 gc->OnFacesDetected(aMetaData);
1774 void
1775 OnRateLimitPreview(nsGonkCameraControl* gc, bool aLimit)
1777 gc->OnRateLimitPreview(aLimit);
1780 void
1781 OnNewPreviewFrame(nsGonkCameraControl* gc, layers::TextureClient* aBuffer)
1783 gc->OnNewPreviewFrame(aBuffer);
1786 void
1787 OnShutter(nsGonkCameraControl* gc)
1789 gc->OnShutter();
1792 void
1793 OnClosed(nsGonkCameraControl* gc)
1795 gc->OnClosed();
1798 void
1799 OnSystemError(nsGonkCameraControl* gc,
1800 CameraControlListener::SystemContext aWhere,
1801 int32_t aArg1, int32_t aArg2)
1803 #ifdef PR_LOGGING
1804 DOM_CAMERA_LOGE("OnSystemError : aWhere=%d, aArg1=%d, aArg2=%d\n", aWhere, aArg1, aArg2);
1805 #else
1806 unused << aArg1;
1807 unused << aArg2;
1808 #endif
1809 gc->OnSystemError(aWhere, NS_ERROR_FAILURE);
1812 } // namespace mozilla