зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1175656 - Implement generation of recording posters in Gecko. r=dhylands,bz
This commit is contained in:
Родитель
ddccac2fc1
Коммит
d74af6faf3
|
@ -177,6 +177,19 @@ CameraControlImpl::OnTakePictureComplete(const uint8_t* aData, uint32_t aLength,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CameraControlImpl::OnPoster(dom::BlobImpl* aBlobImpl)
|
||||||
|
{
|
||||||
|
// This callback can run on threads other than the Main Thread and
|
||||||
|
// the Camera Thread.
|
||||||
|
RwLockAutoEnterRead lock(mListenerLock);
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < mListeners.Length(); ++i) {
|
||||||
|
CameraControlListener* l = mListeners[i];
|
||||||
|
l->OnPoster(aBlobImpl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
CameraControlImpl::OnShutter()
|
CameraControlImpl::OnShutter()
|
||||||
{
|
{
|
||||||
|
|
|
@ -20,6 +20,10 @@
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
|
|
||||||
|
namespace dom {
|
||||||
|
class BlobImpl;
|
||||||
|
}
|
||||||
|
|
||||||
namespace layers {
|
namespace layers {
|
||||||
class Image;
|
class Image;
|
||||||
}
|
}
|
||||||
|
@ -57,6 +61,7 @@ protected:
|
||||||
void OnAutoFocusComplete(bool aAutoFocusSucceeded);
|
void OnAutoFocusComplete(bool aAutoFocusSucceeded);
|
||||||
void OnFacesDetected(const nsTArray<Face>& aFaces);
|
void OnFacesDetected(const nsTArray<Face>& aFaces);
|
||||||
void OnTakePictureComplete(const uint8_t* aData, uint32_t aLength, const nsAString& aMimeType);
|
void OnTakePictureComplete(const uint8_t* aData, uint32_t aLength, const nsAString& aMimeType);
|
||||||
|
void OnPoster(dom::BlobImpl* aBlobImpl);
|
||||||
|
|
||||||
void OnRateLimitPreview(bool aLimit);
|
void OnRateLimitPreview(bool aLimit);
|
||||||
bool OnNewPreviewFrame(layers::Image* aImage, uint32_t aWidth, uint32_t aHeight);
|
bool OnNewPreviewFrame(layers::Image* aImage, uint32_t aWidth, uint32_t aHeight);
|
||||||
|
|
|
@ -10,6 +10,10 @@
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
|
|
||||||
|
namespace dom {
|
||||||
|
class BlobImpl;
|
||||||
|
}
|
||||||
|
|
||||||
namespace layers {
|
namespace layers {
|
||||||
class Image;
|
class Image;
|
||||||
}
|
}
|
||||||
|
@ -60,6 +64,8 @@ public:
|
||||||
{
|
{
|
||||||
kRecorderStopped,
|
kRecorderStopped,
|
||||||
kRecorderStarted,
|
kRecorderStarted,
|
||||||
|
kPosterCreated,
|
||||||
|
kPosterFailed,
|
||||||
#ifdef MOZ_B2G_CAMERA
|
#ifdef MOZ_B2G_CAMERA
|
||||||
kFileSizeLimitReached,
|
kFileSizeLimitReached,
|
||||||
kVideoLengthLimitReached,
|
kVideoLengthLimitReached,
|
||||||
|
@ -91,6 +97,7 @@ public:
|
||||||
virtual void OnAutoFocusMoving(bool aIsMoving) { }
|
virtual void OnAutoFocusMoving(bool aIsMoving) { }
|
||||||
virtual void OnTakePictureComplete(const uint8_t* aData, uint32_t aLength, const nsAString& aMimeType) { }
|
virtual void OnTakePictureComplete(const uint8_t* aData, uint32_t aLength, const nsAString& aMimeType) { }
|
||||||
virtual void OnFacesDetected(const nsTArray<ICameraControl::Face>& aFaces) { }
|
virtual void OnFacesDetected(const nsTArray<ICameraControl::Face>& aFaces) { }
|
||||||
|
virtual void OnPoster(dom::BlobImpl* aBlobImpl) { }
|
||||||
|
|
||||||
enum UserContext
|
enum UserContext
|
||||||
{
|
{
|
||||||
|
|
|
@ -82,6 +82,19 @@ nsDOMCameraControl::HasSupport(JSContext* aCx, JSObject* aGlobal)
|
||||||
return Navigator::HasCameraSupport(aCx, aGlobal);
|
return Navigator::HasCameraSupport(aCx, aGlobal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static nsresult
|
||||||
|
RegisterStorageRequestEvents(DOMRequest* aRequest, nsIDOMEventListener* aListener)
|
||||||
|
{
|
||||||
|
EventListenerManager* elm = aRequest->GetOrCreateListenerManager();
|
||||||
|
if (NS_WARN_IF(!elm)) {
|
||||||
|
return NS_ERROR_UNEXPECTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
elm->AddEventListener(NS_LITERAL_STRING("success"), aListener, false, false);
|
||||||
|
elm->AddEventListener(NS_LITERAL_STRING("error"), aListener, false, false);
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
class mozilla::StartRecordingHelper : public nsIDOMEventListener
|
class mozilla::StartRecordingHelper : public nsIDOMEventListener
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -90,6 +103,7 @@ public:
|
||||||
|
|
||||||
explicit StartRecordingHelper(nsDOMCameraControl* aDOMCameraControl)
|
explicit StartRecordingHelper(nsDOMCameraControl* aDOMCameraControl)
|
||||||
: mDOMCameraControl(aDOMCameraControl)
|
: mDOMCameraControl(aDOMCameraControl)
|
||||||
|
, mState(false)
|
||||||
{
|
{
|
||||||
MOZ_COUNT_CTOR(StartRecordingHelper);
|
MOZ_COUNT_CTOR(StartRecordingHelper);
|
||||||
}
|
}
|
||||||
|
@ -98,10 +112,12 @@ protected:
|
||||||
virtual ~StartRecordingHelper()
|
virtual ~StartRecordingHelper()
|
||||||
{
|
{
|
||||||
MOZ_COUNT_DTOR(StartRecordingHelper);
|
MOZ_COUNT_DTOR(StartRecordingHelper);
|
||||||
|
mDOMCameraControl->OnCreatedFileDescriptor(mState);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
nsRefPtr<nsDOMCameraControl> mDOMCameraControl;
|
nsRefPtr<nsDOMCameraControl> mDOMCameraControl;
|
||||||
|
bool mState;
|
||||||
};
|
};
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
|
@ -109,13 +125,50 @@ StartRecordingHelper::HandleEvent(nsIDOMEvent* aEvent)
|
||||||
{
|
{
|
||||||
nsString eventType;
|
nsString eventType;
|
||||||
aEvent->GetType(eventType);
|
aEvent->GetType(eventType);
|
||||||
|
mState = eventType.EqualsLiteral("success");
|
||||||
mDOMCameraControl->OnCreatedFileDescriptor(eventType.EqualsLiteral("success"));
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMPL_ISUPPORTS(mozilla::StartRecordingHelper, nsIDOMEventListener)
|
NS_IMPL_ISUPPORTS(mozilla::StartRecordingHelper, nsIDOMEventListener)
|
||||||
|
|
||||||
|
class mozilla::RecorderPosterHelper : public nsIDOMEventListener
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NS_DECL_ISUPPORTS
|
||||||
|
NS_DECL_NSIDOMEVENTLISTENER
|
||||||
|
|
||||||
|
explicit RecorderPosterHelper(nsDOMCameraControl* aDOMCameraControl)
|
||||||
|
: mDOMCameraControl(aDOMCameraControl)
|
||||||
|
, mState(CameraControlListener::kPosterFailed)
|
||||||
|
{
|
||||||
|
MOZ_COUNT_CTOR(RecorderPosterHelper);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ~RecorderPosterHelper()
|
||||||
|
{
|
||||||
|
MOZ_COUNT_DTOR(RecorderPosterHelper);
|
||||||
|
mDOMCameraControl->OnRecorderStateChange(mState, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
nsRefPtr<nsDOMCameraControl> mDOMCameraControl;
|
||||||
|
CameraControlListener::RecorderState mState;
|
||||||
|
};
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
RecorderPosterHelper::HandleEvent(nsIDOMEvent* aEvent)
|
||||||
|
{
|
||||||
|
nsString eventType;
|
||||||
|
aEvent->GetType(eventType);
|
||||||
|
if (eventType.EqualsLiteral("success")) {
|
||||||
|
mState = CameraControlListener::kPosterCreated;
|
||||||
|
}
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMPL_ISUPPORTS(mozilla::RecorderPosterHelper, nsIDOMEventListener)
|
||||||
|
|
||||||
nsDOMCameraControl::DOMCameraConfiguration::DOMCameraConfiguration()
|
nsDOMCameraControl::DOMCameraConfiguration::DOMCameraConfiguration()
|
||||||
: CameraConfiguration()
|
: CameraConfiguration()
|
||||||
, mMaxFocusAreas(0)
|
, mMaxFocusAreas(0)
|
||||||
|
@ -202,6 +255,7 @@ nsDOMCameraControl::nsDOMCameraControl(uint32_t aCameraId,
|
||||||
, mWindow(aWindow)
|
, mWindow(aWindow)
|
||||||
, mPreviewState(CameraControlListener::kPreviewStopped)
|
, mPreviewState(CameraControlListener::kPreviewStopped)
|
||||||
, mRecording(false)
|
, mRecording(false)
|
||||||
|
, mRecordingStoppedDeferred(false)
|
||||||
, mSetInitialConfig(false)
|
, mSetInitialConfig(false)
|
||||||
{
|
{
|
||||||
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
|
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
|
||||||
|
@ -743,7 +797,18 @@ nsDOMCameraControl::StartRecording(const CameraStartRecordingOptions& aOptions,
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mStartRecordingPromise || mRecording) {
|
// Must supply both the poster path and storage area or neither
|
||||||
|
if (aOptions.mPosterFilepath.IsEmpty() ==
|
||||||
|
static_cast<bool>(aOptions.mPosterStorageArea.get())) {
|
||||||
|
promise->MaybeReject(NS_ERROR_ILLEGAL_VALUE);
|
||||||
|
return promise.forget();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we are trying to start recording, already recording or are still
|
||||||
|
// waiting for a poster to be created/fail, we need to wait
|
||||||
|
if (mStartRecordingPromise || mRecording ||
|
||||||
|
mRecordingStoppedDeferred ||
|
||||||
|
!mOptions.mPosterFilepath.IsEmpty()) {
|
||||||
promise->MaybeReject(NS_ERROR_IN_PROGRESS);
|
promise->MaybeReject(NS_ERROR_IN_PROGRESS);
|
||||||
return promise.forget();
|
return promise.forget();
|
||||||
}
|
}
|
||||||
|
@ -762,20 +827,16 @@ nsDOMCameraControl::StartRecording(const CameraStartRecordingOptions& aOptions,
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
mStartRecordingPromise = promise;
|
nsCOMPtr<nsIDOMEventListener> listener = new StartRecordingHelper(this);
|
||||||
mOptions = aOptions;
|
aRv = RegisterStorageRequestEvents(request, listener);
|
||||||
|
if (aRv.Failed()) {
|
||||||
EventListenerManager* elm = request->GetOrCreateListenerManager();
|
|
||||||
if (!elm) {
|
|
||||||
NotifyRecordingStatusChange(NS_LITERAL_STRING("shutdown"));
|
NotifyRecordingStatusChange(NS_LITERAL_STRING("shutdown"));
|
||||||
aRv.Throw(NS_ERROR_UNEXPECTED);
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mStartRecordingPromise = promise;
|
||||||
|
mOptions = aOptions;
|
||||||
mRecording = true;
|
mRecording = true;
|
||||||
nsCOMPtr<nsIDOMEventListener> listener = new StartRecordingHelper(this);
|
|
||||||
elm->AddEventListener(NS_LITERAL_STRING("success"), listener, false, false);
|
|
||||||
elm->AddEventListener(NS_LITERAL_STRING("error"), listener, false, false);
|
|
||||||
return promise.forget();
|
return promise.forget();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -790,6 +851,8 @@ nsDOMCameraControl::OnCreatedFileDescriptor(bool aSucceeded)
|
||||||
// Race condition where StopRecording comes in before we issue
|
// Race condition where StopRecording comes in before we issue
|
||||||
// the start recording request to Gonk
|
// the start recording request to Gonk
|
||||||
rv = NS_ERROR_ABORT;
|
rv = NS_ERROR_ABORT;
|
||||||
|
mOptions.mPosterFilepath.Truncate();
|
||||||
|
mOptions.mPosterStorageArea = nullptr;
|
||||||
} else if (aSucceeded && mDSFileDescriptor->mFileDescriptor.IsValid()) {
|
} else if (aSucceeded && mDSFileDescriptor->mFileDescriptor.IsValid()) {
|
||||||
ICameraControl::StartRecordingOptions o;
|
ICameraControl::StartRecordingOptions o;
|
||||||
|
|
||||||
|
@ -797,6 +860,7 @@ nsDOMCameraControl::OnCreatedFileDescriptor(bool aSucceeded)
|
||||||
o.maxFileSizeBytes = mOptions.mMaxFileSizeBytes;
|
o.maxFileSizeBytes = mOptions.mMaxFileSizeBytes;
|
||||||
o.maxVideoLengthMs = mOptions.mMaxVideoLengthMs;
|
o.maxVideoLengthMs = mOptions.mMaxVideoLengthMs;
|
||||||
o.autoEnableLowLightTorch = mOptions.mAutoEnableLowLightTorch;
|
o.autoEnableLowLightTorch = mOptions.mAutoEnableLowLightTorch;
|
||||||
|
o.createPoster = !mOptions.mPosterFilepath.IsEmpty();
|
||||||
rv = mCameraControl->StartRecording(mDSFileDescriptor.get(), &o);
|
rv = mCameraControl->StartRecording(mDSFileDescriptor.get(), &o);
|
||||||
if (NS_SUCCEEDED(rv)) {
|
if (NS_SUCCEEDED(rv)) {
|
||||||
return;
|
return;
|
||||||
|
@ -1254,6 +1318,38 @@ nsDOMCameraControl::OnPreviewStateChange(CameraControlListener::PreviewState aSt
|
||||||
DispatchPreviewStateEvent(aState);
|
DispatchPreviewStateEvent(aState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nsDOMCameraControl::OnPoster(BlobImpl* aPoster)
|
||||||
|
{
|
||||||
|
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
MOZ_ASSERT(!mOptions.mPosterFilepath.IsEmpty());
|
||||||
|
|
||||||
|
// Destructor will trigger an error notification if any step fails
|
||||||
|
nsRefPtr<RecorderPosterHelper> listener = new RecorderPosterHelper(this);
|
||||||
|
if (NS_WARN_IF(!aPoster)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsRefPtr<Blob> blob = Blob::Create(GetParentObject(), aPoster);
|
||||||
|
if (NS_WARN_IF(!blob)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NS_WARN_IF(!mOptions.mPosterStorageArea)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorResult rv;
|
||||||
|
nsRefPtr<DOMRequest> request =
|
||||||
|
mOptions.mPosterStorageArea->AddNamed(blob, mOptions.mPosterFilepath, rv);
|
||||||
|
if (NS_WARN_IF(rv.Failed())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RegisterStorageRequestEvents(request, listener);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nsDOMCameraControl::OnRecorderStateChange(CameraControlListener::RecorderState aState,
|
nsDOMCameraControl::OnRecorderStateChange(CameraControlListener::RecorderState aState,
|
||||||
int32_t aArg, int32_t aTrackNum)
|
int32_t aArg, int32_t aTrackNum)
|
||||||
|
@ -1278,10 +1374,27 @@ nsDOMCameraControl::OnRecorderStateChange(CameraControlListener::RecorderState a
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CameraControlListener::kRecorderStopped:
|
case CameraControlListener::kRecorderStopped:
|
||||||
|
if (!mOptions.mPosterFilepath.IsEmpty()) {
|
||||||
|
mRecordingStoppedDeferred = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
NotifyRecordingStatusChange(NS_LITERAL_STRING("shutdown"));
|
NotifyRecordingStatusChange(NS_LITERAL_STRING("shutdown"));
|
||||||
state = NS_LITERAL_STRING("Stopped");
|
state = NS_LITERAL_STRING("Stopped");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case CameraControlListener::kPosterCreated:
|
||||||
|
state = NS_LITERAL_STRING("PosterCreated");
|
||||||
|
mOptions.mPosterFilepath.Truncate();
|
||||||
|
mOptions.mPosterStorageArea = nullptr;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CameraControlListener::kPosterFailed:
|
||||||
|
state = NS_LITERAL_STRING("PosterFailed");
|
||||||
|
mOptions.mPosterFilepath.Truncate();
|
||||||
|
mOptions.mPosterStorageArea = nullptr;
|
||||||
|
break;
|
||||||
|
|
||||||
#ifdef MOZ_B2G_CAMERA
|
#ifdef MOZ_B2G_CAMERA
|
||||||
case CameraControlListener::kFileSizeLimitReached:
|
case CameraControlListener::kFileSizeLimitReached:
|
||||||
state = NS_LITERAL_STRING("FileSizeLimitReached");
|
state = NS_LITERAL_STRING("FileSizeLimitReached");
|
||||||
|
@ -1314,6 +1427,11 @@ nsDOMCameraControl::OnRecorderStateChange(CameraControlListener::RecorderState a
|
||||||
}
|
}
|
||||||
|
|
||||||
DispatchStateEvent(NS_LITERAL_STRING("recorderstatechange"), state);
|
DispatchStateEvent(NS_LITERAL_STRING("recorderstatechange"), state);
|
||||||
|
|
||||||
|
if (mRecordingStoppedDeferred && mOptions.mPosterFilepath.IsEmpty()) {
|
||||||
|
mRecordingStoppedDeferred = false;
|
||||||
|
OnRecorderStateChange(CameraControlListener::kRecorderStopped, 0, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
@ -39,6 +39,7 @@ namespace dom {
|
||||||
}
|
}
|
||||||
class ErrorResult;
|
class ErrorResult;
|
||||||
class StartRecordingHelper;
|
class StartRecordingHelper;
|
||||||
|
class RecorderPosterHelper;
|
||||||
|
|
||||||
#define NS_DOM_CAMERA_CONTROL_CID \
|
#define NS_DOM_CAMERA_CONTROL_CID \
|
||||||
{ 0x3700c096, 0xf920, 0x438d, \
|
{ 0x3700c096, 0xf920, 0x438d, \
|
||||||
|
@ -163,6 +164,7 @@ protected:
|
||||||
|
|
||||||
friend class DOMCameraControlListener;
|
friend class DOMCameraControlListener;
|
||||||
friend class mozilla::StartRecordingHelper;
|
friend class mozilla::StartRecordingHelper;
|
||||||
|
friend class mozilla::RecorderPosterHelper;
|
||||||
|
|
||||||
void OnCreatedFileDescriptor(bool aSucceeded);
|
void OnCreatedFileDescriptor(bool aSucceeded);
|
||||||
|
|
||||||
|
@ -170,6 +172,7 @@ protected:
|
||||||
void OnAutoFocusMoving(bool aIsMoving);
|
void OnAutoFocusMoving(bool aIsMoving);
|
||||||
void OnTakePictureComplete(nsIDOMBlob* aPicture);
|
void OnTakePictureComplete(nsIDOMBlob* aPicture);
|
||||||
void OnFacesDetected(const nsTArray<ICameraControl::Face>& aFaces);
|
void OnFacesDetected(const nsTArray<ICameraControl::Face>& aFaces);
|
||||||
|
void OnPoster(dom::BlobImpl* aPoster);
|
||||||
|
|
||||||
void OnGetCameraComplete();
|
void OnGetCameraComplete();
|
||||||
void OnHardwareStateChange(DOMCameraControlListener::HardwareState aState, nsresult aReason);
|
void OnHardwareStateChange(DOMCameraControlListener::HardwareState aState, nsresult aReason);
|
||||||
|
@ -225,6 +228,7 @@ protected:
|
||||||
nsRefPtr<DeviceStorageFileDescriptor> mDSFileDescriptor;
|
nsRefPtr<DeviceStorageFileDescriptor> mDSFileDescriptor;
|
||||||
DOMCameraControlListener::PreviewState mPreviewState;
|
DOMCameraControlListener::PreviewState mPreviewState;
|
||||||
bool mRecording;
|
bool mRecording;
|
||||||
|
bool mRecordingStoppedDeferred;
|
||||||
bool mSetInitialConfig;
|
bool mSetInitialConfig;
|
||||||
|
|
||||||
#ifdef MOZ_WIDGET_GONK
|
#ifdef MOZ_WIDGET_GONK
|
||||||
|
|
|
@ -410,3 +410,27 @@ DOMCameraControlListener::OnUserError(UserContext aContext, nsresult aError)
|
||||||
|
|
||||||
NS_DispatchToMainThread(new Callback(mDOMCameraControl, aContext, aError));
|
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:
|
||||||
|
nsRefPtr<BlobImpl> mBlobImpl;
|
||||||
|
};
|
||||||
|
|
||||||
|
NS_DispatchToMainThread(new Callback(mDOMCameraControl, aBlobImpl));
|
||||||
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@ public:
|
||||||
virtual void OnRateLimitPreview(bool aLimit) override;
|
virtual void OnRateLimitPreview(bool aLimit) override;
|
||||||
virtual bool OnNewPreviewFrame(layers::Image* aImage, uint32_t aWidth, uint32_t aHeight) override;
|
virtual bool OnNewPreviewFrame(layers::Image* aImage, uint32_t aWidth, uint32_t aHeight) override;
|
||||||
virtual void OnUserError(UserContext aContext, nsresult aError) override;
|
virtual void OnUserError(UserContext aContext, nsresult aError) override;
|
||||||
|
virtual void OnPoster(dom::BlobImpl* aBlobImpl) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual ~DOMCameraControlListener();
|
virtual ~DOMCameraControlListener();
|
||||||
|
|
|
@ -27,7 +27,12 @@
|
||||||
#include <media/mediaplayer.h>
|
#include <media/mediaplayer.h>
|
||||||
#include <media/MediaProfiles.h>
|
#include <media/MediaProfiles.h>
|
||||||
#include "GrallocImages.h"
|
#include "GrallocImages.h"
|
||||||
|
#include "imgIEncoder.h"
|
||||||
|
#include "libyuv.h"
|
||||||
|
#include "nsNetUtil.h" // for NS_ReadInputStreamToBuffer
|
||||||
#endif
|
#endif
|
||||||
|
#include "nsNetCID.h" // for NS_STREAMTRANSPORTSERVICE_CONTRACTID
|
||||||
|
#include "nsAutoPtr.h" // for nsAutoArrayPtr
|
||||||
#include "nsCOMPtr.h"
|
#include "nsCOMPtr.h"
|
||||||
#include "nsMemory.h"
|
#include "nsMemory.h"
|
||||||
#include "nsThread.h"
|
#include "nsThread.h"
|
||||||
|
@ -38,7 +43,6 @@
|
||||||
#include "mozilla/ipc/FileDescriptorUtils.h"
|
#include "mozilla/ipc/FileDescriptorUtils.h"
|
||||||
#include "nsAlgorithm.h"
|
#include "nsAlgorithm.h"
|
||||||
#include "nsPrintfCString.h"
|
#include "nsPrintfCString.h"
|
||||||
#include "AutoRwLock.h"
|
|
||||||
#include "GonkCameraHwMgr.h"
|
#include "GonkCameraHwMgr.h"
|
||||||
#include "GonkRecorderProfiles.h"
|
#include "GonkRecorderProfiles.h"
|
||||||
#include "CameraCommon.h"
|
#include "CameraCommon.h"
|
||||||
|
@ -46,6 +50,7 @@
|
||||||
#include "DeviceStorageFileDescriptor.h"
|
#include "DeviceStorageFileDescriptor.h"
|
||||||
|
|
||||||
using namespace mozilla;
|
using namespace mozilla;
|
||||||
|
using namespace mozilla::dom;
|
||||||
using namespace mozilla::layers;
|
using namespace mozilla::layers;
|
||||||
using namespace mozilla::gfx;
|
using namespace mozilla::gfx;
|
||||||
using namespace mozilla::ipc;
|
using namespace mozilla::ipc;
|
||||||
|
@ -79,6 +84,7 @@ nsGonkCameraControl::nsGonkCameraControl(uint32_t aCameraId)
|
||||||
#endif
|
#endif
|
||||||
, mRecorderMonitor("GonkCameraControl::mRecorder.Monitor")
|
, mRecorderMonitor("GonkCameraControl::mRecorder.Monitor")
|
||||||
, mVideoFile(nullptr)
|
, mVideoFile(nullptr)
|
||||||
|
, mCapturePoster(false)
|
||||||
, mAutoFocusPending(false)
|
, mAutoFocusPending(false)
|
||||||
, mAutoFocusCompleteExpired(0)
|
, mAutoFocusCompleteExpired(0)
|
||||||
, mReentrantMonitor("GonkCameraControl::OnTakePicture.Monitor")
|
, mReentrantMonitor("GonkCameraControl::OnTakePicture.Monitor")
|
||||||
|
@ -1252,6 +1258,7 @@ nsGonkCameraControl::StartRecordingImpl(DeviceStorageFileDescriptor* aFileDescri
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
OnRecorderStateChange(CameraControlListener::kRecorderStarted);
|
OnRecorderStateChange(CameraControlListener::kRecorderStarted);
|
||||||
|
mCapturePoster = aOptions->createPoster;
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1296,6 +1303,9 @@ nsGonkCameraControl::StopRecordingImpl()
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
if (mCapturePoster.exchange(false)) {
|
||||||
|
OnPoster(nullptr, 0);
|
||||||
|
}
|
||||||
OnRecorderStateChange(CameraControlListener::kRecorderStopped);
|
OnRecorderStateChange(CameraControlListener::kRecorderStopped);
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -2018,11 +2028,13 @@ nsGonkCameraControl::SetupRecording(int aFd, int aRotation,
|
||||||
NS_ERROR_INVALID_ARG);
|
NS_ERROR_INVALID_ARG);
|
||||||
|
|
||||||
// adjust rotation by camera sensor offset
|
// adjust rotation by camera sensor offset
|
||||||
int r = aRotation;
|
mVideoRotation = aRotation;
|
||||||
r += mCameraHw->GetSensorOrientation();
|
mVideoRotation += mCameraHw->GetSensorOrientation();
|
||||||
r = RationalizeRotation(r);
|
mVideoRotation = RationalizeRotation(mVideoRotation);
|
||||||
DOM_CAMERA_LOGI("setting video rotation to %d degrees (mapped from %d)\n", r, aRotation);
|
DOM_CAMERA_LOGI("setting video rotation to %d degrees (mapped from %d)\n",
|
||||||
snprintf(buffer, SIZE, "video-param-rotation-angle-degrees=%d", r);
|
mVideoRotation, aRotation);
|
||||||
|
snprintf(buffer, SIZE, "video-param-rotation-angle-degrees=%d",
|
||||||
|
mVideoRotation);
|
||||||
CHECK_SETARG_RETURN(mRecorder->setParameters(String8(buffer)),
|
CHECK_SETARG_RETURN(mRecorder->setParameters(String8(buffer)),
|
||||||
NS_ERROR_INVALID_ARG);
|
NS_ERROR_INVALID_ARG);
|
||||||
|
|
||||||
|
@ -2165,6 +2177,137 @@ nsGonkCameraControl::OnRateLimitPreview(bool aLimit)
|
||||||
CameraControlImpl::OnRateLimitPreview(aLimit);
|
CameraControlImpl::OnRateLimitPreview(aLimit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nsGonkCameraControl::CreatePoster(Image* aImage, uint32_t aWidth, uint32_t aHeight, int32_t aRotation)
|
||||||
|
{
|
||||||
|
class PosterRunnable : public nsRunnable {
|
||||||
|
public:
|
||||||
|
PosterRunnable(nsGonkCameraControl* aTarget, Image* aImage,
|
||||||
|
uint32_t aWidth, uint32_t aHeight, int32_t aRotation)
|
||||||
|
: mTarget(aTarget)
|
||||||
|
, mImage(aImage)
|
||||||
|
, mWidth(aWidth)
|
||||||
|
, mHeight(aHeight)
|
||||||
|
, mRotation(aRotation)
|
||||||
|
, mDst(nullptr)
|
||||||
|
, mDstLength(0)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
virtual ~PosterRunnable()
|
||||||
|
{
|
||||||
|
mTarget->OnPoster(mDst, mDstLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP Run() override
|
||||||
|
{
|
||||||
|
#ifdef MOZ_WIDGET_GONK
|
||||||
|
// NV21 (yuv420sp) is 12 bits / pixel
|
||||||
|
size_t srcLength = (mWidth * mHeight * 3 + 1) / 2;
|
||||||
|
|
||||||
|
// ARGB is 32 bits / pixel
|
||||||
|
size_t tmpLength = mWidth * mHeight * sizeof(uint32_t);
|
||||||
|
nsAutoArrayPtr<uint8_t> tmp;
|
||||||
|
tmp = new uint8_t[tmpLength];
|
||||||
|
|
||||||
|
GrallocImage* nativeImage = static_cast<GrallocImage*>(mImage.get());
|
||||||
|
android::sp<GraphicBuffer> graphicBuffer = nativeImage->GetGraphicBuffer();
|
||||||
|
|
||||||
|
void* graphicSrc = nullptr;
|
||||||
|
graphicBuffer->lock(GraphicBuffer::USAGE_SW_READ_MASK, &graphicSrc);
|
||||||
|
|
||||||
|
uint32_t stride = mWidth * 4;
|
||||||
|
int err = libyuv::ConvertToARGB(static_cast<uint8_t*>(graphicSrc),
|
||||||
|
srcLength, tmp, stride, 0, 0,
|
||||||
|
mWidth, mHeight, mWidth, mHeight,
|
||||||
|
libyuv::kRotate0, libyuv::FOURCC_NV21);
|
||||||
|
|
||||||
|
graphicBuffer->unlock();
|
||||||
|
graphicSrc = nullptr;
|
||||||
|
graphicBuffer.clear();
|
||||||
|
nativeImage = nullptr;
|
||||||
|
mImage = nullptr;
|
||||||
|
|
||||||
|
if (NS_WARN_IF(err < 0)) {
|
||||||
|
DOM_CAMERA_LOGE("CreatePoster: to ARGB failed (%d)\n", err);
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsCOMPtr<imgIEncoder> encoder =
|
||||||
|
do_CreateInstance("@mozilla.org/image/encoder;2?type=image/jpeg");
|
||||||
|
if (NS_WARN_IF(!encoder)) {
|
||||||
|
DOM_CAMERA_LOGE("CreatePoster: no JPEG encoder\n");
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsString opt;
|
||||||
|
nsresult rv = encoder->InitFromData(tmp, tmpLength, mWidth,
|
||||||
|
mHeight, stride,
|
||||||
|
imgIEncoder::INPUT_FORMAT_HOSTARGB,
|
||||||
|
opt);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
DOM_CAMERA_LOGE("CreatePoster: encoder init failed (0x%x)\n",
|
||||||
|
rv);
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsCOMPtr<nsIInputStream> stream = do_QueryInterface(encoder);
|
||||||
|
if (NS_WARN_IF(!stream)) {
|
||||||
|
DOM_CAMERA_LOGE("CreatePoster: to input stream failed\n");
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t length = 0;
|
||||||
|
rv = stream->Available(&length);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
DOM_CAMERA_LOGE("CreatePoster: get length failed (0x%x)\n",
|
||||||
|
rv);
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = NS_ReadInputStreamToBuffer(stream, &mDst, length);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
DOM_CAMERA_LOGE("CreatePoster: read failed (0x%x)\n", rv);
|
||||||
|
mDst = nullptr;
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
mDstLength = length;
|
||||||
|
#endif
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
nsRefPtr<nsGonkCameraControl> mTarget;
|
||||||
|
nsRefPtr<Image> mImage;
|
||||||
|
int32_t mWidth;
|
||||||
|
int32_t mHeight;
|
||||||
|
int32_t mRotation;
|
||||||
|
void* mDst;
|
||||||
|
size_t mDstLength;
|
||||||
|
};
|
||||||
|
|
||||||
|
nsCOMPtr<nsIRunnable> event = new PosterRunnable(this, aImage,
|
||||||
|
aWidth,
|
||||||
|
aHeight,
|
||||||
|
aRotation);
|
||||||
|
|
||||||
|
nsCOMPtr<nsIEventTarget> target
|
||||||
|
= do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
|
||||||
|
MOZ_ASSERT(target);
|
||||||
|
|
||||||
|
target->Dispatch(event, NS_DISPATCH_NORMAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nsGonkCameraControl::OnPoster(void* aData, uint32_t aLength)
|
||||||
|
{
|
||||||
|
nsRefPtr<BlobImpl> blobImpl;
|
||||||
|
if (aData) {
|
||||||
|
blobImpl = new BlobImplMemory(aData, aLength, NS_LITERAL_STRING("image/jpeg"));
|
||||||
|
}
|
||||||
|
CameraControlImpl::OnPoster(blobImpl);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nsGonkCameraControl::OnNewPreviewFrame(layers::TextureClient* aBuffer)
|
nsGonkCameraControl::OnNewPreviewFrame(layers::TextureClient* aBuffer)
|
||||||
{
|
{
|
||||||
|
@ -2179,6 +2322,14 @@ nsGonkCameraControl::OnNewPreviewFrame(layers::TextureClient* aBuffer)
|
||||||
mCurrentConfiguration.mPreviewSize.height);
|
mCurrentConfiguration.mPreviewSize.height);
|
||||||
videoImage->SetData(data);
|
videoImage->SetData(data);
|
||||||
|
|
||||||
|
if (mCapturePoster.exchange(false)) {
|
||||||
|
CreatePoster(frame,
|
||||||
|
mCurrentConfiguration.mPreviewSize.width,
|
||||||
|
mCurrentConfiguration.mPreviewSize.height,
|
||||||
|
mVideoRotation);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
OnNewPreviewFrame(frame, mCurrentConfiguration.mPreviewSize.width,
|
OnNewPreviewFrame(frame, mCurrentConfiguration.mPreviewSize.width,
|
||||||
mCurrentConfiguration.mPreviewSize.height);
|
mCurrentConfiguration.mPreviewSize.height);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -47,6 +47,7 @@ namespace mozilla {
|
||||||
namespace layers {
|
namespace layers {
|
||||||
class TextureClient;
|
class TextureClient;
|
||||||
class ImageContainer;
|
class ImageContainer;
|
||||||
|
class Image;
|
||||||
}
|
}
|
||||||
|
|
||||||
class nsGonkCameraControl : public CameraControlImpl
|
class nsGonkCameraControl : public CameraControlImpl
|
||||||
|
@ -60,6 +61,7 @@ public:
|
||||||
void OnTakePictureComplete(uint8_t* aData, uint32_t aLength);
|
void OnTakePictureComplete(uint8_t* aData, uint32_t aLength);
|
||||||
void OnTakePictureError();
|
void OnTakePictureError();
|
||||||
void OnRateLimitPreview(bool aLimit);
|
void OnRateLimitPreview(bool aLimit);
|
||||||
|
void OnPoster(void* aData, uint32_t aLength);
|
||||||
void OnNewPreviewFrame(layers::TextureClient* aBuffer);
|
void OnNewPreviewFrame(layers::TextureClient* aBuffer);
|
||||||
#ifdef MOZ_WIDGET_GONK
|
#ifdef MOZ_WIDGET_GONK
|
||||||
void OnRecorderEvent(int msg, int ext1, int ext2);
|
void OnRecorderEvent(int msg, int ext1, int ext2);
|
||||||
|
@ -149,6 +151,8 @@ protected:
|
||||||
nsresult PausePreview();
|
nsresult PausePreview();
|
||||||
nsresult GetSupportedSize(const Size& aSize, const nsTArray<Size>& supportedSizes, Size& best);
|
nsresult GetSupportedSize(const Size& aSize, const nsTArray<Size>& supportedSizes, Size& best);
|
||||||
|
|
||||||
|
void CreatePoster(layers::Image* aImage, uint32_t aWidth, uint32_t aHeight, int32_t aRotation);
|
||||||
|
|
||||||
nsresult LoadRecorderProfiles();
|
nsresult LoadRecorderProfiles();
|
||||||
static PLDHashOperator Enumerate(const nsAString& aProfileName,
|
static PLDHashOperator Enumerate(const nsAString& aProfileName,
|
||||||
RecorderProfile* aProfile,
|
RecorderProfile* aProfile,
|
||||||
|
@ -198,6 +202,9 @@ protected:
|
||||||
nsRefPtr<DeviceStorageFile> mVideoFile;
|
nsRefPtr<DeviceStorageFile> mVideoFile;
|
||||||
nsString mFileFormat;
|
nsString mFileFormat;
|
||||||
|
|
||||||
|
Atomic<bool> mCapturePoster;
|
||||||
|
int32_t mVideoRotation;
|
||||||
|
|
||||||
bool mAutoFocusPending;
|
bool mAutoFocusPending;
|
||||||
nsCOMPtr<nsITimer> mAutoFocusCompleteTimer;
|
nsCOMPtr<nsITimer> mAutoFocusCompleteTimer;
|
||||||
int32_t mAutoFocusCompleteExpired;
|
int32_t mAutoFocusCompleteExpired;
|
||||||
|
|
|
@ -138,6 +138,7 @@ public:
|
||||||
uint64_t maxFileSizeBytes;
|
uint64_t maxFileSizeBytes;
|
||||||
uint64_t maxVideoLengthMs;
|
uint64_t maxVideoLengthMs;
|
||||||
bool autoEnableLowLightTorch;
|
bool autoEnableLowLightTorch;
|
||||||
|
bool createPoster;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Configuration {
|
struct Configuration {
|
||||||
|
|
|
@ -65,6 +65,7 @@ FAIL_ON_WARNINGS = True
|
||||||
|
|
||||||
LOCAL_INCLUDES += [
|
LOCAL_INCLUDES += [
|
||||||
'../base',
|
'../base',
|
||||||
|
'/media/libyuv/include',
|
||||||
]
|
]
|
||||||
|
|
||||||
include('/ipc/chromium/chromium-config.mozbuild')
|
include('/ipc/chromium/chromium-config.mozbuild')
|
||||||
|
|
|
@ -109,6 +109,12 @@ dictionary CameraStartRecordingOptions
|
||||||
will be left as-is on stopRecording(). If the camera does not
|
will be left as-is on stopRecording(). If the camera does not
|
||||||
support this setting, it will be ignored. */
|
support this setting, it will be ignored. */
|
||||||
boolean autoEnableLowLightTorch = false;
|
boolean autoEnableLowLightTorch = false;
|
||||||
|
|
||||||
|
/* If given, a poster JPG will be created from the recording and saved
|
||||||
|
at the given path. PosterCreated or PosterFailed recording state
|
||||||
|
changes will indicate whether or not it was created. */
|
||||||
|
DOMString posterFilepath = "";
|
||||||
|
DeviceStorage? posterStorageArea = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
Загрузка…
Ссылка в новой задаче