Backout a74bd5095902, Bug 959405 - Please update the Buri Moz-central, 1.3, 1.2 with...
[gecko.git] / dom / camera / GonkCameraControl.cpp
blob14854e80b168dd3002007ce185874a77c56bf052
1 /*
2 * Copyright (C) 2012 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 <time.h>
18 #include <string.h>
19 #include <sys/stat.h>
20 #include <fcntl.h>
21 #include <errno.h>
22 #include <libgen.h>
23 #include "base/basictypes.h"
24 #include "camera/CameraParameters.h"
25 #include "nsCOMPtr.h"
26 #include "nsDOMClassInfo.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 "nsAlgorithm.h"
33 #include <media/mediaplayer.h>
34 #include "nsPrintfCString.h"
35 #include "nsIObserverService.h"
36 #include "nsIVolume.h"
37 #include "nsIVolumeService.h"
38 #include "DOMCameraManager.h"
39 #include "GonkCameraHwMgr.h"
40 #include "DOMCameraCapabilities.h"
41 #include "DOMCameraControl.h"
42 #include "GonkRecorderProfiles.h"
43 #include "GonkCameraControl.h"
44 #include "CameraCommon.h"
46 using namespace mozilla;
47 using namespace mozilla::dom;
48 using namespace mozilla::layers;
49 using namespace android;
50 using mozilla::gfx::IntSize;
52 /**
53 * See bug 783682. Most camera implementations, despite claiming they
54 * support 'yuv420p' as a preview format, actually ignore this setting
55 * and return 'yuv420sp' data anyway. We have come across a new implementation
56 * that, while reporting that 'yuv420p' is supported *and* has been accepted,
57 * still returns the frame data in 'yuv420sp' anyway. So for now, since
58 * everyone seems to return this format, we just force it.
60 #define FORCE_PREVIEW_FORMAT_YUV420SP 1
62 #define RETURN_IF_NO_CAMERA_HW() \
63 do { \
64 if (!mCameraHw.get()) { \
65 DOM_CAMERA_LOGE("%s:%d : mCameraHw is null\n", __func__, __LINE__); \
66 return NS_ERROR_NOT_AVAILABLE; \
67 } \
68 } while(0)
70 static const char* getKeyText(uint32_t aKey)
72 switch (aKey) {
73 case CAMERA_PARAM_EFFECT:
74 return CameraParameters::KEY_EFFECT;
75 case CAMERA_PARAM_WHITEBALANCE:
76 return CameraParameters::KEY_WHITE_BALANCE;
77 case CAMERA_PARAM_SCENEMODE:
78 return CameraParameters::KEY_SCENE_MODE;
79 case CAMERA_PARAM_FLASHMODE:
80 return CameraParameters::KEY_FLASH_MODE;
81 case CAMERA_PARAM_FOCUSMODE:
82 return CameraParameters::KEY_FOCUS_MODE;
83 case CAMERA_PARAM_ZOOM:
84 return CameraParameters::KEY_ZOOM;
85 case CAMERA_PARAM_METERINGAREAS:
86 return CameraParameters::KEY_METERING_AREAS;
87 case CAMERA_PARAM_FOCUSAREAS:
88 return CameraParameters::KEY_FOCUS_AREAS;
89 case CAMERA_PARAM_FOCALLENGTH:
90 return CameraParameters::KEY_FOCAL_LENGTH;
91 case CAMERA_PARAM_FOCUSDISTANCENEAR:
92 return CameraParameters::KEY_FOCUS_DISTANCES;
93 case CAMERA_PARAM_FOCUSDISTANCEOPTIMUM:
94 return CameraParameters::KEY_FOCUS_DISTANCES;
95 case CAMERA_PARAM_FOCUSDISTANCEFAR:
96 return CameraParameters::KEY_FOCUS_DISTANCES;
97 case CAMERA_PARAM_EXPOSURECOMPENSATION:
98 return CameraParameters::KEY_EXPOSURE_COMPENSATION;
99 case CAMERA_PARAM_PICTURESIZE:
100 return CameraParameters::KEY_PICTURE_SIZE;
101 case CAMERA_PARAM_THUMBNAILQUALITY:
102 return CameraParameters::KEY_JPEG_THUMBNAIL_QUALITY;
104 case CAMERA_PARAM_SUPPORTED_PREVIEWSIZES:
105 return CameraParameters::KEY_SUPPORTED_PREVIEW_SIZES;
106 case CAMERA_PARAM_SUPPORTED_VIDEOSIZES:
107 return CameraParameters::KEY_SUPPORTED_VIDEO_SIZES;
108 case CAMERA_PARAM_SUPPORTED_PICTURESIZES:
109 return CameraParameters::KEY_SUPPORTED_PICTURE_SIZES;
110 case CAMERA_PARAM_SUPPORTED_PICTUREFORMATS:
111 return CameraParameters::KEY_SUPPORTED_PICTURE_FORMATS;
112 case CAMERA_PARAM_SUPPORTED_WHITEBALANCES:
113 return CameraParameters::KEY_SUPPORTED_WHITE_BALANCE;
114 case CAMERA_PARAM_SUPPORTED_SCENEMODES:
115 return CameraParameters::KEY_SUPPORTED_SCENE_MODES;
116 case CAMERA_PARAM_SUPPORTED_EFFECTS:
117 return CameraParameters::KEY_SUPPORTED_EFFECTS;
118 case CAMERA_PARAM_SUPPORTED_FLASHMODES:
119 return CameraParameters::KEY_SUPPORTED_FLASH_MODES;
120 case CAMERA_PARAM_SUPPORTED_FOCUSMODES:
121 return CameraParameters::KEY_SUPPORTED_FOCUS_MODES;
122 case CAMERA_PARAM_SUPPORTED_MAXFOCUSAREAS:
123 return CameraParameters::KEY_MAX_NUM_FOCUS_AREAS;
124 case CAMERA_PARAM_SUPPORTED_MAXMETERINGAREAS:
125 return CameraParameters::KEY_MAX_NUM_METERING_AREAS;
126 case CAMERA_PARAM_SUPPORTED_MINEXPOSURECOMPENSATION:
127 return CameraParameters::KEY_MIN_EXPOSURE_COMPENSATION;
128 case CAMERA_PARAM_SUPPORTED_MAXEXPOSURECOMPENSATION:
129 return CameraParameters::KEY_MAX_EXPOSURE_COMPENSATION;
130 case CAMERA_PARAM_SUPPORTED_EXPOSURECOMPENSATIONSTEP:
131 return CameraParameters::KEY_EXPOSURE_COMPENSATION_STEP;
132 case CAMERA_PARAM_SUPPORTED_ZOOM:
133 return CameraParameters::KEY_ZOOM_SUPPORTED;
134 case CAMERA_PARAM_SUPPORTED_ZOOMRATIOS:
135 return CameraParameters::KEY_ZOOM_RATIOS;
136 case CAMERA_PARAM_SUPPORTED_JPEG_THUMBNAIL_SIZES:
137 return CameraParameters::KEY_SUPPORTED_JPEG_THUMBNAIL_SIZES;
138 default:
139 return nullptr;
143 // nsDOMCameraControl implementation-specific constructor
144 nsDOMCameraControl::nsDOMCameraControl(uint32_t aCameraId, nsIThread* aCameraThread, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError, nsPIDOMWindow* aWindow)
145 : mDOMCapabilities(nullptr), mWindow(aWindow)
147 MOZ_ASSERT(aWindow, "shouldn't be created with null window!");
148 DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
149 SetIsDOMBinding();
152 * nsDOMCameraControl is a cycle-collection participant, which means it is
153 * not threadsafe--so we need to bump up its reference count here to make
154 * sure that it exists long enough to be initialized.
156 * Once it is initialized, the GetCameraResult main-thread runnable will
157 * decrement it again to make sure it can be cleaned up.
159 * nsGonkCameraControl MUST NOT hold a strong reference to this
160 * nsDOMCameraControl or memory will leak!
162 NS_ADDREF_THIS();
163 nsRefPtr<nsGonkCameraControl> control = new nsGonkCameraControl(aCameraId, aCameraThread, this, onSuccess, onError, aWindow->WindowID());
164 control->DispatchInit(this, onSuccess, onError, aWindow->WindowID());
165 mCameraControl = control;
168 // Gonk-specific CameraControl implementation.
170 // Initialize nsGonkCameraControl instance--runs on camera thread.
171 class InitGonkCameraControl : public nsRunnable
173 public:
174 InitGonkCameraControl(nsGonkCameraControl* aCameraControl, nsDOMCameraControl* aDOMCameraControl, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError, uint64_t aWindowId)
175 : mCameraControl(aCameraControl)
176 , mDOMCameraControl(aDOMCameraControl)
177 , mOnSuccessCb(new nsMainThreadPtrHolder<nsICameraGetCameraCallback>(onSuccess))
178 , mOnErrorCb(new nsMainThreadPtrHolder<nsICameraErrorCallback>(onError))
179 , mWindowId(aWindowId)
181 DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
184 ~InitGonkCameraControl()
186 DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
189 NS_IMETHOD Run()
191 nsresult rv = mCameraControl->Init();
192 return mDOMCameraControl->Result(rv, mOnSuccessCb, mOnErrorCb, mWindowId);
195 nsRefPtr<nsGonkCameraControl> mCameraControl;
196 // Raw pointer to DOM-facing camera control--it must NS_ADDREF itself for us
197 nsDOMCameraControl* mDOMCameraControl;
198 nsMainThreadPtrHandle<nsICameraGetCameraCallback> mOnSuccessCb;
199 nsMainThreadPtrHandle<nsICameraErrorCallback> mOnErrorCb;
200 uint64_t mWindowId;
203 // Construct nsGonkCameraControl on the main thread.
204 nsGonkCameraControl::nsGonkCameraControl(uint32_t aCameraId, nsIThread* aCameraThread, nsDOMCameraControl* aDOMCameraControl, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError, uint64_t aWindowId)
205 : CameraControlImpl(aCameraId, aCameraThread, aWindowId)
206 , mExposureCompensationMin(0.0)
207 , mExposureCompensationStep(0.0)
208 , mDeferConfigUpdate(false)
209 , mWidth(0)
210 , mHeight(0)
211 , mLastPictureWidth(0)
212 , mLastPictureHeight(0)
213 , mLastThumbnailWidth(0)
214 , mLastThumbnailHeight(0)
215 #if !FORCE_PREVIEW_FORMAT_YUV420SP
216 , mFormat(PREVIEW_FORMAT_UNKNOWN)
217 #else
218 , mFormat(PREVIEW_FORMAT_YUV420SP)
219 #endif
220 , mFps(30)
221 , mDiscardedFrameCount(0)
222 , mMediaProfiles(nullptr)
223 , mRecorder(nullptr)
224 , mProfileManager(nullptr)
225 , mRecorderProfile(nullptr)
226 , mVideoFile(nullptr)
228 // Constructor runs on the main thread...
229 DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
230 mRwLock = PR_NewRWLock(PR_RWLOCK_RANK_NONE, "GonkCameraControl.Parameters.Lock");
233 void nsGonkCameraControl::DispatchInit(nsDOMCameraControl* aDOMCameraControl, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError, uint64_t aWindowId)
235 // ...but initialization is carried out on the camera thread.
236 nsCOMPtr<nsIRunnable> init = new InitGonkCameraControl(this, aDOMCameraControl, onSuccess, onError, aWindowId);
237 mCameraThread->Dispatch(init, NS_DISPATCH_NORMAL);
240 nsresult
241 nsGonkCameraControl::Init()
243 mCameraHw = GonkCameraHardware::Connect(this, mCameraId);
244 if (!mCameraHw.get()) {
245 DOM_CAMERA_LOGE("Failed to connect to camera %d (this=%p)\n", mCameraId, this);
246 return NS_ERROR_FAILURE;
249 DOM_CAMERA_LOGI("Initializing camera %d (this=%p, mCameraHw=%p)\n", mCameraId, this, mCameraHw.get());
251 // Initialize our camera configuration database.
252 PullParametersImpl();
254 // Try to set preferred image format and frame rate
255 #if !FORCE_PREVIEW_FORMAT_YUV420SP
256 DOM_CAMERA_LOGI("Camera preview formats: %s\n", mParams.get(mParams.KEY_SUPPORTED_PREVIEW_FORMATS));
257 const char* const PREVIEW_FORMAT = "yuv420p";
258 const char* const BAD_PREVIEW_FORMAT = "yuv420sp";
259 mParams.setPreviewFormat(PREVIEW_FORMAT);
260 mParams.setPreviewFrameRate(mFps);
261 #else
262 mParams.setPreviewFormat("yuv420sp");
263 mParams.setPreviewFrameRate(mFps);
264 #endif
265 PushParametersImpl();
267 // Check that our settings stuck
268 PullParametersImpl();
269 #if !FORCE_PREVIEW_FORMAT_YUV420SP
270 const char* format = mParams.getPreviewFormat();
271 if (strcmp(format, PREVIEW_FORMAT) == 0) {
272 mFormat = PREVIEW_FORMAT_YUV420P; /* \o/ */
273 } else if (strcmp(format, BAD_PREVIEW_FORMAT) == 0) {
274 mFormat = PREVIEW_FORMAT_YUV420SP;
275 DOM_CAMERA_LOGA("Camera ignored our request for '%s' preview, will have to convert (from %d)\n", PREVIEW_FORMAT, mFormat);
276 } else {
277 mFormat = PREVIEW_FORMAT_UNKNOWN;
278 DOM_CAMERA_LOGE("Camera ignored our request for '%s' preview, returned UNSUPPORTED format '%s'\n", PREVIEW_FORMAT, format);
280 #endif
282 // Check the frame rate and log if the camera ignored our setting
283 uint32_t fps = mParams.getPreviewFrameRate();
284 if (fps != mFps) {
285 DOM_CAMERA_LOGA("We asked for %d fps but camera returned %d fps, using that", mFps, fps);
286 mFps = fps;
289 // Grab any other settings we'll need later.
290 mExposureCompensationMin = mParams.getFloat(mParams.KEY_MIN_EXPOSURE_COMPENSATION);
291 mExposureCompensationStep = mParams.getFloat(mParams.KEY_EXPOSURE_COMPENSATION_STEP);
292 mMaxMeteringAreas = mParams.getInt(mParams.KEY_MAX_NUM_METERING_AREAS);
293 mMaxFocusAreas = mParams.getInt(mParams.KEY_MAX_NUM_FOCUS_AREAS);
294 mLastThumbnailWidth = mParams.getInt(mParams.KEY_JPEG_THUMBNAIL_WIDTH);
295 mLastThumbnailHeight = mParams.getInt(mParams.KEY_JPEG_THUMBNAIL_HEIGHT);
297 int w;
298 int h;
299 mParams.getPictureSize(&w, &h);
300 MOZ_ASSERT(w > 0 && h > 0); // make sure the driver returns sane values
301 mLastPictureWidth = static_cast<uint32_t>(w);
302 mLastPictureHeight = static_cast<uint32_t>(h);
304 DOM_CAMERA_LOGI(" - minimum exposure compensation: %f\n", mExposureCompensationMin);
305 DOM_CAMERA_LOGI(" - exposure compensation step: %f\n", mExposureCompensationStep);
306 DOM_CAMERA_LOGI(" - maximum metering areas: %d\n", mMaxMeteringAreas);
307 DOM_CAMERA_LOGI(" - maximum focus areas: %d\n", mMaxFocusAreas);
308 DOM_CAMERA_LOGI(" - default picture size: %u x %u\n", mLastPictureWidth, mLastPictureHeight);
309 DOM_CAMERA_LOGI(" - default thumbnail size: %u x %u\n", mLastThumbnailWidth, mLastThumbnailHeight);
311 return NS_OK;
314 nsGonkCameraControl::~nsGonkCameraControl()
316 DOM_CAMERA_LOGT("%s:%d : this=%p, mCameraHw = %p\n", __func__, __LINE__, this, mCameraHw.get());
318 ReleaseHardwareImpl(nullptr);
319 if (mRwLock) {
320 PRRWLock* lock = mRwLock;
321 mRwLock = nullptr;
322 PR_DestroyRWLock(lock);
325 DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
328 class RwAutoLockRead
330 public:
331 RwAutoLockRead(PRRWLock* aRwLock)
332 : mRwLock(aRwLock)
334 PR_RWLock_Rlock(mRwLock);
337 ~RwAutoLockRead()
339 PR_RWLock_Unlock(mRwLock);
342 protected:
343 PRRWLock* mRwLock;
346 class RwAutoLockWrite
348 public:
349 RwAutoLockWrite(PRRWLock* aRwLock)
350 : mRwLock(aRwLock)
352 PR_RWLock_Wlock(mRwLock);
355 ~RwAutoLockWrite()
357 PR_RWLock_Unlock(mRwLock);
360 protected:
361 PRRWLock* mRwLock;
364 const char*
365 nsGonkCameraControl::GetParameter(const char* aKey)
367 RwAutoLockRead lock(mRwLock);
368 return mParams.get(aKey);
371 const char*
372 nsGonkCameraControl::GetParameterConstChar(uint32_t aKey)
374 const char* key = getKeyText(aKey);
375 if (!key) {
376 return nullptr;
379 RwAutoLockRead lock(mRwLock);
380 return mParams.get(key);
383 double
384 nsGonkCameraControl::GetParameterDouble(uint32_t aKey)
386 double val;
387 int index = 0;
388 double focusDistance[3];
389 const char* s;
391 const char* key = getKeyText(aKey);
392 if (!key) {
393 // return 1x when zooming is not supported
394 return aKey == CAMERA_PARAM_ZOOM ? 1.0 : 0.0;
397 RwAutoLockRead lock(mRwLock);
398 switch (aKey) {
399 case CAMERA_PARAM_ZOOM:
400 val = mParams.getInt(key);
401 return val / 100;
404 * The gonk camera parameters API only exposes one focus distance property
405 * that contains "Near,Optimum,Far" distances, in metres, where 'Far' may
406 * be 'Infinity'.
408 case CAMERA_PARAM_FOCUSDISTANCEFAR:
409 ++index;
410 // intentional fallthrough
412 case CAMERA_PARAM_FOCUSDISTANCEOPTIMUM:
413 ++index;
414 // intentional fallthrough
416 case CAMERA_PARAM_FOCUSDISTANCENEAR:
417 s = mParams.get(key);
418 if (sscanf(s, "%lf,%lf,%lf", &focusDistance[0], &focusDistance[1], &focusDistance[2]) == 3) {
419 return focusDistance[index];
421 return 0.0;
423 case CAMERA_PARAM_EXPOSURECOMPENSATION:
424 index = mParams.getInt(key);
425 if (!index) {
426 // NaN indicates automatic exposure compensation
427 return NAN;
429 val = (index - 1) * mExposureCompensationStep + mExposureCompensationMin;
430 DOM_CAMERA_LOGI("index = %d --> compensation = %f\n", index, val);
431 return val;
433 default:
434 return mParams.getFloat(key);
438 int32_t
439 nsGonkCameraControl::GetParameterInt32(uint32_t aKey)
441 if (aKey == CAMERA_PARAM_SENSORANGLE) {
442 if (!mCameraHw.get()) {
443 return 0;
445 return mCameraHw->GetSensorOrientation();
448 const char* key = getKeyText(aKey);
449 if (!key) {
450 return 0;
453 RwAutoLockRead lock(mRwLock);
454 return mParams.getInt(key);
457 void
458 nsGonkCameraControl::GetParameter(uint32_t aKey,
459 nsTArray<idl::CameraRegion>& aRegions)
461 aRegions.Clear();
463 const char* key = getKeyText(aKey);
464 if (!key) {
465 return;
468 RwAutoLockRead lock(mRwLock);
470 const char* value = mParams.get(key);
471 DOM_CAMERA_LOGI("key='%s' --> value='%s'\n", key, value);
472 if (!value) {
473 return;
476 const char* p = value;
477 uint32_t count = 1;
479 // count the number of regions in the string
480 while ((p = strstr(p, "),("))) {
481 ++count;
482 p += 3;
485 aRegions.SetCapacity(count);
486 idl::CameraRegion* r;
488 // parse all of the region sets
489 uint32_t i;
490 for (i = 0, p = value; p && i < count; ++i, p = strchr(p + 1, '(')) {
491 r = aRegions.AppendElement();
492 if (sscanf(p, "(%d,%d,%d,%d,%u)", &r->top, &r->left, &r->bottom, &r->right, &r->weight) != 5) {
493 DOM_CAMERA_LOGE("%s:%d : region tuple has bad format: '%s'\n", __func__, __LINE__, p);
494 aRegions.Clear();
495 return;
499 return;
502 void
503 nsGonkCameraControl::GetParameter(uint32_t aKey,
504 nsTArray<idl::CameraSize>& aSizes)
506 const char* key = getKeyText(aKey);
507 if (!key) {
508 return;
511 RwAutoLockRead lock(mRwLock);
513 const char* value = mParams.get(key);
514 DOM_CAMERA_LOGI("key='%s' --> value='%s'\n", key, value);
515 if (!value) {
516 return;
519 const char* p = value;
520 idl::CameraSize* s;
522 // The 'value' string is in the format "w1xh1,w2xh2,w3xh3,..."
523 while (p) {
524 s = aSizes.AppendElement();
525 if (sscanf(p, "%dx%d", &s->width, &s->height) != 2) {
526 DOM_CAMERA_LOGE("%s:%d : size tuple has bad format: '%s'\n", __func__, __LINE__, p);
527 aSizes.Clear();
528 return;
530 // Look for the next record...
531 p = strchr(p, ',');
532 if (p) {
533 // ...skip the comma too
534 ++p;
538 return;
541 void
542 nsGonkCameraControl::GetParameter(uint32_t aKey, idl::CameraSize& aSize)
544 if (aKey == CAMERA_PARAM_THUMBNAILSIZE) {
545 // This is a special case--for some reason the thumbnail size
546 // is accessed as two separate values instead of a tuple.
547 RwAutoLockRead lock(mRwLock);
549 aSize.width = mParams.getInt(CameraParameters::KEY_JPEG_THUMBNAIL_WIDTH);
550 aSize.height = mParams.getInt(CameraParameters::KEY_JPEG_THUMBNAIL_HEIGHT);
551 DOM_CAMERA_LOGI("thumbnail size --> value='%ux%u'\n", aSize.width, aSize.height);
552 return;
555 const char* key = getKeyText(aKey);
556 if (!key) {
557 return;
560 RwAutoLockRead lock(mRwLock);
562 const char* value = mParams.get(key);
563 DOM_CAMERA_LOGI("key='%s' --> value='%s'\n", key, value);
564 if (!value) {
565 return;
568 if (sscanf(value, "%ux%u", &aSize.width, &aSize.height) != 2) {
569 DOM_CAMERA_LOGE("%s:%d : size tuple has bad format: '%s'\n", __func__, __LINE__, value);
570 aSize.width = 0;
571 aSize.height = 0;
575 nsresult
576 nsGonkCameraControl::PushParameters()
578 if (mDeferConfigUpdate) {
579 DOM_CAMERA_LOGT("%s:%d - defering config update\n", __func__, __LINE__);
580 return NS_OK;
584 * If we're already on the camera thread, call PushParametersImpl()
585 * directly, so that it executes synchronously. Some callers
586 * require this so that changes take effect immediately before
587 * we can proceed.
589 if (NS_IsMainThread()) {
590 DOM_CAMERA_LOGT("%s:%d - dispatching to camera thread\n", __func__, __LINE__);
591 nsCOMPtr<nsIRunnable> pushParametersTask = NS_NewRunnableMethod(this, &nsGonkCameraControl::PushParametersImpl);
592 return mCameraThread->Dispatch(pushParametersTask, NS_DISPATCH_NORMAL);
595 DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
596 return PushParametersImpl();
599 void
600 nsGonkCameraControl::SetParameter(const char* aKey, const char* aValue)
603 RwAutoLockWrite lock(mRwLock);
604 mParams.set(aKey, aValue);
606 PushParameters();
609 void
610 nsGonkCameraControl::SetParameter(uint32_t aKey, const char* aValue)
612 const char* key = getKeyText(aKey);
613 if (!key) {
614 return;
618 RwAutoLockWrite lock(mRwLock);
619 mParams.set(key, aValue);
621 PushParameters();
624 void
625 nsGonkCameraControl::SetParameter(uint32_t aKey, double aValue)
627 uint32_t index;
629 const char* key = getKeyText(aKey);
630 if (!key) {
631 return;
635 RwAutoLockWrite lock(mRwLock);
636 if (aKey == CAMERA_PARAM_EXPOSURECOMPENSATION) {
638 * Convert from real value to a Gonk index, round
639 * to the nearest step; index is 1-based.
641 index = (aValue - mExposureCompensationMin + mExposureCompensationStep / 2) / mExposureCompensationStep + 1;
642 DOM_CAMERA_LOGI("compensation = %f --> index = %d\n", aValue, index);
643 mParams.set(key, index);
644 } else {
645 mParams.setFloat(key, aValue);
648 PushParameters();
651 void
652 nsGonkCameraControl::SetParameter(uint32_t aKey,
653 const nsTArray<idl::CameraRegion>& aRegions)
655 const char* key = getKeyText(aKey);
656 if (!key) {
657 return;
660 uint32_t length = aRegions.Length();
662 if (!length) {
663 // This tells the camera driver to revert to automatic regioning.
665 RwAutoLockWrite lock(mRwLock);
666 mParams.set(key, "(0,0,0,0,0)");
668 PushParameters();
669 return;
672 nsCString s;
674 for (uint32_t i = 0; i < length; ++i) {
675 const idl::CameraRegion* r = &aRegions[i];
676 s.AppendPrintf("(%d,%d,%d,%d,%d),", r->top, r->left, r->bottom, r->right, r->weight);
679 // remove the trailing comma
680 s.Trim(",", false, true, true);
682 DOM_CAMERA_LOGI("camera region string '%s'\n", s.get());
685 RwAutoLockWrite lock(mRwLock);
686 mParams.set(key, s.get());
688 PushParameters();
691 void
692 nsGonkCameraControl::SetParameter(uint32_t aKey, int aValue)
694 const char* key = getKeyText(aKey);
695 if (!key) {
696 return;
699 RwAutoLockWrite lock(mRwLock);
700 mParams.set(key, aValue);
702 PushParameters();
705 void
706 nsGonkCameraControl::SetParameter(uint32_t aKey, const idl::CameraSize& aSize)
708 switch (aKey) {
709 case CAMERA_PARAM_PICTURESIZE:
710 DOM_CAMERA_LOGI("setting picture size to %ux%u\n", aSize.width, aSize.height);
711 SetPictureSize(aSize.width, aSize.height);
712 break;
714 case CAMERA_PARAM_THUMBNAILSIZE:
715 DOM_CAMERA_LOGI("setting thumbnail size to %ux%u\n", aSize.width, aSize.height);
716 SetThumbnailSize(aSize.width, aSize.height);
717 break;
719 default:
721 const char* key = getKeyText(aKey);
722 if (!key) {
723 return;
726 nsCString s;
727 s.AppendPrintf("%ux%u", aSize.width, aSize.height);
728 DOM_CAMERA_LOGI("setting '%s' to %s\n", key, s.get());
730 RwAutoLockWrite lock(mRwLock);
731 mParams.set(key, s.get());
733 break;
735 PushParameters();
738 nsresult
739 nsGonkCameraControl::GetPreviewStreamImpl(GetPreviewStreamTask* aGetPreviewStream)
741 // stop any currently running preview
742 StopPreviewInternal(true /* forced */);
744 // remove any existing recorder profile
745 mRecorderProfile = nullptr;
747 SetPreviewSize(aGetPreviewStream->mSize.width, aGetPreviewStream->mSize.height);
748 DOM_CAMERA_LOGI("picture preview: wanted %d x %d, got %d x %d (%d fps, format %d)\n", aGetPreviewStream->mSize.width, aGetPreviewStream->mSize.height, mWidth, mHeight, mFps, mFormat);
750 nsMainThreadPtrHandle<nsICameraPreviewStreamCallback> onSuccess = aGetPreviewStream->mOnSuccessCb;
751 nsCOMPtr<GetPreviewStreamResult> getPreviewStreamResult = new GetPreviewStreamResult(this, mWidth, mHeight, mFps, onSuccess, mWindowId);
752 return NS_DispatchToMainThread(getPreviewStreamResult);
755 nsresult
756 nsGonkCameraControl::StartPreviewImpl(StartPreviewTask* aStartPreview)
759 * If 'aStartPreview->mDOMPreview' is null, we are just restarting
760 * the preview after taking a picture. No need to monkey with the
761 * currently set DOM-facing preview object.
763 if (aStartPreview->mDOMPreview) {
764 StopPreviewInternal(true /* forced */);
765 mDOMPreview = aStartPreview->mDOMPreview;
766 } else if (!mDOMPreview) {
767 return NS_ERROR_INVALID_ARG;
770 DOM_CAMERA_LOGI("%s: starting preview (mDOMPreview=%p)\n", __func__, mDOMPreview);
772 RETURN_IF_NO_CAMERA_HW();
773 if (mCameraHw->StartPreview() != OK) {
774 DOM_CAMERA_LOGE("%s: failed to start preview\n", __func__);
775 return NS_ERROR_FAILURE;
778 if (aStartPreview->mDOMPreview) {
779 mDOMPreview->Started();
782 OnPreviewStateChange(PREVIEW_STARTED);
783 return NS_OK;
786 nsresult
787 nsGonkCameraControl::StopPreviewInternal(bool aForced)
789 DOM_CAMERA_LOGI("%s: stopping preview (mDOMPreview=%p)\n", __func__, mDOMPreview);
791 // StopPreview() is a synchronous call--it doesn't return
792 // until the camera preview thread exits.
793 if (mDOMPreview) {
794 if (mCameraHw.get()) {
795 mCameraHw->StopPreview();
797 mDOMPreview->Stopped(aForced);
798 mDOMPreview = nullptr;
801 OnPreviewStateChange(PREVIEW_STOPPED);
802 return NS_OK;
805 nsresult
806 nsGonkCameraControl::StopPreviewImpl(StopPreviewTask* aStopPreview)
808 return StopPreviewInternal();
811 nsresult
812 nsGonkCameraControl::AutoFocusImpl(AutoFocusTask* aAutoFocus)
814 if (aAutoFocus->mCancel) {
815 if (mCameraHw.get()) {
816 mCameraHw->CancelAutoFocus();
820 mAutoFocusOnSuccessCb = aAutoFocus->mOnSuccessCb;
821 mAutoFocusOnErrorCb = aAutoFocus->mOnErrorCb;
823 RETURN_IF_NO_CAMERA_HW();
824 if (mCameraHw->AutoFocus() != OK) {
825 return NS_ERROR_FAILURE;
827 return NS_OK;
830 void
831 nsGonkCameraControl::SetThumbnailSize(uint32_t aWidth, uint32_t aHeight)
834 * We keep a copy of the specified size so that if the picture size
835 * changes, we can choose a new thumbnail size close to what was asked for
836 * last time.
838 mLastThumbnailWidth = aWidth;
839 mLastThumbnailHeight = aHeight;
842 * If either of width or height is zero, set the other to zero as well.
843 * This should disable inclusion of a thumbnail in the final picture.
845 if (!aWidth || !aHeight) {
846 DOM_CAMERA_LOGW("Requested thumbnail size %ux%u, disabling thumbnail\n", aWidth, aHeight);
847 RwAutoLockWrite write(mRwLock);
848 mParams.set(CameraParameters::KEY_JPEG_THUMBNAIL_WIDTH, 0);
849 mParams.set(CameraParameters::KEY_JPEG_THUMBNAIL_HEIGHT, 0);
850 return;
854 * Choose the supported thumbnail size that is closest to the specified size.
855 * Some drivers will fail to take a picture if the thumbnail does not have
856 * the same aspect ratio as the set picture size, so we need to enforce that
857 * too.
859 int smallestDelta = INT_MAX;
860 uint32_t smallestDeltaIndex = UINT32_MAX;
861 int targetArea = aWidth * aHeight;
863 nsAutoTArray<idl::CameraSize, 8> supportedSizes;
864 GetParameter(CAMERA_PARAM_SUPPORTED_JPEG_THUMBNAIL_SIZES, supportedSizes);
866 for (uint32_t i = 0; i < supportedSizes.Length(); ++i) {
867 int area = supportedSizes[i].width * supportedSizes[i].height;
868 int delta = abs(area - targetArea);
870 if (area != 0
871 && delta < smallestDelta
872 && supportedSizes[i].width * mLastPictureHeight / supportedSizes[i].height == mLastPictureWidth
874 smallestDelta = delta;
875 smallestDeltaIndex = i;
879 if (smallestDeltaIndex == UINT32_MAX) {
880 DOM_CAMERA_LOGW("Unable to find a thumbnail size close to %ux%u\n", aWidth, aHeight);
881 return;
884 uint32_t w = supportedSizes[smallestDeltaIndex].width;
885 uint32_t h = supportedSizes[smallestDeltaIndex].height;
886 DOM_CAMERA_LOGI("Requested thumbnail size %ux%u --> using supported size %ux%u\n", aWidth, aHeight, w, h);
887 if (w > INT32_MAX || h > INT32_MAX) {
888 DOM_CAMERA_LOGE("Supported thumbnail size is too big, no change\n");
889 return;
892 RwAutoLockWrite write(mRwLock);
893 mParams.set(CameraParameters::KEY_JPEG_THUMBNAIL_WIDTH, static_cast<int>(w));
894 mParams.set(CameraParameters::KEY_JPEG_THUMBNAIL_HEIGHT, static_cast<int>(h));
897 void
898 nsGonkCameraControl::UpdateThumbnailSize()
900 SetThumbnailSize(mLastThumbnailWidth, mLastThumbnailHeight);
903 void
904 nsGonkCameraControl::SetPictureSize(uint32_t aWidth, uint32_t aHeight)
907 * Some drivers are less friendly about getting one of these set to zero,
908 * so if either is not specified, ignore both and go with current or
909 * default settings.
911 if (!aWidth || !aHeight) {
912 DOM_CAMERA_LOGW("Ignoring requested picture size of %ux%u\n", aWidth, aHeight);
913 return;
916 if (aWidth == mLastPictureWidth && aHeight == mLastPictureHeight) {
917 DOM_CAMERA_LOGI("Requested picture size %ux%u unchanged\n", aWidth, aHeight);
918 return;
922 * Choose the supported picture size that is closest in area to the
923 * specified size. Some drivers will fail to take a picture if the
924 * thumbnail size is not the same aspect ratio, so we update that
925 * as well to a size closest to the last user-requested one.
927 int smallestDelta = INT_MAX;
928 uint32_t smallestDeltaIndex = UINT32_MAX;
929 int targetArea = aWidth * aHeight;
931 nsAutoTArray<idl::CameraSize, 8> supportedSizes;
932 GetParameter(CAMERA_PARAM_SUPPORTED_PICTURESIZES, supportedSizes);
934 for (uint32_t i = 0; i < supportedSizes.Length(); ++i) {
935 int area = supportedSizes[i].width * supportedSizes[i].height;
936 int delta = abs(area - targetArea);
938 if (area != 0 && delta < smallestDelta) {
939 smallestDelta = delta;
940 smallestDeltaIndex = i;
944 if (smallestDeltaIndex == UINT32_MAX) {
945 DOM_CAMERA_LOGW("Unable to find a picture size close to %ux%u\n", aWidth, aHeight);
946 return;
949 uint32_t w = supportedSizes[smallestDeltaIndex].width;
950 uint32_t h = supportedSizes[smallestDeltaIndex].height;
951 DOM_CAMERA_LOGI("Requested picture size %ux%u --> using supported size %ux%u\n", aWidth, aHeight, w, h);
952 if (w > INT32_MAX || h > INT32_MAX) {
953 DOM_CAMERA_LOGE("Supported picture size is too big, no change\n");
954 return;
957 mLastPictureWidth = w;
958 mLastPictureHeight = h;
961 // We must release the write-lock before updating the thumbnail size
962 RwAutoLockWrite write(mRwLock);
963 mParams.setPictureSize(static_cast<int>(w), static_cast<int>(h));
966 // Finally, update the thumbnail size
967 UpdateThumbnailSize();
970 nsresult
971 nsGonkCameraControl::TakePictureImpl(TakePictureTask* aTakePicture)
973 if (aTakePicture->mCancel) {
974 if (mCameraHw.get()) {
975 mCameraHw->CancelTakePicture();
979 mTakePictureOnSuccessCb = aTakePicture->mOnSuccessCb;
980 mTakePictureOnErrorCb = aTakePicture->mOnErrorCb;
982 RETURN_IF_NO_CAMERA_HW();
984 // batch-update camera configuration
985 mDeferConfigUpdate = true;
987 SetPictureSize(aTakePicture->mSize.width, aTakePicture->mSize.height);
989 // Picture format -- need to keep it for the callback.
990 mFileFormat = aTakePicture->mFileFormat;
991 SetParameter(CameraParameters::KEY_PICTURE_FORMAT, NS_ConvertUTF16toUTF8(mFileFormat).get());
993 // Convert 'rotation' to a positive value from 0..270 degrees, in steps of 90.
994 uint32_t r = static_cast<uint32_t>(aTakePicture->mRotation);
995 r += mCameraHw->GetSensorOrientation(GonkCameraHardware::OFFSET_SENSOR_ORIENTATION);
996 r %= 360;
997 r += 45;
998 r /= 90;
999 r *= 90;
1000 DOM_CAMERA_LOGI("setting picture rotation to %d degrees (mapped from %d)\n", r, aTakePicture->mRotation);
1001 SetParameter(CameraParameters::KEY_ROTATION, nsPrintfCString("%u", r).get());
1003 // Add any specified positional information -- don't care if these fail.
1004 if (!isnan(aTakePicture->mPosition.latitude)) {
1005 DOM_CAMERA_LOGI("setting picture latitude to %lf\n", aTakePicture->mPosition.latitude);
1006 SetParameter(CameraParameters::KEY_GPS_LATITUDE, nsPrintfCString("%lf", aTakePicture->mPosition.latitude).get());
1008 if (!isnan(aTakePicture->mPosition.longitude)) {
1009 DOM_CAMERA_LOGI("setting picture longitude to %lf\n", aTakePicture->mPosition.longitude);
1010 SetParameter(CameraParameters::KEY_GPS_LONGITUDE, nsPrintfCString("%lf", aTakePicture->mPosition.longitude).get());
1012 if (!isnan(aTakePicture->mPosition.altitude)) {
1013 DOM_CAMERA_LOGI("setting picture altitude to %lf\n", aTakePicture->mPosition.altitude);
1014 SetParameter(CameraParameters::KEY_GPS_ALTITUDE, nsPrintfCString("%lf", aTakePicture->mPosition.altitude).get());
1016 if (!isnan(aTakePicture->mPosition.timestamp)) {
1017 DOM_CAMERA_LOGI("setting picture timestamp to %lf\n", aTakePicture->mPosition.timestamp);
1018 SetParameter(CameraParameters::KEY_GPS_TIMESTAMP, nsPrintfCString("%lf", aTakePicture->mPosition.timestamp).get());
1021 // Add the non-GPS timestamp. The EXIF date/time field is formatted as
1022 // "YYYY:MM:DD HH:MM:SS", without room for a time-zone; as such, the time
1023 // is meant to be stored as a local time. Since we are given seconds from
1024 // Epoch GMT, we use localtime_r() to handle the conversion.
1025 time_t time = aTakePicture->mDateTime;
1026 if (time != aTakePicture->mDateTime) {
1027 DOM_CAMERA_LOGE("picture date/time '%llu' is too far in the future\n", aTakePicture->mDateTime);
1028 } else {
1029 struct tm t;
1030 if (localtime_r(&time, &t)) {
1031 char dateTime[20];
1032 if (strftime(dateTime, sizeof(dateTime), "%Y:%m:%d %T", &t)) {
1033 DOM_CAMERA_LOGI("setting picture date/time to %s\n", dateTime);
1034 // Not every platform defines a CameraParameters::KEY_EXIF_DATETIME;
1035 // for those who don't, we use the raw string key, and if the platform
1036 // doesn't support it, it will be ignored.
1038 // See bug 832494.
1039 SetParameter("exif-datetime", dateTime);
1040 } else {
1041 DOM_CAMERA_LOGE("picture date/time couldn't be converted to string\n");
1043 } else {
1044 DOM_CAMERA_LOGE("picture date/time couldn't be converted to local time: (%d) %s\n", errno, strerror(errno));
1048 mDeferConfigUpdate = false;
1049 PushParameters();
1051 if (mCameraHw->TakePicture() != OK) {
1052 return NS_ERROR_FAILURE;
1055 // In Gonk, taking a picture implicitly kills the preview stream,
1056 // so we need to reflect that here.
1057 OnPreviewStateChange(PREVIEW_STOPPED);
1058 return NS_OK;
1061 nsresult
1062 nsGonkCameraControl::PushParametersImpl()
1064 DOM_CAMERA_LOGI("Pushing camera parameters\n");
1065 RETURN_IF_NO_CAMERA_HW();
1067 RwAutoLockRead lock(mRwLock);
1068 if (mCameraHw->PushParameters(mParams) != OK) {
1069 return NS_ERROR_FAILURE;
1072 return NS_OK;
1075 nsresult
1076 nsGonkCameraControl::PullParametersImpl()
1078 DOM_CAMERA_LOGI("Pulling camera parameters\n");
1079 RETURN_IF_NO_CAMERA_HW();
1081 RwAutoLockWrite lock(mRwLock);
1082 mCameraHw->PullParameters(mParams);
1083 return NS_OK;
1086 nsresult
1087 nsGonkCameraControl::StartRecordingImpl(StartRecordingTask* aStartRecording)
1089 NS_ENSURE_TRUE(mRecorderProfile, NS_ERROR_NOT_INITIALIZED);
1090 NS_ENSURE_FALSE(mRecorder, NS_ERROR_FAILURE);
1093 * Get the base path from device storage and append the app-specified
1094 * filename to it. The filename may include a relative subpath
1095 * (e.g.) "DCIM/IMG_0001.jpg".
1097 * The camera app needs to provide the file extension '.3gp' for now.
1098 * See bug 795202.
1100 nsCOMPtr<nsIFile> filename = aStartRecording->mFolder;
1101 filename->AppendRelativePath(aStartRecording->mFilename);
1103 nsString fullpath;
1104 filename->GetPath(fullpath);
1106 nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID);
1107 NS_ENSURE_TRUE(vs, NS_ERROR_FAILURE);
1109 nsCOMPtr<nsIVolume> vol;
1110 nsresult rv = vs->GetVolumeByPath(fullpath, getter_AddRefs(vol));
1111 NS_ENSURE_SUCCESS(rv, NS_ERROR_INVALID_ARG);
1113 nsString volName;
1114 vol->GetName(volName);
1116 mVideoFile = new DeviceStorageFile(NS_LITERAL_STRING("videos"),
1117 volName,
1118 aStartRecording->mFilename);
1120 nsAutoCString nativeFilename;
1121 filename->GetNativePath(nativeFilename);
1122 DOM_CAMERA_LOGI("Video filename is '%s'\n", nativeFilename.get());
1124 if (!mVideoFile->IsSafePath()) {
1125 DOM_CAMERA_LOGE("Invalid video file name\n");
1126 return NS_ERROR_INVALID_ARG;
1129 ScopedClose fd(open(nativeFilename.get(), O_RDWR | O_CREAT, 0644));
1130 if (fd < 0) {
1131 DOM_CAMERA_LOGE("Couldn't create file '%s': (%d) %s\n", nativeFilename.get(), errno, strerror(errno));
1132 return NS_ERROR_FAILURE;
1135 rv = SetupRecording(fd, aStartRecording->mOptions.rotation, aStartRecording->mOptions.maxFileSizeBytes, aStartRecording->mOptions.maxVideoLengthMs);
1136 NS_ENSURE_SUCCESS(rv, rv);
1138 if (mRecorder->start() != OK) {
1139 DOM_CAMERA_LOGE("mRecorder->start() failed\n");
1140 // important: we MUST destroy the recorder if start() fails!
1141 mRecorder = nullptr;
1142 return NS_ERROR_FAILURE;
1145 return NS_OK;
1148 class RecordingComplete : public nsRunnable
1150 public:
1151 RecordingComplete(DeviceStorageFile* aFile)
1152 : mFile(aFile)
1155 ~RecordingComplete() { }
1157 NS_IMETHOD Run()
1159 MOZ_ASSERT(NS_IsMainThread());
1161 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
1162 obs->NotifyObservers(mFile, "file-watcher-notify", MOZ_UTF16("modified"));
1163 return NS_OK;
1166 private:
1167 nsRefPtr<DeviceStorageFile> mFile;
1170 nsresult
1171 nsGonkCameraControl::StopRecordingImpl(StopRecordingTask* aStopRecording)
1173 // nothing to do if we have no mRecorder
1174 NS_ENSURE_TRUE(mRecorder, NS_OK);
1176 mRecorder->stop();
1177 mRecorder = nullptr;
1179 // notify DeviceStorage that the new video file is closed and ready
1180 nsCOMPtr<nsIRunnable> recordingComplete = new RecordingComplete(mVideoFile);
1181 return NS_DispatchToMainThread(recordingComplete, NS_DISPATCH_NORMAL);
1184 void
1185 nsGonkCameraControl::AutoFocusComplete(bool aSuccess)
1188 * Auto focusing can change some of the camera's parameters, so
1189 * we need to pull a new set before sending the result to the
1190 * main thread.
1192 PullParametersImpl();
1195 * If we make it here, regardless of the value of 'aSuccess', we
1196 * consider the autofocus _process_ to have succeeded. It is up
1197 * to the onSuccess callback to determine how to handle the case
1198 * where the camera wasn't actually able to acquire focus.
1200 nsCOMPtr<nsIRunnable> autoFocusResult = new AutoFocusResult(aSuccess, mAutoFocusOnSuccessCb, mWindowId);
1202 * Remember to set these to null so that we don't hold any extra
1203 * references to our document's window.
1205 mAutoFocusOnSuccessCb = nullptr;
1206 mAutoFocusOnErrorCb = nullptr;
1207 nsresult rv = NS_DispatchToMainThread(autoFocusResult);
1208 if (NS_FAILED(rv)) {
1209 NS_WARNING("Failed to dispatch autoFocus() onSuccess callback to main thread!");
1213 void
1214 nsGonkCameraControl::TakePictureComplete(uint8_t* aData, uint32_t aLength)
1216 uint8_t* data = new uint8_t[aLength];
1218 memcpy(data, aData, aLength);
1220 // TODO: see bug 779144.
1221 nsCOMPtr<nsIRunnable> takePictureResult = new TakePictureResult(data, aLength, NS_LITERAL_STRING("image/jpeg"), mTakePictureOnSuccessCb, mWindowId);
1223 * Remember to set these to null so that we don't hold any extra
1224 * references to our document's window.
1226 mTakePictureOnSuccessCb = nullptr;
1227 mTakePictureOnErrorCb = nullptr;
1228 nsresult rv = NS_DispatchToMainThread(takePictureResult);
1229 if (NS_FAILED(rv)) {
1230 NS_WARNING("Failed to dispatch takePicture() onSuccess callback to main thread!");
1234 void
1235 nsGonkCameraControl::TakePictureError()
1237 nsCOMPtr<nsIRunnable> takePictureError = new CameraErrorResult(mTakePictureOnErrorCb, NS_LITERAL_STRING("FAILURE"), mWindowId);
1238 mTakePictureOnSuccessCb = nullptr;
1239 mTakePictureOnErrorCb = nullptr;
1240 nsresult rv = NS_DispatchToMainThread(takePictureError);
1241 if (NS_FAILED(rv)) {
1242 NS_WARNING("Failed to dispatch takePicture() onError callback to main thread!");
1246 void
1247 nsGonkCameraControl::SetPreviewSize(uint32_t aWidth, uint32_t aHeight)
1249 android::Vector<Size> previewSizes;
1250 uint32_t bestWidth = aWidth;
1251 uint32_t bestHeight = aHeight;
1252 uint32_t minSizeDelta = UINT32_MAX;
1253 uint32_t delta;
1254 Size size;
1257 RwAutoLockRead lock(mRwLock);
1258 mParams.getSupportedPreviewSizes(previewSizes);
1261 if (!aWidth && !aHeight) {
1262 // no size specified, take the first supported size
1263 size = previewSizes[0];
1264 bestWidth = size.width;
1265 bestHeight = size.height;
1266 } else if (aWidth && aHeight) {
1267 // both height and width specified, find the supported size closest to requested size
1268 for (uint32_t i = 0; i < previewSizes.size(); i++) {
1269 Size size = previewSizes[i];
1270 uint32_t delta = abs((long int)(size.width * size.height - aWidth * aHeight));
1271 if (delta < minSizeDelta) {
1272 minSizeDelta = delta;
1273 bestWidth = size.width;
1274 bestHeight = size.height;
1277 } else if (!aWidth) {
1278 // width not specified, find closest height match
1279 for (uint32_t i = 0; i < previewSizes.size(); i++) {
1280 size = previewSizes[i];
1281 delta = abs((long int)(size.height - aHeight));
1282 if (delta < minSizeDelta) {
1283 minSizeDelta = delta;
1284 bestWidth = size.width;
1285 bestHeight = size.height;
1288 } else if (!aHeight) {
1289 // height not specified, find closest width match
1290 for (uint32_t i = 0; i < previewSizes.size(); i++) {
1291 size = previewSizes[i];
1292 delta = abs((long int)(size.width - aWidth));
1293 if (delta < minSizeDelta) {
1294 minSizeDelta = delta;
1295 bestWidth = size.width;
1296 bestHeight = size.height;
1301 mWidth = bestWidth;
1302 mHeight = bestHeight;
1304 RwAutoLockWrite lock(mRwLock);
1305 mParams.setPreviewSize(mWidth, mHeight);
1307 PushParameters();
1310 nsresult
1311 nsGonkCameraControl::SetupVideoMode(const nsAString& aProfile)
1313 // read preferences for camcorder
1314 mMediaProfiles = MediaProfiles::getInstance();
1316 nsAutoCString profile = NS_ConvertUTF16toUTF8(aProfile);
1317 mRecorderProfile = GetGonkRecorderProfileManager().get()->Get(profile.get());
1318 if (!mRecorderProfile) {
1319 DOM_CAMERA_LOGE("Recorder profile '%s' is not supported\n", profile.get());
1320 return NS_ERROR_INVALID_ARG;
1323 const GonkRecorderVideoProfile* video = mRecorderProfile->GetGonkVideoProfile();
1324 int width = video->GetWidth();
1325 int height = video->GetHeight();
1326 int fps = video->GetFramerate();
1327 if (fps == -1 || width == -1 || height == -1) {
1328 DOM_CAMERA_LOGE("Can't configure preview with fps=%d, width=%d, height=%d\n", fps, width, height);
1329 return NS_ERROR_FAILURE;
1332 PullParametersImpl();
1334 // configure camera video recording parameters
1335 const size_t SIZE = 256;
1336 char buffer[SIZE];
1339 RwAutoLockWrite lock(mRwLock);
1340 mParams.setPreviewSize(width, height);
1341 mParams.setPreviewFrameRate(fps);
1344 * "record-size" is probably deprecated in later ICS;
1345 * might need to set "video-size" instead of "record-size".
1346 * See bug 795332.
1348 snprintf(buffer, SIZE, "%dx%d", width, height);
1349 mParams.set("record-size", buffer);
1352 // push the updated camera configuration immediately
1353 PushParameters();
1354 return NS_OK;
1357 class GonkRecorderListener : public IMediaRecorderClient
1359 public:
1360 GonkRecorderListener(nsGonkCameraControl* aCameraControl)
1361 : mCameraControl(aCameraControl)
1363 DOM_CAMERA_LOGT("%s:%d : this=%p, aCameraControl=%p\n", __func__, __LINE__, this, mCameraControl.get());
1366 void notify(int msg, int ext1, int ext2)
1368 if (mCameraControl) {
1369 mCameraControl->HandleRecorderEvent(msg, ext1, ext2);
1373 IBinder* onAsBinder()
1375 DOM_CAMERA_LOGE("onAsBinder() called, should NEVER get called!\n");
1376 return nullptr;
1379 protected:
1380 ~GonkRecorderListener() { }
1381 nsRefPtr<nsGonkCameraControl> mCameraControl;
1384 void
1385 nsGonkCameraControl::HandleRecorderEvent(int msg, int ext1, int ext2)
1388 * Refer to base/include/media/mediarecorder.h for a complete list
1389 * of error and info message codes. There are duplicate values
1390 * within the status/error code space, as determined by code inspection:
1392 * +------- msg
1393 * | +----- ext1
1394 * | | +--- ext2
1395 * V V V
1396 * 1 MEDIA_RECORDER_EVENT_ERROR
1397 * 1 MEDIA_RECORDER_ERROR_UNKNOWN
1398 * [3] ERROR_MALFORMED
1399 * 100 mediaplayer.h::MEDIA_ERROR_SERVER_DIED
1400 * 0 <always zero>
1401 * 2 MEDIA_RECORDER_EVENT_INFO
1402 * 800 MEDIA_RECORDER_INFO_MAX_DURATION_REACHED
1403 * 0 <always zero>
1404 * 801 MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED
1405 * 0 <always zero>
1406 * 1000 MEDIA_RECORDER_TRACK_INFO_COMPLETION_STATUS[1b]
1407 * [3] UNKNOWN_ERROR, etc.
1408 * 100 MEDIA_ERROR[4]
1409 * 100 mediaplayer.h::MEDIA_ERROR_SERVER_DIED
1410 * 0 <always zero>
1411 * 100 MEDIA_RECORDER_TRACK_EVENT_ERROR
1412 * 100 MEDIA_RECORDER_TRACK_ERROR_GENERAL[1a]
1413 * [3] UNKNOWN_ERROR, etc.
1414 * 200 MEDIA_RECORDER_ERROR_VIDEO_NO_SYNC_FRAME[2]
1415 * ? <unknown>
1416 * 101 MEDIA_RECORDER_TRACK_EVENT_INFO
1417 * 1000 MEDIA_RECORDER_TRACK_INFO_COMPLETION_STATUS[1a]
1418 * [3] UNKNOWN_ERROR, etc.
1419 * N see mediarecorder.h::media_recorder_info_type[5]
1421 * 1. a) High 4 bits are the track number, the next 12 bits are reserved,
1422 * and the final 16 bits are the actual error code (above).
1423 * b) But not in this case.
1424 * 2. Never actually used in AOSP code?
1425 * 3. Specific error codes are from utils/Errors.h and/or
1426 * include/media/stagefright/MediaErrors.h.
1427 * 4. Only in frameworks/base/media/libmedia/mediaplayer.cpp.
1428 * 5. These are mostly informational and we can ignore them; note that
1429 * although the MEDIA_RECORDER_INFO_MAX_DURATION_REACHED and
1430 * MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED values are defined in this
1431 * enum, they are used with different ext1 codes. /o\
1433 int trackNum = -1; // no track
1435 switch (msg) {
1436 // Recorder-related events
1437 case MEDIA_RECORDER_EVENT_INFO:
1438 switch (ext1) {
1439 case MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED:
1440 DOM_CAMERA_LOGI("recorder-event : info: maximum file size reached\n");
1441 OnRecorderStateChange(NS_LITERAL_STRING("FileSizeLimitReached"), ext2, trackNum);
1442 return;
1444 case MEDIA_RECORDER_INFO_MAX_DURATION_REACHED:
1445 DOM_CAMERA_LOGI("recorder-event : info: maximum video duration reached\n");
1446 OnRecorderStateChange(NS_LITERAL_STRING("VideoLengthLimitReached"), ext2, trackNum);
1447 return;
1449 case MEDIA_RECORDER_TRACK_INFO_COMPLETION_STATUS:
1450 DOM_CAMERA_LOGI("recorder-event : info: track completed\n");
1451 OnRecorderStateChange(NS_LITERAL_STRING("TrackCompleted"), ext2, trackNum);
1452 return;
1454 break;
1456 case MEDIA_RECORDER_EVENT_ERROR:
1457 switch (ext1) {
1458 case MEDIA_RECORDER_ERROR_UNKNOWN:
1459 DOM_CAMERA_LOGE("recorder-event : recorder-error: %d (0x%08x)\n", ext2, ext2);
1460 OnRecorderStateChange(NS_LITERAL_STRING("MediaRecorderFailed"), ext2, trackNum);
1461 return;
1463 case MEDIA_ERROR_SERVER_DIED:
1464 DOM_CAMERA_LOGE("recorder-event : recorder-error: server died\n");
1465 OnRecorderStateChange(NS_LITERAL_STRING("MediaServerFailed"), ext2, trackNum);
1466 return;
1468 break;
1470 // Track-related events, see note 1(a) above.
1471 case MEDIA_RECORDER_TRACK_EVENT_INFO:
1472 trackNum = (ext1 & 0xF0000000) >> 28;
1473 ext1 &= 0xFFFF;
1474 switch (ext1) {
1475 case MEDIA_RECORDER_TRACK_INFO_COMPLETION_STATUS:
1476 if (ext2 == OK) {
1477 DOM_CAMERA_LOGI("recorder-event : track-complete: track %d, %d (0x%08x)\n", trackNum, ext2, ext2);
1478 OnRecorderStateChange(NS_LITERAL_STRING("TrackCompleted"), ext2, trackNum);
1479 return;
1481 DOM_CAMERA_LOGE("recorder-event : track-error: track %d, %d (0x%08x)\n", trackNum, ext2, ext2);
1482 OnRecorderStateChange(NS_LITERAL_STRING("TrackFailed"), ext2, trackNum);
1483 return;
1485 case MEDIA_RECORDER_TRACK_INFO_PROGRESS_IN_TIME:
1486 DOM_CAMERA_LOGI("recorder-event : track-info: progress in time: %d ms\n", ext2);
1487 return;
1489 break;
1491 case MEDIA_RECORDER_TRACK_EVENT_ERROR:
1492 trackNum = (ext1 & 0xF0000000) >> 28;
1493 ext1 &= 0xFFFF;
1494 DOM_CAMERA_LOGE("recorder-event : track-error: track %d, %d (0x%08x)\n", trackNum, ext2, ext2);
1495 OnRecorderStateChange(NS_LITERAL_STRING("TrackFailed"), ext2, trackNum);
1496 return;
1499 // All unhandled cases wind up here
1500 DOM_CAMERA_LOGW("recorder-event : unhandled: msg=%d, ext1=%d, ext2=%d\n", msg, ext1, ext2);
1503 nsresult
1504 nsGonkCameraControl::SetupRecording(int aFd, int aRotation, int64_t aMaxFileSizeBytes, int64_t aMaxVideoLengthMs)
1506 RETURN_IF_NO_CAMERA_HW();
1508 // choosing a size big enough to hold the params
1509 const size_t SIZE = 256;
1510 char buffer[SIZE];
1512 mRecorder = new GonkRecorder();
1513 CHECK_SETARG(mRecorder->init());
1515 nsresult rv = mRecorderProfile->ConfigureRecorder(mRecorder);
1516 NS_ENSURE_SUCCESS(rv, rv);
1518 CHECK_SETARG(mRecorder->setCamera(mCameraHw));
1520 DOM_CAMERA_LOGI("maxVideoLengthMs=%lld\n", aMaxVideoLengthMs);
1521 if (aMaxVideoLengthMs == 0) {
1522 aMaxVideoLengthMs = -1;
1524 snprintf(buffer, SIZE, "max-duration=%lld", aMaxVideoLengthMs);
1525 CHECK_SETARG(mRecorder->setParameters(String8(buffer)));
1527 DOM_CAMERA_LOGI("maxFileSizeBytes=%lld\n", aMaxFileSizeBytes);
1528 if (aMaxFileSizeBytes == 0) {
1529 aMaxFileSizeBytes = -1;
1531 snprintf(buffer, SIZE, "max-filesize=%lld", aMaxFileSizeBytes);
1532 CHECK_SETARG(mRecorder->setParameters(String8(buffer)));
1534 // adjust rotation by camera sensor offset
1535 int r = aRotation;
1536 r += mCameraHw->GetSensorOrientation();
1537 r %= 360;
1538 r += 45;
1539 r /= 90;
1540 r *= 90;
1541 if (r < 0) {
1542 // the video recorder only supports positive rotations
1543 r += 360;
1545 DOM_CAMERA_LOGI("setting video rotation to %d degrees (mapped from %d)\n", r, aRotation);
1546 snprintf(buffer, SIZE, "video-param-rotation-angle-degrees=%d", r);
1547 CHECK_SETARG(mRecorder->setParameters(String8(buffer)));
1549 CHECK_SETARG(mRecorder->setListener(new GonkRecorderListener(this)));
1551 // recording API needs file descriptor of output file
1552 CHECK_SETARG(mRecorder->setOutputFile(aFd, 0, 0));
1553 CHECK_SETARG(mRecorder->prepare());
1554 return NS_OK;
1557 nsresult
1558 nsGonkCameraControl::GetPreviewStreamVideoModeImpl(GetPreviewStreamVideoModeTask* aGetPreviewStreamVideoMode)
1560 // stop any currently running preview
1561 StopPreviewInternal(true /* forced */);
1563 // setup the video mode
1564 nsresult rv = SetupVideoMode(aGetPreviewStreamVideoMode->mOptions.profile);
1565 NS_ENSURE_SUCCESS(rv, rv);
1567 const RecorderVideoProfile* video = mRecorderProfile->GetVideoProfile();
1568 int width = video->GetWidth();
1569 int height = video->GetHeight();
1570 int fps = video->GetFramerate();
1571 DOM_CAMERA_LOGI("recording preview format: %d x %d (%d fps)\n", width, height, fps);
1573 // create and return new preview stream object
1574 nsCOMPtr<GetPreviewStreamResult> getPreviewStreamResult = new GetPreviewStreamResult(this, width, height, fps, aGetPreviewStreamVideoMode->mOnSuccessCb, mWindowId);
1575 rv = NS_DispatchToMainThread(getPreviewStreamResult);
1576 if (NS_FAILED(rv)) {
1577 NS_WARNING("Failed to dispatch GetPreviewStreamVideoMode() onSuccess callback to main thread!");
1578 return rv;
1581 return NS_OK;
1584 nsresult
1585 nsGonkCameraControl::ReleaseHardwareImpl(ReleaseHardwareTask* aReleaseHardware)
1587 DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
1589 // if we're recording, stop recording
1590 if (mRecorder) {
1591 DOM_CAMERA_LOGI("shutting down existing video recorder\n");
1592 mRecorder->stop();
1593 mRecorder = nullptr;
1596 // stop the preview
1597 StopPreviewInternal(true /* forced */);
1599 // release the hardware handle
1600 if (mCameraHw.get()){
1601 mCameraHw->Close();
1602 mCameraHw.clear();
1605 if (aReleaseHardware) {
1606 nsCOMPtr<nsIRunnable> releaseHardwareResult = new ReleaseHardwareResult(aReleaseHardware->mOnSuccessCb, mWindowId);
1607 return NS_DispatchToMainThread(releaseHardwareResult);
1610 return NS_OK;
1613 already_AddRefed<GonkRecorderProfileManager>
1614 nsGonkCameraControl::GetGonkRecorderProfileManager()
1616 if (!mProfileManager) {
1617 nsTArray<idl::CameraSize> sizes;
1618 nsresult rv = GetVideoSizes(sizes);
1619 NS_ENSURE_SUCCESS(rv, nullptr);
1621 mProfileManager = new GonkRecorderProfileManager(mCameraId);
1622 mProfileManager->SetSupportedResolutions(sizes);
1625 nsRefPtr<GonkRecorderProfileManager> profileMgr = mProfileManager;
1626 return profileMgr.forget();
1629 already_AddRefed<RecorderProfileManager>
1630 nsGonkCameraControl::GetRecorderProfileManagerImpl()
1632 nsRefPtr<RecorderProfileManager> profileMgr = GetGonkRecorderProfileManager();
1633 return profileMgr.forget();
1636 nsresult
1637 nsGonkCameraControl::GetVideoSizes(nsTArray<idl::CameraSize>& aVideoSizes)
1639 aVideoSizes.Clear();
1641 android::Vector<Size> sizes;
1643 RwAutoLockRead lock(mRwLock);
1645 mParams.getSupportedVideoSizes(sizes);
1646 if (sizes.size() == 0) {
1647 DOM_CAMERA_LOGI("Camera doesn't support video independent of the preview\n");
1648 mParams.getSupportedPreviewSizes(sizes);
1652 if (sizes.size() == 0) {
1653 DOM_CAMERA_LOGW("Camera doesn't report any supported video sizes at all\n");
1654 return NS_OK;
1657 for (size_t i = 0; i < sizes.size(); ++i) {
1658 idl::CameraSize size;
1659 size.width = sizes[i].width;
1660 size.height = sizes[i].height;
1661 aVideoSizes.AppendElement(size);
1663 return NS_OK;
1666 // Gonk callback handlers.
1667 namespace mozilla {
1669 void
1670 ReceiveImage(nsGonkCameraControl* gc, uint8_t* aData, uint32_t aLength)
1672 gc->TakePictureComplete(aData, aLength);
1675 void
1676 ReceiveImageError(nsGonkCameraControl* gc)
1678 gc->TakePictureError();
1681 void
1682 AutoFocusComplete(nsGonkCameraControl* gc, bool aSuccess)
1684 gc->AutoFocusComplete(aSuccess);
1687 static void
1688 GonkFrameBuilder(Image* aImage, void* aBuffer, uint32_t aWidth, uint32_t aHeight)
1691 * Cast the generic Image back to our platform-specific type and
1692 * populate it.
1694 GrallocImage* videoImage = static_cast<GrallocImage*>(aImage);
1695 GrallocImage::GrallocData data;
1696 data.mGraphicBuffer = static_cast<layers::GraphicBufferLocked*>(aBuffer);
1697 data.mPicSize = IntSize(aWidth, aHeight);
1698 videoImage->SetData(data);
1701 void
1702 ReceiveFrame(nsGonkCameraControl* gc, layers::GraphicBufferLocked* aBuffer)
1704 if (!gc->ReceiveFrame(aBuffer, ImageFormat::GRALLOC_PLANAR_YCBCR, GonkFrameBuilder)) {
1705 aBuffer->Unlock();
1709 void
1710 OnShutter(nsGonkCameraControl* gc)
1712 gc->OnShutter();
1715 void
1716 OnClosed(nsGonkCameraControl* gc)
1718 gc->OnClosed();
1721 } // namespace mozilla