mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1175656 - Implement generation of recording posters in Gecko. r=dhylands,bz
This commit is contained in:
parent
390a6a4a1a
commit
42582d10da
@ -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
|
||||
CameraControlImpl::OnShutter()
|
||||
{
|
||||
|
@ -20,6 +20,10 @@
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace dom {
|
||||
class BlobImpl;
|
||||
}
|
||||
|
||||
namespace layers {
|
||||
class Image;
|
||||
}
|
||||
@ -57,6 +61,7 @@ protected:
|
||||
void OnAutoFocusComplete(bool aAutoFocusSucceeded);
|
||||
void OnFacesDetected(const nsTArray<Face>& aFaces);
|
||||
void OnTakePictureComplete(const uint8_t* aData, uint32_t aLength, const nsAString& aMimeType);
|
||||
void OnPoster(dom::BlobImpl* aBlobImpl);
|
||||
|
||||
void OnRateLimitPreview(bool aLimit);
|
||||
bool OnNewPreviewFrame(layers::Image* aImage, uint32_t aWidth, uint32_t aHeight);
|
||||
|
@ -10,6 +10,10 @@
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace dom {
|
||||
class BlobImpl;
|
||||
}
|
||||
|
||||
namespace layers {
|
||||
class Image;
|
||||
}
|
||||
@ -60,6 +64,8 @@ public:
|
||||
{
|
||||
kRecorderStopped,
|
||||
kRecorderStarted,
|
||||
kPosterCreated,
|
||||
kPosterFailed,
|
||||
#ifdef MOZ_B2G_CAMERA
|
||||
kFileSizeLimitReached,
|
||||
kVideoLengthLimitReached,
|
||||
@ -91,6 +97,7 @@ public:
|
||||
virtual void OnAutoFocusMoving(bool aIsMoving) { }
|
||||
virtual void OnTakePictureComplete(const uint8_t* aData, uint32_t aLength, const nsAString& aMimeType) { }
|
||||
virtual void OnFacesDetected(const nsTArray<ICameraControl::Face>& aFaces) { }
|
||||
virtual void OnPoster(dom::BlobImpl* aBlobImpl) { }
|
||||
|
||||
enum UserContext
|
||||
{
|
||||
|
@ -82,6 +82,19 @@ nsDOMCameraControl::HasSupport(JSContext* aCx, JSObject* 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
|
||||
{
|
||||
public:
|
||||
@ -90,6 +103,7 @@ public:
|
||||
|
||||
explicit StartRecordingHelper(nsDOMCameraControl* aDOMCameraControl)
|
||||
: mDOMCameraControl(aDOMCameraControl)
|
||||
, mState(false)
|
||||
{
|
||||
MOZ_COUNT_CTOR(StartRecordingHelper);
|
||||
}
|
||||
@ -98,10 +112,12 @@ protected:
|
||||
virtual ~StartRecordingHelper()
|
||||
{
|
||||
MOZ_COUNT_DTOR(StartRecordingHelper);
|
||||
mDOMCameraControl->OnCreatedFileDescriptor(mState);
|
||||
}
|
||||
|
||||
protected:
|
||||
nsRefPtr<nsDOMCameraControl> mDOMCameraControl;
|
||||
bool mState;
|
||||
};
|
||||
|
||||
NS_IMETHODIMP
|
||||
@ -109,13 +125,50 @@ StartRecordingHelper::HandleEvent(nsIDOMEvent* aEvent)
|
||||
{
|
||||
nsString eventType;
|
||||
aEvent->GetType(eventType);
|
||||
|
||||
mDOMCameraControl->OnCreatedFileDescriptor(eventType.EqualsLiteral("success"));
|
||||
mState = eventType.EqualsLiteral("success");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
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()
|
||||
: CameraConfiguration()
|
||||
, mMaxFocusAreas(0)
|
||||
@ -202,6 +255,7 @@ nsDOMCameraControl::nsDOMCameraControl(uint32_t aCameraId,
|
||||
, mWindow(aWindow)
|
||||
, mPreviewState(CameraControlListener::kPreviewStopped)
|
||||
, mRecording(false)
|
||||
, mRecordingStoppedDeferred(false)
|
||||
, mSetInitialConfig(false)
|
||||
{
|
||||
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
|
||||
@ -743,7 +797,18 @@ nsDOMCameraControl::StartRecording(const CameraStartRecordingOptions& aOptions,
|
||||
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);
|
||||
return promise.forget();
|
||||
}
|
||||
@ -762,20 +827,16 @@ nsDOMCameraControl::StartRecording(const CameraStartRecordingOptions& aOptions,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
mStartRecordingPromise = promise;
|
||||
mOptions = aOptions;
|
||||
|
||||
EventListenerManager* elm = request->GetOrCreateListenerManager();
|
||||
if (!elm) {
|
||||
nsCOMPtr<nsIDOMEventListener> listener = new StartRecordingHelper(this);
|
||||
aRv = RegisterStorageRequestEvents(request, listener);
|
||||
if (aRv.Failed()) {
|
||||
NotifyRecordingStatusChange(NS_LITERAL_STRING("shutdown"));
|
||||
aRv.Throw(NS_ERROR_UNEXPECTED);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
mStartRecordingPromise = promise;
|
||||
mOptions = aOptions;
|
||||
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();
|
||||
}
|
||||
|
||||
@ -790,6 +851,8 @@ nsDOMCameraControl::OnCreatedFileDescriptor(bool aSucceeded)
|
||||
// Race condition where StopRecording comes in before we issue
|
||||
// the start recording request to Gonk
|
||||
rv = NS_ERROR_ABORT;
|
||||
mOptions.mPosterFilepath.Truncate();
|
||||
mOptions.mPosterStorageArea = nullptr;
|
||||
} else if (aSucceeded && mDSFileDescriptor->mFileDescriptor.IsValid()) {
|
||||
ICameraControl::StartRecordingOptions o;
|
||||
|
||||
@ -797,6 +860,7 @@ nsDOMCameraControl::OnCreatedFileDescriptor(bool aSucceeded)
|
||||
o.maxFileSizeBytes = mOptions.mMaxFileSizeBytes;
|
||||
o.maxVideoLengthMs = mOptions.mMaxVideoLengthMs;
|
||||
o.autoEnableLowLightTorch = mOptions.mAutoEnableLowLightTorch;
|
||||
o.createPoster = !mOptions.mPosterFilepath.IsEmpty();
|
||||
rv = mCameraControl->StartRecording(mDSFileDescriptor.get(), &o);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
return;
|
||||
@ -1254,6 +1318,38 @@ nsDOMCameraControl::OnPreviewStateChange(CameraControlListener::PreviewState aSt
|
||||
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
|
||||
nsDOMCameraControl::OnRecorderStateChange(CameraControlListener::RecorderState aState,
|
||||
int32_t aArg, int32_t aTrackNum)
|
||||
@ -1278,10 +1374,27 @@ nsDOMCameraControl::OnRecorderStateChange(CameraControlListener::RecorderState a
|
||||
break;
|
||||
|
||||
case CameraControlListener::kRecorderStopped:
|
||||
if (!mOptions.mPosterFilepath.IsEmpty()) {
|
||||
mRecordingStoppedDeferred = true;
|
||||
return;
|
||||
}
|
||||
|
||||
NotifyRecordingStatusChange(NS_LITERAL_STRING("shutdown"));
|
||||
state = NS_LITERAL_STRING("Stopped");
|
||||
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
|
||||
case CameraControlListener::kFileSizeLimitReached:
|
||||
state = NS_LITERAL_STRING("FileSizeLimitReached");
|
||||
@ -1314,6 +1427,11 @@ nsDOMCameraControl::OnRecorderStateChange(CameraControlListener::RecorderState a
|
||||
}
|
||||
|
||||
DispatchStateEvent(NS_LITERAL_STRING("recorderstatechange"), state);
|
||||
|
||||
if (mRecordingStoppedDeferred && mOptions.mPosterFilepath.IsEmpty()) {
|
||||
mRecordingStoppedDeferred = false;
|
||||
OnRecorderStateChange(CameraControlListener::kRecorderStopped, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -39,6 +39,7 @@ namespace dom {
|
||||
}
|
||||
class ErrorResult;
|
||||
class StartRecordingHelper;
|
||||
class RecorderPosterHelper;
|
||||
|
||||
#define NS_DOM_CAMERA_CONTROL_CID \
|
||||
{ 0x3700c096, 0xf920, 0x438d, \
|
||||
@ -163,6 +164,7 @@ protected:
|
||||
|
||||
friend class DOMCameraControlListener;
|
||||
friend class mozilla::StartRecordingHelper;
|
||||
friend class mozilla::RecorderPosterHelper;
|
||||
|
||||
void OnCreatedFileDescriptor(bool aSucceeded);
|
||||
|
||||
@ -170,6 +172,7 @@ protected:
|
||||
void OnAutoFocusMoving(bool aIsMoving);
|
||||
void OnTakePictureComplete(nsIDOMBlob* aPicture);
|
||||
void OnFacesDetected(const nsTArray<ICameraControl::Face>& aFaces);
|
||||
void OnPoster(dom::BlobImpl* aPoster);
|
||||
|
||||
void OnGetCameraComplete();
|
||||
void OnHardwareStateChange(DOMCameraControlListener::HardwareState aState, nsresult aReason);
|
||||
@ -225,6 +228,7 @@ protected:
|
||||
nsRefPtr<DeviceStorageFileDescriptor> mDSFileDescriptor;
|
||||
DOMCameraControlListener::PreviewState mPreviewState;
|
||||
bool mRecording;
|
||||
bool mRecordingStoppedDeferred;
|
||||
bool mSetInitialConfig;
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
|
@ -410,3 +410,27 @@ DOMCameraControlListener::OnUserError(UserContext aContext, nsresult 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 bool OnNewPreviewFrame(layers::Image* aImage, uint32_t aWidth, uint32_t aHeight) override;
|
||||
virtual void OnUserError(UserContext aContext, nsresult aError) override;
|
||||
virtual void OnPoster(dom::BlobImpl* aBlobImpl) override;
|
||||
|
||||
protected:
|
||||
virtual ~DOMCameraControlListener();
|
||||
|
@ -27,7 +27,12 @@
|
||||
#include <media/mediaplayer.h>
|
||||
#include <media/MediaProfiles.h>
|
||||
#include "GrallocImages.h"
|
||||
#include "imgIEncoder.h"
|
||||
#include "libyuv.h"
|
||||
#include "nsNetUtil.h" // for NS_ReadInputStreamToBuffer
|
||||
#endif
|
||||
#include "nsNetCID.h" // for NS_STREAMTRANSPORTSERVICE_CONTRACTID
|
||||
#include "nsAutoPtr.h" // for nsAutoArrayPtr
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsMemory.h"
|
||||
#include "nsThread.h"
|
||||
@ -38,7 +43,6 @@
|
||||
#include "mozilla/ipc/FileDescriptorUtils.h"
|
||||
#include "nsAlgorithm.h"
|
||||
#include "nsPrintfCString.h"
|
||||
#include "AutoRwLock.h"
|
||||
#include "GonkCameraHwMgr.h"
|
||||
#include "GonkRecorderProfiles.h"
|
||||
#include "CameraCommon.h"
|
||||
@ -46,6 +50,7 @@
|
||||
#include "DeviceStorageFileDescriptor.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
using namespace mozilla::layers;
|
||||
using namespace mozilla::gfx;
|
||||
using namespace mozilla::ipc;
|
||||
@ -79,6 +84,7 @@ nsGonkCameraControl::nsGonkCameraControl(uint32_t aCameraId)
|
||||
#endif
|
||||
, mRecorderMonitor("GonkCameraControl::mRecorder.Monitor")
|
||||
, mVideoFile(nullptr)
|
||||
, mCapturePoster(false)
|
||||
, mAutoFocusPending(false)
|
||||
, mAutoFocusCompleteExpired(0)
|
||||
, mReentrantMonitor("GonkCameraControl::OnTakePicture.Monitor")
|
||||
@ -1252,6 +1258,7 @@ nsGonkCameraControl::StartRecordingImpl(DeviceStorageFileDescriptor* aFileDescri
|
||||
#endif
|
||||
|
||||
OnRecorderStateChange(CameraControlListener::kRecorderStarted);
|
||||
mCapturePoster = aOptions->createPoster;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -1296,6 +1303,9 @@ nsGonkCameraControl::StopRecordingImpl()
|
||||
return NS_OK;
|
||||
}
|
||||
#endif
|
||||
if (mCapturePoster.exchange(false)) {
|
||||
OnPoster(nullptr, 0);
|
||||
}
|
||||
OnRecorderStateChange(CameraControlListener::kRecorderStopped);
|
||||
|
||||
{
|
||||
@ -2018,11 +2028,13 @@ nsGonkCameraControl::SetupRecording(int aFd, int aRotation,
|
||||
NS_ERROR_INVALID_ARG);
|
||||
|
||||
// adjust rotation by camera sensor offset
|
||||
int r = aRotation;
|
||||
r += mCameraHw->GetSensorOrientation();
|
||||
r = RationalizeRotation(r);
|
||||
DOM_CAMERA_LOGI("setting video rotation to %d degrees (mapped from %d)\n", r, aRotation);
|
||||
snprintf(buffer, SIZE, "video-param-rotation-angle-degrees=%d", r);
|
||||
mVideoRotation = aRotation;
|
||||
mVideoRotation += mCameraHw->GetSensorOrientation();
|
||||
mVideoRotation = RationalizeRotation(mVideoRotation);
|
||||
DOM_CAMERA_LOGI("setting video rotation to %d degrees (mapped from %d)\n",
|
||||
mVideoRotation, aRotation);
|
||||
snprintf(buffer, SIZE, "video-param-rotation-angle-degrees=%d",
|
||||
mVideoRotation);
|
||||
CHECK_SETARG_RETURN(mRecorder->setParameters(String8(buffer)),
|
||||
NS_ERROR_INVALID_ARG);
|
||||
|
||||
@ -2165,6 +2177,137 @@ nsGonkCameraControl::OnRateLimitPreview(bool 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
|
||||
nsGonkCameraControl::OnNewPreviewFrame(layers::TextureClient* aBuffer)
|
||||
{
|
||||
@ -2179,6 +2322,14 @@ nsGonkCameraControl::OnNewPreviewFrame(layers::TextureClient* aBuffer)
|
||||
mCurrentConfiguration.mPreviewSize.height);
|
||||
videoImage->SetData(data);
|
||||
|
||||
if (mCapturePoster.exchange(false)) {
|
||||
CreatePoster(frame,
|
||||
mCurrentConfiguration.mPreviewSize.width,
|
||||
mCurrentConfiguration.mPreviewSize.height,
|
||||
mVideoRotation);
|
||||
return;
|
||||
}
|
||||
|
||||
OnNewPreviewFrame(frame, mCurrentConfiguration.mPreviewSize.width,
|
||||
mCurrentConfiguration.mPreviewSize.height);
|
||||
#endif
|
||||
|
@ -47,6 +47,7 @@ namespace mozilla {
|
||||
namespace layers {
|
||||
class TextureClient;
|
||||
class ImageContainer;
|
||||
class Image;
|
||||
}
|
||||
|
||||
class nsGonkCameraControl : public CameraControlImpl
|
||||
@ -60,6 +61,7 @@ public:
|
||||
void OnTakePictureComplete(uint8_t* aData, uint32_t aLength);
|
||||
void OnTakePictureError();
|
||||
void OnRateLimitPreview(bool aLimit);
|
||||
void OnPoster(void* aData, uint32_t aLength);
|
||||
void OnNewPreviewFrame(layers::TextureClient* aBuffer);
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
void OnRecorderEvent(int msg, int ext1, int ext2);
|
||||
@ -149,6 +151,8 @@ protected:
|
||||
nsresult PausePreview();
|
||||
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();
|
||||
static PLDHashOperator Enumerate(const nsAString& aProfileName,
|
||||
RecorderProfile* aProfile,
|
||||
@ -198,6 +202,9 @@ protected:
|
||||
nsRefPtr<DeviceStorageFile> mVideoFile;
|
||||
nsString mFileFormat;
|
||||
|
||||
Atomic<bool> mCapturePoster;
|
||||
int32_t mVideoRotation;
|
||||
|
||||
bool mAutoFocusPending;
|
||||
nsCOMPtr<nsITimer> mAutoFocusCompleteTimer;
|
||||
int32_t mAutoFocusCompleteExpired;
|
||||
|
@ -138,6 +138,7 @@ public:
|
||||
uint64_t maxFileSizeBytes;
|
||||
uint64_t maxVideoLengthMs;
|
||||
bool autoEnableLowLightTorch;
|
||||
bool createPoster;
|
||||
};
|
||||
|
||||
struct Configuration {
|
||||
|
@ -65,6 +65,7 @@ FAIL_ON_WARNINGS = True
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'../base',
|
||||
'/media/libyuv/include',
|
||||
]
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
@ -109,6 +109,12 @@ dictionary CameraStartRecordingOptions
|
||||
will be left as-is on stopRecording(). If the camera does not
|
||||
support this setting, it will be ignored. */
|
||||
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;
|
||||
};
|
||||
|
||||
/*
|
||||
|
Loading…
Reference in New Issue
Block a user