dom/camera/GonkCameraControl.cpp

changeset 0
6474c204b198
     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

mercurial