dom/camera/GonkCameraControl.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

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

mercurial