dom/camera/DOMCameraControlListener.cpp
author Chris H-C <chutten@mozilla.com>
Mon, 04 Jul 2016 11:16:05 -0400
changeset 312997 df28918fe2361f0b54ca9ce4773a29c4c0675d06
parent 294851 fcc0936b576daa150697671849a191009ca33811
permissions -rw-r--r--
bug 1218576 - Support remote accumulation via JS histograms. r=gfritzsche The JS histograms, too, need to dispatch their accumulations from child to parent. JSHistograms_Add now only supports histograms that are in gHistogramsMap or that were created in the parent process. After bug 1288745, maybe we'll be able to change this to be less convoluted. MozReview-Commit-ID: 3qTH89YKbGP

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "DOMCameraControlListener.h"
#include "nsThreadUtils.h"
#include "CameraCommon.h"
#include "DOMCameraControl.h"
#include "CameraPreviewMediaStream.h"
#include "mozilla/dom/CameraManagerBinding.h"
#include "mozilla/dom/File.h"
#include "nsQueryObject.h"

using namespace mozilla;
using namespace mozilla::dom;

DOMCameraControlListener::DOMCameraControlListener(nsDOMCameraControl* aDOMCameraControl,
                                                   CameraPreviewMediaStream* aStream)
  : mDOMCameraControl(
      new nsMainThreadPtrHolder<nsISupports>(static_cast<DOMMediaStream*>(aDOMCameraControl)))
  , mStream(aStream)
{
  DOM_CAMERA_LOGT("%s:%d : this=%p, camera=%p, stream=%p\n",
    __func__, __LINE__, this, aDOMCameraControl, aStream);
}

DOMCameraControlListener::~DOMCameraControlListener()
{
  DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
}

// Boilerplate callback runnable
class DOMCameraControlListener::DOMCallback : public Runnable
{
public:
  explicit DOMCallback(nsMainThreadPtrHandle<nsISupports> aDOMCameraControl)
    : mDOMCameraControl(aDOMCameraControl)
  {
    MOZ_COUNT_CTOR(DOMCameraControlListener::DOMCallback);
  }

protected:
  virtual ~DOMCallback()
  {
    MOZ_COUNT_DTOR(DOMCameraControlListener::DOMCallback);
  }

public:
  virtual void RunCallback(nsDOMCameraControl* aDOMCameraControl) = 0;

  NS_IMETHOD
  Run() override
  {
    MOZ_ASSERT(NS_IsMainThread());

    RefPtr<nsDOMCameraControl> camera = do_QueryObject(mDOMCameraControl.get());
    if (!camera) {
      DOM_CAMERA_LOGE("do_QueryObject failed to get an nsDOMCameraControl\n");
      return NS_ERROR_INVALID_ARG;
    }
    RunCallback(camera);
    return NS_OK;
  }

protected:
  nsMainThreadPtrHandle<nsISupports> mDOMCameraControl;
};

// Specific callback handlers
void
DOMCameraControlListener::OnHardwareStateChange(HardwareState aState,
                                                nsresult aReason)
{
  class Callback : public DOMCallback
  {
  public:
    Callback(nsMainThreadPtrHandle<nsISupports> aDOMCameraControl,
             HardwareState aState, nsresult aReason)
      : DOMCallback(aDOMCameraControl)
      , mState(aState)
      , mReason(aReason)
    { }

    void
    RunCallback(nsDOMCameraControl* aDOMCameraControl) override
    {
      aDOMCameraControl->OnHardwareStateChange(mState, mReason);
    }

  protected:
    HardwareState mState;
    nsresult mReason;
  };

  NS_DispatchToMainThread(new Callback(mDOMCameraControl, aState, aReason));
}

void
DOMCameraControlListener::OnPreviewStateChange(PreviewState aState)
{
  class Callback : public DOMCallback
  {
  public:
    Callback(nsMainThreadPtrHandle<nsISupports> aDOMCameraControl,
             PreviewState aState)
      : DOMCallback(aDOMCameraControl)
      , mState(aState)
    { }

    void
    RunCallback(nsDOMCameraControl* aDOMCameraControl) override
    {
      aDOMCameraControl->OnPreviewStateChange(mState);
    }

  protected:
    PreviewState mState;
  };

  switch (aState) {
    case kPreviewStopped:
      // Clear the current frame right away, without dispatching a
      //  runnable. This is an ugly coupling between the camera's
      //  SurfaceTextureClient and the MediaStream/ImageContainer,
      //  but without it, the preview can fail to start.
      DOM_CAMERA_LOGI("Preview stopped, clearing current frame\n");
      mStream->ClearCurrentFrame();
      break;

    case kPreviewPaused:
      // In the paused state, we still want to reflect the change
      //  in preview state, but we don't want to clear the current
      //  frame as above, since doing so seems to cause genlock
      //  problems when we restart the preview. See bug 957749.
      DOM_CAMERA_LOGI("Preview paused\n");
      break;

    case kPreviewStarted:
      DOM_CAMERA_LOGI("Preview started\n");
      break;

    default:
      DOM_CAMERA_LOGE("Unknown preview state %d\n", aState);
      MOZ_ASSERT_UNREACHABLE("Invalid preview state");
      return;
  }
  mStream->OnPreviewStateChange(aState == kPreviewStarted);
  NS_DispatchToMainThread(new Callback(mDOMCameraControl, aState));
}

