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