Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
1 /*
2 * Copyright (C) 2012-2014 Mozilla Foundation
3 *
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
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
15 */
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 DOM_CAMERA_LOGE("%s:%d : mCameraHw is null\n", __func__, __LINE__); \
57 return NS_ERROR_NOT_AVAILABLE; \
58 } \
59 } while(0)
61 // Construct nsGonkCameraControl on the main thread.
62 nsGonkCameraControl::nsGonkCameraControl(uint32_t aCameraId)
63 : CameraControlImpl(aCameraId)
64 , mLastPictureSize({0, 0})
65 , mLastThumbnailSize({0, 0})
66 , mPreviewFps(30)
67 , mResumePreviewAfterTakingPicture(false) // XXXmikeh - see bug 950102
68 , mFlashSupported(false)
69 , mLuminanceSupported(false)
70 , mAutoFlashModeOverridden(false)
71 , mDeferConfigUpdate(0)
72 , mMediaProfiles(nullptr)
73 , mRecorder(nullptr)
74 , mProfileManager(nullptr)
75 , mRecorderProfile(nullptr)
76 , mVideoFile(nullptr)
77 , mReentrantMonitor("GonkCameraControl::OnTakePictureMonitor")
78 {
79 // Constructor runs on the main thread...
80 DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
81 mImageContainer = LayerManager::CreateImageContainer();
82 }
84 nsresult
85 nsGonkCameraControl::StartImpl(const Configuration* aInitialConfig)
86 {
87 /**
88 * For initialization, we try to return the camera control to the upper
89 * upper layer (i.e. the DOM) as quickly as possible. To do this, the
90 * camera is initialized in the following stages:
91 *
92 * 0. Initialize() initializes the hardware;
93 * 1. SetConfigurationInternal() does the minimal configuration
94 * required so that we can start the preview -and- report a valid
95 * configuration to the upper layer;
96 * 2. OnHardwareStateChange() reports that the hardware is ready,
97 * which the upper (e.g. DOM) layer can (and does) use to return
98 * the camera control object;
99 * 3. StartPreviewImpl() starts the flow of preview frames from the
100 * camera hardware.
101 *
102 * The intent of the above flow is to let the Main Thread do as much work
103 * up-front as possible without waiting for blocking Camera Thread calls
104 * to complete.
105 */
106 MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
108 nsresult rv = Initialize();
109 if (NS_WARN_IF(NS_FAILED(rv))) {
110 return rv;
111 }
113 if (aInitialConfig) {
114 rv = SetConfigurationInternal(*aInitialConfig);
115 if (NS_WARN_IF(NS_FAILED(rv))) {
116 // The initial configuration failed, close up the hardware
117 StopImpl();
118 return rv;
119 }
120 }
122 OnHardwareStateChange(CameraControlListener::kHardwareOpen);
123 return StartPreviewImpl();
124 }
126 nsresult
127 nsGonkCameraControl::Initialize()
128 {
129 mCameraHw = GonkCameraHardware::Connect(this, mCameraId);
130 if (!mCameraHw.get()) {
131 DOM_CAMERA_LOGE("Failed to connect to camera %d (this=%p)\n", mCameraId, this);
132 return NS_ERROR_FAILURE;
133 }
135 DOM_CAMERA_LOGI("Initializing camera %d (this=%p, mCameraHw=%p)\n", mCameraId, this, mCameraHw.get());
137 // Initialize our camera configuration database.
138 PullParametersImpl();
140 // Set preferred preview frame format.
141 mParams.Set(CAMERA_PARAM_PREVIEWFORMAT, NS_LITERAL_STRING("yuv420sp"));
142 // Turn off any normal pictures returned by the HDR scene mode
143 mParams.Set(CAMERA_PARAM_SCENEMODE_HDR_RETURNNORMALPICTURE, false);
144 PushParametersImpl();
146 // Grab any other settings we'll need later.
147 mParams.Get(CAMERA_PARAM_PICTURE_FILEFORMAT, mFileFormat);
148 mParams.Get(CAMERA_PARAM_THUMBNAILSIZE, mLastThumbnailSize);
150 // The emulator's camera returns -1 for these values; bump them up to 0
151 int areas;
152 mParams.Get(CAMERA_PARAM_SUPPORTED_MAXMETERINGAREAS, areas);
153 mCurrentConfiguration.mMaxMeteringAreas = areas != -1 ? areas : 0;
154 mParams.Get(CAMERA_PARAM_SUPPORTED_MAXFOCUSAREAS, areas);
155 mCurrentConfiguration.mMaxFocusAreas = areas != -1 ? areas : 0;
157 mParams.Get(CAMERA_PARAM_PICTURE_SIZE, mLastPictureSize);
158 mParams.Get(CAMERA_PARAM_PREVIEWSIZE, mCurrentConfiguration.mPreviewSize);
159 mParams.Get(CAMERA_PARAM_VIDEOSIZE, mLastRecorderSize);
161 nsString luminance; // check for support
162 mParams.Get(CAMERA_PARAM_LUMINANCE, luminance);
163 mLuminanceSupported = !luminance.IsEmpty();
165 nsString flashMode;
166 mParams.Get(CAMERA_PARAM_FLASHMODE, flashMode);
167 mFlashSupported = !flashMode.IsEmpty();
169 DOM_CAMERA_LOGI(" - maximum metering areas: %u\n", mCurrentConfiguration.mMaxMeteringAreas);
170 DOM_CAMERA_LOGI(" - maximum focus areas: %u\n", mCurrentConfiguration.mMaxFocusAreas);
171 DOM_CAMERA_LOGI(" - default picture size: %u x %u\n",
172 mLastPictureSize.width, mLastPictureSize.height);
173 DOM_CAMERA_LOGI(" - default thumbnail size: %u x %u\n",
174 mLastThumbnailSize.width, mLastThumbnailSize.height);
175 DOM_CAMERA_LOGI(" - default preview size: %u x %u\n",
176 mCurrentConfiguration.mPreviewSize.width, mCurrentConfiguration.mPreviewSize.height);
177 DOM_CAMERA_LOGI(" - default video recorder size: %u x %u\n",
178 mLastRecorderSize.width, mLastRecorderSize.height);
179 DOM_CAMERA_LOGI(" - default picture file format: %s\n",
180 NS_ConvertUTF16toUTF8(mFileFormat).get());
181 DOM_CAMERA_LOGI(" - luminance reporting: %ssupported\n",
182 mLuminanceSupported ? "" : "NOT ");
183 if (mFlashSupported) {
184 DOM_CAMERA_LOGI(" - flash: supported, default mode '%s'\n",
185 NS_ConvertUTF16toUTF8(flashMode).get());
186 } else {
187 DOM_CAMERA_LOGI(" - flash: NOT supported\n");
188 }
190 return NS_OK;
191 }
193 nsGonkCameraControl::~nsGonkCameraControl()
194 {
195 DOM_CAMERA_LOGT("%s:%d : this=%p, mCameraHw = %p\n", __func__, __LINE__, this, mCameraHw.get());
197 StopImpl();
198 DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
199 }
201 nsresult
202 nsGonkCameraControl::SetConfigurationInternal(const Configuration& aConfig)
203 {
204 DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
206 nsresult rv;
208 switch (aConfig.mMode) {
209 case kPictureMode:
210 rv = SetPictureConfiguration(aConfig);
211 break;
213 case kVideoMode:
214 rv = SetVideoConfiguration(aConfig);
215 break;
217 default:
218 MOZ_ASSUME_UNREACHABLE("Unanticipated camera mode in SetConfigurationInternal()");
219 }
221 DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
222 NS_ENSURE_SUCCESS(rv, rv);
224 mCurrentConfiguration.mMode = aConfig.mMode;
225 mCurrentConfiguration.mRecorderProfile = aConfig.mRecorderProfile;
226 if (aConfig.mMode == kVideoMode) {
227 mCurrentConfiguration.mPreviewSize = mLastRecorderSize;
228 }
230 OnConfigurationChange();
231 return NS_OK;
232 }
234 nsresult
235 nsGonkCameraControl::SetConfigurationImpl(const Configuration& aConfig)
236 {
237 DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
238 MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
240 // Stop any currently running preview
241 nsresult rv = PausePreview();
242 if (NS_FAILED(rv)) {
243 // warn, but plow ahead
244 NS_WARNING("PausePreview() in SetConfigurationImpl() failed");
245 }
247 DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
248 rv = SetConfigurationInternal(aConfig);
249 if (NS_WARN_IF(NS_FAILED(rv))) {
250 StopPreviewImpl();
251 return rv;
252 }
254 // Restart the preview
255 DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
256 return StartPreviewImpl();
257 }
259 nsresult
260 nsGonkCameraControl::SetPictureConfiguration(const Configuration& aConfig)
261 {
262 DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
264 // remove any existing recorder profile
265 mRecorderProfile = nullptr;
267 nsresult rv = SetPreviewSize(aConfig.mPreviewSize);
268 if (NS_WARN_IF(NS_FAILED(rv))) {
269 return rv;
270 }
272 rv = PushParameters();
273 if (NS_WARN_IF(NS_FAILED(rv))) {
274 return rv;
275 }
277 mParams.Get(CAMERA_PARAM_PREVIEWFRAMERATE, mPreviewFps);
279 DOM_CAMERA_LOGI("picture mode preview: wanted %ux%u, got %ux%u (%u fps)\n",
280 aConfig.mPreviewSize.width, aConfig.mPreviewSize.height,
281 mCurrentConfiguration.mPreviewSize.width, mCurrentConfiguration.mPreviewSize.height,
282 mPreviewFps);
284 return NS_OK;
285 }
287 nsresult
288 nsGonkCameraControl::SetVideoConfiguration(const Configuration& aConfig)
289 {
290 DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
292 nsresult rv = SetupVideoMode(aConfig.mRecorderProfile);
293 NS_ENSURE_SUCCESS(rv, rv);
295 DOM_CAMERA_LOGI("video mode preview: profile '%s', got %ux%u (%u fps)\n",
296 NS_ConvertUTF16toUTF8(aConfig.mRecorderProfile).get(),
297 mLastRecorderSize.width, mLastRecorderSize.height,
298 mPreviewFps);
300 return rv;
301 }
303 // Parameter management.
304 nsresult
305 nsGonkCameraControl::PushParameters()
306 {
307 uint32_t dcu = mDeferConfigUpdate;
308 if (dcu > 0) {
309 DOM_CAMERA_LOGI("Defering config update (nest level %u)\n", dcu);
310 return NS_OK;
311 }
313 /**
314 * If we're already on the camera thread, call PushParametersImpl()
315 * directly, so that it executes synchronously. Some callers
316 * require this so that changes take effect immediately before
317 * we can proceed.
318 */
319 if (NS_GetCurrentThread() != mCameraThread) {
320 DOM_CAMERA_LOGT("%s:%d - dispatching to Camera Thread\n", __func__, __LINE__);
321 nsCOMPtr<nsIRunnable> pushParametersTask =
322 NS_NewRunnableMethod(this, &nsGonkCameraControl::PushParametersImpl);
323 return mCameraThread->Dispatch(pushParametersTask, NS_DISPATCH_NORMAL);
324 }
326 DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
327 return PushParametersImpl();
328 }
330 void
331 nsGonkCameraControl::BeginBatchParameterSet()
332 {
333 uint32_t dcu = ++mDeferConfigUpdate;
334 if (dcu == 0) {
335 NS_WARNING("Overflow weirdness incrementing mDeferConfigUpdate!");
336 MOZ_CRASH();
337 }
338 DOM_CAMERA_LOGI("Begin deferring camera configuration updates (nest level %u)\n", dcu);
339 }
341 void
342 nsGonkCameraControl::EndBatchParameterSet()
343 {
344 uint32_t dcu = mDeferConfigUpdate--;
345 if (dcu == 0) {
346 NS_WARNING("Underflow badness decrementing mDeferConfigUpdate!");
347 MOZ_CRASH();
348 }
349 DOM_CAMERA_LOGI("End deferring camera configuration updates (nest level %u)\n", dcu);
351 if (dcu == 1) {
352 PushParameters();
353 }
354 }
356 template<class T> nsresult
357 nsGonkCameraControl::SetAndPush(uint32_t aKey, const T& aValue)
358 {
359 nsresult rv = mParams.Set(aKey, aValue);
360 if (NS_FAILED(rv)) {
361 DOM_CAMERA_LOGE("Camera parameter aKey=%d failed to set (0x%x)\n", aKey, rv);
362 return rv;
363 }
364 return PushParameters();
365 }
367 // Array-of-Size parameter accessor.
368 nsresult
369 nsGonkCameraControl::Get(uint32_t aKey, nsTArray<Size>& aSizes)
370 {
371 if (aKey == CAMERA_PARAM_SUPPORTED_VIDEOSIZES) {
372 nsresult rv = mParams.Get(aKey, aSizes);
373 if (aSizes.Length() != 0) {
374 return rv;
375 }
376 DOM_CAMERA_LOGI("Camera doesn't support video independent of the preview\n");
377 aKey = CAMERA_PARAM_SUPPORTED_PREVIEWSIZES;
378 }
380 return mParams.Get(aKey, aSizes);
381 }
383 // Array-of-doubles parameter accessor.
384 nsresult
385 nsGonkCameraControl::Get(uint32_t aKey, nsTArray<double>& aValues)
386 {
387 return mParams.Get(aKey, aValues);
388 }
390 // Array-of-nsString parameter accessor.
391 nsresult
392 nsGonkCameraControl::Get(uint32_t aKey, nsTArray<nsString>& aValues)
393 {
394 return mParams.Get(aKey, aValues);
395 }
397 // nsString-valued parameter accessors
398 nsresult
399 nsGonkCameraControl::Set(uint32_t aKey, const nsAString& aValue)
400 {
401 nsresult rv = mParams.Set(aKey, aValue);
402 if (NS_FAILED(rv)) {
403 return rv;
404 }
406 switch (aKey) {
407 case CAMERA_PARAM_PICTURE_FILEFORMAT:
408 // Picture format -- need to keep it for the TakePicture() callback.
409 mFileFormat = aValue;
410 break;
412 case CAMERA_PARAM_FLASHMODE:
413 // Explicit flash mode changes always win and stick.
414 mAutoFlashModeOverridden = false;
415 break;
416 }
418 return PushParameters();
419 }
421 nsresult
422 nsGonkCameraControl::Get(uint32_t aKey, nsAString& aRet)
423 {
424 return mParams.Get(aKey, aRet);
425 }
427 // Double-valued parameter accessors
428 nsresult
429 nsGonkCameraControl::Set(uint32_t aKey, double aValue)
430 {
431 return SetAndPush(aKey, aValue);
432 }
434 nsresult
435 nsGonkCameraControl::Get(uint32_t aKey, double& aRet)
436 {
437 return mParams.Get(aKey, aRet);
438 }
440 // Signed-64-bit parameter accessors.
441 nsresult
442 nsGonkCameraControl::Set(uint32_t aKey, int64_t aValue)
443 {
444 return SetAndPush(aKey, aValue);
445 }
447 nsresult
448 nsGonkCameraControl::Get(uint32_t aKey, int64_t& aRet)
449 {
450 return mParams.Get(aKey, aRet);
451 }
453 // Weighted-region parameter accessors.
454 nsresult
455 nsGonkCameraControl::Set(uint32_t aKey, const nsTArray<Region>& aRegions)
456 {
457 return SetAndPush(aKey, aRegions);
458 }
460 nsresult
461 nsGonkCameraControl::Get(uint32_t aKey, nsTArray<Region>& aRegions)
462 {
463 return mParams.Get(aKey, aRegions);
464 }
466 // Singleton-size parameter accessors.
467 nsresult
468 nsGonkCameraControl::Set(uint32_t aKey, const Size& aSize)
469 {
470 switch (aKey) {
471 case CAMERA_PARAM_PICTURE_SIZE:
472 DOM_CAMERA_LOGI("setting picture size to %ux%u\n", aSize.width, aSize.height);
473 return SetPictureSize(aSize);
475 case CAMERA_PARAM_THUMBNAILSIZE:
476 DOM_CAMERA_LOGI("setting thumbnail size to %ux%u\n", aSize.width, aSize.height);
477 return SetThumbnailSize(aSize);
479 default:
480 return SetAndPush(aKey, aSize);
481 }
482 }
484 nsresult
485 nsGonkCameraControl::Get(uint32_t aKey, Size& aSize)
486 {
487 return mParams.Get(aKey, aSize);
488 }
490 // Signed int parameter accessors.
491 nsresult
492 nsGonkCameraControl::Set(uint32_t aKey, int aValue)
493 {
494 if (aKey == CAMERA_PARAM_PICTURE_ROTATION) {
495 RETURN_IF_NO_CAMERA_HW();
496 aValue = RationalizeRotation(aValue + mCameraHw->GetSensorOrientation());
497 }
498 return SetAndPush(aKey, aValue);
499 }
501 nsresult
502 nsGonkCameraControl::Get(uint32_t aKey, int& aRet)
503 {
504 if (aKey == CAMERA_PARAM_SENSORANGLE) {
505 RETURN_IF_NO_CAMERA_HW();
506 aRet = mCameraHw->GetSensorOrientation();
507 return NS_OK;
508 }
510 return mParams.Get(aKey, aRet);
511 }
513 // GPS location parameter accessors.
514 nsresult
515 nsGonkCameraControl::SetLocation(const Position& aLocation)
516 {
517 return SetAndPush(CAMERA_PARAM_PICTURE_LOCATION, aLocation);
518 }
520 nsresult
521 nsGonkCameraControl::StartPreviewImpl()
522 {
523 MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
524 RETURN_IF_NO_CAMERA_HW();
526 ReentrantMonitorAutoEnter mon(mReentrantMonitor);
528 if (mPreviewState == CameraControlListener::kPreviewStarted) {
529 DOM_CAMERA_LOGW("Camera preview already started, nothing to do\n");
530 return NS_OK;
531 }
533 DOM_CAMERA_LOGI("Starting preview (this=%p)\n", this);
535 if (mCameraHw->StartPreview() != OK) {
536 DOM_CAMERA_LOGE("Failed to start camera preview\n");
537 return NS_ERROR_FAILURE;
538 }
540 OnPreviewStateChange(CameraControlListener::kPreviewStarted);
541 return NS_OK;
542 }
544 nsresult
545 nsGonkCameraControl::StopPreviewImpl()
546 {
547 RETURN_IF_NO_CAMERA_HW();
549 DOM_CAMERA_LOGI("Stopping preview (this=%p)\n", this);
551 mCameraHw->StopPreview();
552 OnPreviewStateChange(CameraControlListener::kPreviewStopped);
553 return NS_OK;
554 }
556 nsresult
557 nsGonkCameraControl::PausePreview()
558 {
559 RETURN_IF_NO_CAMERA_HW();
561 DOM_CAMERA_LOGI("Pausing preview (this=%p)\n", this);
563 mCameraHw->StopPreview();
564 OnPreviewStateChange(CameraControlListener::kPreviewPaused);
565 return NS_OK;
566 }
568 nsresult
569 nsGonkCameraControl::AutoFocusImpl()
570 {
571 MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
572 RETURN_IF_NO_CAMERA_HW();
574 DOM_CAMERA_LOGI("Starting auto focus\n");
576 if (mCameraHw->AutoFocus() != OK) {
577 return NS_ERROR_FAILURE;
578 }
579 return NS_OK;
580 }
582 nsresult
583 nsGonkCameraControl::StartFaceDetectionImpl()
584 {
585 MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
586 RETURN_IF_NO_CAMERA_HW();
588 DOM_CAMERA_LOGI("Starting face detection\n");
590 if (mCameraHw->StartFaceDetection() != OK) {
591 return NS_ERROR_FAILURE;
592 }
593 return NS_OK;
594 }
596 nsresult
597 nsGonkCameraControl::StopFaceDetectionImpl()
598 {
599 MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
600 RETURN_IF_NO_CAMERA_HW();
602 DOM_CAMERA_LOGI("Stopping face detection\n");
604 if (mCameraHw->StopFaceDetection() != OK) {
605 return NS_ERROR_FAILURE;
606 }
607 return NS_OK;
608 }
610 nsresult
611 nsGonkCameraControl::SetThumbnailSizeImpl(const Size& aSize)
612 {
613 MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
615 /**
616 * We keep a copy of the specified size so that if the picture size
617 * changes, we can choose a new thumbnail size close to what was asked for
618 * last time.
619 */
620 mLastThumbnailSize = aSize;
622 /**
623 * If either of width or height is zero, set the other to zero as well.
624 * This should disable inclusion of a thumbnail in the final picture.
625 */
626 if (!aSize.width || !aSize.height) {
627 DOM_CAMERA_LOGW("Requested thumbnail size %ux%u, disabling thumbnail\n",
628 aSize.width, aSize.height);
629 Size size = { 0, 0 };
630 return SetAndPush(CAMERA_PARAM_THUMBNAILSIZE, size);
631 }
633 /**
634 * Choose the supported thumbnail size that is closest to the specified size.
635 * Some drivers will fail to take a picture if the thumbnail does not have
636 * the same aspect ratio as the set picture size, so we need to enforce that
637 * too.
638 */
639 int smallestDelta = INT_MAX;
640 uint32_t smallestDeltaIndex = UINT32_MAX;
641 int targetArea = aSize.width * aSize.height;
643 nsAutoTArray<Size, 8> supportedSizes;
644 Get(CAMERA_PARAM_SUPPORTED_JPEG_THUMBNAIL_SIZES, supportedSizes);
646 for (uint32_t i = 0; i < supportedSizes.Length(); ++i) {
647 int area = supportedSizes[i].width * supportedSizes[i].height;
648 int delta = abs(area - targetArea);
650 if (area != 0
651 && delta < smallestDelta
652 && supportedSizes[i].width * mLastPictureSize.height /
653 supportedSizes[i].height == mLastPictureSize.width
654 ) {
655 smallestDelta = delta;
656 smallestDeltaIndex = i;
657 }
658 }
660 if (smallestDeltaIndex == UINT32_MAX) {
661 DOM_CAMERA_LOGW("Unable to find a thumbnail size close to %ux%u, disabling thumbnail\n",
662 aSize.width, aSize.height);
663 // If we are unable to find a thumbnail size with a suitable aspect ratio,
664 // just disable the thumbnail altogether.
665 Size size = { 0, 0 };
666 return SetAndPush(CAMERA_PARAM_THUMBNAILSIZE, size);
667 }
669 Size size = supportedSizes[smallestDeltaIndex];
670 DOM_CAMERA_LOGI("camera-param set thumbnail-size = %ux%u (requested %ux%u)\n",
671 size.width, size.height, aSize.width, aSize.height);
672 if (size.width > INT32_MAX || size.height > INT32_MAX) {
673 DOM_CAMERA_LOGE("Supported thumbnail size is too big, no change\n");
674 return NS_ERROR_FAILURE;
675 }
677 return SetAndPush(CAMERA_PARAM_THUMBNAILSIZE, size);
678 }
680 nsresult
681 nsGonkCameraControl::SetThumbnailSize(const Size& aSize)
682 {
683 class SetThumbnailSize : public nsRunnable
684 {
685 public:
686 SetThumbnailSize(nsGonkCameraControl* aCameraControl, const Size& aSize)
687 : mCameraControl(aCameraControl)
688 , mSize(aSize)
689 {
690 MOZ_COUNT_CTOR(SetThumbnailSize);
691 }
692 ~SetThumbnailSize() { MOZ_COUNT_DTOR(SetThumbnailSize); }
694 NS_IMETHODIMP
695 Run() MOZ_OVERRIDE
696 {
697 nsresult rv = mCameraControl->SetThumbnailSizeImpl(mSize);
698 if (NS_FAILED(rv)) {
699 mCameraControl->OnError(CameraControlListener::kInUnspecified,
700 CameraControlListener::kErrorSetThumbnailSizeFailed);
701 }
702 return NS_OK;
703 }
705 protected:
706 nsRefPtr<nsGonkCameraControl> mCameraControl;
707 Size mSize;
708 };
710 if (NS_GetCurrentThread() == mCameraThread) {
711 return SetThumbnailSizeImpl(aSize);
712 }
714 return mCameraThread->Dispatch(new SetThumbnailSize(this, aSize), NS_DISPATCH_NORMAL);
715 }
717 nsresult
718 nsGonkCameraControl::UpdateThumbnailSize()
719 {
720 return SetThumbnailSize(mLastThumbnailSize);
721 }
723 nsresult
724 nsGonkCameraControl::SetPictureSizeImpl(const Size& aSize)
725 {
726 MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
728 /**
729 * Some drivers are less friendly about getting one of these set to zero,
730 * so if either is not specified, ignore both and go with current or
731 * default settings.
732 */
733 if (!aSize.width || !aSize.height) {
734 DOM_CAMERA_LOGW("Ignoring requested picture size of %ux%u\n", aSize.width, aSize.height);
735 return NS_ERROR_INVALID_ARG;
736 }
738 if (aSize.width == mLastPictureSize.width && aSize.height == mLastPictureSize.height) {
739 DOM_CAMERA_LOGI("Requested picture size %ux%u unchanged\n", aSize.width, aSize.height);
740 return NS_OK;
741 }
743 /**
744 * Choose the supported picture size that is closest in area to the
745 * specified size. Some drivers will fail to take a picture if the
746 * thumbnail size is not the same aspect ratio, so we update that
747 * as well to a size closest to the last user-requested one.
748 */
749 int smallestDelta = INT_MAX;
750 uint32_t smallestDeltaIndex = UINT32_MAX;
751 int targetArea = aSize.width * aSize.height;
753 nsAutoTArray<Size, 8> supportedSizes;
754 Get(CAMERA_PARAM_SUPPORTED_PICTURESIZES, supportedSizes);
756 for (uint32_t i = 0; i < supportedSizes.Length(); ++i) {
757 int area = supportedSizes[i].width * supportedSizes[i].height;
758 int delta = abs(area - targetArea);
760 if (area != 0 && delta < smallestDelta) {
761 smallestDelta = delta;
762 smallestDeltaIndex = i;
763 }
764 }
766 if (smallestDeltaIndex == UINT32_MAX) {
767 DOM_CAMERA_LOGW("Unable to find a picture size close to %ux%u\n",
768 aSize.width, aSize.height);
769 return NS_ERROR_INVALID_ARG;
770 }
772 Size size = supportedSizes[smallestDeltaIndex];
773 DOM_CAMERA_LOGI("camera-param set picture-size = %ux%u (requested %ux%u)\n",
774 size.width, size.height, aSize.width, aSize.height);
775 if (size.width > INT32_MAX || size.height > INT32_MAX) {
776 DOM_CAMERA_LOGE("Supported picture size is too big, no change\n");
777 return NS_ERROR_FAILURE;
778 }
780 nsresult rv = mParams.Set(CAMERA_PARAM_PICTURE_SIZE, size);
781 if (NS_FAILED(rv)) {
782 return rv;
783 }
785 mLastPictureSize = size;
787 // Finally, update the thumbnail size in case the picture
788 // aspect ratio changed.
789 return UpdateThumbnailSize();
790 }
792 int32_t
793 nsGonkCameraControl::RationalizeRotation(int32_t aRotation)
794 {
795 int32_t r = aRotation;
797 // The result of this operation is an angle from 0..270 degrees,
798 // in steps of 90 degrees. Angles are rounded to the nearest
799 // magnitude, so 45 will be rounded to 90, and -45 will be rounded
800 // to -90 (not 0).
801 if (r >= 0) {
802 r += 45;
803 } else {
804 r -= 45;
805 }
806 r /= 90;
807 r %= 4;
808 r *= 90;
809 if (r < 0) {
810 r += 360;
811 }
813 return r;
814 }
816 nsresult
817 nsGonkCameraControl::SetPictureSize(const Size& aSize)
818 {
819 class SetPictureSize : public nsRunnable
820 {
821 public:
822 SetPictureSize(nsGonkCameraControl* aCameraControl, const Size& aSize)
823 : mCameraControl(aCameraControl)
824 , mSize(aSize)
825 {
826 MOZ_COUNT_CTOR(SetPictureSize);
827 }
828 ~SetPictureSize() { MOZ_COUNT_DTOR(SetPictureSize); }
830 NS_IMETHODIMP
831 Run() MOZ_OVERRIDE
832 {
833 nsresult rv = mCameraControl->SetPictureSizeImpl(mSize);
834 if (NS_FAILED(rv)) {
835 mCameraControl->OnError(CameraControlListener::kInUnspecified,
836 CameraControlListener::kErrorSetPictureSizeFailed);
837 }
838 return NS_OK;
839 }
841 protected:
842 nsRefPtr<nsGonkCameraControl> mCameraControl;
843 Size mSize;
844 };
846 if (NS_GetCurrentThread() == mCameraThread) {
847 return SetPictureSizeImpl(aSize);
848 }
850 return mCameraThread->Dispatch(new SetPictureSize(this, aSize), NS_DISPATCH_NORMAL);
851 }
853 nsresult
854 nsGonkCameraControl::TakePictureImpl()
855 {
856 MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
857 RETURN_IF_NO_CAMERA_HW();
859 if (mCameraHw->TakePicture() != OK) {
860 return NS_ERROR_FAILURE;
861 }
863 // In Gonk, taking a picture implicitly stops the preview stream,
864 // so we need to reflect that here.
865 OnPreviewStateChange(CameraControlListener::kPreviewPaused);
866 return NS_OK;
867 }
869 nsresult
870 nsGonkCameraControl::PushParametersImpl()
871 {
872 MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
873 DOM_CAMERA_LOGI("Pushing camera parameters\n");
874 RETURN_IF_NO_CAMERA_HW();
876 if (mCameraHw->PushParameters(mParams) != OK) {
877 return NS_ERROR_FAILURE;
878 }
880 return NS_OK;
881 }
883 nsresult
884 nsGonkCameraControl::PullParametersImpl()
885 {
886 MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
887 DOM_CAMERA_LOGI("Pulling camera parameters\n");
888 RETURN_IF_NO_CAMERA_HW();
890 return mCameraHw->PullParameters(mParams);
891 }
893 nsresult
894 nsGonkCameraControl::SetupRecordingFlash(bool aAutoEnableLowLightTorch)
895 {
896 mAutoFlashModeOverridden = false;
898 if (!aAutoEnableLowLightTorch || !mLuminanceSupported || !mFlashSupported) {
899 return NS_OK;
900 }
902 DOM_CAMERA_LOGI("Luminance reporting and flash supported\n");
904 nsresult rv = PullParametersImpl();
905 if (NS_WARN_IF(NS_FAILED(rv))) {
906 return rv;
907 }
909 nsString luminance;
910 rv = mParams.Get(CAMERA_PARAM_LUMINANCE, luminance);
911 if (NS_WARN_IF(NS_FAILED(rv))) {
912 // If we failed to get the luminance, assume it's "high"
913 return NS_OK;
914 }
916 nsString flashMode;
917 rv = mParams.Get(CAMERA_PARAM_FLASHMODE, flashMode);
918 if (NS_WARN_IF(NS_FAILED(rv))) {
919 // If we failed to get the current flash mode, swallow the error
920 return NS_OK;
921 }
923 if (luminance.EqualsASCII("low") && flashMode.EqualsASCII("auto")) {
924 DOM_CAMERA_LOGI("Low luminance detected, turning on flash\n");
925 rv = SetAndPush(CAMERA_PARAM_FLASHMODE, NS_LITERAL_STRING("torch"));
926 if (NS_WARN_IF(NS_FAILED(rv))) {
927 // If we failed to turn on the flash, swallow the error
928 return NS_OK;
929 }
931 mAutoFlashModeOverridden = true;
932 }
934 return NS_OK;
935 }
937 nsresult
938 nsGonkCameraControl::StartRecordingImpl(DeviceStorageFileDescriptor* aFileDescriptor,
939 const StartRecordingOptions* aOptions)
940 {
941 MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
943 NS_ENSURE_TRUE(mRecorderProfile, NS_ERROR_NOT_INITIALIZED);
944 NS_ENSURE_FALSE(mRecorder, NS_ERROR_FAILURE);
946 /**
947 * Get the base path from device storage and append the app-specified
948 * filename to it. The filename may include a relative subpath
949 * (e.g.) "DCIM/IMG_0001.jpg".
950 *
951 * The camera app needs to provide the file extension '.3gp' for now.
952 * See bug 795202.
953 */
954 NS_ENSURE_TRUE(aFileDescriptor, NS_ERROR_FAILURE);
955 nsAutoString fullPath;
956 mVideoFile = aFileDescriptor->mDSFile;
957 mVideoFile->GetFullPath(fullPath);
958 DOM_CAMERA_LOGI("Video filename is '%s'\n",
959 NS_LossyConvertUTF16toASCII(fullPath).get());
961 if (!mVideoFile->IsSafePath()) {
962 DOM_CAMERA_LOGE("Invalid video file name\n");
963 return NS_ERROR_INVALID_ARG;
964 }
966 // SetupRecording creates a dup of the file descriptor, so we need to
967 // close the file descriptor when we leave this function. Also note, that
968 // since we're already off the main thread, we don't need to dispatch this.
969 // We just let the CloseFileRunnable destructor do the work.
970 nsRefPtr<CloseFileRunnable> closer;
971 if (aFileDescriptor->mFileDescriptor.IsValid()) {
972 closer = new CloseFileRunnable(aFileDescriptor->mFileDescriptor);
973 }
974 nsresult rv;
975 int fd = aFileDescriptor->mFileDescriptor.PlatformHandle();
976 if (aOptions) {
977 rv = SetupRecording(fd, aOptions->rotation, aOptions->maxFileSizeBytes,
978 aOptions->maxVideoLengthMs);
979 if (NS_SUCCEEDED(rv)) {
980 rv = SetupRecordingFlash(aOptions->autoEnableLowLightTorch);
981 }
982 } else {
983 rv = SetupRecording(fd, 0, 0, 0);
984 }
985 if (NS_WARN_IF(NS_FAILED(rv))) {
986 return rv;
987 }
989 if (mRecorder->start() != OK) {
990 DOM_CAMERA_LOGE("mRecorder->start() failed\n");
991 // important: we MUST destroy the recorder if start() fails!
992 mRecorder = nullptr;
993 // put the flash back to the 'auto' state
994 if (mAutoFlashModeOverridden) {
995 SetAndPush(CAMERA_PARAM_FLASHMODE, NS_LITERAL_STRING("auto"));
996 }
997 return NS_ERROR_FAILURE;
998 }
1000 OnRecorderStateChange(CameraControlListener::kRecorderStarted);
1001 return NS_OK;
1002 }
1004 nsresult
1005 nsGonkCameraControl::StopRecordingImpl()
1006 {
1007 class RecordingComplete : public nsRunnable
1008 {
1009 public:
1010 RecordingComplete(DeviceStorageFile* aFile)
1011 : mFile(aFile)
1012 { }
1014 ~RecordingComplete() { }
1016 NS_IMETHODIMP
1017 Run()
1018 {
1019 MOZ_ASSERT(NS_IsMainThread());
1021 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
1022 obs->NotifyObservers(mFile, "file-watcher-notify", NS_LITERAL_STRING("modified").get());
1023 return NS_OK;
1024 }
1026 private:
1027 nsRefPtr<DeviceStorageFile> mFile;
1028 };
1030 MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
1032 // nothing to do if we have no mRecorder
1033 NS_ENSURE_TRUE(mRecorder, NS_OK);
1035 mRecorder->stop();
1036 mRecorder = nullptr;
1037 OnRecorderStateChange(CameraControlListener::kRecorderStopped);
1039 if (mAutoFlashModeOverridden) {
1040 SetAndPush(CAMERA_PARAM_FLASHMODE, NS_LITERAL_STRING("auto"));
1041 }
1043 // notify DeviceStorage that the new video file is closed and ready
1044 return NS_DispatchToMainThread(new RecordingComplete(mVideoFile), NS_DISPATCH_NORMAL);
1045 }
1047 nsresult
1048 nsGonkCameraControl::ResumeContinuousFocusImpl()
1049 {
1050 MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
1051 RETURN_IF_NO_CAMERA_HW();
1053 DOM_CAMERA_LOGI("Resuming continuous autofocus\n");
1055 // see
1056 // http://developer.android.com/reference/android/hardware/Camera.Parameters.html#FOCUS_MODE_CONTINUOUS_PICTURE
1057 if (NS_WARN_IF(mCameraHw->CancelAutoFocus() != OK)) {
1058 return NS_ERROR_FAILURE;
1059 }
1061 return NS_OK;
1062 }
1064 void
1065 nsGonkCameraControl::OnAutoFocusComplete(bool aSuccess)
1066 {
1067 class AutoFocusComplete : public nsRunnable
1068 {
1069 public:
1070 AutoFocusComplete(nsGonkCameraControl* aCameraControl, bool aSuccess)
1071 : mCameraControl(aCameraControl)
1072 , mSuccess(aSuccess)
1073 { }
1075 NS_IMETHODIMP
1076 Run() MOZ_OVERRIDE
1077 {
1078 mCameraControl->OnAutoFocusComplete(mSuccess);
1079 return NS_OK;
1080 }
1082 protected:
1083 nsRefPtr<nsGonkCameraControl> mCameraControl;
1084 bool mSuccess;
1085 };
1087 if (NS_GetCurrentThread() == mCameraThread) {
1088 /**
1089 * Auto focusing can change some of the camera's parameters, so
1090 * we need to pull a new set before notifying any clients.
1091 */
1092 PullParametersImpl();
1093 CameraControlImpl::OnAutoFocusComplete(aSuccess);
1094 return;
1095 }
1097 /**
1098 * Because the callback needs to call PullParametersImpl(),
1099 * we need to dispatch this callback through the Camera Thread.
1100 */
1101 mCameraThread->Dispatch(new AutoFocusComplete(this, aSuccess), NS_DISPATCH_NORMAL);
1102 }
1104 bool
1105 FeatureDetected(int32_t feature[])
1106 {
1107 /**
1108 * For information on what constitutes a valid feature, see:
1109 * http://androidxref.com/4.0.4/xref/system/core/include/system/camera.h#202
1110 *
1111 * Although the comments explicitly state that undetected features are
1112 * indicated using the value -2000, we conservatively include anything
1113 * outside the explicitly valid range of [-1000, 1000] as undetected
1114 * as well.
1115 */
1116 const int32_t kLowerFeatureBound = -1000;
1117 const int32_t kUpperFeatureBound = 1000;
1118 return (feature[0] >= kLowerFeatureBound && feature[0] <= kUpperFeatureBound) ||
1119 (feature[1] >= kLowerFeatureBound && feature[1] <= kUpperFeatureBound);
1120 }
1122 void
1123 nsGonkCameraControl::OnFacesDetected(camera_frame_metadata_t* aMetaData)
1124 {
1125 NS_ENSURE_TRUE_VOID(aMetaData);
1127 nsTArray<Face> faces;
1128 uint32_t numFaces = aMetaData->number_of_faces;
1129 DOM_CAMERA_LOGI("Camera detected %d face(s)", numFaces);
1131 faces.SetCapacity(numFaces);
1133 for (uint32_t i = 0; i < numFaces; ++i) {
1134 Face* f = faces.AppendElement();
1136 f->id = aMetaData->faces[i].id;
1137 f->score = aMetaData->faces[i].score;
1138 if (f->score > 100) {
1139 f->score = 100;
1140 }
1141 f->bound.left = aMetaData->faces[i].rect[0];
1142 f->bound.top = aMetaData->faces[i].rect[1];
1143 f->bound.right = aMetaData->faces[i].rect[2];
1144 f->bound.bottom = aMetaData->faces[i].rect[3];
1145 DOM_CAMERA_LOGI("Camera face[%u] appended: id=%d, score=%d, bound=(%d, %d)-(%d, %d)\n",
1146 i, f->id, f->score, f->bound.left, f->bound.top, f->bound.right, f->bound.bottom);
1148 f->hasLeftEye = FeatureDetected(aMetaData->faces[i].left_eye);
1149 if (f->hasLeftEye) {
1150 f->leftEye.x = aMetaData->faces[i].left_eye[0];
1151 f->leftEye.y = aMetaData->faces[i].left_eye[1];
1152 DOM_CAMERA_LOGI(" Left eye detected at (%d, %d)\n",
1153 f->leftEye.x, f->leftEye.y);
1154 } else {
1155 DOM_CAMERA_LOGI(" No left eye detected\n");
1156 }
1158 f->hasRightEye = FeatureDetected(aMetaData->faces[i].right_eye);
1159 if (f->hasRightEye) {
1160 f->rightEye.x = aMetaData->faces[i].right_eye[0];
1161 f->rightEye.y = aMetaData->faces[i].right_eye[1];
1162 DOM_CAMERA_LOGI(" Right eye detected at (%d, %d)\n",
1163 f->rightEye.x, f->rightEye.y);
1164 } else {
1165 DOM_CAMERA_LOGI(" No right eye detected\n");
1166 }
1168 f->hasMouth = FeatureDetected(aMetaData->faces[i].mouth);
1169 if (f->hasMouth) {
1170 f->mouth.x = aMetaData->faces[i].mouth[0];
1171 f->mouth.y = aMetaData->faces[i].mouth[1];
1172 DOM_CAMERA_LOGI(" Mouth detected at (%d, %d)\n", f->mouth.x, f->mouth.y);
1173 } else {
1174 DOM_CAMERA_LOGI(" No mouth detected\n");
1175 }
1176 }
1178 CameraControlImpl::OnFacesDetected(faces);
1179 }
1181 void
1182 nsGonkCameraControl::OnTakePictureComplete(uint8_t* aData, uint32_t aLength)
1183 {
1184 ReentrantMonitorAutoEnter mon(mReentrantMonitor);
1186 uint8_t* data = new uint8_t[aLength];
1188 memcpy(data, aData, aLength);
1190 nsString s(NS_LITERAL_STRING("image/"));
1191 s.Append(mFileFormat);
1192 DOM_CAMERA_LOGI("Got picture, type '%s', %u bytes\n", NS_ConvertUTF16toUTF8(s).get(), aLength);
1193 OnTakePictureComplete(data, aLength, s);
1195 if (mResumePreviewAfterTakingPicture) {
1196 nsresult rv = StartPreview();
1197 if (NS_FAILED(rv)) {
1198 DOM_CAMERA_LOGE("Failed to restart camera preview (%x)\n", rv);
1199 OnPreviewStateChange(CameraControlListener::kPreviewStopped);
1200 }
1201 }
1203 DOM_CAMERA_LOGI("nsGonkCameraControl::OnTakePictureComplete() done\n");
1204 }
1206 void
1207 nsGonkCameraControl::OnTakePictureError()
1208 {
1209 CameraControlImpl::OnError(CameraControlListener::kInTakePicture,
1210 CameraControlListener::kErrorApiFailed);
1211 }
1213 nsresult
1214 nsGonkCameraControl::SetPreviewSize(const Size& aSize)
1215 {
1216 MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
1218 nsTArray<Size> previewSizes;
1219 nsresult rv = mParams.Get(CAMERA_PARAM_SUPPORTED_PREVIEWSIZES, previewSizes);
1220 if (NS_FAILED(rv)) {
1221 DOM_CAMERA_LOGE("Camera failed to return any preview sizes (0x%x)\n", rv);
1222 return rv;
1223 }
1225 Size best;
1226 rv = GetSupportedSize(aSize, previewSizes, best);
1227 if (NS_FAILED(rv)) {
1228 DOM_CAMERA_LOGE("Failed to find a supported preview size, requested size %dx%d",
1229 aSize.width, aSize.height);
1230 return rv;
1231 }
1233 // Some camera drivers will ignore our preview size if it's larger
1234 // than the currently set video recording size, so we need to set
1235 // the video size here as well, just in case.
1236 if (best.width > mLastRecorderSize.width || best.height > mLastRecorderSize.height) {
1237 SetVideoSize(best);
1238 }
1239 mCurrentConfiguration.mPreviewSize = best;
1240 return mParams.Set(CAMERA_PARAM_PREVIEWSIZE, best);
1241 }
1243 nsresult
1244 nsGonkCameraControl::SetVideoSize(const Size& aSize)
1245 {
1246 MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
1248 nsTArray<Size> videoSizes;
1249 nsresult rv = mParams.Get(CAMERA_PARAM_SUPPORTED_VIDEOSIZES, videoSizes);
1250 if (NS_FAILED(rv)) {
1251 DOM_CAMERA_LOGE("Camera failed to return any video sizes (0x%x)\n", rv);
1252 return rv;
1253 }
1255 Size best;
1256 rv = GetSupportedSize(aSize, videoSizes, best);
1257 if (NS_FAILED(rv)) {
1258 DOM_CAMERA_LOGE("Failed to find a supported video size, requested size %dx%d",
1259 aSize.width, aSize.height);
1260 return rv;
1261 }
1262 mLastRecorderSize = best;
1263 return mParams.Set(CAMERA_PARAM_VIDEOSIZE, best);
1264 }
1266 nsresult
1267 nsGonkCameraControl::GetSupportedSize(const Size& aSize,
1268 const nsTArray<Size>& supportedSizes,
1269 Size& best)
1270 {
1271 nsresult rv = NS_ERROR_INVALID_ARG;
1272 best = aSize;
1273 uint32_t minSizeDelta = UINT32_MAX;
1274 uint32_t delta;
1276 if (!aSize.width && !aSize.height) {
1277 // no size specified, take the first supported size
1278 best = supportedSizes[0];
1279 rv = NS_OK;
1280 } else if (aSize.width && aSize.height) {
1281 // both height and width specified, find the supported size closest to requested size
1282 uint32_t targetArea = aSize.width * aSize.height;
1283 for (nsTArray<Size>::index_type i = 0; i < supportedSizes.Length(); i++) {
1284 Size size = supportedSizes[i];
1285 uint32_t delta = abs((long int)(size.width * size.height - targetArea));
1286 if (delta < minSizeDelta) {
1287 minSizeDelta = delta;
1288 best = size;
1289 rv = NS_OK;
1290 }
1291 }
1292 } else if (!aSize.width) {
1293 // width not specified, find closest height match
1294 for (nsTArray<Size>::index_type i = 0; i < supportedSizes.Length(); i++) {
1295 Size size = supportedSizes[i];
1296 delta = abs((long int)(size.height - aSize.height));
1297 if (delta < minSizeDelta) {
1298 minSizeDelta = delta;
1299 best = size;
1300 rv = NS_OK;
1301 }
1302 }
1303 } else if (!aSize.height) {
1304 // height not specified, find closest width match
1305 for (nsTArray<Size>::index_type i = 0; i < supportedSizes.Length(); i++) {
1306 Size size = supportedSizes[i];
1307 delta = abs((long int)(size.width - aSize.width));
1308 if (delta < minSizeDelta) {
1309 minSizeDelta = delta;
1310 best = size;
1311 rv = NS_OK;
1312 }
1313 }
1314 }
1315 return rv;
1316 }
1318 nsresult
1319 nsGonkCameraControl::SetupVideoMode(const nsAString& aProfile)
1320 {
1321 DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
1323 // read preferences for camcorder
1324 mMediaProfiles = MediaProfiles::getInstance();
1326 nsAutoCString profile = NS_ConvertUTF16toUTF8(aProfile);
1327 // XXXkhuey are we leaking?
1328 mRecorderProfile = GetGonkRecorderProfileManager().take()->Get(profile.get());
1329 if (!mRecorderProfile) {
1330 DOM_CAMERA_LOGE("Recorder profile '%s' is not supported\n", profile.get());
1331 return NS_ERROR_INVALID_ARG;
1332 }
1334 const GonkRecorderVideoProfile* video = mRecorderProfile->GetGonkVideoProfile();
1335 int width = video->GetWidth();
1336 int height = video->GetHeight();
1337 int fps = video->GetFramerate();
1338 if (fps == -1 || width < 0 || height < 0) {
1339 DOM_CAMERA_LOGE("Can't configure preview with fps=%d, width=%d, height=%d\n",
1340 fps, width, height);
1341 return NS_ERROR_FAILURE;
1342 }
1344 PullParametersImpl();
1346 Size size;
1347 size.width = static_cast<uint32_t>(width);
1348 size.height = static_cast<uint32_t>(height);
1350 {
1351 ICameraControlParameterSetAutoEnter set(this);
1353 // The camera interface allows for hardware to provide two video
1354 // streams, a low resolution preview and a potentially high resolution
1355 // stream for encoding. For now we don't use this and set preview and video
1356 // size to the same thing.
1357 nsresult rv = SetVideoSize(size);
1358 if (NS_FAILED(rv)) {
1359 DOM_CAMERA_LOGE("Failed to set video mode video size (0x%x)\n", rv);
1360 return rv;
1361 }
1363 rv = SetPreviewSize(size);
1364 if (NS_FAILED(rv)) {
1365 DOM_CAMERA_LOGE("Failed to set video mode preview size (0x%x)\n", rv);
1366 return rv;
1367 }
1369 rv = mParams.Set(CAMERA_PARAM_PREVIEWFRAMERATE, fps);
1370 if (NS_FAILED(rv)) {
1371 DOM_CAMERA_LOGE("Failed to set video mode frame rate (0x%x)\n", rv);
1372 return rv;
1373 }
1375 rv = PushParameters();
1376 if (NS_FAILED(rv)) {
1377 DOM_CAMERA_LOGE("Failed to set video mode settings (0x%x)\n", rv);
1378 return rv;
1379 }
1381 mPreviewFps = fps;
1382 }
1383 return NS_OK;
1384 }
1386 class GonkRecorderListener : public IMediaRecorderClient
1387 {
1388 public:
1389 GonkRecorderListener(nsGonkCameraControl* aCameraControl)
1390 : mCameraControl(aCameraControl)
1391 {
1392 DOM_CAMERA_LOGT("%s:%d : this=%p, aCameraControl=%p\n",
1393 __func__, __LINE__, this, mCameraControl.get());
1394 }
1396 void notify(int msg, int ext1, int ext2)
1397 {
1398 if (mCameraControl) {
1399 mCameraControl->OnRecorderEvent(msg, ext1, ext2);
1400 }
1401 }
1403 IBinder* onAsBinder()
1404 {
1405 DOM_CAMERA_LOGE("onAsBinder() called, should NEVER get called!\n");
1406 return nullptr;
1407 }
1409 protected:
1410 ~GonkRecorderListener() { }
1411 nsRefPtr<nsGonkCameraControl> mCameraControl;
1412 };
1414 void
1415 nsGonkCameraControl::OnRecorderEvent(int msg, int ext1, int ext2)
1416 {
1417 /**
1418 * Refer to base/include/media/mediarecorder.h for a complete list
1419 * of error and info message codes. There are duplicate values
1420 * within the status/error code space, as determined by code inspection:
1421 *
1422 * +------- msg
1423 * | +----- ext1
1424 * | | +--- ext2
1425 * V V V
1426 * 1 MEDIA_RECORDER_EVENT_ERROR
1427 * 1 MEDIA_RECORDER_ERROR_UNKNOWN
1428 * [3] ERROR_MALFORMED
1429 * 100 mediaplayer.h::MEDIA_ERROR_SERVER_DIED
1430 * 0 <always zero>
1431 * 2 MEDIA_RECORDER_EVENT_INFO
1432 * 800 MEDIA_RECORDER_INFO_MAX_DURATION_REACHED
1433 * 0 <always zero>
1434 * 801 MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED
1435 * 0 <always zero>
1436 * 1000 MEDIA_RECORDER_TRACK_INFO_COMPLETION_STATUS[1b]
1437 * [3] UNKNOWN_ERROR, etc.
1438 * 100 MEDIA_ERROR[4]
1439 * 100 mediaplayer.h::MEDIA_ERROR_SERVER_DIED
1440 * 0 <always zero>
1441 * 100 MEDIA_RECORDER_TRACK_EVENT_ERROR
1442 * 100 MEDIA_RECORDER_TRACK_ERROR_GENERAL[1a]
1443 * [3] UNKNOWN_ERROR, etc.
1444 * 200 MEDIA_RECORDER_ERROR_VIDEO_NO_SYNC_FRAME[2]
1445 * ? <unknown>
1446 * 101 MEDIA_RECORDER_TRACK_EVENT_INFO
1447 * 1000 MEDIA_RECORDER_TRACK_INFO_COMPLETION_STATUS[1a]
1448 * [3] UNKNOWN_ERROR, etc.
1449 * N see mediarecorder.h::media_recorder_info_type[5]
1450 *
1451 * 1. a) High 4 bits are the track number, the next 12 bits are reserved,
1452 * and the final 16 bits are the actual error code (above).
1453 * b) But not in this case.
1454 * 2. Never actually used in AOSP code?
1455 * 3. Specific error codes are from utils/Errors.h and/or
1456 * include/media/stagefright/MediaErrors.h.
1457 * 4. Only in frameworks/base/media/libmedia/mediaplayer.cpp.
1458 * 5. These are mostly informational and we can ignore them; note that
1459 * although the MEDIA_RECORDER_INFO_MAX_DURATION_REACHED and
1460 * MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED values are defined in this
1461 * enum, they are used with different ext1 codes. /o\
1462 */
1463 int trackNum = CameraControlListener::kNoTrackNumber;
1465 switch (msg) {
1466 // Recorder-related events
1467 case MEDIA_RECORDER_EVENT_INFO:
1468 switch (ext1) {
1469 case MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED:
1470 DOM_CAMERA_LOGI("recorder-event : info: maximum file size reached\n");
1471 OnRecorderStateChange(CameraControlListener::kFileSizeLimitReached, ext2, trackNum);
1472 return;
1474 case MEDIA_RECORDER_INFO_MAX_DURATION_REACHED:
1475 DOM_CAMERA_LOGI("recorder-event : info: maximum video duration reached\n");
1476 OnRecorderStateChange(CameraControlListener::kVideoLengthLimitReached, ext2, trackNum);
1477 return;
1479 case MEDIA_RECORDER_TRACK_INFO_COMPLETION_STATUS:
1480 DOM_CAMERA_LOGI("recorder-event : info: track completed\n");
1481 OnRecorderStateChange(CameraControlListener::kTrackCompleted, ext2, trackNum);
1482 return;
1483 }
1484 break;
1486 case MEDIA_RECORDER_EVENT_ERROR:
1487 switch (ext1) {
1488 case MEDIA_RECORDER_ERROR_UNKNOWN:
1489 DOM_CAMERA_LOGE("recorder-event : recorder-error: %d (0x%08x)\n", ext2, ext2);
1490 OnRecorderStateChange(CameraControlListener::kMediaRecorderFailed, ext2, trackNum);
1491 return;
1493 case MEDIA_ERROR_SERVER_DIED:
1494 DOM_CAMERA_LOGE("recorder-event : recorder-error: server died\n");
1495 OnRecorderStateChange(CameraControlListener::kMediaServerFailed, ext2, trackNum);
1496 return;
1497 }
1498 break;
1500 // Track-related events, see note 1(a) above.
1501 case MEDIA_RECORDER_TRACK_EVENT_INFO:
1502 trackNum = (ext1 & 0xF0000000) >> 28;
1503 ext1 &= 0xFFFF;
1504 switch (ext1) {
1505 case MEDIA_RECORDER_TRACK_INFO_COMPLETION_STATUS:
1506 if (ext2 == OK) {
1507 DOM_CAMERA_LOGI("recorder-event : track-complete: track %d, %d (0x%08x)\n", trackNum, ext2, ext2);
1508 OnRecorderStateChange(CameraControlListener::kTrackCompleted, ext2, trackNum);
1509 return;
1510 }
1511 DOM_CAMERA_LOGE("recorder-event : track-error: track %d, %d (0x%08x)\n", trackNum, ext2, ext2);
1512 OnRecorderStateChange(CameraControlListener::kTrackFailed, ext2, trackNum);
1513 return;
1515 case MEDIA_RECORDER_TRACK_INFO_PROGRESS_IN_TIME:
1516 DOM_CAMERA_LOGI("recorder-event : track-info: progress in time: %d ms\n", ext2);
1517 return;
1518 }
1519 break;
1521 case MEDIA_RECORDER_TRACK_EVENT_ERROR:
1522 trackNum = (ext1 & 0xF0000000) >> 28;
1523 ext1 &= 0xFFFF;
1524 DOM_CAMERA_LOGE("recorder-event : track-error: track %d, %d (0x%08x)\n", trackNum, ext2, ext2);
1525 OnRecorderStateChange(CameraControlListener::kTrackFailed, ext2, trackNum);
1526 return;
1527 }
1529 // All unhandled cases wind up here
1530 DOM_CAMERA_LOGW("recorder-event : unhandled: msg=%d, ext1=%d, ext2=%d\n", msg, ext1, ext2);
1531 }
1533 nsresult
1534 nsGonkCameraControl::SetupRecording(int aFd, int aRotation,
1535 int64_t aMaxFileSizeBytes,
1536 int64_t aMaxVideoLengthMs)
1537 {
1538 RETURN_IF_NO_CAMERA_HW();
1540 // choosing a size big enough to hold the params
1541 const size_t SIZE = 256;
1542 char buffer[SIZE];
1544 mRecorder = new GonkRecorder();
1545 CHECK_SETARG(mRecorder->init());
1547 nsresult rv = mRecorderProfile->ConfigureRecorder(mRecorder);
1548 NS_ENSURE_SUCCESS(rv, rv);
1550 CHECK_SETARG(mRecorder->setCamera(mCameraHw));
1552 DOM_CAMERA_LOGI("maxVideoLengthMs=%lld\n", aMaxVideoLengthMs);
1553 if (aMaxVideoLengthMs == 0) {
1554 aMaxVideoLengthMs = -1;
1555 }
1556 snprintf(buffer, SIZE, "max-duration=%lld", aMaxVideoLengthMs);
1557 CHECK_SETARG(mRecorder->setParameters(String8(buffer)));
1559 DOM_CAMERA_LOGI("maxFileSizeBytes=%lld\n", aMaxFileSizeBytes);
1560 if (aMaxFileSizeBytes == 0) {
1561 aMaxFileSizeBytes = -1;
1562 }
1563 snprintf(buffer, SIZE, "max-filesize=%lld", aMaxFileSizeBytes);
1564 CHECK_SETARG(mRecorder->setParameters(String8(buffer)));
1566 // adjust rotation by camera sensor offset
1567 int r = aRotation;
1568 r += mCameraHw->GetSensorOrientation();
1569 r = RationalizeRotation(r);
1570 DOM_CAMERA_LOGI("setting video rotation to %d degrees (mapped from %d)\n", r, aRotation);
1571 snprintf(buffer, SIZE, "video-param-rotation-angle-degrees=%d", r);
1572 CHECK_SETARG(mRecorder->setParameters(String8(buffer)));
1574 CHECK_SETARG(mRecorder->setListener(new GonkRecorderListener(this)));
1576 // recording API needs file descriptor of output file
1577 CHECK_SETARG(mRecorder->setOutputFile(aFd, 0, 0));
1578 CHECK_SETARG(mRecorder->prepare());
1580 return NS_OK;
1581 }
1583 nsresult
1584 nsGonkCameraControl::StopImpl()
1585 {
1586 DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
1588 // if we're recording, stop recording
1589 StopRecordingImpl();
1591 // stop the preview
1592 StopPreviewImpl();
1594 // release the hardware handle
1595 if (mCameraHw.get()){
1596 mCameraHw->Close();
1597 mCameraHw.clear();
1598 }
1600 OnHardwareStateChange(CameraControlListener::kHardwareClosed);
1601 return NS_OK;
1602 }
1604 already_AddRefed<GonkRecorderProfileManager>
1605 nsGonkCameraControl::GetGonkRecorderProfileManager()
1606 {
1607 if (!mProfileManager) {
1608 nsTArray<Size> sizes;
1609 nsresult rv = Get(CAMERA_PARAM_SUPPORTED_VIDEOSIZES, sizes);
1610 NS_ENSURE_SUCCESS(rv, nullptr);
1612 mProfileManager = new GonkRecorderProfileManager(mCameraId);
1613 mProfileManager->SetSupportedResolutions(sizes);
1614 }
1616 nsRefPtr<GonkRecorderProfileManager> profileMgr = mProfileManager;
1617 return profileMgr.forget();
1618 }
1620 already_AddRefed<RecorderProfileManager>
1621 nsGonkCameraControl::GetRecorderProfileManagerImpl()
1622 {
1623 nsRefPtr<RecorderProfileManager> profileMgr = GetGonkRecorderProfileManager();
1624 return profileMgr.forget();
1625 }
1627 void
1628 nsGonkCameraControl::OnNewPreviewFrame(layers::TextureClient* aBuffer)
1629 {
1630 nsRefPtr<Image> frame = mImageContainer->CreateImage(ImageFormat::GRALLOC_PLANAR_YCBCR);
1632 GrallocImage* videoImage = static_cast<GrallocImage*>(frame.get());
1634 GrallocImage::GrallocData data;
1635 data.mGraphicBuffer = aBuffer;
1636 data.mPicSize = IntSize(mCurrentConfiguration.mPreviewSize.width,
1637 mCurrentConfiguration.mPreviewSize.height);
1638 videoImage->SetData(data);
1640 OnNewPreviewFrame(frame, mCurrentConfiguration.mPreviewSize.width,
1641 mCurrentConfiguration.mPreviewSize.height);
1642 }
1644 void
1645 nsGonkCameraControl::OnError(CameraControlListener::CameraErrorContext aWhere,
1646 CameraControlListener::CameraError aError)
1647 {
1648 if (aError == CameraControlListener::kErrorServiceFailed) {
1649 OnPreviewStateChange(CameraControlListener::kPreviewStopped);
1650 OnHardwareStateChange(CameraControlListener::kHardwareClosed);
1651 }
1653 CameraControlImpl::OnError(aWhere, aError);
1654 }
1656 // Gonk callback handlers.
1657 namespace mozilla {
1659 void
1660 OnTakePictureComplete(nsGonkCameraControl* gc, uint8_t* aData, uint32_t aLength)
1661 {
1662 gc->OnTakePictureComplete(aData, aLength);
1663 }
1665 void
1666 OnTakePictureError(nsGonkCameraControl* gc)
1667 {
1668 gc->OnTakePictureError();
1669 }
1671 void
1672 OnAutoFocusComplete(nsGonkCameraControl* gc, bool aSuccess)
1673 {
1674 gc->OnAutoFocusComplete(aSuccess);
1675 }
1677 void
1678 OnAutoFocusMoving(nsGonkCameraControl* gc, bool aIsMoving)
1679 {
1680 gc->OnAutoFocusMoving(aIsMoving);
1681 }
1683 void
1684 OnFacesDetected(nsGonkCameraControl* gc, camera_frame_metadata_t* aMetaData)
1685 {
1686 gc->OnFacesDetected(aMetaData);
1687 }
1689 void
1690 OnNewPreviewFrame(nsGonkCameraControl* gc, layers::TextureClient* aBuffer)
1691 {
1692 gc->OnNewPreviewFrame(aBuffer);
1693 }
1695 void
1696 OnShutter(nsGonkCameraControl* gc)
1697 {
1698 gc->OnShutter();
1699 }
1701 void
1702 OnClosed(nsGonkCameraControl* gc)
1703 {
1704 gc->OnClosed();
1705 }
1707 void
1708 OnError(nsGonkCameraControl* gc, CameraControlListener::CameraError aError,
1709 int32_t aArg1, int32_t aArg2)
1710 {
1711 #ifdef PR_LOGGING
1712 DOM_CAMERA_LOGE("OnError : aError=%d, aArg1=%d, aArg2=%d\n", aError, aArg1, aArg2);
1713 #else
1714 unused << aArg1;
1715 unused << aArg2;
1716 #endif
1717 gc->OnError(CameraControlListener::kInUnspecified, aError);
1718 }
1720 } // namespace mozilla