1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/camera/GonkCameraControl.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1720 @@ 1.4 +/* 1.5 + * Copyright (C) 2012-2014 Mozilla Foundation 1.6 + * 1.7 + * Licensed under the Apache License, Version 2.0 (the "License"); 1.8 + * you may not use this file except in compliance with the License. 1.9 + * You may obtain a copy of the License at 1.10 + * 1.11 + * http://www.apache.org/licenses/LICENSE-2.0 1.12 + * 1.13 + * Unless required by applicable law or agreed to in writing, software 1.14 + * distributed under the License is distributed on an "AS IS" BASIS, 1.15 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1.16 + * See the License for the specific language governing permissions and 1.17 + * limitations under the License. 1.18 + */ 1.19 + 1.20 +#include "GonkCameraControl.h" 1.21 +#include <time.h> 1.22 +#include <string.h> 1.23 +#include <sys/stat.h> 1.24 +#include <fcntl.h> 1.25 +#include <errno.h> 1.26 +#include <libgen.h> 1.27 +#include "base/basictypes.h" 1.28 +#include "camera/CameraParameters.h" 1.29 +#include "nsCOMPtr.h" 1.30 +#include "nsMemory.h" 1.31 +#include "nsThread.h" 1.32 +#include <media/MediaProfiles.h> 1.33 +#include "mozilla/FileUtils.h" 1.34 +#include "mozilla/Services.h" 1.35 +#include "mozilla/unused.h" 1.36 +#include "mozilla/ipc/FileDescriptorUtils.h" 1.37 +#include "nsAlgorithm.h" 1.38 +#include <media/mediaplayer.h> 1.39 +#include "nsPrintfCString.h" 1.40 +#include "nsIObserverService.h" 1.41 +#include "nsIVolume.h" 1.42 +#include "nsIVolumeService.h" 1.43 +#include "AutoRwLock.h" 1.44 +#include "GonkCameraHwMgr.h" 1.45 +#include "GonkRecorderProfiles.h" 1.46 +#include "CameraCommon.h" 1.47 +#include "GonkCameraParameters.h" 1.48 +#include "DeviceStorageFileDescriptor.h" 1.49 + 1.50 +using namespace mozilla; 1.51 +using namespace mozilla::layers; 1.52 +using namespace mozilla::gfx; 1.53 +using namespace mozilla::ipc; 1.54 +using namespace android; 1.55 + 1.56 +#define RETURN_IF_NO_CAMERA_HW() \ 1.57 + do { \ 1.58 + if (!mCameraHw.get()) { \ 1.59 + DOM_CAMERA_LOGE("%s:%d : mCameraHw is null\n", __func__, __LINE__); \ 1.60 + return NS_ERROR_NOT_AVAILABLE; \ 1.61 + } \ 1.62 + } while(0) 1.63 + 1.64 +// Construct nsGonkCameraControl on the main thread. 1.65 +nsGonkCameraControl::nsGonkCameraControl(uint32_t aCameraId) 1.66 + : CameraControlImpl(aCameraId) 1.67 + , mLastPictureSize({0, 0}) 1.68 + , mLastThumbnailSize({0, 0}) 1.69 + , mPreviewFps(30) 1.70 + , mResumePreviewAfterTakingPicture(false) // XXXmikeh - see bug 950102 1.71 + , mFlashSupported(false) 1.72 + , mLuminanceSupported(false) 1.73 + , mAutoFlashModeOverridden(false) 1.74 + , mDeferConfigUpdate(0) 1.75 + , mMediaProfiles(nullptr) 1.76 + , mRecorder(nullptr) 1.77 + , mProfileManager(nullptr) 1.78 + , mRecorderProfile(nullptr) 1.79 + , mVideoFile(nullptr) 1.80 + , mReentrantMonitor("GonkCameraControl::OnTakePictureMonitor") 1.81 +{ 1.82 + // Constructor runs on the main thread... 1.83 + DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); 1.84 + mImageContainer = LayerManager::CreateImageContainer(); 1.85 +} 1.86 + 1.87 +nsresult 1.88 +nsGonkCameraControl::StartImpl(const Configuration* aInitialConfig) 1.89 +{ 1.90 + /** 1.91 + * For initialization, we try to return the camera control to the upper 1.92 + * upper layer (i.e. the DOM) as quickly as possible. To do this, the 1.93 + * camera is initialized in the following stages: 1.94 + * 1.95 + * 0. Initialize() initializes the hardware; 1.96 + * 1. SetConfigurationInternal() does the minimal configuration 1.97 + * required so that we can start the preview -and- report a valid 1.98 + * configuration to the upper layer; 1.99 + * 2. OnHardwareStateChange() reports that the hardware is ready, 1.100 + * which the upper (e.g. DOM) layer can (and does) use to return 1.101 + * the camera control object; 1.102 + * 3. StartPreviewImpl() starts the flow of preview frames from the 1.103 + * camera hardware. 1.104 + * 1.105 + * The intent of the above flow is to let the Main Thread do as much work 1.106 + * up-front as possible without waiting for blocking Camera Thread calls 1.107 + * to complete. 1.108 + */ 1.109 + MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread); 1.110 + 1.111 + nsresult rv = Initialize(); 1.112 + if (NS_WARN_IF(NS_FAILED(rv))) { 1.113 + return rv; 1.114 + } 1.115 + 1.116 + if (aInitialConfig) { 1.117 + rv = SetConfigurationInternal(*aInitialConfig); 1.118 + if (NS_WARN_IF(NS_FAILED(rv))) { 1.119 + // The initial configuration failed, close up the hardware 1.120 + StopImpl(); 1.121 + return rv; 1.122 + } 1.123 + } 1.124 + 1.125 + OnHardwareStateChange(CameraControlListener::kHardwareOpen); 1.126 + return StartPreviewImpl(); 1.127 +} 1.128 + 1.129 +nsresult 1.130 +nsGonkCameraControl::Initialize() 1.131 +{ 1.132 + mCameraHw = GonkCameraHardware::Connect(this, mCameraId); 1.133 + if (!mCameraHw.get()) { 1.134 + DOM_CAMERA_LOGE("Failed to connect to camera %d (this=%p)\n", mCameraId, this); 1.135 + return NS_ERROR_FAILURE; 1.136 + } 1.137 + 1.138 + DOM_CAMERA_LOGI("Initializing camera %d (this=%p, mCameraHw=%p)\n", mCameraId, this, mCameraHw.get()); 1.139 + 1.140 + // Initialize our camera configuration database. 1.141 + PullParametersImpl(); 1.142 + 1.143 + // Set preferred preview frame format. 1.144 + mParams.Set(CAMERA_PARAM_PREVIEWFORMAT, NS_LITERAL_STRING("yuv420sp")); 1.145 + // Turn off any normal pictures returned by the HDR scene mode 1.146 + mParams.Set(CAMERA_PARAM_SCENEMODE_HDR_RETURNNORMALPICTURE, false); 1.147 + PushParametersImpl(); 1.148 + 1.149 + // Grab any other settings we'll need later. 1.150 + mParams.Get(CAMERA_PARAM_PICTURE_FILEFORMAT, mFileFormat); 1.151 + mParams.Get(CAMERA_PARAM_THUMBNAILSIZE, mLastThumbnailSize); 1.152 + 1.153 + // The emulator's camera returns -1 for these values; bump them up to 0 1.154 + int areas; 1.155 + mParams.Get(CAMERA_PARAM_SUPPORTED_MAXMETERINGAREAS, areas); 1.156 + mCurrentConfiguration.mMaxMeteringAreas = areas != -1 ? areas : 0; 1.157 + mParams.Get(CAMERA_PARAM_SUPPORTED_MAXFOCUSAREAS, areas); 1.158 + mCurrentConfiguration.mMaxFocusAreas = areas != -1 ? areas : 0; 1.159 + 1.160 + mParams.Get(CAMERA_PARAM_PICTURE_SIZE, mLastPictureSize); 1.161 + mParams.Get(CAMERA_PARAM_PREVIEWSIZE, mCurrentConfiguration.mPreviewSize); 1.162 + mParams.Get(CAMERA_PARAM_VIDEOSIZE, mLastRecorderSize); 1.163 + 1.164 + nsString luminance; // check for support 1.165 + mParams.Get(CAMERA_PARAM_LUMINANCE, luminance); 1.166 + mLuminanceSupported = !luminance.IsEmpty(); 1.167 + 1.168 + nsString flashMode; 1.169 + mParams.Get(CAMERA_PARAM_FLASHMODE, flashMode); 1.170 + mFlashSupported = !flashMode.IsEmpty(); 1.171 + 1.172 + DOM_CAMERA_LOGI(" - maximum metering areas: %u\n", mCurrentConfiguration.mMaxMeteringAreas); 1.173 + DOM_CAMERA_LOGI(" - maximum focus areas: %u\n", mCurrentConfiguration.mMaxFocusAreas); 1.174 + DOM_CAMERA_LOGI(" - default picture size: %u x %u\n", 1.175 + mLastPictureSize.width, mLastPictureSize.height); 1.176 + DOM_CAMERA_LOGI(" - default thumbnail size: %u x %u\n", 1.177 + mLastThumbnailSize.width, mLastThumbnailSize.height); 1.178 + DOM_CAMERA_LOGI(" - default preview size: %u x %u\n", 1.179 + mCurrentConfiguration.mPreviewSize.width, mCurrentConfiguration.mPreviewSize.height); 1.180 + DOM_CAMERA_LOGI(" - default video recorder size: %u x %u\n", 1.181 + mLastRecorderSize.width, mLastRecorderSize.height); 1.182 + DOM_CAMERA_LOGI(" - default picture file format: %s\n", 1.183 + NS_ConvertUTF16toUTF8(mFileFormat).get()); 1.184 + DOM_CAMERA_LOGI(" - luminance reporting: %ssupported\n", 1.185 + mLuminanceSupported ? "" : "NOT "); 1.186 + if (mFlashSupported) { 1.187 + DOM_CAMERA_LOGI(" - flash: supported, default mode '%s'\n", 1.188 + NS_ConvertUTF16toUTF8(flashMode).get()); 1.189 + } else { 1.190 + DOM_CAMERA_LOGI(" - flash: NOT supported\n"); 1.191 + } 1.192 + 1.193 + return NS_OK; 1.194 +} 1.195 + 1.196 +nsGonkCameraControl::~nsGonkCameraControl() 1.197 +{ 1.198 + DOM_CAMERA_LOGT("%s:%d : this=%p, mCameraHw = %p\n", __func__, __LINE__, this, mCameraHw.get()); 1.199 + 1.200 + StopImpl(); 1.201 + DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__); 1.202 +} 1.203 + 1.204 +nsresult 1.205 +nsGonkCameraControl::SetConfigurationInternal(const Configuration& aConfig) 1.206 +{ 1.207 + DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__); 1.208 + 1.209 + nsresult rv; 1.210 + 1.211 + switch (aConfig.mMode) { 1.212 + case kPictureMode: 1.213 + rv = SetPictureConfiguration(aConfig); 1.214 + break; 1.215 + 1.216 + case kVideoMode: 1.217 + rv = SetVideoConfiguration(aConfig); 1.218 + break; 1.219 + 1.220 + default: 1.221 + MOZ_ASSUME_UNREACHABLE("Unanticipated camera mode in SetConfigurationInternal()"); 1.222 + } 1.223 + 1.224 + DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__); 1.225 + NS_ENSURE_SUCCESS(rv, rv); 1.226 + 1.227 + mCurrentConfiguration.mMode = aConfig.mMode; 1.228 + mCurrentConfiguration.mRecorderProfile = aConfig.mRecorderProfile; 1.229 + if (aConfig.mMode == kVideoMode) { 1.230 + mCurrentConfiguration.mPreviewSize = mLastRecorderSize; 1.231 + } 1.232 + 1.233 + OnConfigurationChange(); 1.234 + return NS_OK; 1.235 +} 1.236 + 1.237 +nsresult 1.238 +nsGonkCameraControl::SetConfigurationImpl(const Configuration& aConfig) 1.239 +{ 1.240 + DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__); 1.241 + MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread); 1.242 + 1.243 + // Stop any currently running preview 1.244 + nsresult rv = PausePreview(); 1.245 + if (NS_FAILED(rv)) { 1.246 + // warn, but plow ahead 1.247 + NS_WARNING("PausePreview() in SetConfigurationImpl() failed"); 1.248 + } 1.249 + 1.250 + DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__); 1.251 + rv = SetConfigurationInternal(aConfig); 1.252 + if (NS_WARN_IF(NS_FAILED(rv))) { 1.253 + StopPreviewImpl(); 1.254 + return rv; 1.255 + } 1.256 + 1.257 + // Restart the preview 1.258 + DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__); 1.259 + return StartPreviewImpl(); 1.260 +} 1.261 + 1.262 +nsresult 1.263 +nsGonkCameraControl::SetPictureConfiguration(const Configuration& aConfig) 1.264 +{ 1.265 + DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__); 1.266 + 1.267 + // remove any existing recorder profile 1.268 + mRecorderProfile = nullptr; 1.269 + 1.270 + nsresult rv = SetPreviewSize(aConfig.mPreviewSize); 1.271 + if (NS_WARN_IF(NS_FAILED(rv))) { 1.272 + return rv; 1.273 + } 1.274 + 1.275 + rv = PushParameters(); 1.276 + if (NS_WARN_IF(NS_FAILED(rv))) { 1.277 + return rv; 1.278 + } 1.279 + 1.280 + mParams.Get(CAMERA_PARAM_PREVIEWFRAMERATE, mPreviewFps); 1.281 + 1.282 + DOM_CAMERA_LOGI("picture mode preview: wanted %ux%u, got %ux%u (%u fps)\n", 1.283 + aConfig.mPreviewSize.width, aConfig.mPreviewSize.height, 1.284 + mCurrentConfiguration.mPreviewSize.width, mCurrentConfiguration.mPreviewSize.height, 1.285 + mPreviewFps); 1.286 + 1.287 + return NS_OK; 1.288 +} 1.289 + 1.290 +nsresult 1.291 +nsGonkCameraControl::SetVideoConfiguration(const Configuration& aConfig) 1.292 +{ 1.293 + DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__); 1.294 + 1.295 + nsresult rv = SetupVideoMode(aConfig.mRecorderProfile); 1.296 + NS_ENSURE_SUCCESS(rv, rv); 1.297 + 1.298 + DOM_CAMERA_LOGI("video mode preview: profile '%s', got %ux%u (%u fps)\n", 1.299 + NS_ConvertUTF16toUTF8(aConfig.mRecorderProfile).get(), 1.300 + mLastRecorderSize.width, mLastRecorderSize.height, 1.301 + mPreviewFps); 1.302 + 1.303 + return rv; 1.304 +} 1.305 + 1.306 +// Parameter management. 1.307 +nsresult 1.308 +nsGonkCameraControl::PushParameters() 1.309 +{ 1.310 + uint32_t dcu = mDeferConfigUpdate; 1.311 + if (dcu > 0) { 1.312 + DOM_CAMERA_LOGI("Defering config update (nest level %u)\n", dcu); 1.313 + return NS_OK; 1.314 + } 1.315 + 1.316 + /** 1.317 + * If we're already on the camera thread, call PushParametersImpl() 1.318 + * directly, so that it executes synchronously. Some callers 1.319 + * require this so that changes take effect immediately before 1.320 + * we can proceed. 1.321 + */ 1.322 + if (NS_GetCurrentThread() != mCameraThread) { 1.323 + DOM_CAMERA_LOGT("%s:%d - dispatching to Camera Thread\n", __func__, __LINE__); 1.324 + nsCOMPtr<nsIRunnable> pushParametersTask = 1.325 + NS_NewRunnableMethod(this, &nsGonkCameraControl::PushParametersImpl); 1.326 + return mCameraThread->Dispatch(pushParametersTask, NS_DISPATCH_NORMAL); 1.327 + } 1.328 + 1.329 + DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__); 1.330 + return PushParametersImpl(); 1.331 +} 1.332 + 1.333 +void 1.334 +nsGonkCameraControl::BeginBatchParameterSet() 1.335 +{ 1.336 + uint32_t dcu = ++mDeferConfigUpdate; 1.337 + if (dcu == 0) { 1.338 + NS_WARNING("Overflow weirdness incrementing mDeferConfigUpdate!"); 1.339 + MOZ_CRASH(); 1.340 + } 1.341 + DOM_CAMERA_LOGI("Begin deferring camera configuration updates (nest level %u)\n", dcu); 1.342 +} 1.343 + 1.344 +void 1.345 +nsGonkCameraControl::EndBatchParameterSet() 1.346 +{ 1.347 + uint32_t dcu = mDeferConfigUpdate--; 1.348 + if (dcu == 0) { 1.349 + NS_WARNING("Underflow badness decrementing mDeferConfigUpdate!"); 1.350 + MOZ_CRASH(); 1.351 + } 1.352 + DOM_CAMERA_LOGI("End deferring camera configuration updates (nest level %u)\n", dcu); 1.353 + 1.354 + if (dcu == 1) { 1.355 + PushParameters(); 1.356 + } 1.357 +} 1.358 + 1.359 +template<class T> nsresult 1.360 +nsGonkCameraControl::SetAndPush(uint32_t aKey, const T& aValue) 1.361 +{ 1.362 + nsresult rv = mParams.Set(aKey, aValue); 1.363 + if (NS_FAILED(rv)) { 1.364 + DOM_CAMERA_LOGE("Camera parameter aKey=%d failed to set (0x%x)\n", aKey, rv); 1.365 + return rv; 1.366 + } 1.367 + return PushParameters(); 1.368 +} 1.369 + 1.370 +// Array-of-Size parameter accessor. 1.371 +nsresult 1.372 +nsGonkCameraControl::Get(uint32_t aKey, nsTArray<Size>& aSizes) 1.373 +{ 1.374 + if (aKey == CAMERA_PARAM_SUPPORTED_VIDEOSIZES) { 1.375 + nsresult rv = mParams.Get(aKey, aSizes); 1.376 + if (aSizes.Length() != 0) { 1.377 + return rv; 1.378 + } 1.379 + DOM_CAMERA_LOGI("Camera doesn't support video independent of the preview\n"); 1.380 + aKey = CAMERA_PARAM_SUPPORTED_PREVIEWSIZES; 1.381 + } 1.382 + 1.383 + return mParams.Get(aKey, aSizes); 1.384 +} 1.385 + 1.386 +// Array-of-doubles parameter accessor. 1.387 +nsresult 1.388 +nsGonkCameraControl::Get(uint32_t aKey, nsTArray<double>& aValues) 1.389 +{ 1.390 + return mParams.Get(aKey, aValues); 1.391 +} 1.392 + 1.393 +// Array-of-nsString parameter accessor. 1.394 +nsresult 1.395 +nsGonkCameraControl::Get(uint32_t aKey, nsTArray<nsString>& aValues) 1.396 +{ 1.397 + return mParams.Get(aKey, aValues); 1.398 +} 1.399 + 1.400 +// nsString-valued parameter accessors 1.401 +nsresult 1.402 +nsGonkCameraControl::Set(uint32_t aKey, const nsAString& aValue) 1.403 +{ 1.404 + nsresult rv = mParams.Set(aKey, aValue); 1.405 + if (NS_FAILED(rv)) { 1.406 + return rv; 1.407 + } 1.408 + 1.409 + switch (aKey) { 1.410 + case CAMERA_PARAM_PICTURE_FILEFORMAT: 1.411 + // Picture format -- need to keep it for the TakePicture() callback. 1.412 + mFileFormat = aValue; 1.413 + break; 1.414 + 1.415 + case CAMERA_PARAM_FLASHMODE: 1.416 + // Explicit flash mode changes always win and stick. 1.417 + mAutoFlashModeOverridden = false; 1.418 + break; 1.419 + } 1.420 + 1.421 + return PushParameters(); 1.422 +} 1.423 + 1.424 +nsresult 1.425 +nsGonkCameraControl::Get(uint32_t aKey, nsAString& aRet) 1.426 +{ 1.427 + return mParams.Get(aKey, aRet); 1.428 +} 1.429 + 1.430 +// Double-valued parameter accessors 1.431 +nsresult 1.432 +nsGonkCameraControl::Set(uint32_t aKey, double aValue) 1.433 +{ 1.434 + return SetAndPush(aKey, aValue); 1.435 +} 1.436 + 1.437 +nsresult 1.438 +nsGonkCameraControl::Get(uint32_t aKey, double& aRet) 1.439 +{ 1.440 + return mParams.Get(aKey, aRet); 1.441 +} 1.442 + 1.443 +// Signed-64-bit parameter accessors. 1.444 +nsresult 1.445 +nsGonkCameraControl::Set(uint32_t aKey, int64_t aValue) 1.446 +{ 1.447 + return SetAndPush(aKey, aValue); 1.448 +} 1.449 + 1.450 +nsresult 1.451 +nsGonkCameraControl::Get(uint32_t aKey, int64_t& aRet) 1.452 +{ 1.453 + return mParams.Get(aKey, aRet); 1.454 +} 1.455 + 1.456 +// Weighted-region parameter accessors. 1.457 +nsresult 1.458 +nsGonkCameraControl::Set(uint32_t aKey, const nsTArray<Region>& aRegions) 1.459 +{ 1.460 + return SetAndPush(aKey, aRegions); 1.461 +} 1.462 + 1.463 +nsresult 1.464 +nsGonkCameraControl::Get(uint32_t aKey, nsTArray<Region>& aRegions) 1.465 +{ 1.466 + return mParams.Get(aKey, aRegions); 1.467 +} 1.468 + 1.469 +// Singleton-size parameter accessors. 1.470 +nsresult 1.471 +nsGonkCameraControl::Set(uint32_t aKey, const Size& aSize) 1.472 +{ 1.473 + switch (aKey) { 1.474 + case CAMERA_PARAM_PICTURE_SIZE: 1.475 + DOM_CAMERA_LOGI("setting picture size to %ux%u\n", aSize.width, aSize.height); 1.476 + return SetPictureSize(aSize); 1.477 + 1.478 + case CAMERA_PARAM_THUMBNAILSIZE: 1.479 + DOM_CAMERA_LOGI("setting thumbnail size to %ux%u\n", aSize.width, aSize.height); 1.480 + return SetThumbnailSize(aSize); 1.481 + 1.482 + default: 1.483 + return SetAndPush(aKey, aSize); 1.484 + } 1.485 +} 1.486 + 1.487 +nsresult 1.488 +nsGonkCameraControl::Get(uint32_t aKey, Size& aSize) 1.489 +{ 1.490 + return mParams.Get(aKey, aSize); 1.491 +} 1.492 + 1.493 +// Signed int parameter accessors. 1.494 +nsresult 1.495 +nsGonkCameraControl::Set(uint32_t aKey, int aValue) 1.496 +{ 1.497 + if (aKey == CAMERA_PARAM_PICTURE_ROTATION) { 1.498 + RETURN_IF_NO_CAMERA_HW(); 1.499 + aValue = RationalizeRotation(aValue + mCameraHw->GetSensorOrientation()); 1.500 + } 1.501 + return SetAndPush(aKey, aValue); 1.502 +} 1.503 + 1.504 +nsresult 1.505 +nsGonkCameraControl::Get(uint32_t aKey, int& aRet) 1.506 +{ 1.507 + if (aKey == CAMERA_PARAM_SENSORANGLE) { 1.508 + RETURN_IF_NO_CAMERA_HW(); 1.509 + aRet = mCameraHw->GetSensorOrientation(); 1.510 + return NS_OK; 1.511 + } 1.512 + 1.513 + return mParams.Get(aKey, aRet); 1.514 +} 1.515 + 1.516 +// GPS location parameter accessors. 1.517 +nsresult 1.518 +nsGonkCameraControl::SetLocation(const Position& aLocation) 1.519 +{ 1.520 + return SetAndPush(CAMERA_PARAM_PICTURE_LOCATION, aLocation); 1.521 +} 1.522 + 1.523 +nsresult 1.524 +nsGonkCameraControl::StartPreviewImpl() 1.525 +{ 1.526 + MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread); 1.527 + RETURN_IF_NO_CAMERA_HW(); 1.528 + 1.529 + ReentrantMonitorAutoEnter mon(mReentrantMonitor); 1.530 + 1.531 + if (mPreviewState == CameraControlListener::kPreviewStarted) { 1.532 + DOM_CAMERA_LOGW("Camera preview already started, nothing to do\n"); 1.533 + return NS_OK; 1.534 + } 1.535 + 1.536 + DOM_CAMERA_LOGI("Starting preview (this=%p)\n", this); 1.537 + 1.538 + if (mCameraHw->StartPreview() != OK) { 1.539 + DOM_CAMERA_LOGE("Failed to start camera preview\n"); 1.540 + return NS_ERROR_FAILURE; 1.541 + } 1.542 + 1.543 + OnPreviewStateChange(CameraControlListener::kPreviewStarted); 1.544 + return NS_OK; 1.545 +} 1.546 + 1.547 +nsresult 1.548 +nsGonkCameraControl::StopPreviewImpl() 1.549 +{ 1.550 + RETURN_IF_NO_CAMERA_HW(); 1.551 + 1.552 + DOM_CAMERA_LOGI("Stopping preview (this=%p)\n", this); 1.553 + 1.554 + mCameraHw->StopPreview(); 1.555 + OnPreviewStateChange(CameraControlListener::kPreviewStopped); 1.556 + return NS_OK; 1.557 +} 1.558 + 1.559 +nsresult 1.560 +nsGonkCameraControl::PausePreview() 1.561 +{ 1.562 + RETURN_IF_NO_CAMERA_HW(); 1.563 + 1.564 + DOM_CAMERA_LOGI("Pausing preview (this=%p)\n", this); 1.565 + 1.566 + mCameraHw->StopPreview(); 1.567 + OnPreviewStateChange(CameraControlListener::kPreviewPaused); 1.568 + return NS_OK; 1.569 +} 1.570 + 1.571 +nsresult 1.572 +nsGonkCameraControl::AutoFocusImpl() 1.573 +{ 1.574 + MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread); 1.575 + RETURN_IF_NO_CAMERA_HW(); 1.576 + 1.577 + DOM_CAMERA_LOGI("Starting auto focus\n"); 1.578 + 1.579 + if (mCameraHw->AutoFocus() != OK) { 1.580 + return NS_ERROR_FAILURE; 1.581 + } 1.582 + return NS_OK; 1.583 +} 1.584 + 1.585 +nsresult 1.586 +nsGonkCameraControl::StartFaceDetectionImpl() 1.587 +{ 1.588 + MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread); 1.589 + RETURN_IF_NO_CAMERA_HW(); 1.590 + 1.591 + DOM_CAMERA_LOGI("Starting face detection\n"); 1.592 + 1.593 + if (mCameraHw->StartFaceDetection() != OK) { 1.594 + return NS_ERROR_FAILURE; 1.595 + } 1.596 + return NS_OK; 1.597 +} 1.598 + 1.599 +nsresult 1.600 +nsGonkCameraControl::StopFaceDetectionImpl() 1.601 +{ 1.602 + MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread); 1.603 + RETURN_IF_NO_CAMERA_HW(); 1.604 + 1.605 + DOM_CAMERA_LOGI("Stopping face detection\n"); 1.606 + 1.607 + if (mCameraHw->StopFaceDetection() != OK) { 1.608 + return NS_ERROR_FAILURE; 1.609 + } 1.610 + return NS_OK; 1.611 +} 1.612 + 1.613 +nsresult 1.614 +nsGonkCameraControl::SetThumbnailSizeImpl(const Size& aSize) 1.615 +{ 1.616 + MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread); 1.617 + 1.618 + /** 1.619 + * We keep a copy of the specified size so that if the picture size 1.620 + * changes, we can choose a new thumbnail size close to what was asked for 1.621 + * last time. 1.622 + */ 1.623 + mLastThumbnailSize = aSize; 1.624 + 1.625 + /** 1.626 + * If either of width or height is zero, set the other to zero as well. 1.627 + * This should disable inclusion of a thumbnail in the final picture. 1.628 + */ 1.629 + if (!aSize.width || !aSize.height) { 1.630 + DOM_CAMERA_LOGW("Requested thumbnail size %ux%u, disabling thumbnail\n", 1.631 + aSize.width, aSize.height); 1.632 + Size size = { 0, 0 }; 1.633 + return SetAndPush(CAMERA_PARAM_THUMBNAILSIZE, size); 1.634 + } 1.635 + 1.636 + /** 1.637 + * Choose the supported thumbnail size that is closest to the specified size. 1.638 + * Some drivers will fail to take a picture if the thumbnail does not have 1.639 + * the same aspect ratio as the set picture size, so we need to enforce that 1.640 + * too. 1.641 + */ 1.642 + int smallestDelta = INT_MAX; 1.643 + uint32_t smallestDeltaIndex = UINT32_MAX; 1.644 + int targetArea = aSize.width * aSize.height; 1.645 + 1.646 + nsAutoTArray<Size, 8> supportedSizes; 1.647 + Get(CAMERA_PARAM_SUPPORTED_JPEG_THUMBNAIL_SIZES, supportedSizes); 1.648 + 1.649 + for (uint32_t i = 0; i < supportedSizes.Length(); ++i) { 1.650 + int area = supportedSizes[i].width * supportedSizes[i].height; 1.651 + int delta = abs(area - targetArea); 1.652 + 1.653 + if (area != 0 1.654 + && delta < smallestDelta 1.655 + && supportedSizes[i].width * mLastPictureSize.height / 1.656 + supportedSizes[i].height == mLastPictureSize.width 1.657 + ) { 1.658 + smallestDelta = delta; 1.659 + smallestDeltaIndex = i; 1.660 + } 1.661 + } 1.662 + 1.663 + if (smallestDeltaIndex == UINT32_MAX) { 1.664 + DOM_CAMERA_LOGW("Unable to find a thumbnail size close to %ux%u, disabling thumbnail\n", 1.665 + aSize.width, aSize.height); 1.666 + // If we are unable to find a thumbnail size with a suitable aspect ratio, 1.667 + // just disable the thumbnail altogether. 1.668 + Size size = { 0, 0 }; 1.669 + return SetAndPush(CAMERA_PARAM_THUMBNAILSIZE, size); 1.670 + } 1.671 + 1.672 + Size size = supportedSizes[smallestDeltaIndex]; 1.673 + DOM_CAMERA_LOGI("camera-param set thumbnail-size = %ux%u (requested %ux%u)\n", 1.674 + size.width, size.height, aSize.width, aSize.height); 1.675 + if (size.width > INT32_MAX || size.height > INT32_MAX) { 1.676 + DOM_CAMERA_LOGE("Supported thumbnail size is too big, no change\n"); 1.677 + return NS_ERROR_FAILURE; 1.678 + } 1.679 + 1.680 + return SetAndPush(CAMERA_PARAM_THUMBNAILSIZE, size); 1.681 +} 1.682 + 1.683 +nsresult 1.684 +nsGonkCameraControl::SetThumbnailSize(const Size& aSize) 1.685 +{ 1.686 + class SetThumbnailSize : public nsRunnable 1.687 + { 1.688 + public: 1.689 + SetThumbnailSize(nsGonkCameraControl* aCameraControl, const Size& aSize) 1.690 + : mCameraControl(aCameraControl) 1.691 + , mSize(aSize) 1.692 + { 1.693 + MOZ_COUNT_CTOR(SetThumbnailSize); 1.694 + } 1.695 + ~SetThumbnailSize() { MOZ_COUNT_DTOR(SetThumbnailSize); } 1.696 + 1.697 + NS_IMETHODIMP 1.698 + Run() MOZ_OVERRIDE 1.699 + { 1.700 + nsresult rv = mCameraControl->SetThumbnailSizeImpl(mSize); 1.701 + if (NS_FAILED(rv)) { 1.702 + mCameraControl->OnError(CameraControlListener::kInUnspecified, 1.703 + CameraControlListener::kErrorSetThumbnailSizeFailed); 1.704 + } 1.705 + return NS_OK; 1.706 + } 1.707 + 1.708 + protected: 1.709 + nsRefPtr<nsGonkCameraControl> mCameraControl; 1.710 + Size mSize; 1.711 + }; 1.712 + 1.713 + if (NS_GetCurrentThread() == mCameraThread) { 1.714 + return SetThumbnailSizeImpl(aSize); 1.715 + } 1.716 + 1.717 + return mCameraThread->Dispatch(new SetThumbnailSize(this, aSize), NS_DISPATCH_NORMAL); 1.718 +} 1.719 + 1.720 +nsresult 1.721 +nsGonkCameraControl::UpdateThumbnailSize() 1.722 +{ 1.723 + return SetThumbnailSize(mLastThumbnailSize); 1.724 +} 1.725 + 1.726 +nsresult 1.727 +nsGonkCameraControl::SetPictureSizeImpl(const Size& aSize) 1.728 +{ 1.729 + MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread); 1.730 + 1.731 + /** 1.732 + * Some drivers are less friendly about getting one of these set to zero, 1.733 + * so if either is not specified, ignore both and go with current or 1.734 + * default settings. 1.735 + */ 1.736 + if (!aSize.width || !aSize.height) { 1.737 + DOM_CAMERA_LOGW("Ignoring requested picture size of %ux%u\n", aSize.width, aSize.height); 1.738 + return NS_ERROR_INVALID_ARG; 1.739 + } 1.740 + 1.741 + if (aSize.width == mLastPictureSize.width && aSize.height == mLastPictureSize.height) { 1.742 + DOM_CAMERA_LOGI("Requested picture size %ux%u unchanged\n", aSize.width, aSize.height); 1.743 + return NS_OK; 1.744 + } 1.745 + 1.746 + /** 1.747 + * Choose the supported picture size that is closest in area to the 1.748 + * specified size. Some drivers will fail to take a picture if the 1.749 + * thumbnail size is not the same aspect ratio, so we update that 1.750 + * as well to a size closest to the last user-requested one. 1.751 + */ 1.752 + int smallestDelta = INT_MAX; 1.753 + uint32_t smallestDeltaIndex = UINT32_MAX; 1.754 + int targetArea = aSize.width * aSize.height; 1.755 + 1.756 + nsAutoTArray<Size, 8> supportedSizes; 1.757 + Get(CAMERA_PARAM_SUPPORTED_PICTURESIZES, supportedSizes); 1.758 + 1.759 + for (uint32_t i = 0; i < supportedSizes.Length(); ++i) { 1.760 + int area = supportedSizes[i].width * supportedSizes[i].height; 1.761 + int delta = abs(area - targetArea); 1.762 + 1.763 + if (area != 0 && delta < smallestDelta) { 1.764 + smallestDelta = delta; 1.765 + smallestDeltaIndex = i; 1.766 + } 1.767 + } 1.768 + 1.769 + if (smallestDeltaIndex == UINT32_MAX) { 1.770 + DOM_CAMERA_LOGW("Unable to find a picture size close to %ux%u\n", 1.771 + aSize.width, aSize.height); 1.772 + return NS_ERROR_INVALID_ARG; 1.773 + } 1.774 + 1.775 + Size size = supportedSizes[smallestDeltaIndex]; 1.776 + DOM_CAMERA_LOGI("camera-param set picture-size = %ux%u (requested %ux%u)\n", 1.777 + size.width, size.height, aSize.width, aSize.height); 1.778 + if (size.width > INT32_MAX || size.height > INT32_MAX) { 1.779 + DOM_CAMERA_LOGE("Supported picture size is too big, no change\n"); 1.780 + return NS_ERROR_FAILURE; 1.781 + } 1.782 + 1.783 + nsresult rv = mParams.Set(CAMERA_PARAM_PICTURE_SIZE, size); 1.784 + if (NS_FAILED(rv)) { 1.785 + return rv; 1.786 + } 1.787 + 1.788 + mLastPictureSize = size; 1.789 + 1.790 + // Finally, update the thumbnail size in case the picture 1.791 + // aspect ratio changed. 1.792 + return UpdateThumbnailSize(); 1.793 +} 1.794 + 1.795 +int32_t 1.796 +nsGonkCameraControl::RationalizeRotation(int32_t aRotation) 1.797 +{ 1.798 + int32_t r = aRotation; 1.799 + 1.800 + // The result of this operation is an angle from 0..270 degrees, 1.801 + // in steps of 90 degrees. Angles are rounded to the nearest 1.802 + // magnitude, so 45 will be rounded to 90, and -45 will be rounded 1.803 + // to -90 (not 0). 1.804 + if (r >= 0) { 1.805 + r += 45; 1.806 + } else { 1.807 + r -= 45; 1.808 + } 1.809 + r /= 90; 1.810 + r %= 4; 1.811 + r *= 90; 1.812 + if (r < 0) { 1.813 + r += 360; 1.814 + } 1.815 + 1.816 + return r; 1.817 +} 1.818 + 1.819 +nsresult 1.820 +nsGonkCameraControl::SetPictureSize(const Size& aSize) 1.821 +{ 1.822 + class SetPictureSize : public nsRunnable 1.823 + { 1.824 + public: 1.825 + SetPictureSize(nsGonkCameraControl* aCameraControl, const Size& aSize) 1.826 + : mCameraControl(aCameraControl) 1.827 + , mSize(aSize) 1.828 + { 1.829 + MOZ_COUNT_CTOR(SetPictureSize); 1.830 + } 1.831 + ~SetPictureSize() { MOZ_COUNT_DTOR(SetPictureSize); } 1.832 + 1.833 + NS_IMETHODIMP 1.834 + Run() MOZ_OVERRIDE 1.835 + { 1.836 + nsresult rv = mCameraControl->SetPictureSizeImpl(mSize); 1.837 + if (NS_FAILED(rv)) { 1.838 + mCameraControl->OnError(CameraControlListener::kInUnspecified, 1.839 + CameraControlListener::kErrorSetPictureSizeFailed); 1.840 + } 1.841 + return NS_OK; 1.842 + } 1.843 + 1.844 + protected: 1.845 + nsRefPtr<nsGonkCameraControl> mCameraControl; 1.846 + Size mSize; 1.847 + }; 1.848 + 1.849 + if (NS_GetCurrentThread() == mCameraThread) { 1.850 + return SetPictureSizeImpl(aSize); 1.851 + } 1.852 + 1.853 + return mCameraThread->Dispatch(new SetPictureSize(this, aSize), NS_DISPATCH_NORMAL); 1.854 +} 1.855 + 1.856 +nsresult 1.857 +nsGonkCameraControl::TakePictureImpl() 1.858 +{ 1.859 + MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread); 1.860 + RETURN_IF_NO_CAMERA_HW(); 1.861 + 1.862 + if (mCameraHw->TakePicture() != OK) { 1.863 + return NS_ERROR_FAILURE; 1.864 + } 1.865 + 1.866 + // In Gonk, taking a picture implicitly stops the preview stream, 1.867 + // so we need to reflect that here. 1.868 + OnPreviewStateChange(CameraControlListener::kPreviewPaused); 1.869 + return NS_OK; 1.870 +} 1.871 + 1.872 +nsresult 1.873 +nsGonkCameraControl::PushParametersImpl() 1.874 +{ 1.875 + MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread); 1.876 + DOM_CAMERA_LOGI("Pushing camera parameters\n"); 1.877 + RETURN_IF_NO_CAMERA_HW(); 1.878 + 1.879 + if (mCameraHw->PushParameters(mParams) != OK) { 1.880 + return NS_ERROR_FAILURE; 1.881 + } 1.882 + 1.883 + return NS_OK; 1.884 +} 1.885 + 1.886 +nsresult 1.887 +nsGonkCameraControl::PullParametersImpl() 1.888 +{ 1.889 + MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread); 1.890 + DOM_CAMERA_LOGI("Pulling camera parameters\n"); 1.891 + RETURN_IF_NO_CAMERA_HW(); 1.892 + 1.893 + return mCameraHw->PullParameters(mParams); 1.894 +} 1.895 + 1.896 +nsresult 1.897 +nsGonkCameraControl::SetupRecordingFlash(bool aAutoEnableLowLightTorch) 1.898 +{ 1.899 + mAutoFlashModeOverridden = false; 1.900 + 1.901 + if (!aAutoEnableLowLightTorch || !mLuminanceSupported || !mFlashSupported) { 1.902 + return NS_OK; 1.903 + } 1.904 + 1.905 + DOM_CAMERA_LOGI("Luminance reporting and flash supported\n"); 1.906 + 1.907 + nsresult rv = PullParametersImpl(); 1.908 + if (NS_WARN_IF(NS_FAILED(rv))) { 1.909 + return rv; 1.910 + } 1.911 + 1.912 + nsString luminance; 1.913 + rv = mParams.Get(CAMERA_PARAM_LUMINANCE, luminance); 1.914 + if (NS_WARN_IF(NS_FAILED(rv))) { 1.915 + // If we failed to get the luminance, assume it's "high" 1.916 + return NS_OK; 1.917 + } 1.918 + 1.919 + nsString flashMode; 1.920 + rv = mParams.Get(CAMERA_PARAM_FLASHMODE, flashMode); 1.921 + if (NS_WARN_IF(NS_FAILED(rv))) { 1.922 + // If we failed to get the current flash mode, swallow the error 1.923 + return NS_OK; 1.924 + } 1.925 + 1.926 + if (luminance.EqualsASCII("low") && flashMode.EqualsASCII("auto")) { 1.927 + DOM_CAMERA_LOGI("Low luminance detected, turning on flash\n"); 1.928 + rv = SetAndPush(CAMERA_PARAM_FLASHMODE, NS_LITERAL_STRING("torch")); 1.929 + if (NS_WARN_IF(NS_FAILED(rv))) { 1.930 + // If we failed to turn on the flash, swallow the error 1.931 + return NS_OK; 1.932 + } 1.933 + 1.934 + mAutoFlashModeOverridden = true; 1.935 + } 1.936 + 1.937 + return NS_OK; 1.938 +} 1.939 + 1.940 +nsresult 1.941 +nsGonkCameraControl::StartRecordingImpl(DeviceStorageFileDescriptor* aFileDescriptor, 1.942 + const StartRecordingOptions* aOptions) 1.943 +{ 1.944 + MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread); 1.945 + 1.946 + NS_ENSURE_TRUE(mRecorderProfile, NS_ERROR_NOT_INITIALIZED); 1.947 + NS_ENSURE_FALSE(mRecorder, NS_ERROR_FAILURE); 1.948 + 1.949 + /** 1.950 + * Get the base path from device storage and append the app-specified 1.951 + * filename to it. The filename may include a relative subpath 1.952 + * (e.g.) "DCIM/IMG_0001.jpg". 1.953 + * 1.954 + * The camera app needs to provide the file extension '.3gp' for now. 1.955 + * See bug 795202. 1.956 + */ 1.957 + NS_ENSURE_TRUE(aFileDescriptor, NS_ERROR_FAILURE); 1.958 + nsAutoString fullPath; 1.959 + mVideoFile = aFileDescriptor->mDSFile; 1.960 + mVideoFile->GetFullPath(fullPath); 1.961 + DOM_CAMERA_LOGI("Video filename is '%s'\n", 1.962 + NS_LossyConvertUTF16toASCII(fullPath).get()); 1.963 + 1.964 + if (!mVideoFile->IsSafePath()) { 1.965 + DOM_CAMERA_LOGE("Invalid video file name\n"); 1.966 + return NS_ERROR_INVALID_ARG; 1.967 + } 1.968 + 1.969 + // SetupRecording creates a dup of the file descriptor, so we need to 1.970 + // close the file descriptor when we leave this function. Also note, that 1.971 + // since we're already off the main thread, we don't need to dispatch this. 1.972 + // We just let the CloseFileRunnable destructor do the work. 1.973 + nsRefPtr<CloseFileRunnable> closer; 1.974 + if (aFileDescriptor->mFileDescriptor.IsValid()) { 1.975 + closer = new CloseFileRunnable(aFileDescriptor->mFileDescriptor); 1.976 + } 1.977 + nsresult rv; 1.978 + int fd = aFileDescriptor->mFileDescriptor.PlatformHandle(); 1.979 + if (aOptions) { 1.980 + rv = SetupRecording(fd, aOptions->rotation, aOptions->maxFileSizeBytes, 1.981 + aOptions->maxVideoLengthMs); 1.982 + if (NS_SUCCEEDED(rv)) { 1.983 + rv = SetupRecordingFlash(aOptions->autoEnableLowLightTorch); 1.984 + } 1.985 + } else { 1.986 + rv = SetupRecording(fd, 0, 0, 0); 1.987 + } 1.988 + if (NS_WARN_IF(NS_FAILED(rv))) { 1.989 + return rv; 1.990 + } 1.991 + 1.992 + if (mRecorder->start() != OK) { 1.993 + DOM_CAMERA_LOGE("mRecorder->start() failed\n"); 1.994 + // important: we MUST destroy the recorder if start() fails! 1.995 + mRecorder = nullptr; 1.996 + // put the flash back to the 'auto' state 1.997 + if (mAutoFlashModeOverridden) { 1.998 + SetAndPush(CAMERA_PARAM_FLASHMODE, NS_LITERAL_STRING("auto")); 1.999 + } 1.1000 + return NS_ERROR_FAILURE; 1.1001 + } 1.1002 + 1.1003 + OnRecorderStateChange(CameraControlListener::kRecorderStarted); 1.1004 + return NS_OK; 1.1005 +} 1.1006 + 1.1007 +nsresult 1.1008 +nsGonkCameraControl::StopRecordingImpl() 1.1009 +{ 1.1010 + class RecordingComplete : public nsRunnable 1.1011 + { 1.1012 + public: 1.1013 + RecordingComplete(DeviceStorageFile* aFile) 1.1014 + : mFile(aFile) 1.1015 + { } 1.1016 + 1.1017 + ~RecordingComplete() { } 1.1018 + 1.1019 + NS_IMETHODIMP 1.1020 + Run() 1.1021 + { 1.1022 + MOZ_ASSERT(NS_IsMainThread()); 1.1023 + 1.1024 + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); 1.1025 + obs->NotifyObservers(mFile, "file-watcher-notify", NS_LITERAL_STRING("modified").get()); 1.1026 + return NS_OK; 1.1027 + } 1.1028 + 1.1029 + private: 1.1030 + nsRefPtr<DeviceStorageFile> mFile; 1.1031 + }; 1.1032 + 1.1033 + MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread); 1.1034 + 1.1035 + // nothing to do if we have no mRecorder 1.1036 + NS_ENSURE_TRUE(mRecorder, NS_OK); 1.1037 + 1.1038 + mRecorder->stop(); 1.1039 + mRecorder = nullptr; 1.1040 + OnRecorderStateChange(CameraControlListener::kRecorderStopped); 1.1041 + 1.1042 + if (mAutoFlashModeOverridden) { 1.1043 + SetAndPush(CAMERA_PARAM_FLASHMODE, NS_LITERAL_STRING("auto")); 1.1044 + } 1.1045 + 1.1046 + // notify DeviceStorage that the new video file is closed and ready 1.1047 + return NS_DispatchToMainThread(new RecordingComplete(mVideoFile), NS_DISPATCH_NORMAL); 1.1048 +} 1.1049 + 1.1050 +nsresult 1.1051 +nsGonkCameraControl::ResumeContinuousFocusImpl() 1.1052 +{ 1.1053 + MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread); 1.1054 + RETURN_IF_NO_CAMERA_HW(); 1.1055 + 1.1056 + DOM_CAMERA_LOGI("Resuming continuous autofocus\n"); 1.1057 + 1.1058 + // see 1.1059 + // http://developer.android.com/reference/android/hardware/Camera.Parameters.html#FOCUS_MODE_CONTINUOUS_PICTURE 1.1060 + if (NS_WARN_IF(mCameraHw->CancelAutoFocus() != OK)) { 1.1061 + return NS_ERROR_FAILURE; 1.1062 + } 1.1063 + 1.1064 + return NS_OK; 1.1065 +} 1.1066 + 1.1067 +void 1.1068 +nsGonkCameraControl::OnAutoFocusComplete(bool aSuccess) 1.1069 +{ 1.1070 + class AutoFocusComplete : public nsRunnable 1.1071 + { 1.1072 + public: 1.1073 + AutoFocusComplete(nsGonkCameraControl* aCameraControl, bool aSuccess) 1.1074 + : mCameraControl(aCameraControl) 1.1075 + , mSuccess(aSuccess) 1.1076 + { } 1.1077 + 1.1078 + NS_IMETHODIMP 1.1079 + Run() MOZ_OVERRIDE 1.1080 + { 1.1081 + mCameraControl->OnAutoFocusComplete(mSuccess); 1.1082 + return NS_OK; 1.1083 + } 1.1084 + 1.1085 + protected: 1.1086 + nsRefPtr<nsGonkCameraControl> mCameraControl; 1.1087 + bool mSuccess; 1.1088 + }; 1.1089 + 1.1090 + if (NS_GetCurrentThread() == mCameraThread) { 1.1091 + /** 1.1092 + * Auto focusing can change some of the camera's parameters, so 1.1093 + * we need to pull a new set before notifying any clients. 1.1094 + */ 1.1095 + PullParametersImpl(); 1.1096 + CameraControlImpl::OnAutoFocusComplete(aSuccess); 1.1097 + return; 1.1098 + } 1.1099 + 1.1100 + /** 1.1101 + * Because the callback needs to call PullParametersImpl(), 1.1102 + * we need to dispatch this callback through the Camera Thread. 1.1103 + */ 1.1104 + mCameraThread->Dispatch(new AutoFocusComplete(this, aSuccess), NS_DISPATCH_NORMAL); 1.1105 +} 1.1106 + 1.1107 +bool 1.1108 +FeatureDetected(int32_t feature[]) 1.1109 +{ 1.1110 + /** 1.1111 + * For information on what constitutes a valid feature, see: 1.1112 + * http://androidxref.com/4.0.4/xref/system/core/include/system/camera.h#202 1.1113 + * 1.1114 + * Although the comments explicitly state that undetected features are 1.1115 + * indicated using the value -2000, we conservatively include anything 1.1116 + * outside the explicitly valid range of [-1000, 1000] as undetected 1.1117 + * as well. 1.1118 + */ 1.1119 + const int32_t kLowerFeatureBound = -1000; 1.1120 + const int32_t kUpperFeatureBound = 1000; 1.1121 + return (feature[0] >= kLowerFeatureBound && feature[0] <= kUpperFeatureBound) || 1.1122 + (feature[1] >= kLowerFeatureBound && feature[1] <= kUpperFeatureBound); 1.1123 +} 1.1124 + 1.1125 +void 1.1126 +nsGonkCameraControl::OnFacesDetected(camera_frame_metadata_t* aMetaData) 1.1127 +{ 1.1128 + NS_ENSURE_TRUE_VOID(aMetaData); 1.1129 + 1.1130 + nsTArray<Face> faces; 1.1131 + uint32_t numFaces = aMetaData->number_of_faces; 1.1132 + DOM_CAMERA_LOGI("Camera detected %d face(s)", numFaces); 1.1133 + 1.1134 + faces.SetCapacity(numFaces); 1.1135 + 1.1136 + for (uint32_t i = 0; i < numFaces; ++i) { 1.1137 + Face* f = faces.AppendElement(); 1.1138 + 1.1139 + f->id = aMetaData->faces[i].id; 1.1140 + f->score = aMetaData->faces[i].score; 1.1141 + if (f->score > 100) { 1.1142 + f->score = 100; 1.1143 + } 1.1144 + f->bound.left = aMetaData->faces[i].rect[0]; 1.1145 + f->bound.top = aMetaData->faces[i].rect[1]; 1.1146 + f->bound.right = aMetaData->faces[i].rect[2]; 1.1147 + f->bound.bottom = aMetaData->faces[i].rect[3]; 1.1148 + DOM_CAMERA_LOGI("Camera face[%u] appended: id=%d, score=%d, bound=(%d, %d)-(%d, %d)\n", 1.1149 + i, f->id, f->score, f->bound.left, f->bound.top, f->bound.right, f->bound.bottom); 1.1150 + 1.1151 + f->hasLeftEye = FeatureDetected(aMetaData->faces[i].left_eye); 1.1152 + if (f->hasLeftEye) { 1.1153 + f->leftEye.x = aMetaData->faces[i].left_eye[0]; 1.1154 + f->leftEye.y = aMetaData->faces[i].left_eye[1]; 1.1155 + DOM_CAMERA_LOGI(" Left eye detected at (%d, %d)\n", 1.1156 + f->leftEye.x, f->leftEye.y); 1.1157 + } else { 1.1158 + DOM_CAMERA_LOGI(" No left eye detected\n"); 1.1159 + } 1.1160 + 1.1161 + f->hasRightEye = FeatureDetected(aMetaData->faces[i].right_eye); 1.1162 + if (f->hasRightEye) { 1.1163 + f->rightEye.x = aMetaData->faces[i].right_eye[0]; 1.1164 + f->rightEye.y = aMetaData->faces[i].right_eye[1]; 1.1165 + DOM_CAMERA_LOGI(" Right eye detected at (%d, %d)\n", 1.1166 + f->rightEye.x, f->rightEye.y); 1.1167 + } else { 1.1168 + DOM_CAMERA_LOGI(" No right eye detected\n"); 1.1169 + } 1.1170 + 1.1171 + f->hasMouth = FeatureDetected(aMetaData->faces[i].mouth); 1.1172 + if (f->hasMouth) { 1.1173 + f->mouth.x = aMetaData->faces[i].mouth[0]; 1.1174 + f->mouth.y = aMetaData->faces[i].mouth[1]; 1.1175 + DOM_CAMERA_LOGI(" Mouth detected at (%d, %d)\n", f->mouth.x, f->mouth.y); 1.1176 + } else { 1.1177 + DOM_CAMERA_LOGI(" No mouth detected\n"); 1.1178 + } 1.1179 + } 1.1180 + 1.1181 + CameraControlImpl::OnFacesDetected(faces); 1.1182 +} 1.1183 + 1.1184 +void 1.1185 +nsGonkCameraControl::OnTakePictureComplete(uint8_t* aData, uint32_t aLength) 1.1186 +{ 1.1187 + ReentrantMonitorAutoEnter mon(mReentrantMonitor); 1.1188 + 1.1189 + uint8_t* data = new uint8_t[aLength]; 1.1190 + 1.1191 + memcpy(data, aData, aLength); 1.1192 + 1.1193 + nsString s(NS_LITERAL_STRING("image/")); 1.1194 + s.Append(mFileFormat); 1.1195 + DOM_CAMERA_LOGI("Got picture, type '%s', %u bytes\n", NS_ConvertUTF16toUTF8(s).get(), aLength); 1.1196 + OnTakePictureComplete(data, aLength, s); 1.1197 + 1.1198 + if (mResumePreviewAfterTakingPicture) { 1.1199 + nsresult rv = StartPreview(); 1.1200 + if (NS_FAILED(rv)) { 1.1201 + DOM_CAMERA_LOGE("Failed to restart camera preview (%x)\n", rv); 1.1202 + OnPreviewStateChange(CameraControlListener::kPreviewStopped); 1.1203 + } 1.1204 + } 1.1205 + 1.1206 + DOM_CAMERA_LOGI("nsGonkCameraControl::OnTakePictureComplete() done\n"); 1.1207 +} 1.1208 + 1.1209 +void 1.1210 +nsGonkCameraControl::OnTakePictureError() 1.1211 +{ 1.1212 + CameraControlImpl::OnError(CameraControlListener::kInTakePicture, 1.1213 + CameraControlListener::kErrorApiFailed); 1.1214 +} 1.1215 + 1.1216 +nsresult 1.1217 +nsGonkCameraControl::SetPreviewSize(const Size& aSize) 1.1218 +{ 1.1219 + MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread); 1.1220 + 1.1221 + nsTArray<Size> previewSizes; 1.1222 + nsresult rv = mParams.Get(CAMERA_PARAM_SUPPORTED_PREVIEWSIZES, previewSizes); 1.1223 + if (NS_FAILED(rv)) { 1.1224 + DOM_CAMERA_LOGE("Camera failed to return any preview sizes (0x%x)\n", rv); 1.1225 + return rv; 1.1226 + } 1.1227 + 1.1228 + Size best; 1.1229 + rv = GetSupportedSize(aSize, previewSizes, best); 1.1230 + if (NS_FAILED(rv)) { 1.1231 + DOM_CAMERA_LOGE("Failed to find a supported preview size, requested size %dx%d", 1.1232 + aSize.width, aSize.height); 1.1233 + return rv; 1.1234 + } 1.1235 + 1.1236 + // Some camera drivers will ignore our preview size if it's larger 1.1237 + // than the currently set video recording size, so we need to set 1.1238 + // the video size here as well, just in case. 1.1239 + if (best.width > mLastRecorderSize.width || best.height > mLastRecorderSize.height) { 1.1240 + SetVideoSize(best); 1.1241 + } 1.1242 + mCurrentConfiguration.mPreviewSize = best; 1.1243 + return mParams.Set(CAMERA_PARAM_PREVIEWSIZE, best); 1.1244 +} 1.1245 + 1.1246 +nsresult 1.1247 +nsGonkCameraControl::SetVideoSize(const Size& aSize) 1.1248 +{ 1.1249 + MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread); 1.1250 + 1.1251 + nsTArray<Size> videoSizes; 1.1252 + nsresult rv = mParams.Get(CAMERA_PARAM_SUPPORTED_VIDEOSIZES, videoSizes); 1.1253 + if (NS_FAILED(rv)) { 1.1254 + DOM_CAMERA_LOGE("Camera failed to return any video sizes (0x%x)\n", rv); 1.1255 + return rv; 1.1256 + } 1.1257 + 1.1258 + Size best; 1.1259 + rv = GetSupportedSize(aSize, videoSizes, best); 1.1260 + if (NS_FAILED(rv)) { 1.1261 + DOM_CAMERA_LOGE("Failed to find a supported video size, requested size %dx%d", 1.1262 + aSize.width, aSize.height); 1.1263 + return rv; 1.1264 + } 1.1265 + mLastRecorderSize = best; 1.1266 + return mParams.Set(CAMERA_PARAM_VIDEOSIZE, best); 1.1267 +} 1.1268 + 1.1269 +nsresult 1.1270 +nsGonkCameraControl::GetSupportedSize(const Size& aSize, 1.1271 + const nsTArray<Size>& supportedSizes, 1.1272 + Size& best) 1.1273 +{ 1.1274 + nsresult rv = NS_ERROR_INVALID_ARG; 1.1275 + best = aSize; 1.1276 + uint32_t minSizeDelta = UINT32_MAX; 1.1277 + uint32_t delta; 1.1278 + 1.1279 + if (!aSize.width && !aSize.height) { 1.1280 + // no size specified, take the first supported size 1.1281 + best = supportedSizes[0]; 1.1282 + rv = NS_OK; 1.1283 + } else if (aSize.width && aSize.height) { 1.1284 + // both height and width specified, find the supported size closest to requested size 1.1285 + uint32_t targetArea = aSize.width * aSize.height; 1.1286 + for (nsTArray<Size>::index_type i = 0; i < supportedSizes.Length(); i++) { 1.1287 + Size size = supportedSizes[i]; 1.1288 + uint32_t delta = abs((long int)(size.width * size.height - targetArea)); 1.1289 + if (delta < minSizeDelta) { 1.1290 + minSizeDelta = delta; 1.1291 + best = size; 1.1292 + rv = NS_OK; 1.1293 + } 1.1294 + } 1.1295 + } else if (!aSize.width) { 1.1296 + // width not specified, find closest height match 1.1297 + for (nsTArray<Size>::index_type i = 0; i < supportedSizes.Length(); i++) { 1.1298 + Size size = supportedSizes[i]; 1.1299 + delta = abs((long int)(size.height - aSize.height)); 1.1300 + if (delta < minSizeDelta) { 1.1301 + minSizeDelta = delta; 1.1302 + best = size; 1.1303 + rv = NS_OK; 1.1304 + } 1.1305 + } 1.1306 + } else if (!aSize.height) { 1.1307 + // height not specified, find closest width match 1.1308 + for (nsTArray<Size>::index_type i = 0; i < supportedSizes.Length(); i++) { 1.1309 + Size size = supportedSizes[i]; 1.1310 + delta = abs((long int)(size.width - aSize.width)); 1.1311 + if (delta < minSizeDelta) { 1.1312 + minSizeDelta = delta; 1.1313 + best = size; 1.1314 + rv = NS_OK; 1.1315 + } 1.1316 + } 1.1317 + } 1.1318 + return rv; 1.1319 +} 1.1320 + 1.1321 +nsresult 1.1322 +nsGonkCameraControl::SetupVideoMode(const nsAString& aProfile) 1.1323 +{ 1.1324 + DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__); 1.1325 + 1.1326 + // read preferences for camcorder 1.1327 + mMediaProfiles = MediaProfiles::getInstance(); 1.1328 + 1.1329 + nsAutoCString profile = NS_ConvertUTF16toUTF8(aProfile); 1.1330 + // XXXkhuey are we leaking? 1.1331 + mRecorderProfile = GetGonkRecorderProfileManager().take()->Get(profile.get()); 1.1332 + if (!mRecorderProfile) { 1.1333 + DOM_CAMERA_LOGE("Recorder profile '%s' is not supported\n", profile.get()); 1.1334 + return NS_ERROR_INVALID_ARG; 1.1335 + } 1.1336 + 1.1337 + const GonkRecorderVideoProfile* video = mRecorderProfile->GetGonkVideoProfile(); 1.1338 + int width = video->GetWidth(); 1.1339 + int height = video->GetHeight(); 1.1340 + int fps = video->GetFramerate(); 1.1341 + if (fps == -1 || width < 0 || height < 0) { 1.1342 + DOM_CAMERA_LOGE("Can't configure preview with fps=%d, width=%d, height=%d\n", 1.1343 + fps, width, height); 1.1344 + return NS_ERROR_FAILURE; 1.1345 + } 1.1346 + 1.1347 + PullParametersImpl(); 1.1348 + 1.1349 + Size size; 1.1350 + size.width = static_cast<uint32_t>(width); 1.1351 + size.height = static_cast<uint32_t>(height); 1.1352 + 1.1353 + { 1.1354 + ICameraControlParameterSetAutoEnter set(this); 1.1355 + 1.1356 + // The camera interface allows for hardware to provide two video 1.1357 + // streams, a low resolution preview and a potentially high resolution 1.1358 + // stream for encoding. For now we don't use this and set preview and video 1.1359 + // size to the same thing. 1.1360 + nsresult rv = SetVideoSize(size); 1.1361 + if (NS_FAILED(rv)) { 1.1362 + DOM_CAMERA_LOGE("Failed to set video mode video size (0x%x)\n", rv); 1.1363 + return rv; 1.1364 + } 1.1365 + 1.1366 + rv = SetPreviewSize(size); 1.1367 + if (NS_FAILED(rv)) { 1.1368 + DOM_CAMERA_LOGE("Failed to set video mode preview size (0x%x)\n", rv); 1.1369 + return rv; 1.1370 + } 1.1371 + 1.1372 + rv = mParams.Set(CAMERA_PARAM_PREVIEWFRAMERATE, fps); 1.1373 + if (NS_FAILED(rv)) { 1.1374 + DOM_CAMERA_LOGE("Failed to set video mode frame rate (0x%x)\n", rv); 1.1375 + return rv; 1.1376 + } 1.1377 + 1.1378 + rv = PushParameters(); 1.1379 + if (NS_FAILED(rv)) { 1.1380 + DOM_CAMERA_LOGE("Failed to set video mode settings (0x%x)\n", rv); 1.1381 + return rv; 1.1382 + } 1.1383 + 1.1384 + mPreviewFps = fps; 1.1385 + } 1.1386 + return NS_OK; 1.1387 +} 1.1388 + 1.1389 +class GonkRecorderListener : public IMediaRecorderClient 1.1390 +{ 1.1391 +public: 1.1392 + GonkRecorderListener(nsGonkCameraControl* aCameraControl) 1.1393 + : mCameraControl(aCameraControl) 1.1394 + { 1.1395 + DOM_CAMERA_LOGT("%s:%d : this=%p, aCameraControl=%p\n", 1.1396 + __func__, __LINE__, this, mCameraControl.get()); 1.1397 + } 1.1398 + 1.1399 + void notify(int msg, int ext1, int ext2) 1.1400 + { 1.1401 + if (mCameraControl) { 1.1402 + mCameraControl->OnRecorderEvent(msg, ext1, ext2); 1.1403 + } 1.1404 + } 1.1405 + 1.1406 + IBinder* onAsBinder() 1.1407 + { 1.1408 + DOM_CAMERA_LOGE("onAsBinder() called, should NEVER get called!\n"); 1.1409 + return nullptr; 1.1410 + } 1.1411 + 1.1412 +protected: 1.1413 + ~GonkRecorderListener() { } 1.1414 + nsRefPtr<nsGonkCameraControl> mCameraControl; 1.1415 +}; 1.1416 + 1.1417 +void 1.1418 +nsGonkCameraControl::OnRecorderEvent(int msg, int ext1, int ext2) 1.1419 +{ 1.1420 + /** 1.1421 + * Refer to base/include/media/mediarecorder.h for a complete list 1.1422 + * of error and info message codes. There are duplicate values 1.1423 + * within the status/error code space, as determined by code inspection: 1.1424 + * 1.1425 + * +------- msg 1.1426 + * | +----- ext1 1.1427 + * | | +--- ext2 1.1428 + * V V V 1.1429 + * 1 MEDIA_RECORDER_EVENT_ERROR 1.1430 + * 1 MEDIA_RECORDER_ERROR_UNKNOWN 1.1431 + * [3] ERROR_MALFORMED 1.1432 + * 100 mediaplayer.h::MEDIA_ERROR_SERVER_DIED 1.1433 + * 0 <always zero> 1.1434 + * 2 MEDIA_RECORDER_EVENT_INFO 1.1435 + * 800 MEDIA_RECORDER_INFO_MAX_DURATION_REACHED 1.1436 + * 0 <always zero> 1.1437 + * 801 MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED 1.1438 + * 0 <always zero> 1.1439 + * 1000 MEDIA_RECORDER_TRACK_INFO_COMPLETION_STATUS[1b] 1.1440 + * [3] UNKNOWN_ERROR, etc. 1.1441 + * 100 MEDIA_ERROR[4] 1.1442 + * 100 mediaplayer.h::MEDIA_ERROR_SERVER_DIED 1.1443 + * 0 <always zero> 1.1444 + * 100 MEDIA_RECORDER_TRACK_EVENT_ERROR 1.1445 + * 100 MEDIA_RECORDER_TRACK_ERROR_GENERAL[1a] 1.1446 + * [3] UNKNOWN_ERROR, etc. 1.1447 + * 200 MEDIA_RECORDER_ERROR_VIDEO_NO_SYNC_FRAME[2] 1.1448 + * ? <unknown> 1.1449 + * 101 MEDIA_RECORDER_TRACK_EVENT_INFO 1.1450 + * 1000 MEDIA_RECORDER_TRACK_INFO_COMPLETION_STATUS[1a] 1.1451 + * [3] UNKNOWN_ERROR, etc. 1.1452 + * N see mediarecorder.h::media_recorder_info_type[5] 1.1453 + * 1.1454 + * 1. a) High 4 bits are the track number, the next 12 bits are reserved, 1.1455 + * and the final 16 bits are the actual error code (above). 1.1456 + * b) But not in this case. 1.1457 + * 2. Never actually used in AOSP code? 1.1458 + * 3. Specific error codes are from utils/Errors.h and/or 1.1459 + * include/media/stagefright/MediaErrors.h. 1.1460 + * 4. Only in frameworks/base/media/libmedia/mediaplayer.cpp. 1.1461 + * 5. These are mostly informational and we can ignore them; note that 1.1462 + * although the MEDIA_RECORDER_INFO_MAX_DURATION_REACHED and 1.1463 + * MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED values are defined in this 1.1464 + * enum, they are used with different ext1 codes. /o\ 1.1465 + */ 1.1466 + int trackNum = CameraControlListener::kNoTrackNumber; 1.1467 + 1.1468 + switch (msg) { 1.1469 + // Recorder-related events 1.1470 + case MEDIA_RECORDER_EVENT_INFO: 1.1471 + switch (ext1) { 1.1472 + case MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED: 1.1473 + DOM_CAMERA_LOGI("recorder-event : info: maximum file size reached\n"); 1.1474 + OnRecorderStateChange(CameraControlListener::kFileSizeLimitReached, ext2, trackNum); 1.1475 + return; 1.1476 + 1.1477 + case MEDIA_RECORDER_INFO_MAX_DURATION_REACHED: 1.1478 + DOM_CAMERA_LOGI("recorder-event : info: maximum video duration reached\n"); 1.1479 + OnRecorderStateChange(CameraControlListener::kVideoLengthLimitReached, ext2, trackNum); 1.1480 + return; 1.1481 + 1.1482 + case MEDIA_RECORDER_TRACK_INFO_COMPLETION_STATUS: 1.1483 + DOM_CAMERA_LOGI("recorder-event : info: track completed\n"); 1.1484 + OnRecorderStateChange(CameraControlListener::kTrackCompleted, ext2, trackNum); 1.1485 + return; 1.1486 + } 1.1487 + break; 1.1488 + 1.1489 + case MEDIA_RECORDER_EVENT_ERROR: 1.1490 + switch (ext1) { 1.1491 + case MEDIA_RECORDER_ERROR_UNKNOWN: 1.1492 + DOM_CAMERA_LOGE("recorder-event : recorder-error: %d (0x%08x)\n", ext2, ext2); 1.1493 + OnRecorderStateChange(CameraControlListener::kMediaRecorderFailed, ext2, trackNum); 1.1494 + return; 1.1495 + 1.1496 + case MEDIA_ERROR_SERVER_DIED: 1.1497 + DOM_CAMERA_LOGE("recorder-event : recorder-error: server died\n"); 1.1498 + OnRecorderStateChange(CameraControlListener::kMediaServerFailed, ext2, trackNum); 1.1499 + return; 1.1500 + } 1.1501 + break; 1.1502 + 1.1503 + // Track-related events, see note 1(a) above. 1.1504 + case MEDIA_RECORDER_TRACK_EVENT_INFO: 1.1505 + trackNum = (ext1 & 0xF0000000) >> 28; 1.1506 + ext1 &= 0xFFFF; 1.1507 + switch (ext1) { 1.1508 + case MEDIA_RECORDER_TRACK_INFO_COMPLETION_STATUS: 1.1509 + if (ext2 == OK) { 1.1510 + DOM_CAMERA_LOGI("recorder-event : track-complete: track %d, %d (0x%08x)\n", trackNum, ext2, ext2); 1.1511 + OnRecorderStateChange(CameraControlListener::kTrackCompleted, ext2, trackNum); 1.1512 + return; 1.1513 + } 1.1514 + DOM_CAMERA_LOGE("recorder-event : track-error: track %d, %d (0x%08x)\n", trackNum, ext2, ext2); 1.1515 + OnRecorderStateChange(CameraControlListener::kTrackFailed, ext2, trackNum); 1.1516 + return; 1.1517 + 1.1518 + case MEDIA_RECORDER_TRACK_INFO_PROGRESS_IN_TIME: 1.1519 + DOM_CAMERA_LOGI("recorder-event : track-info: progress in time: %d ms\n", ext2); 1.1520 + return; 1.1521 + } 1.1522 + break; 1.1523 + 1.1524 + case MEDIA_RECORDER_TRACK_EVENT_ERROR: 1.1525 + trackNum = (ext1 & 0xF0000000) >> 28; 1.1526 + ext1 &= 0xFFFF; 1.1527 + DOM_CAMERA_LOGE("recorder-event : track-error: track %d, %d (0x%08x)\n", trackNum, ext2, ext2); 1.1528 + OnRecorderStateChange(CameraControlListener::kTrackFailed, ext2, trackNum); 1.1529 + return; 1.1530 + } 1.1531 + 1.1532 + // All unhandled cases wind up here 1.1533 + DOM_CAMERA_LOGW("recorder-event : unhandled: msg=%d, ext1=%d, ext2=%d\n", msg, ext1, ext2); 1.1534 +} 1.1535 + 1.1536 +nsresult 1.1537 +nsGonkCameraControl::SetupRecording(int aFd, int aRotation, 1.1538 + int64_t aMaxFileSizeBytes, 1.1539 + int64_t aMaxVideoLengthMs) 1.1540 +{ 1.1541 + RETURN_IF_NO_CAMERA_HW(); 1.1542 + 1.1543 + // choosing a size big enough to hold the params 1.1544 + const size_t SIZE = 256; 1.1545 + char buffer[SIZE]; 1.1546 + 1.1547 + mRecorder = new GonkRecorder(); 1.1548 + CHECK_SETARG(mRecorder->init()); 1.1549 + 1.1550 + nsresult rv = mRecorderProfile->ConfigureRecorder(mRecorder); 1.1551 + NS_ENSURE_SUCCESS(rv, rv); 1.1552 + 1.1553 + CHECK_SETARG(mRecorder->setCamera(mCameraHw)); 1.1554 + 1.1555 + DOM_CAMERA_LOGI("maxVideoLengthMs=%lld\n", aMaxVideoLengthMs); 1.1556 + if (aMaxVideoLengthMs == 0) { 1.1557 + aMaxVideoLengthMs = -1; 1.1558 + } 1.1559 + snprintf(buffer, SIZE, "max-duration=%lld", aMaxVideoLengthMs); 1.1560 + CHECK_SETARG(mRecorder->setParameters(String8(buffer))); 1.1561 + 1.1562 + DOM_CAMERA_LOGI("maxFileSizeBytes=%lld\n", aMaxFileSizeBytes); 1.1563 + if (aMaxFileSizeBytes == 0) { 1.1564 + aMaxFileSizeBytes = -1; 1.1565 + } 1.1566 + snprintf(buffer, SIZE, "max-filesize=%lld", aMaxFileSizeBytes); 1.1567 + CHECK_SETARG(mRecorder->setParameters(String8(buffer))); 1.1568 + 1.1569 + // adjust rotation by camera sensor offset 1.1570 + int r = aRotation; 1.1571 + r += mCameraHw->GetSensorOrientation(); 1.1572 + r = RationalizeRotation(r); 1.1573 + DOM_CAMERA_LOGI("setting video rotation to %d degrees (mapped from %d)\n", r, aRotation); 1.1574 + snprintf(buffer, SIZE, "video-param-rotation-angle-degrees=%d", r); 1.1575 + CHECK_SETARG(mRecorder->setParameters(String8(buffer))); 1.1576 + 1.1577 + CHECK_SETARG(mRecorder->setListener(new GonkRecorderListener(this))); 1.1578 + 1.1579 + // recording API needs file descriptor of output file 1.1580 + CHECK_SETARG(mRecorder->setOutputFile(aFd, 0, 0)); 1.1581 + CHECK_SETARG(mRecorder->prepare()); 1.1582 + 1.1583 + return NS_OK; 1.1584 +} 1.1585 + 1.1586 +nsresult 1.1587 +nsGonkCameraControl::StopImpl() 1.1588 +{ 1.1589 + DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); 1.1590 + 1.1591 + // if we're recording, stop recording 1.1592 + StopRecordingImpl(); 1.1593 + 1.1594 + // stop the preview 1.1595 + StopPreviewImpl(); 1.1596 + 1.1597 + // release the hardware handle 1.1598 + if (mCameraHw.get()){ 1.1599 + mCameraHw->Close(); 1.1600 + mCameraHw.clear(); 1.1601 + } 1.1602 + 1.1603 + OnHardwareStateChange(CameraControlListener::kHardwareClosed); 1.1604 + return NS_OK; 1.1605 +} 1.1606 + 1.1607 +already_AddRefed<GonkRecorderProfileManager> 1.1608 +nsGonkCameraControl::GetGonkRecorderProfileManager() 1.1609 +{ 1.1610 + if (!mProfileManager) { 1.1611 + nsTArray<Size> sizes; 1.1612 + nsresult rv = Get(CAMERA_PARAM_SUPPORTED_VIDEOSIZES, sizes); 1.1613 + NS_ENSURE_SUCCESS(rv, nullptr); 1.1614 + 1.1615 + mProfileManager = new GonkRecorderProfileManager(mCameraId); 1.1616 + mProfileManager->SetSupportedResolutions(sizes); 1.1617 + } 1.1618 + 1.1619 + nsRefPtr<GonkRecorderProfileManager> profileMgr = mProfileManager; 1.1620 + return profileMgr.forget(); 1.1621 +} 1.1622 + 1.1623 +already_AddRefed<RecorderProfileManager> 1.1624 +nsGonkCameraControl::GetRecorderProfileManagerImpl() 1.1625 +{ 1.1626 + nsRefPtr<RecorderProfileManager> profileMgr = GetGonkRecorderProfileManager(); 1.1627 + return profileMgr.forget(); 1.1628 +} 1.1629 + 1.1630 +void 1.1631 +nsGonkCameraControl::OnNewPreviewFrame(layers::TextureClient* aBuffer) 1.1632 +{ 1.1633 + nsRefPtr<Image> frame = mImageContainer->CreateImage(ImageFormat::GRALLOC_PLANAR_YCBCR); 1.1634 + 1.1635 + GrallocImage* videoImage = static_cast<GrallocImage*>(frame.get()); 1.1636 + 1.1637 + GrallocImage::GrallocData data; 1.1638 + data.mGraphicBuffer = aBuffer; 1.1639 + data.mPicSize = IntSize(mCurrentConfiguration.mPreviewSize.width, 1.1640 + mCurrentConfiguration.mPreviewSize.height); 1.1641 + videoImage->SetData(data); 1.1642 + 1.1643 + OnNewPreviewFrame(frame, mCurrentConfiguration.mPreviewSize.width, 1.1644 + mCurrentConfiguration.mPreviewSize.height); 1.1645 +} 1.1646 + 1.1647 +void 1.1648 +nsGonkCameraControl::OnError(CameraControlListener::CameraErrorContext aWhere, 1.1649 + CameraControlListener::CameraError aError) 1.1650 +{ 1.1651 + if (aError == CameraControlListener::kErrorServiceFailed) { 1.1652 + OnPreviewStateChange(CameraControlListener::kPreviewStopped); 1.1653 + OnHardwareStateChange(CameraControlListener::kHardwareClosed); 1.1654 + } 1.1655 + 1.1656 + CameraControlImpl::OnError(aWhere, aError); 1.1657 +} 1.1658 + 1.1659 +// Gonk callback handlers. 1.1660 +namespace mozilla { 1.1661 + 1.1662 +void 1.1663 +OnTakePictureComplete(nsGonkCameraControl* gc, uint8_t* aData, uint32_t aLength) 1.1664 +{ 1.1665 + gc->OnTakePictureComplete(aData, aLength); 1.1666 +} 1.1667 + 1.1668 +void 1.1669 +OnTakePictureError(nsGonkCameraControl* gc) 1.1670 +{ 1.1671 + gc->OnTakePictureError(); 1.1672 +} 1.1673 + 1.1674 +void 1.1675 +OnAutoFocusComplete(nsGonkCameraControl* gc, bool aSuccess) 1.1676 +{ 1.1677 + gc->OnAutoFocusComplete(aSuccess); 1.1678 +} 1.1679 + 1.1680 +void 1.1681 +OnAutoFocusMoving(nsGonkCameraControl* gc, bool aIsMoving) 1.1682 +{ 1.1683 + gc->OnAutoFocusMoving(aIsMoving); 1.1684 +} 1.1685 + 1.1686 +void 1.1687 +OnFacesDetected(nsGonkCameraControl* gc, camera_frame_metadata_t* aMetaData) 1.1688 +{ 1.1689 + gc->OnFacesDetected(aMetaData); 1.1690 +} 1.1691 + 1.1692 +void 1.1693 +OnNewPreviewFrame(nsGonkCameraControl* gc, layers::TextureClient* aBuffer) 1.1694 +{ 1.1695 + gc->OnNewPreviewFrame(aBuffer); 1.1696 +} 1.1697 + 1.1698 +void 1.1699 +OnShutter(nsGonkCameraControl* gc) 1.1700 +{ 1.1701 + gc->OnShutter(); 1.1702 +} 1.1703 + 1.1704 +void 1.1705 +OnClosed(nsGonkCameraControl* gc) 1.1706 +{ 1.1707 + gc->OnClosed(); 1.1708 +} 1.1709 + 1.1710 +void 1.1711 +OnError(nsGonkCameraControl* gc, CameraControlListener::CameraError aError, 1.1712 + int32_t aArg1, int32_t aArg2) 1.1713 +{ 1.1714 +#ifdef PR_LOGGING 1.1715 + DOM_CAMERA_LOGE("OnError : aError=%d, aArg1=%d, aArg2=%d\n", aError, aArg1, aArg2); 1.1716 +#else 1.1717 + unused << aArg1; 1.1718 + unused << aArg2; 1.1719 +#endif 1.1720 + gc->OnError(CameraControlListener::kInUnspecified, aError); 1.1721 +} 1.1722 + 1.1723 +} // namespace mozilla