dom/camera/DOMCameraControl.cpp

Thu, 15 Jan 2015 15:55:04 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:55:04 +0100
branch
TOR_BUG_9701
changeset 9
a63d609f5ebe
permissions
-rw-r--r--

Back out 97036ab72558 which inappropriately compared turds to third parties.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
michael@0 3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 #include "DOMCameraControl.h"
michael@0 6 #include "base/basictypes.h"
michael@0 7 #include "nsCOMPtr.h"
michael@0 8 #include "nsDOMClassInfo.h"
michael@0 9 #include "nsHashPropertyBag.h"
michael@0 10 #include "nsThread.h"
michael@0 11 #include "DeviceStorage.h"
michael@0 12 #include "DeviceStorageFileDescriptor.h"
michael@0 13 #include "mozilla/dom/TabChild.h"
michael@0 14 #include "mozilla/ipc/FileDescriptorUtils.h"
michael@0 15 #include "mozilla/MediaManager.h"
michael@0 16 #include "mozilla/Services.h"
michael@0 17 #include "mozilla/unused.h"
michael@0 18 #include "nsIAppsService.h"
michael@0 19 #include "nsIObserverService.h"
michael@0 20 #include "nsIDOMDeviceStorage.h"
michael@0 21 #include "nsIDOMEventListener.h"
michael@0 22 #include "nsIScriptSecurityManager.h"
michael@0 23 #include "Navigator.h"
michael@0 24 #include "nsXULAppAPI.h"
michael@0 25 #include "DOMCameraManager.h"
michael@0 26 #include "DOMCameraCapabilities.h"
michael@0 27 #include "CameraCommon.h"
michael@0 28 #include "nsGlobalWindow.h"
michael@0 29 #include "CameraPreviewMediaStream.h"
michael@0 30 #include "mozilla/dom/CameraControlBinding.h"
michael@0 31 #include "mozilla/dom/CameraManagerBinding.h"
michael@0 32 #include "mozilla/dom/CameraCapabilitiesBinding.h"
michael@0 33 #include "DOMCameraDetectedFace.h"
michael@0 34 #include "mozilla/dom/BindingUtils.h"
michael@0 35
michael@0 36 using namespace mozilla;
michael@0 37 using namespace mozilla::dom;
michael@0 38 using namespace mozilla::ipc;
michael@0 39
michael@0 40 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDOMCameraControl)
michael@0 41 NS_INTERFACE_MAP_ENTRY(nsISupports)
michael@0 42 NS_INTERFACE_MAP_ENTRY(nsIDOMMediaStream)
michael@0 43 NS_INTERFACE_MAP_END_INHERITING(DOMMediaStream)
michael@0 44
michael@0 45 NS_IMPL_ADDREF_INHERITED(nsDOMCameraControl, DOMMediaStream)
michael@0 46 NS_IMPL_RELEASE_INHERITED(nsDOMCameraControl, DOMMediaStream)
michael@0 47
michael@0 48 NS_IMPL_CYCLE_COLLECTION_INHERITED(nsDOMCameraControl, DOMMediaStream,
michael@0 49 mCapabilities,
michael@0 50 mWindow,
michael@0 51 mGetCameraOnSuccessCb,
michael@0 52 mGetCameraOnErrorCb,
michael@0 53 mAutoFocusOnSuccessCb,
michael@0 54 mAutoFocusOnErrorCb,
michael@0 55 mTakePictureOnSuccessCb,
michael@0 56 mTakePictureOnErrorCb,
michael@0 57 mStartRecordingOnSuccessCb,
michael@0 58 mStartRecordingOnErrorCb,
michael@0 59 mReleaseOnSuccessCb,
michael@0 60 mReleaseOnErrorCb,
michael@0 61 mSetConfigurationOnSuccessCb,
michael@0 62 mSetConfigurationOnErrorCb,
michael@0 63 mOnShutterCb,
michael@0 64 mOnClosedCb,
michael@0 65 mOnRecorderStateChangeCb,
michael@0 66 mOnPreviewStateChangeCb,
michael@0 67 mOnAutoFocusMovingCb,
michael@0 68 mOnFacesDetectedCb)
michael@0 69
michael@0 70 /* static */
michael@0 71 bool
michael@0 72 nsDOMCameraControl::HasSupport(JSContext* aCx, JSObject* aGlobal)
michael@0 73 {
michael@0 74 return Navigator::HasCameraSupport(aCx, aGlobal);
michael@0 75 }
michael@0 76
michael@0 77 class mozilla::StartRecordingHelper : public nsIDOMEventListener
michael@0 78 {
michael@0 79 public:
michael@0 80 NS_DECL_ISUPPORTS
michael@0 81 NS_DECL_NSIDOMEVENTLISTENER
michael@0 82
michael@0 83 StartRecordingHelper(nsDOMCameraControl* aDOMCameraControl)
michael@0 84 : mDOMCameraControl(aDOMCameraControl)
michael@0 85 {
michael@0 86 MOZ_COUNT_CTOR(StartRecordingHelper);
michael@0 87 }
michael@0 88
michael@0 89 protected:
michael@0 90 virtual ~StartRecordingHelper()
michael@0 91 {
michael@0 92 MOZ_COUNT_DTOR(StartRecordingHelper);
michael@0 93 }
michael@0 94
michael@0 95 protected:
michael@0 96 nsRefPtr<nsDOMCameraControl> mDOMCameraControl;
michael@0 97 };
michael@0 98
michael@0 99 NS_IMETHODIMP
michael@0 100 StartRecordingHelper::HandleEvent(nsIDOMEvent* aEvent)
michael@0 101 {
michael@0 102 nsString eventType;
michael@0 103 aEvent->GetType(eventType);
michael@0 104
michael@0 105 mDOMCameraControl->OnCreatedFileDescriptor(eventType.EqualsLiteral("success"));
michael@0 106 return NS_OK;
michael@0 107 }
michael@0 108
michael@0 109 NS_IMPL_ISUPPORTS(mozilla::StartRecordingHelper, nsIDOMEventListener)
michael@0 110
michael@0 111 nsDOMCameraControl::DOMCameraConfiguration::DOMCameraConfiguration()
michael@0 112 : CameraConfiguration()
michael@0 113 , mMaxFocusAreas(0)
michael@0 114 , mMaxMeteringAreas(0)
michael@0 115 {
michael@0 116 MOZ_COUNT_CTOR(nsDOMCameraControl::DOMCameraConfiguration);
michael@0 117 }
michael@0 118
michael@0 119 nsDOMCameraControl::DOMCameraConfiguration::DOMCameraConfiguration(const CameraConfiguration& aConfiguration)
michael@0 120 : CameraConfiguration(aConfiguration)
michael@0 121 , mMaxFocusAreas(0)
michael@0 122 , mMaxMeteringAreas(0)
michael@0 123 {
michael@0 124 MOZ_COUNT_CTOR(nsDOMCameraControl::DOMCameraConfiguration);
michael@0 125 }
michael@0 126
michael@0 127 nsDOMCameraControl::DOMCameraConfiguration::~DOMCameraConfiguration()
michael@0 128 {
michael@0 129 MOZ_COUNT_DTOR(nsDOMCameraControl::DOMCameraConfiguration);
michael@0 130 }
michael@0 131
michael@0 132 nsDOMCameraControl::nsDOMCameraControl(uint32_t aCameraId,
michael@0 133 const CameraConfiguration& aInitialConfig,
michael@0 134 GetCameraCallback* aOnSuccess,
michael@0 135 CameraErrorCallback* aOnError,
michael@0 136 nsPIDOMWindow* aWindow)
michael@0 137 : DOMMediaStream()
michael@0 138 , mCameraControl(nullptr)
michael@0 139 , mAudioChannelAgent(nullptr)
michael@0 140 , mGetCameraOnSuccessCb(aOnSuccess)
michael@0 141 , mGetCameraOnErrorCb(aOnError)
michael@0 142 , mAutoFocusOnSuccessCb(nullptr)
michael@0 143 , mAutoFocusOnErrorCb(nullptr)
michael@0 144 , mTakePictureOnSuccessCb(nullptr)
michael@0 145 , mTakePictureOnErrorCb(nullptr)
michael@0 146 , mStartRecordingOnSuccessCb(nullptr)
michael@0 147 , mStartRecordingOnErrorCb(nullptr)
michael@0 148 , mReleaseOnSuccessCb(nullptr)
michael@0 149 , mReleaseOnErrorCb(nullptr)
michael@0 150 , mSetConfigurationOnSuccessCb(nullptr)
michael@0 151 , mSetConfigurationOnErrorCb(nullptr)
michael@0 152 , mOnShutterCb(nullptr)
michael@0 153 , mOnClosedCb(nullptr)
michael@0 154 , mOnRecorderStateChangeCb(nullptr)
michael@0 155 , mOnPreviewStateChangeCb(nullptr)
michael@0 156 , mOnAutoFocusMovingCb(nullptr)
michael@0 157 , mOnFacesDetectedCb(nullptr)
michael@0 158 , mWindow(aWindow)
michael@0 159 {
michael@0 160 DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
michael@0 161 mInput = new CameraPreviewMediaStream(this);
michael@0 162
michael@0 163 SetIsDOMBinding();
michael@0 164
michael@0 165 nsRefPtr<DOMCameraConfiguration> initialConfig =
michael@0 166 new DOMCameraConfiguration(aInitialConfig);
michael@0 167
michael@0 168 // Create and initialize the underlying camera.
michael@0 169 ICameraControl::Configuration config;
michael@0 170
michael@0 171 switch (aInitialConfig.mMode) {
michael@0 172 case CameraMode::Picture:
michael@0 173 config.mMode = ICameraControl::kPictureMode;
michael@0 174 break;
michael@0 175
michael@0 176 case CameraMode::Video:
michael@0 177 config.mMode = ICameraControl::kVideoMode;
michael@0 178 break;
michael@0 179
michael@0 180 default:
michael@0 181 MOZ_ASSUME_UNREACHABLE("Unanticipated camera mode!");
michael@0 182 }
michael@0 183
michael@0 184 config.mPreviewSize.width = aInitialConfig.mPreviewSize.mWidth;
michael@0 185 config.mPreviewSize.height = aInitialConfig.mPreviewSize.mHeight;
michael@0 186 config.mRecorderProfile = aInitialConfig.mRecorderProfile;
michael@0 187
michael@0 188 mCameraControl = ICameraControl::Create(aCameraId);
michael@0 189 mCurrentConfiguration = initialConfig.forget();
michael@0 190
michael@0 191 // Attach our DOM-facing media stream to our viewfinder stream.
michael@0 192 mStream = mInput;
michael@0 193 MOZ_ASSERT(mWindow, "Shouldn't be created with a null window!");
michael@0 194 if (mWindow->GetExtantDoc()) {
michael@0 195 CombineWithPrincipal(mWindow->GetExtantDoc()->NodePrincipal());
michael@0 196 }
michael@0 197
michael@0 198 // Register a listener for camera events.
michael@0 199 mListener = new DOMCameraControlListener(this, mInput);
michael@0 200 mCameraControl->AddListener(mListener);
michael@0 201
michael@0 202 // Start the camera...
michael@0 203 nsresult rv = mCameraControl->Start(&config);
michael@0 204 if (NS_FAILED(rv)) {
michael@0 205 mListener->OnError(DOMCameraControlListener::kInStartCamera,
michael@0 206 DOMCameraControlListener::kErrorApiFailed);
michael@0 207 }
michael@0 208 }
michael@0 209
michael@0 210 nsDOMCameraControl::~nsDOMCameraControl()
michael@0 211 {
michael@0 212 DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
michael@0 213 }
michael@0 214
michael@0 215 JSObject*
michael@0 216 nsDOMCameraControl::WrapObject(JSContext* aCx)
michael@0 217 {
michael@0 218 return CameraControlBinding::Wrap(aCx, this);
michael@0 219 }
michael@0 220
michael@0 221 bool
michael@0 222 nsDOMCameraControl::IsWindowStillActive()
michael@0 223 {
michael@0 224 return nsDOMCameraManager::IsWindowStillActive(mWindow->WindowID());
michael@0 225 }
michael@0 226
michael@0 227 // JS-to-native helpers
michael@0 228 // Setter for weighted regions: { top, bottom, left, right, weight }
michael@0 229 nsresult
michael@0 230 nsDOMCameraControl::Set(JSContext* aCx, uint32_t aKey, const JS::Value& aValue, uint32_t aLimit)
michael@0 231 {
michael@0 232 if (aLimit == 0) {
michael@0 233 DOM_CAMERA_LOGI("%s:%d : aLimit = 0, nothing to do\n", __func__, __LINE__);
michael@0 234 return NS_OK;
michael@0 235 }
michael@0 236
michael@0 237 if (!aValue.isObject()) {
michael@0 238 return NS_ERROR_INVALID_ARG;
michael@0 239 }
michael@0 240
michael@0 241 uint32_t length = 0;
michael@0 242
michael@0 243 JS::Rooted<JSObject*> regions(aCx, &aValue.toObject());
michael@0 244 if (!JS_GetArrayLength(aCx, regions, &length)) {
michael@0 245 return NS_ERROR_FAILURE;
michael@0 246 }
michael@0 247
michael@0 248 DOM_CAMERA_LOGI("%s:%d : got %d regions (limited to %d)\n", __func__, __LINE__, length, aLimit);
michael@0 249 if (length > aLimit) {
michael@0 250 length = aLimit;
michael@0 251 }
michael@0 252
michael@0 253 nsTArray<ICameraControl::Region> regionArray;
michael@0 254 regionArray.SetCapacity(length);
michael@0 255
michael@0 256 for (uint32_t i = 0; i < length; ++i) {
michael@0 257 JS::Rooted<JS::Value> v(aCx);
michael@0 258
michael@0 259 if (!JS_GetElement(aCx, regions, i, &v)) {
michael@0 260 return NS_ERROR_FAILURE;
michael@0 261 }
michael@0 262
michael@0 263 CameraRegion region;
michael@0 264 if (!region.Init(aCx, v)) {
michael@0 265 return NS_ERROR_FAILURE;
michael@0 266 }
michael@0 267
michael@0 268 ICameraControl::Region* r = regionArray.AppendElement();
michael@0 269 r->top = region.mTop;
michael@0 270 r->left = region.mLeft;
michael@0 271 r->bottom = region.mBottom;
michael@0 272 r->right = region.mRight;
michael@0 273 r->weight = region.mWeight;
michael@0 274
michael@0 275 DOM_CAMERA_LOGI("region %d: top=%d, left=%d, bottom=%d, right=%d, weight=%u\n",
michael@0 276 i,
michael@0 277 r->top,
michael@0 278 r->left,
michael@0 279 r->bottom,
michael@0 280 r->right,
michael@0 281 r->weight
michael@0 282 );
michael@0 283 }
michael@0 284 return mCameraControl->Set(aKey, regionArray);
michael@0 285 }
michael@0 286
michael@0 287 // Getter for weighted regions: { top, bottom, left, right, weight }
michael@0 288 nsresult
michael@0 289 nsDOMCameraControl::Get(JSContext* aCx, uint32_t aKey, JS::Value* aValue)
michael@0 290 {
michael@0 291 nsTArray<ICameraControl::Region> regionArray;
michael@0 292
michael@0 293 nsresult rv = mCameraControl->Get(aKey, regionArray);
michael@0 294 NS_ENSURE_SUCCESS(rv, rv);
michael@0 295
michael@0 296 JS::Rooted<JSObject*> array(aCx, JS_NewArrayObject(aCx, 0));
michael@0 297 if (!array) {
michael@0 298 return NS_ERROR_OUT_OF_MEMORY;
michael@0 299 }
michael@0 300
michael@0 301 uint32_t length = regionArray.Length();
michael@0 302 DOM_CAMERA_LOGI("%s:%d : got %d regions\n", __func__, __LINE__, length);
michael@0 303
michael@0 304 for (uint32_t i = 0; i < length; ++i) {
michael@0 305 ICameraControl::Region* r = &regionArray[i];
michael@0 306 JS::Rooted<JS::Value> v(aCx);
michael@0 307
michael@0 308 JS::Rooted<JSObject*> o(aCx, JS_NewObject(aCx, nullptr, JS::NullPtr(), JS::NullPtr()));
michael@0 309 if (!o) {
michael@0 310 return NS_ERROR_OUT_OF_MEMORY;
michael@0 311 }
michael@0 312
michael@0 313 DOM_CAMERA_LOGI("top=%d\n", r->top);
michael@0 314 v = INT_TO_JSVAL(r->top);
michael@0 315 if (!JS_SetProperty(aCx, o, "top", v)) {
michael@0 316 return NS_ERROR_FAILURE;
michael@0 317 }
michael@0 318 DOM_CAMERA_LOGI("left=%d\n", r->left);
michael@0 319 v = INT_TO_JSVAL(r->left);
michael@0 320 if (!JS_SetProperty(aCx, o, "left", v)) {
michael@0 321 return NS_ERROR_FAILURE;
michael@0 322 }
michael@0 323 DOM_CAMERA_LOGI("bottom=%d\n", r->bottom);
michael@0 324 v = INT_TO_JSVAL(r->bottom);
michael@0 325 if (!JS_SetProperty(aCx, o, "bottom", v)) {
michael@0 326 return NS_ERROR_FAILURE;
michael@0 327 }
michael@0 328 DOM_CAMERA_LOGI("right=%d\n", r->right);
michael@0 329 v = INT_TO_JSVAL(r->right);
michael@0 330 if (!JS_SetProperty(aCx, o, "right", v)) {
michael@0 331 return NS_ERROR_FAILURE;
michael@0 332 }
michael@0 333 DOM_CAMERA_LOGI("weight=%d\n", r->weight);
michael@0 334 v = INT_TO_JSVAL(r->weight);
michael@0 335 if (!JS_SetProperty(aCx, o, "weight", v)) {
michael@0 336 return NS_ERROR_FAILURE;
michael@0 337 }
michael@0 338
michael@0 339 if (!JS_SetElement(aCx, array, i, o)) {
michael@0 340 return NS_ERROR_FAILURE;
michael@0 341 }
michael@0 342 }
michael@0 343
michael@0 344 *aValue = JS::ObjectValue(*array);
michael@0 345 return NS_OK;
michael@0 346 }
michael@0 347
michael@0 348 void
michael@0 349 nsDOMCameraControl::GetEffect(nsString& aEffect, ErrorResult& aRv)
michael@0 350 {
michael@0 351 MOZ_ASSERT(mCameraControl);
michael@0 352 aRv = mCameraControl->Get(CAMERA_PARAM_EFFECT, aEffect);
michael@0 353 }
michael@0 354 void
michael@0 355 nsDOMCameraControl::SetEffect(const nsAString& aEffect, ErrorResult& aRv)
michael@0 356 {
michael@0 357 MOZ_ASSERT(mCameraControl);
michael@0 358 aRv = mCameraControl->Set(CAMERA_PARAM_EFFECT, aEffect);
michael@0 359 }
michael@0 360
michael@0 361 void
michael@0 362 nsDOMCameraControl::GetWhiteBalanceMode(nsString& aWhiteBalanceMode, ErrorResult& aRv)
michael@0 363 {
michael@0 364 MOZ_ASSERT(mCameraControl);
michael@0 365 aRv = mCameraControl->Get(CAMERA_PARAM_WHITEBALANCE, aWhiteBalanceMode);
michael@0 366 }
michael@0 367 void
michael@0 368 nsDOMCameraControl::SetWhiteBalanceMode(const nsAString& aWhiteBalanceMode, ErrorResult& aRv)
michael@0 369 {
michael@0 370 MOZ_ASSERT(mCameraControl);
michael@0 371 aRv = mCameraControl->Set(CAMERA_PARAM_WHITEBALANCE, aWhiteBalanceMode);
michael@0 372 }
michael@0 373
michael@0 374 void
michael@0 375 nsDOMCameraControl::GetSceneMode(nsString& aSceneMode, ErrorResult& aRv)
michael@0 376 {
michael@0 377 MOZ_ASSERT(mCameraControl);
michael@0 378 aRv = mCameraControl->Get(CAMERA_PARAM_SCENEMODE, aSceneMode);
michael@0 379 }
michael@0 380 void
michael@0 381 nsDOMCameraControl::SetSceneMode(const nsAString& aSceneMode, ErrorResult& aRv)
michael@0 382 {
michael@0 383 MOZ_ASSERT(mCameraControl);
michael@0 384 aRv = mCameraControl->Set(CAMERA_PARAM_SCENEMODE, aSceneMode);
michael@0 385 }
michael@0 386
michael@0 387 void
michael@0 388 nsDOMCameraControl::GetFlashMode(nsString& aFlashMode, ErrorResult& aRv)
michael@0 389 {
michael@0 390 MOZ_ASSERT(mCameraControl);
michael@0 391 aRv = mCameraControl->Get(CAMERA_PARAM_FLASHMODE, aFlashMode);
michael@0 392 }
michael@0 393 void
michael@0 394 nsDOMCameraControl::SetFlashMode(const nsAString& aFlashMode, ErrorResult& aRv)
michael@0 395 {
michael@0 396 MOZ_ASSERT(mCameraControl);
michael@0 397 aRv = mCameraControl->Set(CAMERA_PARAM_FLASHMODE, aFlashMode);
michael@0 398 }
michael@0 399
michael@0 400 void
michael@0 401 nsDOMCameraControl::GetFocusMode(nsString& aFocusMode, ErrorResult& aRv)
michael@0 402 {
michael@0 403 MOZ_ASSERT(mCameraControl);
michael@0 404 aRv = mCameraControl->Get(CAMERA_PARAM_FOCUSMODE, aFocusMode);
michael@0 405 }
michael@0 406 void
michael@0 407 nsDOMCameraControl::SetFocusMode(const nsAString& aFocusMode, ErrorResult& aRv)
michael@0 408 {
michael@0 409 MOZ_ASSERT(mCameraControl);
michael@0 410 aRv = mCameraControl->Set(CAMERA_PARAM_FOCUSMODE, aFocusMode);
michael@0 411 }
michael@0 412
michael@0 413 void
michael@0 414 nsDOMCameraControl::GetIsoMode(nsString& aIsoMode, ErrorResult& aRv)
michael@0 415 {
michael@0 416 MOZ_ASSERT(mCameraControl);
michael@0 417 aRv = mCameraControl->Get(CAMERA_PARAM_ISOMODE, aIsoMode);
michael@0 418 }
michael@0 419 void
michael@0 420 nsDOMCameraControl::SetIsoMode(const nsAString& aIsoMode, ErrorResult& aRv)
michael@0 421 {
michael@0 422 MOZ_ASSERT(mCameraControl);
michael@0 423 aRv = mCameraControl->Set(CAMERA_PARAM_ISOMODE, aIsoMode);
michael@0 424 }
michael@0 425
michael@0 426 double
michael@0 427 nsDOMCameraControl::GetZoom(ErrorResult& aRv)
michael@0 428 {
michael@0 429 MOZ_ASSERT(mCameraControl);
michael@0 430
michael@0 431 double zoom;
michael@0 432 aRv = mCameraControl->Get(CAMERA_PARAM_ZOOM, zoom);
michael@0 433 return zoom;
michael@0 434 }
michael@0 435
michael@0 436 void
michael@0 437 nsDOMCameraControl::SetZoom(double aZoom, ErrorResult& aRv)
michael@0 438 {
michael@0 439 MOZ_ASSERT(mCameraControl);
michael@0 440 aRv = mCameraControl->Set(CAMERA_PARAM_ZOOM, aZoom);
michael@0 441 }
michael@0 442
michael@0 443 /* attribute jsval meteringAreas; */
michael@0 444 void
michael@0 445 nsDOMCameraControl::GetMeteringAreas(JSContext* cx,
michael@0 446 JS::MutableHandle<JS::Value> aMeteringAreas,
michael@0 447 ErrorResult& aRv)
michael@0 448 {
michael@0 449 aRv = Get(cx, CAMERA_PARAM_METERINGAREAS, aMeteringAreas.address());
michael@0 450 }
michael@0 451
michael@0 452 void
michael@0 453 nsDOMCameraControl::SetMeteringAreas(JSContext* cx, JS::Handle<JS::Value> aMeteringAreas, ErrorResult& aRv)
michael@0 454 {
michael@0 455 aRv = Set(cx, CAMERA_PARAM_METERINGAREAS, aMeteringAreas,
michael@0 456 mCurrentConfiguration->mMaxMeteringAreas);
michael@0 457 }
michael@0 458
michael@0 459 void
michael@0 460 nsDOMCameraControl::GetFocusAreas(JSContext* cx,
michael@0 461 JS::MutableHandle<JS::Value> aFocusAreas,
michael@0 462 ErrorResult& aRv)
michael@0 463 {
michael@0 464 aRv = Get(cx, CAMERA_PARAM_FOCUSAREAS, aFocusAreas.address());
michael@0 465 }
michael@0 466 void
michael@0 467 nsDOMCameraControl::SetFocusAreas(JSContext* cx, JS::Handle<JS::Value> aFocusAreas, ErrorResult& aRv)
michael@0 468 {
michael@0 469 aRv = Set(cx, CAMERA_PARAM_FOCUSAREAS, aFocusAreas,
michael@0 470 mCurrentConfiguration->mMaxFocusAreas);
michael@0 471 }
michael@0 472
michael@0 473 static nsresult
michael@0 474 GetSize(JSContext* aCx, JS::Value* aValue, const ICameraControl::Size& aSize)
michael@0 475 {
michael@0 476 JS::Rooted<JSObject*> o(aCx, JS_NewObject(aCx, nullptr, JS::NullPtr(), JS::NullPtr()));
michael@0 477 if (!o) {
michael@0 478 return NS_ERROR_OUT_OF_MEMORY;
michael@0 479 }
michael@0 480
michael@0 481 JS::Rooted<JS::Value> v(aCx);
michael@0 482
michael@0 483 v = INT_TO_JSVAL(aSize.width);
michael@0 484 if (!JS_SetProperty(aCx, o, "width", v)) {
michael@0 485 return NS_ERROR_FAILURE;
michael@0 486 }
michael@0 487 v = INT_TO_JSVAL(aSize.height);
michael@0 488 if (!JS_SetProperty(aCx, o, "height", v)) {
michael@0 489 return NS_ERROR_FAILURE;
michael@0 490 }
michael@0 491
michael@0 492 *aValue = JS::ObjectValue(*o);
michael@0 493 return NS_OK;
michael@0 494 }
michael@0 495
michael@0 496 /* attribute any pictureSize */
michael@0 497 void
michael@0 498 nsDOMCameraControl::GetPictureSize(JSContext* cx,
michael@0 499 JS::MutableHandle<JS::Value> aSize,
michael@0 500 ErrorResult& aRv)
michael@0 501 {
michael@0 502 ICameraControl::Size size;
michael@0 503 aRv = mCameraControl->Get(CAMERA_PARAM_PICTURE_SIZE, size);
michael@0 504 if (aRv.Failed()) {
michael@0 505 return;
michael@0 506 }
michael@0 507
michael@0 508 aRv = GetSize(cx, aSize.address(), size);
michael@0 509 }
michael@0 510 void
michael@0 511 nsDOMCameraControl::SetPictureSize(JSContext* aCx, JS::Handle<JS::Value> aSize, ErrorResult& aRv)
michael@0 512 {
michael@0 513 CameraSize size;
michael@0 514 if (!size.Init(aCx, aSize)) {
michael@0 515 aRv = NS_ERROR_FAILURE;
michael@0 516 return;
michael@0 517 }
michael@0 518
michael@0 519 ICameraControl::Size s = { size.mWidth, size.mHeight };
michael@0 520 aRv = mCameraControl->Set(CAMERA_PARAM_PICTURE_SIZE, s);
michael@0 521 }
michael@0 522
michael@0 523 /* attribute any thumbnailSize */
michael@0 524 void
michael@0 525 nsDOMCameraControl::GetThumbnailSize(JSContext* aCx,
michael@0 526 JS::MutableHandle<JS::Value> aSize,
michael@0 527 ErrorResult& aRv)
michael@0 528 {
michael@0 529 ICameraControl::Size size;
michael@0 530 aRv = mCameraControl->Get(CAMERA_PARAM_THUMBNAILSIZE, size);
michael@0 531 if (aRv.Failed()) {
michael@0 532 return;
michael@0 533 }
michael@0 534
michael@0 535 aRv = GetSize(aCx, aSize.address(), size);
michael@0 536 }
michael@0 537 void
michael@0 538 nsDOMCameraControl::SetThumbnailSize(JSContext* aCx, JS::Handle<JS::Value> aSize, ErrorResult& aRv)
michael@0 539 {
michael@0 540 CameraSize size;
michael@0 541 if (!size.Init(aCx, aSize)) {
michael@0 542 aRv = NS_ERROR_FAILURE;
michael@0 543 return;
michael@0 544 }
michael@0 545
michael@0 546 ICameraControl::Size s = { size.mWidth, size.mHeight };
michael@0 547 aRv = mCameraControl->Set(CAMERA_PARAM_THUMBNAILSIZE, s);
michael@0 548 }
michael@0 549
michael@0 550 double
michael@0 551 nsDOMCameraControl::GetFocalLength(ErrorResult& aRv)
michael@0 552 {
michael@0 553 MOZ_ASSERT(mCameraControl);
michael@0 554
michael@0 555 double focalLength;
michael@0 556 aRv = mCameraControl->Get(CAMERA_PARAM_FOCALLENGTH, focalLength);
michael@0 557 return focalLength;
michael@0 558 }
michael@0 559
michael@0 560 double
michael@0 561 nsDOMCameraControl::GetFocusDistanceNear(ErrorResult& aRv)
michael@0 562 {
michael@0 563 MOZ_ASSERT(mCameraControl);
michael@0 564
michael@0 565 double distance;
michael@0 566 aRv = mCameraControl->Get(CAMERA_PARAM_FOCUSDISTANCENEAR, distance);
michael@0 567 return distance;
michael@0 568 }
michael@0 569
michael@0 570 double
michael@0 571 nsDOMCameraControl::GetFocusDistanceOptimum(ErrorResult& aRv)
michael@0 572 {
michael@0 573 MOZ_ASSERT(mCameraControl);
michael@0 574
michael@0 575 double distance;
michael@0 576 aRv = mCameraControl->Get(CAMERA_PARAM_FOCUSDISTANCEOPTIMUM, distance);
michael@0 577 return distance;
michael@0 578 }
michael@0 579
michael@0 580 double
michael@0 581 nsDOMCameraControl::GetFocusDistanceFar(ErrorResult& aRv)
michael@0 582 {
michael@0 583 MOZ_ASSERT(mCameraControl);
michael@0 584
michael@0 585 double distance;
michael@0 586 aRv = mCameraControl->Get(CAMERA_PARAM_FOCUSDISTANCEFAR, distance);
michael@0 587 return distance;
michael@0 588 }
michael@0 589
michael@0 590 void
michael@0 591 nsDOMCameraControl::SetExposureCompensation(const Optional<double>& aCompensation, ErrorResult& aRv)
michael@0 592 {
michael@0 593 MOZ_ASSERT(mCameraControl);
michael@0 594
michael@0 595 if (!aCompensation.WasPassed()) {
michael@0 596 // use NaN to switch the camera back into auto mode
michael@0 597 aRv = mCameraControl->Set(CAMERA_PARAM_EXPOSURECOMPENSATION, NAN);
michael@0 598 return;
michael@0 599 }
michael@0 600
michael@0 601 aRv = mCameraControl->Set(CAMERA_PARAM_EXPOSURECOMPENSATION, aCompensation.Value());
michael@0 602 }
michael@0 603
michael@0 604 double
michael@0 605 nsDOMCameraControl::GetExposureCompensation(ErrorResult& aRv)
michael@0 606 {
michael@0 607 MOZ_ASSERT(mCameraControl);
michael@0 608
michael@0 609 double compensation;
michael@0 610 aRv = mCameraControl->Get(CAMERA_PARAM_EXPOSURECOMPENSATION, compensation);
michael@0 611 return compensation;
michael@0 612 }
michael@0 613
michael@0 614 int32_t
michael@0 615 nsDOMCameraControl::SensorAngle()
michael@0 616 {
michael@0 617 MOZ_ASSERT(mCameraControl);
michael@0 618
michael@0 619 int32_t angle = 0;
michael@0 620 mCameraControl->Get(CAMERA_PARAM_SENSORANGLE, angle);
michael@0 621 return angle;
michael@0 622 }
michael@0 623
michael@0 624 // Callback attributes
michael@0 625
michael@0 626 CameraShutterCallback*
michael@0 627 nsDOMCameraControl::GetOnShutter()
michael@0 628 {
michael@0 629 return mOnShutterCb;
michael@0 630 }
michael@0 631 void
michael@0 632 nsDOMCameraControl::SetOnShutter(CameraShutterCallback* aCb)
michael@0 633 {
michael@0 634 mOnShutterCb = aCb;
michael@0 635 }
michael@0 636
michael@0 637 CameraClosedCallback*
michael@0 638 nsDOMCameraControl::GetOnClosed()
michael@0 639 {
michael@0 640 return mOnClosedCb;
michael@0 641 }
michael@0 642 void
michael@0 643 nsDOMCameraControl::SetOnClosed(CameraClosedCallback* aCb)
michael@0 644 {
michael@0 645 mOnClosedCb = aCb;
michael@0 646 }
michael@0 647
michael@0 648 CameraRecorderStateChange*
michael@0 649 nsDOMCameraControl::GetOnRecorderStateChange()
michael@0 650 {
michael@0 651 return mOnRecorderStateChangeCb;
michael@0 652 }
michael@0 653 void
michael@0 654 nsDOMCameraControl::SetOnRecorderStateChange(CameraRecorderStateChange* aCb)
michael@0 655 {
michael@0 656 mOnRecorderStateChangeCb = aCb;
michael@0 657 }
michael@0 658
michael@0 659 CameraPreviewStateChange*
michael@0 660 nsDOMCameraControl::GetOnPreviewStateChange()
michael@0 661 {
michael@0 662 return mOnPreviewStateChangeCb;
michael@0 663 }
michael@0 664 void
michael@0 665 nsDOMCameraControl::SetOnPreviewStateChange(CameraPreviewStateChange* aCb)
michael@0 666 {
michael@0 667 mOnPreviewStateChangeCb = aCb;
michael@0 668 }
michael@0 669
michael@0 670 CameraAutoFocusMovingCallback*
michael@0 671 nsDOMCameraControl::GetOnAutoFocusMoving()
michael@0 672 {
michael@0 673 return mOnAutoFocusMovingCb;
michael@0 674 }
michael@0 675 void
michael@0 676 nsDOMCameraControl::SetOnAutoFocusMoving(CameraAutoFocusMovingCallback* aCb)
michael@0 677 {
michael@0 678 mOnAutoFocusMovingCb = aCb;
michael@0 679 }
michael@0 680
michael@0 681 CameraFaceDetectionCallback*
michael@0 682 nsDOMCameraControl::GetOnFacesDetected()
michael@0 683 {
michael@0 684 return mOnFacesDetectedCb;
michael@0 685 }
michael@0 686 void
michael@0 687 nsDOMCameraControl::SetOnFacesDetected(CameraFaceDetectionCallback* aCb)
michael@0 688 {
michael@0 689 mOnFacesDetectedCb = aCb;
michael@0 690 }
michael@0 691
michael@0 692 already_AddRefed<dom::CameraCapabilities>
michael@0 693 nsDOMCameraControl::Capabilities()
michael@0 694 {
michael@0 695 nsRefPtr<CameraCapabilities> caps = mCapabilities;
michael@0 696
michael@0 697 if (!caps) {
michael@0 698 caps = new CameraCapabilities(mWindow);
michael@0 699 nsresult rv = caps->Populate(mCameraControl);
michael@0 700 if (NS_FAILED(rv)) {
michael@0 701 DOM_CAMERA_LOGW("Failed to populate camera capabilities (%d)\n", rv);
michael@0 702 return nullptr;
michael@0 703 }
michael@0 704 mCapabilities = caps;
michael@0 705 }
michael@0 706
michael@0 707 return caps.forget();
michael@0 708 }
michael@0 709
michael@0 710 // Methods.
michael@0 711 void
michael@0 712 nsDOMCameraControl::StartRecording(const CameraStartRecordingOptions& aOptions,
michael@0 713 nsDOMDeviceStorage& aStorageArea,
michael@0 714 const nsAString& aFilename,
michael@0 715 CameraStartRecordingCallback& aOnSuccess,
michael@0 716 const Optional<OwningNonNull<CameraErrorCallback> >& aOnError,
michael@0 717 ErrorResult& aRv)
michael@0 718 {
michael@0 719 MOZ_ASSERT(mCameraControl);
michael@0 720
michael@0 721 NotifyRecordingStatusChange(NS_LITERAL_STRING("starting"));
michael@0 722
michael@0 723 #ifdef MOZ_B2G
michael@0 724 if (!mAudioChannelAgent) {
michael@0 725 mAudioChannelAgent = do_CreateInstance("@mozilla.org/audiochannelagent;1");
michael@0 726 if (mAudioChannelAgent) {
michael@0 727 // Camera app will stop recording when it falls to the background, so no callback is necessary.
michael@0 728 mAudioChannelAgent->Init(mWindow, (int32_t)AudioChannel::Content, nullptr);
michael@0 729 // Video recording doesn't output any sound, so it's not necessary to check canPlay.
michael@0 730 int32_t canPlay;
michael@0 731 mAudioChannelAgent->StartPlaying(&canPlay);
michael@0 732 }
michael@0 733 }
michael@0 734 #endif
michael@0 735
michael@0 736 nsCOMPtr<nsIDOMDOMRequest> request;
michael@0 737 mDSFileDescriptor = new DeviceStorageFileDescriptor();
michael@0 738 aRv = aStorageArea.CreateFileDescriptor(aFilename, mDSFileDescriptor.get(),
michael@0 739 getter_AddRefs(request));
michael@0 740 if (aRv.Failed()) {
michael@0 741 return;
michael@0 742 }
michael@0 743
michael@0 744 mOptions = aOptions;
michael@0 745 mStartRecordingOnSuccessCb = &aOnSuccess;
michael@0 746 mStartRecordingOnErrorCb = nullptr;
michael@0 747 if (aOnError.WasPassed()) {
michael@0 748 mStartRecordingOnErrorCb = &aOnError.Value();
michael@0 749 }
michael@0 750
michael@0 751 nsCOMPtr<nsIDOMEventListener> listener = new StartRecordingHelper(this);
michael@0 752 request->AddEventListener(NS_LITERAL_STRING("success"), listener, false);
michael@0 753 request->AddEventListener(NS_LITERAL_STRING("error"), listener, false);
michael@0 754 }
michael@0 755
michael@0 756 void
michael@0 757 nsDOMCameraControl::OnCreatedFileDescriptor(bool aSucceeded)
michael@0 758 {
michael@0 759 if (aSucceeded && mDSFileDescriptor->mFileDescriptor.IsValid()) {
michael@0 760 ICameraControl::StartRecordingOptions o;
michael@0 761
michael@0 762 o.rotation = mOptions.mRotation;
michael@0 763 o.maxFileSizeBytes = mOptions.mMaxFileSizeBytes;
michael@0 764 o.maxVideoLengthMs = mOptions.mMaxVideoLengthMs;
michael@0 765 o.autoEnableLowLightTorch = mOptions.mAutoEnableLowLightTorch;
michael@0 766 nsresult rv = mCameraControl->StartRecording(mDSFileDescriptor.get(), &o);
michael@0 767 if (NS_SUCCEEDED(rv)) {
michael@0 768 return;
michael@0 769 }
michael@0 770 }
michael@0 771 OnError(CameraControlListener::kInStartRecording, NS_LITERAL_STRING("FAILURE"));
michael@0 772
michael@0 773 if (mDSFileDescriptor->mFileDescriptor.IsValid()) {
michael@0 774 // An error occured. We need to manually close the file associated with the
michael@0 775 // FileDescriptor, and we shouldn't do this on the main thread, so we
michael@0 776 // use a little helper.
michael@0 777 nsRefPtr<CloseFileRunnable> closer =
michael@0 778 new CloseFileRunnable(mDSFileDescriptor->mFileDescriptor);
michael@0 779 closer->Dispatch();
michael@0 780 }
michael@0 781 }
michael@0 782
michael@0 783 void
michael@0 784 nsDOMCameraControl::StopRecording(ErrorResult& aRv)
michael@0 785 {
michael@0 786 MOZ_ASSERT(mCameraControl);
michael@0 787
michael@0 788 #ifdef MOZ_B2G
michael@0 789 if (mAudioChannelAgent) {
michael@0 790 mAudioChannelAgent->StopPlaying();
michael@0 791 mAudioChannelAgent = nullptr;
michael@0 792 }
michael@0 793 #endif
michael@0 794
michael@0 795 aRv = mCameraControl->StopRecording();
michael@0 796 }
michael@0 797
michael@0 798 void
michael@0 799 nsDOMCameraControl::ResumePreview(ErrorResult& aRv)
michael@0 800 {
michael@0 801 MOZ_ASSERT(mCameraControl);
michael@0 802 aRv = mCameraControl->StartPreview();
michael@0 803 }
michael@0 804
michael@0 805 void
michael@0 806 nsDOMCameraControl::SetConfiguration(const CameraConfiguration& aConfiguration,
michael@0 807 const Optional<OwningNonNull<CameraSetConfigurationCallback> >& aOnSuccess,
michael@0 808 const Optional<OwningNonNull<CameraErrorCallback> >& aOnError,
michael@0 809 ErrorResult& aRv)
michael@0 810 {
michael@0 811 MOZ_ASSERT(mCameraControl);
michael@0 812
michael@0 813 nsRefPtr<CameraTakePictureCallback> cb = mTakePictureOnSuccessCb;
michael@0 814 if (cb) {
michael@0 815 // We're busy taking a picture, can't change modes right now.
michael@0 816 if (aOnError.WasPassed()) {
michael@0 817 ErrorResult ignored;
michael@0 818 aOnError.Value().Call(NS_LITERAL_STRING("Busy"), ignored);
michael@0 819 }
michael@0 820 aRv = NS_ERROR_FAILURE;
michael@0 821 return;
michael@0 822 }
michael@0 823
michael@0 824 ICameraControl::Configuration config;
michael@0 825 config.mRecorderProfile = aConfiguration.mRecorderProfile;
michael@0 826 config.mPreviewSize.width = aConfiguration.mPreviewSize.mWidth;
michael@0 827 config.mPreviewSize.height = aConfiguration.mPreviewSize.mHeight;
michael@0 828 config.mMode = ICameraControl::kPictureMode;
michael@0 829 if (aConfiguration.mMode == CameraMode::Video) {
michael@0 830 config.mMode = ICameraControl::kVideoMode;
michael@0 831 }
michael@0 832
michael@0 833 mSetConfigurationOnSuccessCb = nullptr;
michael@0 834 if (aOnSuccess.WasPassed()) {
michael@0 835 mSetConfigurationOnSuccessCb = &aOnSuccess.Value();
michael@0 836 }
michael@0 837 mSetConfigurationOnErrorCb = nullptr;
michael@0 838 if (aOnError.WasPassed()) {
michael@0 839 mSetConfigurationOnErrorCb = &aOnError.Value();
michael@0 840 }
michael@0 841
michael@0 842 aRv = mCameraControl->SetConfiguration(config);
michael@0 843 }
michael@0 844
michael@0 845 class ImmediateErrorCallback : public nsRunnable
michael@0 846 {
michael@0 847 public:
michael@0 848 ImmediateErrorCallback(CameraErrorCallback* aCallback, const nsAString& aMessage)
michael@0 849 : mCallback(aCallback)
michael@0 850 , mMessage(aMessage)
michael@0 851 { }
michael@0 852
michael@0 853 NS_IMETHODIMP
michael@0 854 Run()
michael@0 855 {
michael@0 856 MOZ_ASSERT(NS_IsMainThread());
michael@0 857 ErrorResult ignored;
michael@0 858 mCallback->Call(mMessage, ignored);
michael@0 859 return NS_OK;
michael@0 860 }
michael@0 861
michael@0 862 protected:
michael@0 863 nsRefPtr<CameraErrorCallback> mCallback;
michael@0 864 nsString mMessage;
michael@0 865 };
michael@0 866
michael@0 867
michael@0 868 void
michael@0 869 nsDOMCameraControl::AutoFocus(CameraAutoFocusCallback& aOnSuccess,
michael@0 870 const Optional<OwningNonNull<CameraErrorCallback> >& aOnError,
michael@0 871 ErrorResult& aRv)
michael@0 872 {
michael@0 873 MOZ_ASSERT(mCameraControl);
michael@0 874
michael@0 875 nsRefPtr<CameraAutoFocusCallback> cb = mAutoFocusOnSuccessCb;
michael@0 876 if (cb) {
michael@0 877 if (aOnError.WasPassed()) {
michael@0 878 // There is already a call to AutoFocus() in progress, abort this new one
michael@0 879 // and invoke the error callback (if one was passed in).
michael@0 880 NS_DispatchToMainThread(new ImmediateErrorCallback(&aOnError.Value(),
michael@0 881 NS_LITERAL_STRING("AutoFocusAlreadyInProgress")));
michael@0 882 }
michael@0 883 aRv = NS_ERROR_FAILURE;
michael@0 884 return;
michael@0 885 }
michael@0 886
michael@0 887 mAutoFocusOnSuccessCb = &aOnSuccess;
michael@0 888 mAutoFocusOnErrorCb = nullptr;
michael@0 889 if (aOnError.WasPassed()) {
michael@0 890 mAutoFocusOnErrorCb = &aOnError.Value();
michael@0 891 }
michael@0 892
michael@0 893 aRv = mCameraControl->AutoFocus();
michael@0 894 }
michael@0 895
michael@0 896 void
michael@0 897 nsDOMCameraControl::StartFaceDetection(ErrorResult& aRv)
michael@0 898 {
michael@0 899 MOZ_ASSERT(mCameraControl);
michael@0 900 aRv = mCameraControl->StartFaceDetection();
michael@0 901 }
michael@0 902
michael@0 903 void
michael@0 904 nsDOMCameraControl::StopFaceDetection(ErrorResult& aRv)
michael@0 905 {
michael@0 906 MOZ_ASSERT(mCameraControl);
michael@0 907 aRv = mCameraControl->StopFaceDetection();
michael@0 908 }
michael@0 909
michael@0 910 void
michael@0 911 nsDOMCameraControl::TakePicture(const CameraPictureOptions& aOptions,
michael@0 912 CameraTakePictureCallback& aOnSuccess,
michael@0 913 const Optional<OwningNonNull<CameraErrorCallback> >& aOnError,
michael@0 914 ErrorResult& aRv)
michael@0 915 {
michael@0 916 MOZ_ASSERT(mCameraControl);
michael@0 917
michael@0 918 nsRefPtr<CameraTakePictureCallback> cb = mTakePictureOnSuccessCb;
michael@0 919 if (cb) {
michael@0 920 if (aOnError.WasPassed()) {
michael@0 921 // There is already a call to TakePicture() in progress, abort this new
michael@0 922 // one and invoke the error callback (if one was passed in).
michael@0 923 NS_DispatchToMainThread(new ImmediateErrorCallback(&aOnError.Value(),
michael@0 924 NS_LITERAL_STRING("TakePictureAlreadyInProgress")));
michael@0 925 }
michael@0 926 aRv = NS_ERROR_FAILURE;
michael@0 927 return;
michael@0 928 }
michael@0 929
michael@0 930 {
michael@0 931 ICameraControlParameterSetAutoEnter batch(mCameraControl);
michael@0 932
michael@0 933 // XXXmikeh - remove this: see bug 931155
michael@0 934 ICameraControl::Size s;
michael@0 935 s.width = aOptions.mPictureSize.mWidth;
michael@0 936 s.height = aOptions.mPictureSize.mHeight;
michael@0 937
michael@0 938 ICameraControl::Position p;
michael@0 939 p.latitude = aOptions.mPosition.mLatitude;
michael@0 940 p.longitude = aOptions.mPosition.mLongitude;
michael@0 941 p.altitude = aOptions.mPosition.mAltitude;
michael@0 942 p.timestamp = aOptions.mPosition.mTimestamp;
michael@0 943
michael@0 944 if (s.width && s.height) {
michael@0 945 mCameraControl->Set(CAMERA_PARAM_PICTURE_SIZE, s);
michael@0 946 }
michael@0 947 mCameraControl->Set(CAMERA_PARAM_PICTURE_ROTATION, aOptions.mRotation);
michael@0 948 mCameraControl->Set(CAMERA_PARAM_PICTURE_FILEFORMAT, aOptions.mFileFormat);
michael@0 949 mCameraControl->Set(CAMERA_PARAM_PICTURE_DATETIME, aOptions.mDateTime);
michael@0 950 mCameraControl->SetLocation(p);
michael@0 951 }
michael@0 952
michael@0 953 mTakePictureOnSuccessCb = &aOnSuccess;
michael@0 954 mTakePictureOnErrorCb = nullptr;
michael@0 955 if (aOnError.WasPassed()) {
michael@0 956 mTakePictureOnErrorCb = &aOnError.Value();
michael@0 957 }
michael@0 958
michael@0 959 aRv = mCameraControl->TakePicture();
michael@0 960 }
michael@0 961
michael@0 962 void
michael@0 963 nsDOMCameraControl::ReleaseHardware(const Optional<OwningNonNull<CameraReleaseCallback> >& aOnSuccess,
michael@0 964 const Optional<OwningNonNull<CameraErrorCallback> >& aOnError,
michael@0 965 ErrorResult& aRv)
michael@0 966 {
michael@0 967 MOZ_ASSERT(mCameraControl);
michael@0 968
michael@0 969 mReleaseOnSuccessCb = nullptr;
michael@0 970 if (aOnSuccess.WasPassed()) {
michael@0 971 mReleaseOnSuccessCb = &aOnSuccess.Value();
michael@0 972 }
michael@0 973 mReleaseOnErrorCb = nullptr;
michael@0 974 if (aOnError.WasPassed()) {
michael@0 975 mReleaseOnErrorCb = &aOnError.Value();
michael@0 976 }
michael@0 977
michael@0 978 aRv = mCameraControl->Stop();
michael@0 979 }
michael@0 980
michael@0 981 void
michael@0 982 nsDOMCameraControl::ResumeContinuousFocus(ErrorResult& aRv)
michael@0 983 {
michael@0 984 MOZ_ASSERT(mCameraControl);
michael@0 985 aRv = mCameraControl->ResumeContinuousFocus();
michael@0 986 }
michael@0 987
michael@0 988 void
michael@0 989 nsDOMCameraControl::Shutdown()
michael@0 990 {
michael@0 991 DOM_CAMERA_LOGI("%s:%d\n", __func__, __LINE__);
michael@0 992 MOZ_ASSERT(mCameraControl);
michael@0 993
michael@0 994 // Remove any pending solicited event handlers; these
michael@0 995 // reference our window object, which in turn references
michael@0 996 // us. If we don't remove them, we can leak DOM objects.
michael@0 997 mGetCameraOnSuccessCb = nullptr;
michael@0 998 mGetCameraOnErrorCb = nullptr;
michael@0 999 mAutoFocusOnSuccessCb = nullptr;
michael@0 1000 mAutoFocusOnErrorCb = nullptr;
michael@0 1001 mTakePictureOnSuccessCb = nullptr;
michael@0 1002 mTakePictureOnErrorCb = nullptr;
michael@0 1003 mStartRecordingOnSuccessCb = nullptr;
michael@0 1004 mStartRecordingOnErrorCb = nullptr;
michael@0 1005 mReleaseOnSuccessCb = nullptr;
michael@0 1006 mReleaseOnErrorCb = nullptr;
michael@0 1007 mSetConfigurationOnSuccessCb = nullptr;
michael@0 1008 mSetConfigurationOnErrorCb = nullptr;
michael@0 1009
michael@0 1010 // Remove all of the unsolicited event handlers too.
michael@0 1011 mOnShutterCb = nullptr;
michael@0 1012 mOnClosedCb = nullptr;
michael@0 1013 mOnRecorderStateChangeCb = nullptr;
michael@0 1014 mOnPreviewStateChangeCb = nullptr;
michael@0 1015 mOnAutoFocusMovingCb = nullptr;
michael@0 1016 mOnFacesDetectedCb = nullptr;
michael@0 1017
michael@0 1018 mCameraControl->Shutdown();
michael@0 1019 }
michael@0 1020
michael@0 1021 nsresult
michael@0 1022 nsDOMCameraControl::NotifyRecordingStatusChange(const nsString& aMsg)
michael@0 1023 {
michael@0 1024 NS_ENSURE_TRUE(mWindow, NS_ERROR_FAILURE);
michael@0 1025
michael@0 1026 return MediaManager::NotifyRecordingStatusChange(mWindow,
michael@0 1027 aMsg,
michael@0 1028 true /* aIsAudio */,
michael@0 1029 true /* aIsVideo */);
michael@0 1030 }
michael@0 1031
michael@0 1032 // Camera Control event handlers--must only be called from the Main Thread!
michael@0 1033 void
michael@0 1034 nsDOMCameraControl::OnHardwareStateChange(CameraControlListener::HardwareState aState)
michael@0 1035 {
michael@0 1036 MOZ_ASSERT(NS_IsMainThread());
michael@0 1037 ErrorResult ignored;
michael@0 1038
michael@0 1039 DOM_CAMERA_LOGI("DOM OnHardwareStateChange(%d)\n", aState);
michael@0 1040
michael@0 1041 switch (aState) {
michael@0 1042 case CameraControlListener::kHardwareOpen:
michael@0 1043 // The hardware is open, so we can return a camera to JS, even if
michael@0 1044 // the preview hasn't started yet.
michael@0 1045 if (mGetCameraOnSuccessCb) {
michael@0 1046 nsRefPtr<GetCameraCallback> cb = mGetCameraOnSuccessCb.forget();
michael@0 1047 ErrorResult ignored;
michael@0 1048 mGetCameraOnErrorCb = nullptr;
michael@0 1049 cb->Call(*this, *mCurrentConfiguration, ignored);
michael@0 1050 }
michael@0 1051 break;
michael@0 1052
michael@0 1053 case CameraControlListener::kHardwareClosed:
michael@0 1054 if (mReleaseOnSuccessCb) {
michael@0 1055 // If we have this event handler, this was a solicited hardware close.
michael@0 1056 nsRefPtr<CameraReleaseCallback> cb = mReleaseOnSuccessCb.forget();
michael@0 1057 mReleaseOnErrorCb = nullptr;
michael@0 1058 cb->Call(ignored);
michael@0 1059 } else if(mOnClosedCb) {
michael@0 1060 // If not, something else closed the hardware.
michael@0 1061 nsRefPtr<CameraClosedCallback> cb = mOnClosedCb;
michael@0 1062 cb->Call(ignored);
michael@0 1063 }
michael@0 1064 break;
michael@0 1065
michael@0 1066 default:
michael@0 1067 MOZ_ASSUME_UNREACHABLE("Unanticipated camera hardware state");
michael@0 1068 }
michael@0 1069 }
michael@0 1070
michael@0 1071 void
michael@0 1072 nsDOMCameraControl::OnShutter()
michael@0 1073 {
michael@0 1074 MOZ_ASSERT(NS_IsMainThread());
michael@0 1075
michael@0 1076 DOM_CAMERA_LOGI("DOM ** SNAP **\n");
michael@0 1077
michael@0 1078 nsRefPtr<CameraShutterCallback> cb = mOnShutterCb;
michael@0 1079 if (cb) {
michael@0 1080 ErrorResult ignored;
michael@0 1081 cb->Call(ignored);
michael@0 1082 }
michael@0 1083 }
michael@0 1084
michael@0 1085 void
michael@0 1086 nsDOMCameraControl::OnPreviewStateChange(CameraControlListener::PreviewState aState)
michael@0 1087 {
michael@0 1088 MOZ_ASSERT(NS_IsMainThread());
michael@0 1089
michael@0 1090 if (!mOnPreviewStateChangeCb) {
michael@0 1091 return;
michael@0 1092 }
michael@0 1093
michael@0 1094 nsString state;
michael@0 1095 switch (aState) {
michael@0 1096 case CameraControlListener::kPreviewStarted:
michael@0 1097 state = NS_LITERAL_STRING("started");
michael@0 1098 break;
michael@0 1099
michael@0 1100 default:
michael@0 1101 state = NS_LITERAL_STRING("stopped");
michael@0 1102 break;
michael@0 1103 }
michael@0 1104
michael@0 1105 nsRefPtr<CameraPreviewStateChange> cb = mOnPreviewStateChangeCb;
michael@0 1106 ErrorResult ignored;
michael@0 1107 cb->Call(state, ignored);
michael@0 1108 }
michael@0 1109
michael@0 1110 void
michael@0 1111 nsDOMCameraControl::OnRecorderStateChange(CameraControlListener::RecorderState aState,
michael@0 1112 int32_t aArg, int32_t aTrackNum)
michael@0 1113 {
michael@0 1114 // For now, we do nothing with 'aStatus' and 'aTrackNum'.
michael@0 1115 MOZ_ASSERT(NS_IsMainThread());
michael@0 1116
michael@0 1117 ErrorResult ignored;
michael@0 1118 nsString state;
michael@0 1119
michael@0 1120 switch (aState) {
michael@0 1121 case CameraControlListener::kRecorderStarted:
michael@0 1122 if (mStartRecordingOnSuccessCb) {
michael@0 1123 nsRefPtr<CameraStartRecordingCallback> cb = mStartRecordingOnSuccessCb.forget();
michael@0 1124 mStartRecordingOnErrorCb = nullptr;
michael@0 1125 cb->Call(ignored);
michael@0 1126 }
michael@0 1127 state = NS_LITERAL_STRING("Started");
michael@0 1128 break;
michael@0 1129
michael@0 1130 case CameraControlListener::kRecorderStopped:
michael@0 1131 NotifyRecordingStatusChange(NS_LITERAL_STRING("shutdown"));
michael@0 1132 state = NS_LITERAL_STRING("Stopped");
michael@0 1133 break;
michael@0 1134
michael@0 1135 #ifdef MOZ_B2G_CAMERA
michael@0 1136 case CameraControlListener::kFileSizeLimitReached:
michael@0 1137 state = NS_LITERAL_STRING("FileSizeLimitReached");
michael@0 1138 break;
michael@0 1139
michael@0 1140 case CameraControlListener::kVideoLengthLimitReached:
michael@0 1141 state = NS_LITERAL_STRING("VideoLengthLimitReached");
michael@0 1142 break;
michael@0 1143
michael@0 1144 case CameraControlListener::kTrackCompleted:
michael@0 1145 state = NS_LITERAL_STRING("TrackCompleted");
michael@0 1146 break;
michael@0 1147
michael@0 1148 case CameraControlListener::kTrackFailed:
michael@0 1149 state = NS_LITERAL_STRING("TrackFailed");
michael@0 1150 break;
michael@0 1151
michael@0 1152 case CameraControlListener::kMediaRecorderFailed:
michael@0 1153 state = NS_LITERAL_STRING("MediaRecorderFailed");
michael@0 1154 break;
michael@0 1155
michael@0 1156 case CameraControlListener::kMediaServerFailed:
michael@0 1157 state = NS_LITERAL_STRING("MediaServerFailed");
michael@0 1158 break;
michael@0 1159 #endif
michael@0 1160
michael@0 1161 default:
michael@0 1162 MOZ_ASSUME_UNREACHABLE("Unanticipated video recorder error");
michael@0 1163 return;
michael@0 1164 }
michael@0 1165
michael@0 1166 nsRefPtr<CameraRecorderStateChange> cb = mOnRecorderStateChangeCb;
michael@0 1167 if (cb) {
michael@0 1168 cb->Call(state, ignored);
michael@0 1169 }
michael@0 1170 }
michael@0 1171
michael@0 1172 void
michael@0 1173 nsDOMCameraControl::OnConfigurationChange(DOMCameraConfiguration* aConfiguration)
michael@0 1174 {
michael@0 1175 MOZ_ASSERT(NS_IsMainThread());
michael@0 1176
michael@0 1177 // Update our record of the current camera configuration
michael@0 1178 mCurrentConfiguration = aConfiguration;
michael@0 1179
michael@0 1180 DOM_CAMERA_LOGI("DOM OnConfigurationChange: this=%p\n", this);
michael@0 1181 DOM_CAMERA_LOGI(" mode : %s\n",
michael@0 1182 mCurrentConfiguration->mMode == CameraMode::Video ? "video" : "picture");
michael@0 1183 DOM_CAMERA_LOGI(" maximum focus areas : %d\n",
michael@0 1184 mCurrentConfiguration->mMaxFocusAreas);
michael@0 1185 DOM_CAMERA_LOGI(" maximum metering areas : %d\n",
michael@0 1186 mCurrentConfiguration->mMaxMeteringAreas);
michael@0 1187 DOM_CAMERA_LOGI(" preview size (w x h) : %d x %d\n",
michael@0 1188 mCurrentConfiguration->mPreviewSize.mWidth, mCurrentConfiguration->mPreviewSize.mHeight);
michael@0 1189 DOM_CAMERA_LOGI(" recorder profile : %s\n",
michael@0 1190 NS_ConvertUTF16toUTF8(mCurrentConfiguration->mRecorderProfile).get());
michael@0 1191
michael@0 1192 nsRefPtr<CameraSetConfigurationCallback> cb = mSetConfigurationOnSuccessCb.forget();
michael@0 1193 mSetConfigurationOnErrorCb = nullptr;
michael@0 1194 if (cb) {
michael@0 1195 ErrorResult ignored;
michael@0 1196 cb->Call(*mCurrentConfiguration, ignored);
michael@0 1197 }
michael@0 1198 }
michael@0 1199
michael@0 1200 void
michael@0 1201 nsDOMCameraControl::OnAutoFocusComplete(bool aAutoFocusSucceeded)
michael@0 1202 {
michael@0 1203 MOZ_ASSERT(NS_IsMainThread());
michael@0 1204
michael@0 1205 nsRefPtr<CameraAutoFocusCallback> cb = mAutoFocusOnSuccessCb.forget();
michael@0 1206 mAutoFocusOnErrorCb = nullptr;
michael@0 1207 if (cb) {
michael@0 1208 ErrorResult ignored;
michael@0 1209 cb->Call(aAutoFocusSucceeded, ignored);
michael@0 1210 }
michael@0 1211 }
michael@0 1212
michael@0 1213 void
michael@0 1214 nsDOMCameraControl::OnAutoFocusMoving(bool aIsMoving)
michael@0 1215 {
michael@0 1216 MOZ_ASSERT(NS_IsMainThread());
michael@0 1217
michael@0 1218 nsRefPtr<CameraAutoFocusMovingCallback> cb = mOnAutoFocusMovingCb;
michael@0 1219 if (cb) {
michael@0 1220 ErrorResult ignored;
michael@0 1221 cb->Call(aIsMoving, ignored);
michael@0 1222 }
michael@0 1223 }
michael@0 1224
michael@0 1225 void
michael@0 1226 nsDOMCameraControl::OnFacesDetected(const nsTArray<ICameraControl::Face>& aFaces)
michael@0 1227 {
michael@0 1228 DOM_CAMERA_LOGI("DOM OnFacesDetected %u face(s)\n", aFaces.Length());
michael@0 1229 MOZ_ASSERT(NS_IsMainThread());
michael@0 1230
michael@0 1231 nsRefPtr<CameraFaceDetectionCallback> cb = mOnFacesDetectedCb;
michael@0 1232 if (!cb) {
michael@0 1233 return;
michael@0 1234 }
michael@0 1235
michael@0 1236 Sequence<OwningNonNull<DOMCameraDetectedFace> > faces;
michael@0 1237 uint32_t len = aFaces.Length();
michael@0 1238
michael@0 1239 if (faces.SetCapacity(len)) {
michael@0 1240 nsRefPtr<DOMCameraDetectedFace> f;
michael@0 1241 for (uint32_t i = 0; i < len; ++i) {
michael@0 1242 f = new DOMCameraDetectedFace(this, aFaces[i]);
michael@0 1243 *faces.AppendElement() = f.forget().take();
michael@0 1244 }
michael@0 1245 }
michael@0 1246
michael@0 1247 ErrorResult ignored;
michael@0 1248 cb->Call(faces, ignored);
michael@0 1249 }
michael@0 1250
michael@0 1251 void
michael@0 1252 nsDOMCameraControl::OnTakePictureComplete(nsIDOMBlob* aPicture)
michael@0 1253 {
michael@0 1254 MOZ_ASSERT(NS_IsMainThread());
michael@0 1255
michael@0 1256 nsRefPtr<CameraTakePictureCallback> cb = mTakePictureOnSuccessCb.forget();
michael@0 1257 mTakePictureOnErrorCb = nullptr;
michael@0 1258 if (!cb) {
michael@0 1259 // Warn because it shouldn't be possible to get here without
michael@0 1260 // having passed a success callback into takePicture(), even
michael@0 1261 // though we guard against a nullptr dereference.
michael@0 1262 NS_WARNING("DOM Null success callback in OnTakePictureComplete()");
michael@0 1263 return;
michael@0 1264 }
michael@0 1265
michael@0 1266 ErrorResult ignored;
michael@0 1267 cb->Call(aPicture, ignored);
michael@0 1268 }
michael@0 1269
michael@0 1270 void
michael@0 1271 nsDOMCameraControl::OnError(CameraControlListener::CameraErrorContext aContext, const nsAString& aError)
michael@0 1272 {
michael@0 1273 DOM_CAMERA_LOGI("DOM OnError context=%d, error='%s'\n", aContext,
michael@0 1274 NS_LossyConvertUTF16toASCII(aError).get());
michael@0 1275 MOZ_ASSERT(NS_IsMainThread());
michael@0 1276
michael@0 1277 nsRefPtr<CameraErrorCallback> errorCb;
michael@0 1278
michael@0 1279 switch (aContext) {
michael@0 1280 case CameraControlListener::kInStartCamera:
michael@0 1281 mGetCameraOnSuccessCb = nullptr;
michael@0 1282 errorCb = mGetCameraOnErrorCb.forget();
michael@0 1283 break;
michael@0 1284
michael@0 1285 case CameraControlListener::kInStopCamera:
michael@0 1286 mReleaseOnSuccessCb = nullptr;
michael@0 1287 errorCb = mReleaseOnErrorCb.forget();
michael@0 1288 break;
michael@0 1289
michael@0 1290 case CameraControlListener::kInSetConfiguration:
michael@0 1291 mSetConfigurationOnSuccessCb = nullptr;
michael@0 1292 errorCb = mSetConfigurationOnErrorCb.forget();
michael@0 1293 break;
michael@0 1294
michael@0 1295 case CameraControlListener::kInAutoFocus:
michael@0 1296 mAutoFocusOnSuccessCb = nullptr;
michael@0 1297 errorCb = mAutoFocusOnErrorCb.forget();
michael@0 1298 break;
michael@0 1299
michael@0 1300 case CameraControlListener::kInTakePicture:
michael@0 1301 mTakePictureOnSuccessCb = nullptr;
michael@0 1302 errorCb = mTakePictureOnErrorCb.forget();
michael@0 1303 break;
michael@0 1304
michael@0 1305 case CameraControlListener::kInStartRecording:
michael@0 1306 mStartRecordingOnSuccessCb = nullptr;
michael@0 1307 errorCb = mStartRecordingOnErrorCb.forget();
michael@0 1308 break;
michael@0 1309
michael@0 1310 case CameraControlListener::kInStopRecording:
michael@0 1311 // This method doesn't have any callbacks, so all we can do is log the
michael@0 1312 // failure. This only happens after the hardware has been released.
michael@0 1313 NS_WARNING("Failed to stop recording");
michael@0 1314 return;
michael@0 1315
michael@0 1316 case CameraControlListener::kInStartPreview:
michael@0 1317 // This method doesn't have any callbacks, so all we can do is log the
michael@0 1318 // failure. This only happens after the hardware has been released.
michael@0 1319 NS_WARNING("Failed to (re)start preview");
michael@0 1320 return;
michael@0 1321
michael@0 1322 case CameraControlListener::kInUnspecified:
michael@0 1323 if (aError.EqualsASCII("ErrorServiceFailed")) {
michael@0 1324 // If the camera service fails, we will get preview-stopped and
michael@0 1325 // hardware-closed events, so nothing to do here.
michael@0 1326 NS_WARNING("Camera service failed");
michael@0 1327 return;
michael@0 1328 }
michael@0 1329 if (aError.EqualsASCII("ErrorSetPictureSizeFailed") ||
michael@0 1330 aError.EqualsASCII("ErrorSetThumbnailSizeFailed")) {
michael@0 1331 // We currently don't handle attribute setter failure. Practically,
michael@0 1332 // this only ever happens if a setter is called after the hardware
michael@0 1333 // has gone away before an asynchronous set gets to happen, so we
michael@0 1334 // swallow these.
michael@0 1335 NS_WARNING("Failed to set either picture or thumbnail size");
michael@0 1336 return;
michael@0 1337 }
michael@0 1338 // fallthrough
michael@0 1339
michael@0 1340 default:
michael@0 1341 MOZ_ASSUME_UNREACHABLE("Error occurred in unanticipated camera state");
michael@0 1342 return;
michael@0 1343 }
michael@0 1344
michael@0 1345 if (!errorCb) {
michael@0 1346 DOM_CAMERA_LOGW("DOM No error handler for error '%s' in context=%d\n",
michael@0 1347 NS_LossyConvertUTF16toASCII(aError).get(), aContext);
michael@0 1348 return;
michael@0 1349 }
michael@0 1350
michael@0 1351 ErrorResult ignored;
michael@0 1352 errorCb->Call(aError, ignored);
michael@0 1353 }
michael@0 1354

mercurial