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.
23 #include "base/basictypes.h"
24 #include "camera/CameraParameters.h"
26 #include "nsDOMClassInfo.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
;
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() \
64 if (!mCameraHw.get()) { \
65 DOM_CAMERA_LOGE("%s:%d : mCameraHw is null\n", __func__, __LINE__); \
66 return NS_ERROR_NOT_AVAILABLE; \
70 static const char* getKeyText(uint32_t 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
;
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);
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!
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
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);
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
;
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)
211 , mLastPictureWidth(0)
212 , mLastPictureHeight(0)
213 , mLastThumbnailWidth(0)
214 , mLastThumbnailHeight(0)
215 #if !FORCE_PREVIEW_FORMAT_YUV420SP
216 , mFormat(PREVIEW_FORMAT_UNKNOWN
)
218 , mFormat(PREVIEW_FORMAT_YUV420SP
)
221 , mDiscardedFrameCount(0)
222 , mMediaProfiles(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
);
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
);
262 mParams
.setPreviewFormat("yuv420sp");
263 mParams
.setPreviewFrameRate(mFps
);
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
);
277 mFormat
= PREVIEW_FORMAT_UNKNOWN
;
278 DOM_CAMERA_LOGE("Camera ignored our request for '%s' preview, returned UNSUPPORTED format '%s'\n", PREVIEW_FORMAT
, format
);
282 // Check the frame rate and log if the camera ignored our setting
283 uint32_t fps
= mParams
.getPreviewFrameRate();
285 DOM_CAMERA_LOGA("We asked for %d fps but camera returned %d fps, using that", 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
);
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
);
314 nsGonkCameraControl::~nsGonkCameraControl()
316 DOM_CAMERA_LOGT("%s:%d : this=%p, mCameraHw = %p\n", __func__
, __LINE__
, this, mCameraHw
.get());
318 ReleaseHardwareImpl(nullptr);
320 PRRWLock
* lock
= mRwLock
;
322 PR_DestroyRWLock(lock
);
325 DOM_CAMERA_LOGT("%s:%d\n", __func__
, __LINE__
);
331 RwAutoLockRead(PRRWLock
* aRwLock
)
334 PR_RWLock_Rlock(mRwLock
);
339 PR_RWLock_Unlock(mRwLock
);
346 class RwAutoLockWrite
349 RwAutoLockWrite(PRRWLock
* aRwLock
)
352 PR_RWLock_Wlock(mRwLock
);
357 PR_RWLock_Unlock(mRwLock
);
365 nsGonkCameraControl::GetParameter(const char* aKey
)
367 RwAutoLockRead
lock(mRwLock
);
368 return mParams
.get(aKey
);
372 nsGonkCameraControl::GetParameterConstChar(uint32_t aKey
)
374 const char* key
= getKeyText(aKey
);
379 RwAutoLockRead
lock(mRwLock
);
380 return mParams
.get(key
);
384 nsGonkCameraControl::GetParameterDouble(uint32_t aKey
)
388 double focusDistance
[3];
391 const char* key
= getKeyText(aKey
);
393 // return 1x when zooming is not supported
394 return aKey
== CAMERA_PARAM_ZOOM
? 1.0 : 0.0;
397 RwAutoLockRead
lock(mRwLock
);
399 case CAMERA_PARAM_ZOOM
:
400 val
= mParams
.getInt(key
);
404 * The gonk camera parameters API only exposes one focus distance property
405 * that contains "Near,Optimum,Far" distances, in metres, where 'Far' may
408 case CAMERA_PARAM_FOCUSDISTANCEFAR
:
410 // intentional fallthrough
412 case CAMERA_PARAM_FOCUSDISTANCEOPTIMUM
:
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
];
423 case CAMERA_PARAM_EXPOSURECOMPENSATION
:
424 index
= mParams
.getInt(key
);
426 // NaN indicates automatic exposure compensation
429 val
= (index
- 1) * mExposureCompensationStep
+ mExposureCompensationMin
;
430 DOM_CAMERA_LOGI("index = %d --> compensation = %f\n", index
, val
);
434 return mParams
.getFloat(key
);
439 nsGonkCameraControl::GetParameterInt32(uint32_t aKey
)
441 if (aKey
== CAMERA_PARAM_SENSORANGLE
) {
442 if (!mCameraHw
.get()) {
445 return mCameraHw
->GetSensorOrientation();
448 const char* key
= getKeyText(aKey
);
453 RwAutoLockRead
lock(mRwLock
);
454 return mParams
.getInt(key
);
458 nsGonkCameraControl::GetParameter(uint32_t aKey
,
459 nsTArray
<idl::CameraRegion
>& aRegions
)
463 const char* key
= getKeyText(aKey
);
468 RwAutoLockRead
lock(mRwLock
);
470 const char* value
= mParams
.get(key
);
471 DOM_CAMERA_LOGI("key='%s' --> value='%s'\n", key
, value
);
476 const char* p
= value
;
479 // count the number of regions in the string
480 while ((p
= strstr(p
, "),("))) {
485 aRegions
.SetCapacity(count
);
486 idl::CameraRegion
* r
;
488 // parse all of the region sets
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
);
503 nsGonkCameraControl::GetParameter(uint32_t aKey
,
504 nsTArray
<idl::CameraSize
>& aSizes
)
506 const char* key
= getKeyText(aKey
);
511 RwAutoLockRead
lock(mRwLock
);
513 const char* value
= mParams
.get(key
);
514 DOM_CAMERA_LOGI("key='%s' --> value='%s'\n", key
, value
);
519 const char* p
= value
;
522 // The 'value' string is in the format "w1xh1,w2xh2,w3xh3,..."
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
);
530 // Look for the next record...
533 // ...skip the comma too
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
);
555 const char* key
= getKeyText(aKey
);
560 RwAutoLockRead
lock(mRwLock
);
562 const char* value
= mParams
.get(key
);
563 DOM_CAMERA_LOGI("key='%s' --> value='%s'\n", key
, value
);
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
);
576 nsGonkCameraControl::PushParameters()
578 if (mDeferConfigUpdate
) {
579 DOM_CAMERA_LOGT("%s:%d - defering config update\n", __func__
, __LINE__
);
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
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();
600 nsGonkCameraControl::SetParameter(const char* aKey
, const char* aValue
)
603 RwAutoLockWrite
lock(mRwLock
);
604 mParams
.set(aKey
, aValue
);
610 nsGonkCameraControl::SetParameter(uint32_t aKey
, const char* aValue
)
612 const char* key
= getKeyText(aKey
);
618 RwAutoLockWrite
lock(mRwLock
);
619 mParams
.set(key
, aValue
);
625 nsGonkCameraControl::SetParameter(uint32_t aKey
, double aValue
)
629 const char* key
= getKeyText(aKey
);
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
);
645 mParams
.setFloat(key
, aValue
);
652 nsGonkCameraControl::SetParameter(uint32_t aKey
,
653 const nsTArray
<idl::CameraRegion
>& aRegions
)
655 const char* key
= getKeyText(aKey
);
660 uint32_t length
= aRegions
.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)");
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());
692 nsGonkCameraControl::SetParameter(uint32_t aKey
, int aValue
)
694 const char* key
= getKeyText(aKey
);
699 RwAutoLockWrite
lock(mRwLock
);
700 mParams
.set(key
, aValue
);
706 nsGonkCameraControl::SetParameter(uint32_t aKey
, const idl::CameraSize
& aSize
)
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
);
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
);
721 const char* key
= getKeyText(aKey
);
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());
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
);
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
);
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.
794 if (mCameraHw
.get()) {
795 mCameraHw
->StopPreview();
797 mDOMPreview
->Stopped(aForced
);
798 mDOMPreview
= nullptr;
801 OnPreviewStateChange(PREVIEW_STOPPED
);
806 nsGonkCameraControl::StopPreviewImpl(StopPreviewTask
* aStopPreview
)
808 return StopPreviewInternal();
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
;
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
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);
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
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
);
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
);
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");
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
));
898 nsGonkCameraControl::UpdateThumbnailSize()
900 SetThumbnailSize(mLastThumbnailWidth
, mLastThumbnailHeight
);
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
911 if (!aWidth
|| !aHeight
) {
912 DOM_CAMERA_LOGW("Ignoring requested picture size of %ux%u\n", aWidth
, aHeight
);
916 if (aWidth
== mLastPictureWidth
&& aHeight
== mLastPictureHeight
) {
917 DOM_CAMERA_LOGI("Requested picture size %ux%u unchanged\n", aWidth
, aHeight
);
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
);
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");
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();
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
);
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
);
1030 if (localtime_r(&time
, &t
)) {
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.
1039 SetParameter("exif-datetime", dateTime
);
1041 DOM_CAMERA_LOGE("picture date/time couldn't be converted to string\n");
1044 DOM_CAMERA_LOGE("picture date/time couldn't be converted to local time: (%d) %s\n", errno
, strerror(errno
));
1048 mDeferConfigUpdate
= false;
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
);
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
;
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
);
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.
1100 nsCOMPtr
<nsIFile
> filename
= aStartRecording
->mFolder
;
1101 filename
->AppendRelativePath(aStartRecording
->mFilename
);
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
);
1114 vol
->GetName(volName
);
1116 mVideoFile
= new DeviceStorageFile(NS_LITERAL_STRING("videos"),
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));
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
;
1148 class RecordingComplete
: public nsRunnable
1151 RecordingComplete(DeviceStorageFile
* aFile
)
1155 ~RecordingComplete() { }
1159 MOZ_ASSERT(NS_IsMainThread());
1161 nsCOMPtr
<nsIObserverService
> obs
= mozilla::services::GetObserverService();
1162 obs
->NotifyObservers(mFile
, "file-watcher-notify", MOZ_UTF16("modified"));
1167 nsRefPtr
<DeviceStorageFile
> mFile
;
1171 nsGonkCameraControl::StopRecordingImpl(StopRecordingTask
* aStopRecording
)
1173 // nothing to do if we have no mRecorder
1174 NS_ENSURE_TRUE(mRecorder
, NS_OK
);
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
);
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
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!");
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!");
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!");
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
;
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
;
1302 mHeight
= bestHeight
;
1304 RwAutoLockWrite
lock(mRwLock
);
1305 mParams
.setPreviewSize(mWidth
, mHeight
);
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;
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".
1348 snprintf(buffer
, SIZE
, "%dx%d", width
, height
);
1349 mParams
.set("record-size", buffer
);
1352 // push the updated camera configuration immediately
1357 class GonkRecorderListener
: public IMediaRecorderClient
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");
1380 ~GonkRecorderListener() { }
1381 nsRefPtr
<nsGonkCameraControl
> mCameraControl
;
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:
1396 * 1 MEDIA_RECORDER_EVENT_ERROR
1397 * 1 MEDIA_RECORDER_ERROR_UNKNOWN
1398 * [3] ERROR_MALFORMED
1399 * 100 mediaplayer.h::MEDIA_ERROR_SERVER_DIED
1401 * 2 MEDIA_RECORDER_EVENT_INFO
1402 * 800 MEDIA_RECORDER_INFO_MAX_DURATION_REACHED
1404 * 801 MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED
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
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]
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
1436 // Recorder-related events
1437 case MEDIA_RECORDER_EVENT_INFO
:
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
);
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
);
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
);
1456 case MEDIA_RECORDER_EVENT_ERROR
:
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
);
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
);
1470 // Track-related events, see note 1(a) above.
1471 case MEDIA_RECORDER_TRACK_EVENT_INFO
:
1472 trackNum
= (ext1
& 0xF0000000) >> 28;
1475 case MEDIA_RECORDER_TRACK_INFO_COMPLETION_STATUS
:
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
);
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
);
1485 case MEDIA_RECORDER_TRACK_INFO_PROGRESS_IN_TIME
:
1486 DOM_CAMERA_LOGI("recorder-event : track-info: progress in time: %d ms\n", ext2
);
1491 case MEDIA_RECORDER_TRACK_EVENT_ERROR
:
1492 trackNum
= (ext1
& 0xF0000000) >> 28;
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
);
1499 // All unhandled cases wind up here
1500 DOM_CAMERA_LOGW("recorder-event : unhandled: msg=%d, ext1=%d, ext2=%d\n", msg
, ext1
, ext2
);
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;
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
1536 r
+= mCameraHw
->GetSensorOrientation();
1542 // the video recorder only supports positive rotations
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());
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!");
1585 nsGonkCameraControl::ReleaseHardwareImpl(ReleaseHardwareTask
* aReleaseHardware
)
1587 DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__
, __LINE__
, this);
1589 // if we're recording, stop recording
1591 DOM_CAMERA_LOGI("shutting down existing video recorder\n");
1593 mRecorder
= nullptr;
1597 StopPreviewInternal(true /* forced */);
1599 // release the hardware handle
1600 if (mCameraHw
.get()){
1605 if (aReleaseHardware
) {
1606 nsCOMPtr
<nsIRunnable
> releaseHardwareResult
= new ReleaseHardwareResult(aReleaseHardware
->mOnSuccessCb
, mWindowId
);
1607 return NS_DispatchToMainThread(releaseHardwareResult
);
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();
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");
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
);
1666 // Gonk callback handlers.
1670 ReceiveImage(nsGonkCameraControl
* gc
, uint8_t* aData
, uint32_t aLength
)
1672 gc
->TakePictureComplete(aData
, aLength
);
1676 ReceiveImageError(nsGonkCameraControl
* gc
)
1678 gc
->TakePictureError();
1682 AutoFocusComplete(nsGonkCameraControl
* gc
, bool aSuccess
)
1684 gc
->AutoFocusComplete(aSuccess
);
1688 GonkFrameBuilder(Image
* aImage
, void* aBuffer
, uint32_t aWidth
, uint32_t aHeight
)
1691 * Cast the generic Image back to our platform-specific type and
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
);
1702 ReceiveFrame(nsGonkCameraControl
* gc
, layers::GraphicBufferLocked
* aBuffer
)
1704 if (!gc
->ReceiveFrame(aBuffer
, ImageFormat::GRALLOC_PLANAR_YCBCR
, GonkFrameBuilder
)) {
1710 OnShutter(nsGonkCameraControl
* gc
)
1716 OnClosed(nsGonkCameraControl
* gc
)
1721 } // namespace mozilla