void
DOMCameraControlListener::OnRecorderStateChange(RecorderState aState,
                                                int32_t aStatus, int32_t aTrackNum)
{
  class Callback : public DOMCallback
  {
  public:
    Callback(nsMainThreadPtrHandle<nsISupports> aDOMCameraControl,
             RecorderState aState,
             int32_t aStatus,
             int32_t aTrackNum)
      : DOMCallback(aDOMCameraControl)
      , mState(aState)
      , mStatus(aStatus)
      , mTrackNum(aTrackNum)
    { }

    void
    RunCallback(nsDOMCameraControl* aDOMCameraControl) override
    {
      aDOMCameraControl->OnRecorderStateChange(mState, mStatus, mTrackNum);
    }

  protected:
    RecorderState mState;
    int32_t mStatus;
    int32_t mTrackNum;
  };

  NS_DispatchToMainThread(new Callback(mDOMCameraControl, aState, aStatus, aTrackNum));
}

void
DOMCameraControlListener::OnConfigurationChange(const CameraListenerConfiguration& aConfiguration)
{
  class Callback : public DOMCallback
  {
  public:
    Callback(nsMainThreadPtrHandle<nsISupports> aDOMCameraControl,
             const CameraListenerConfiguration& aConfiguration)
      : DOMCallback(aDOMCameraControl)
      , mConfiguration(aConfiguration)
    { }

    void
    RunCallback(nsDOMCameraControl* aDOMCameraControl) override
    {
      RefPtr<nsDOMCameraControl::DOMCameraConfiguration> config =
        new nsDOMCameraControl::DOMCameraConfiguration();

      switch (mConfiguration.mMode) {
        case ICameraControl::kVideoMode:
          config->mMode = CameraMode::Video;
          break;

        case ICameraControl::kPictureMode:
          config->mMode = CameraMode::Picture;
          break;

        default:
          DOM_CAMERA_LOGI("Camera mode still unspecified, nothing to do\n");
          return;
      }

      // Map CameraControl parameters to their DOM-facing equivalents
      config->mRecorderProfile = mConfiguration.mRecorderProfile;
      config->mPreviewSize.mWidth = mConfiguration.mPreviewSize.width;
      config->mPreviewSize.mHeight = mConfiguration.mPreviewSize.height;
      config->mPictureSize.mWidth = mConfiguration.mPictureSize.width;
      config->mPictureSize.mHeight = mConfiguration.mPictureSize.height;
      config->mMaxMeteringAreas = mConfiguration.mMaxMeteringAreas;
      config->mMaxFocusAreas = mConfiguration.mMaxFocusAreas;

      aDOMCameraControl->OnConfigurationChange(config);
    }

  protected:
    const CameraListenerConfiguration mConfiguration;
  };

  NS_DispatchToMainThread(new Callback(mDOMCameraControl, aConfiguration));
}

void
DOMCameraControlListener::OnAutoFocusMoving(bool aIsMoving)
{
  class Callback : public DOMCallback
  {
  public:
    Callback(nsMainThreadPtrHandle<nsISupports> aDOMCameraControl, bool aIsMoving)
      : DOMCallback(aDOMCameraControl)
      , mIsMoving(aIsMoving)
    { }

    void
    RunCallback(nsDOMCameraControl* aDOMCameraControl) override
    {
      aDOMCameraControl->OnAutoFocusMoving(mIsMoving);
    }

  protected:
    bool mIsMoving;
  };

  NS_DispatchToMainThread(new Callback(mDOMCameraControl, aIsMoving));
}

void
DOMCameraControlListener::OnFacesDetected(const nsTArray<ICameraControl::Face>& aFaces)
{
  class Callback : public DOMCallback
  {
  public:
    Callback(nsMainThreadPtrHandle<nsISupports> aDOMCameraControl,
             const nsTArray<ICameraControl::Face>& aFaces)
      : DOMCallback(aDOMCameraControl)
      , mFaces(aFaces)
    { }

    void
    RunCallback(nsDOMCameraControl* aDOMCameraControl) override
    {
      aDOMCameraControl->OnFacesDetected(mFaces);
    }

  protected:
    const nsTArray<ICameraControl::Face> mFaces;
  };

  NS_DispatchToMainThread(new Callback(mDOMCameraControl, aFaces));
}

