dom/camera/GonkCameraControl.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

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

mercurial