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