void
DOMCameraControlListener::OnShutter()
{
  class Callback : public DOMCallback
  {
  public:
    explicit Callback(nsMainThreadPtrHandle<nsISupports> aDOMCameraControl)
      : DOMCallback(aDOMCameraControl)
    { }

    void
    RunCallback(nsDOMCameraControl* aDOMCameraControl) override
    {
      aDOMCameraControl->OnShutter();
    }
  };

  NS_DispatchToMainThread(new Callback(mDOMCameraControl));
}

void
DOMCameraControlListener::OnRateLimitPreview(bool aLimit)
{
  mStream->RateLimit(aLimit);
}

bool
DOMCameraControlListener::OnNewPreviewFrame(layers::Image* aImage, uint32_t aWidth, uint32_t aHeight)
{
  DOM_CAMERA_LOGI("OnNewPreviewFrame: got %d x %d frame\n", aWidth, aHeight);

  mStream->SetCurrentFrame(gfx::IntSize(aWidth, aHeight), aImage);
  return true;
}

void
DOMCameraControlListener::OnAutoFocusComplete(bool aAutoFocusSucceeded)
{
  class Callback : public DOMCallback
  {
  public:
    Callback(nsMainThreadPtrHandle<nsISupports> aDOMCameraControl,
             bool aAutoFocusSucceeded)
      : DOMCallback(aDOMCameraControl)
      , mAutoFocusSucceeded(aAutoFocusSucceeded)
    { }

    void
    RunCallback(nsDOMCameraControl* aDOMCameraControl) override
    {
      aDOMCameraControl->OnAutoFocusComplete(mAutoFocusSucceeded);
    }

  protected:
    bool mAutoFocusSucceeded;
  };

  NS_DispatchToMainThread(new Callback(mDOMCameraControl, aAutoFocusSucceeded));
}

void
DOMCameraControlListener::OnTakePictureComplete(const uint8_t* aData, uint32_t aLength, const nsAString& aMimeType)
{
  class Callback : public DOMCallback
  {
  public:
    Callback(nsMainThreadPtrHandle<nsISupports> aDOMCameraControl,
             const uint8_t* aData, uint32_t aLength, const nsAString& aMimeType)
      : DOMCallback(aDOMCameraControl)
      , mLength(aLength)
      , mMimeType(aMimeType)
    {
        mData = (uint8_t*) malloc(aLength);
        memcpy(mData, aData, aLength);
    }

    void
    RunCallback(nsDOMCameraControl* aDOMCameraControl) override
    {
      nsCOMPtr<nsIDOMBlob> picture =
        Blob::CreateMemoryBlob(mDOMCameraControl.get(),
                               static_cast<void*>(mData),
                               static_cast<uint64_t>(mLength),
                               mMimeType);
      aDOMCameraControl->OnTakePictureComplete(picture);
      mData = NULL;
    }

  protected:
    virtual
    ~Callback()
    {
        free(mData);
    }

    uint8_t* mData;
    uint32_t mLength;
    nsString mMimeType;
  };

  NS_DispatchToMainThread(new Callback(mDOMCameraControl, aData, aLength, aMimeType));
}

void
DOMCameraControlListener::OnUserError(UserContext aContext, nsresult aError)
{
  class Callback : public DOMCallback
  {
  public:
    Callback(nsMainThreadPtrHandle<nsISupports> aDOMCameraControl,
             UserContext aContext,
             nsresult aError)
      : DOMCallback(aDOMCameraControl)
      , mContext(aContext)
      , mError(aError)
    { }

    virtual void
    RunCallback(nsDOMCameraControl* aDOMCameraControl) override
    {
      aDOMCameraControl->OnUserError(mContext, mError);
    }

  protected:
    UserContext mContext;
    nsresult mError;
  };

  NS_DispatchToMainThread(new Callback(mDOMCameraControl, aContext, aError));
}

void
DOMCameraControlListener::OnPoster(BlobImpl* aBlobImpl)
{
  class Callback : public DOMCallback
  {
  public:
    Callback(nsMainThreadPtrHandle<nsISupports> aDOMCameraControl, BlobImpl* aBlobImpl)
      : DOMCallback(aDOMCameraControl)
      , mBlobImpl(aBlobImpl)
    { }

    void
    RunCallback(nsDOMCameraControl* aDOMCameraControl) override
    {
      aDOMCameraControl->OnPoster(mBlobImpl);
    }

  protected:
    RefPtr<BlobImpl> mBlobImpl;
  };

  NS_DispatchToMainThread(new Callback(mDOMCameraControl, aBlobImpl));
}