mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
583 lines
16 KiB
C++
583 lines
16 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* 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 <dlfcn.h>
|
|
#include "android/log.h"
|
|
#include "cutils/properties.h"
|
|
|
|
#include "base/basictypes.h"
|
|
#include "GonkCaptureProvider.h"
|
|
#include "nsXULAppAPI.h"
|
|
#include "nsStreamUtils.h"
|
|
#include "nsThreadUtils.h"
|
|
#include "nsRawStructs.h"
|
|
#include "prinit.h"
|
|
|
|
#define USE_GS2_LIBCAMERA
|
|
#define CameraHardwareInterface CameraHardwareInterface_SGS2
|
|
#define HAL_openCameraHardware HAL_openCameraHardware_SGS2
|
|
#include "camera/CameraHardwareInterface.h"
|
|
#undef CameraHardwareInterface
|
|
#undef USE_GS2_LIBCAMERA
|
|
#undef HAL_openCameraHardware
|
|
#undef ANDROID_HARDWARE_CAMERA_HARDWARE_INTERFACE_H
|
|
|
|
// Needed to prevent a redefinition of 'struct android::image_rect_struct'...
|
|
#define image_rect_type image_rect_type2
|
|
#define image_rect_struct image_rect_struct2
|
|
|
|
#define USE_MAGURO_LIBCAMERA
|
|
#define CameraHardwareInterface CameraHardwareInterface_MAGURO
|
|
#define HAL_openCameraHardware HAL_openCameraHardware_MAGURO
|
|
#include "camera/CameraHardwareInterface.h"
|
|
#undef CameraHardwareInterface
|
|
#undef USE_MAGURO_LIBCAMERA
|
|
#undef HAL_openCameraHardware
|
|
#undef ANDROID_HARDWARE_CAMERA_HARDWARE_INTERFACE_H
|
|
#undef image_rect_type
|
|
#undef image_rect_struct
|
|
|
|
#define image_rect_type image_rect_type3
|
|
#define image_rect_struct image_rect_struct3
|
|
#define CameraHardwareInterface CameraHardwareInterface_DEFAULT
|
|
#include "camera/CameraHardwareInterface.h"
|
|
#undef CameraHardwareInterface
|
|
|
|
using namespace android;
|
|
using namespace mozilla;
|
|
|
|
class CameraHardwareInterface {
|
|
public:
|
|
enum Type {
|
|
CAMERA_SGS2,
|
|
CAMERA_MAGURO,
|
|
CAMERA_DEFAULT
|
|
};
|
|
|
|
static Type getType() {
|
|
char propValue[PROPERTY_VALUE_MAX];
|
|
property_get("ro.product.board", propValue, NULL);
|
|
if (!strcmp(propValue, "GT-I9100"))
|
|
return CAMERA_SGS2;
|
|
|
|
if (!strcmp(propValue, "msm7627a_sku1") || !strcmp(propValue, "MSM7627A_SKU3"))
|
|
return CAMERA_MAGURO;
|
|
|
|
printf_stderr("CameraHardwareInterface : unsupported camera for device %s\n", propValue);
|
|
return CAMERA_DEFAULT;
|
|
}
|
|
|
|
static CameraHardwareInterface* openCamera(PRUint32 aCamera);
|
|
|
|
virtual ~CameraHardwareInterface() { };
|
|
|
|
virtual bool ok();
|
|
virtual void enableMsgType(int32_t msgType);
|
|
virtual void disableMsgType(int32_t msgType);
|
|
virtual bool msgTypeEnabled(int32_t msgType);
|
|
virtual void setCallbacks(notify_callback notify_cb,
|
|
data_callback data_cb,
|
|
data_callback_timestamp data_cb_timestamp,
|
|
void* user);
|
|
virtual status_t startPreview();
|
|
virtual void stopPreview();
|
|
virtual void release();
|
|
virtual status_t setParameters(const CameraParameters& params);
|
|
virtual CameraParameters getParameters() const;
|
|
|
|
protected:
|
|
CameraHardwareInterface(PRUint32 aCamera = 0) { };
|
|
};
|
|
|
|
// Intentionally not trying to dlclose() this handle. That's playing
|
|
// Russian roulette with security bugs.
|
|
static void* sCameraLib;
|
|
static PRCallOnceType sInitCameraLib;
|
|
|
|
static PRStatus
|
|
InitCameraLib()
|
|
{
|
|
sCameraLib = dlopen("/system/lib/libcamera.so", RTLD_LAZY);
|
|
// We might fail to open the camera lib. That's OK.
|
|
return PR_SUCCESS;
|
|
}
|
|
|
|
static void*
|
|
GetCameraLibHandle()
|
|
{
|
|
PR_CallOnce(&sInitCameraLib, InitCameraLib);
|
|
return sCameraLib;
|
|
}
|
|
|
|
template<class T> class CameraImpl : public CameraHardwareInterface {
|
|
public:
|
|
typedef sp<T> (*HAL_openCameraHardware_DEFAULT)(int);
|
|
typedef sp<T> (*HAL_openCameraHardware_SGS2)(int);
|
|
typedef sp<T> (*HAL_openCameraHardware_MAGURO)(int, int);
|
|
|
|
CameraImpl(PRUint32 aCamera = 0) : mOk(false), mCamera(nsnull) {
|
|
void* cameraLib = GetCameraLibHandle();
|
|
if (!cameraLib) {
|
|
printf_stderr("CameraImpl: Failed to dlopen() camera library.");
|
|
return;
|
|
}
|
|
|
|
void *hal = dlsym(cameraLib, "HAL_openCameraHardware");
|
|
HAL_openCameraHardware_DEFAULT funct0;
|
|
HAL_openCameraHardware_SGS2 funct1;
|
|
HAL_openCameraHardware_MAGURO funct2;
|
|
switch(getType()) {
|
|
case CAMERA_SGS2:
|
|
funct1 = reinterpret_cast<HAL_openCameraHardware_SGS2> (hal);
|
|
mCamera = funct1(aCamera);
|
|
break;
|
|
case CAMERA_MAGURO:
|
|
funct2 = reinterpret_cast<HAL_openCameraHardware_MAGURO> (hal);
|
|
mCamera = funct2(aCamera, 1);
|
|
break;
|
|
case CAMERA_DEFAULT:
|
|
funct0 = reinterpret_cast<HAL_openCameraHardware_DEFAULT> (hal);
|
|
mCamera = funct0(aCamera);
|
|
break;
|
|
}
|
|
|
|
mOk = mCamera != nsnull;
|
|
if (!mOk) {
|
|
printf_stderr("CameraImpl: HAL_openCameraHardware() returned NULL (no camera interface).");
|
|
}
|
|
}
|
|
|
|
bool ok() {
|
|
return mOk;
|
|
};
|
|
|
|
void enableMsgType(int32_t msgType) {
|
|
mCamera->enableMsgType(msgType);
|
|
};
|
|
|
|
void disableMsgType(int32_t msgType) {
|
|
mCamera->disableMsgType(msgType);
|
|
};
|
|
|
|
bool msgTypeEnabled(int32_t msgType) {
|
|
return mCamera->msgTypeEnabled(msgType);
|
|
};
|
|
|
|
void setCallbacks(notify_callback notify_cb,
|
|
data_callback data_cb,
|
|
data_callback_timestamp data_cb_timestamp,
|
|
void* user) {
|
|
mCamera->setCallbacks(notify_cb, data_cb, data_cb_timestamp, user);
|
|
};
|
|
|
|
status_t startPreview() {
|
|
return mCamera->startPreview();
|
|
};
|
|
|
|
void stopPreview() {
|
|
mCamera->stopPreview();
|
|
};
|
|
|
|
void release() {
|
|
return mCamera->release();
|
|
};
|
|
|
|
status_t setParameters(const CameraParameters& params) {
|
|
return mCamera->setParameters(params);
|
|
}
|
|
|
|
CameraParameters getParameters() const {
|
|
return mCamera->getParameters();
|
|
};
|
|
protected:
|
|
bool mOk;
|
|
sp<T> mCamera;
|
|
};
|
|
|
|
CameraHardwareInterface* CameraHardwareInterface::openCamera(PRUint32 aCamera) {
|
|
nsAutoPtr<CameraHardwareInterface> instance;
|
|
switch(getType()) {
|
|
case CAMERA_SGS2:
|
|
instance = new CameraImpl<CameraHardwareInterface_SGS2>(aCamera);
|
|
break;
|
|
case CAMERA_MAGURO:
|
|
instance = new CameraImpl<CameraHardwareInterface_MAGURO>(aCamera);
|
|
break;
|
|
case CAMERA_DEFAULT:
|
|
instance = new CameraImpl<CameraHardwareInterface_DEFAULT>(aCamera);
|
|
break;
|
|
}
|
|
|
|
if (!instance->ok()) {
|
|
return nsnull;
|
|
}
|
|
|
|
return instance.forget();
|
|
};
|
|
|
|
|
|
// The maximum number of frames we keep in our queue. Don't live in the past.
|
|
#define MAX_FRAMES_QUEUED 5
|
|
|
|
NS_IMPL_THREADSAFE_ISUPPORTS2(GonkCameraInputStream, nsIInputStream, nsIAsyncInputStream)
|
|
|
|
GonkCameraInputStream::GonkCameraInputStream() :
|
|
mAvailable(sizeof(nsRawVideoHeader)), mWidth(0), mHeight(0), mFps(30), mCamera(0),
|
|
mHeaderSent(false), mClosed(true), mIs420p(false), mFrameSize(0), mMonitor("GonkCamera.Monitor")
|
|
{
|
|
|
|
}
|
|
|
|
GonkCameraInputStream::~GonkCameraInputStream() {
|
|
// clear the frame queue
|
|
while (mFrameQueue.GetSize() > 0) {
|
|
free(mFrameQueue.PopFront());
|
|
}
|
|
|
|
// no need to close Close() since the stream is opened here :
|
|
// http://mxr.mozilla.org/mozilla-central/source/netwerk/base/src/nsBaseChannel.cpp#239
|
|
// and this leads to http://mxr.mozilla.org/mozilla-central/source/netwerk/base/src/nsBaseChannel.cpp#259
|
|
// that creates and input pump with closeWhenDone == true
|
|
// the pump cleans up properly at http://mxr.mozilla.org/mozilla-central/source/netwerk/base/src/nsInputStreamPump.cpp#565
|
|
}
|
|
|
|
void
|
|
GonkCameraInputStream::DataCallback(int32_t aMsgType, const sp<IMemory>& aDataPtr, void *aUser) {
|
|
GonkCameraInputStream* stream = (GonkCameraInputStream*)(aUser);
|
|
stream->ReceiveFrame((char*)aDataPtr->pointer(), aDataPtr->size());
|
|
}
|
|
|
|
PRUint32
|
|
GonkCameraInputStream::getNumberOfCameras() {
|
|
typedef int (*HAL_getNumberOfCamerasFunct)(void);
|
|
void* cameraLib = GetCameraLibHandle();
|
|
if (!cameraLib)
|
|
return 0;
|
|
|
|
void *hal = dlsym(cameraLib, "HAL_getNumberOfCameras");
|
|
if (nsnull == hal)
|
|
return 0;
|
|
|
|
HAL_getNumberOfCamerasFunct funct = reinterpret_cast<HAL_getNumberOfCamerasFunct> (hal);
|
|
return funct();
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
GonkCameraInputStream::Init(nsACString& aContentType, nsCaptureParams* aParams)
|
|
{
|
|
if (XRE_GetProcessType() != GeckoProcessType_Default)
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
|
|
mContentType = aContentType;
|
|
mWidth = aParams->width;
|
|
mHeight = aParams->height;
|
|
mCamera = aParams->camera;
|
|
|
|
PRUint32 maxNumCameras = getNumberOfCameras();
|
|
|
|
if (maxNumCameras == 0)
|
|
return NS_ERROR_FAILURE;
|
|
|
|
if (mCamera >= maxNumCameras)
|
|
mCamera = 0;
|
|
|
|
mHardware = CameraHardwareInterface::openCamera(mCamera);
|
|
|
|
if (!mHardware)
|
|
return NS_ERROR_FAILURE;
|
|
|
|
mHardware->setCallbacks(NULL, GonkCameraInputStream::DataCallback, NULL, this);
|
|
|
|
mHardware->enableMsgType(CAMERA_MSG_PREVIEW_FRAME);
|
|
|
|
CameraParameters params = mHardware->getParameters();
|
|
|
|
printf_stderr("Preview format : %s\n", params.get(params.KEY_SUPPORTED_PREVIEW_FORMATS));
|
|
|
|
Vector<Size> previewSizes;
|
|
params.getSupportedPreviewSizes(previewSizes);
|
|
|
|
// find the available preview size closest to the requested size.
|
|
PRUint32 minSizeDelta = PR_UINT32_MAX;
|
|
PRUint32 bestWidth = mWidth;
|
|
PRUint32 bestHeight = mHeight;
|
|
for (PRUint32 i = 0; i < previewSizes.size(); i++) {
|
|
Size size = previewSizes[i];
|
|
PRUint32 delta = abs(size.width * size.height - mWidth * mHeight);
|
|
if (delta < minSizeDelta) {
|
|
minSizeDelta = delta;
|
|
bestWidth = size.width;
|
|
bestHeight = size.height;
|
|
}
|
|
}
|
|
mWidth = bestWidth;
|
|
mHeight = bestHeight;
|
|
params.setPreviewSize(mWidth, mHeight);
|
|
|
|
// try to set preferred image format
|
|
params.setPreviewFormat("yuv420p");
|
|
|
|
params.setPreviewFrameRate(mFps);
|
|
mHardware->setParameters(params);
|
|
params = mHardware->getParameters();
|
|
mFps = params.getPreviewFrameRate();
|
|
|
|
mIs420p = !strcmp(params.getPreviewFormat(), "yuv420p");
|
|
|
|
mHardware->startPreview();
|
|
|
|
mClosed = false;
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
void
|
|
GonkCameraInputStream::ReceiveFrame(char* frame, PRUint32 length) {
|
|
{
|
|
ReentrantMonitorAutoEnter enter(mMonitor);
|
|
if (mFrameQueue.GetSize() > MAX_FRAMES_QUEUED) {
|
|
free(mFrameQueue.PopFront());
|
|
mAvailable -= mFrameSize;
|
|
}
|
|
}
|
|
|
|
mFrameSize = sizeof(nsRawPacketHeader) + length;
|
|
|
|
char* fullFrame = (char*)moz_malloc(mFrameSize);
|
|
|
|
if (!fullFrame)
|
|
return;
|
|
|
|
nsRawPacketHeader* header = reinterpret_cast<nsRawPacketHeader*> (fullFrame);
|
|
header->packetID = 0xFF;
|
|
header->codecID = RAW_ID;
|
|
|
|
if (mIs420p) {
|
|
memcpy(fullFrame + sizeof(nsRawPacketHeader), frame, length);
|
|
} else {
|
|
// we copy the Y plane, and de-interlace the CrCb
|
|
PRUint32 yFrameSize = mWidth * mHeight;
|
|
PRUint32 uvFrameSize = yFrameSize / 4;
|
|
memcpy(fullFrame + sizeof(nsRawPacketHeader), frame, yFrameSize);
|
|
|
|
char* uFrame = fullFrame + sizeof(nsRawPacketHeader) + yFrameSize;
|
|
char* vFrame = fullFrame + sizeof(nsRawPacketHeader) + yFrameSize + uvFrameSize;
|
|
const char* yFrame = frame + yFrameSize;
|
|
for (PRUint32 i = 0; i < uvFrameSize; i++) {
|
|
uFrame[i] = yFrame[2 * i + 1];
|
|
vFrame[i] = yFrame[2 * i];
|
|
}
|
|
}
|
|
|
|
|
|
{
|
|
ReentrantMonitorAutoEnter enter(mMonitor);
|
|
mAvailable += mFrameSize;
|
|
mFrameQueue.Push((void*)fullFrame);
|
|
}
|
|
|
|
NotifyListeners();
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
GonkCameraInputStream::Available(PRUint32 *aAvailable)
|
|
{
|
|
ReentrantMonitorAutoEnter enter(mMonitor);
|
|
|
|
*aAvailable = mAvailable;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP GonkCameraInputStream::IsNonBlocking(bool *aNonBlock) {
|
|
*aNonBlock = true;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP GonkCameraInputStream::Read(char *aBuffer, PRUint32 aCount, PRUint32 *aRead NS_OUTPARAM) {
|
|
return ReadSegments(NS_CopySegmentToBuffer, aBuffer, aCount, aRead);
|
|
}
|
|
|
|
NS_IMETHODIMP GonkCameraInputStream::ReadSegments(nsWriteSegmentFun aWriter, void *aClosure, PRUint32 aCount, PRUint32 *aRead NS_OUTPARAM) {
|
|
*aRead = 0;
|
|
|
|
nsresult rv;
|
|
|
|
if (mAvailable == 0)
|
|
return NS_BASE_STREAM_WOULD_BLOCK;
|
|
|
|
if (aCount > mAvailable)
|
|
aCount = mAvailable;
|
|
|
|
if (!mHeaderSent) {
|
|
nsRawVideoHeader header;
|
|
header.headerPacketID = 0;
|
|
header.codecID = RAW_ID;
|
|
header.majorVersion = 0;
|
|
header.minorVersion = 1;
|
|
header.options = 1 | 1 << 1; // color, 4:2:0
|
|
|
|
header.alphaChannelBpp = 0;
|
|
header.lumaChannelBpp = 8;
|
|
header.chromaChannelBpp = 4;
|
|
header.colorspace = 1;
|
|
|
|
header.frameWidth = mWidth;
|
|
header.frameHeight = mHeight;
|
|
header.aspectNumerator = 1;
|
|
header.aspectDenominator = 1;
|
|
|
|
header.framerateNumerator = mFps;
|
|
header.framerateDenominator = 1;
|
|
|
|
rv = aWriter(this, aClosure, (const char*)&header, 0, sizeof(nsRawVideoHeader), aRead);
|
|
|
|
if (NS_FAILED(rv))
|
|
return NS_OK;
|
|
|
|
mHeaderSent = true;
|
|
aCount -= sizeof(nsRawVideoHeader);
|
|
mAvailable -= sizeof(nsRawVideoHeader);
|
|
}
|
|
|
|
{
|
|
ReentrantMonitorAutoEnter enter(mMonitor);
|
|
while ((mAvailable > 0) && (aCount >= mFrameSize)) {
|
|
PRUint32 readThisTime = 0;
|
|
|
|
char* frame = (char*)mFrameQueue.PopFront();
|
|
rv = aWriter(this, aClosure, (const char*)frame, *aRead, mFrameSize, &readThisTime);
|
|
|
|
if (readThisTime != mFrameSize) {
|
|
mFrameQueue.PushFront((void*)frame);
|
|
return NS_OK;
|
|
}
|
|
|
|
// nsRawReader does a copy when calling VideoData::Create()
|
|
free(frame);
|
|
|
|
if (NS_FAILED(rv))
|
|
return NS_OK;
|
|
|
|
aCount -= readThisTime;
|
|
mAvailable -= readThisTime;
|
|
*aRead += readThisTime;
|
|
}
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP GonkCameraInputStream::Close() {
|
|
return CloseWithStatus(NS_OK);
|
|
}
|
|
|
|
void GonkCameraInputStream::doClose() {
|
|
ReentrantMonitorAutoEnter enter(mMonitor);
|
|
|
|
if (mClosed)
|
|
return;
|
|
|
|
mHardware->disableMsgType(CAMERA_MSG_ALL_MSGS);
|
|
mHardware->stopPreview();
|
|
mHardware->release();
|
|
delete mHardware;
|
|
|
|
mClosed = true;
|
|
}
|
|
|
|
|
|
void GonkCameraInputStream::NotifyListeners() {
|
|
ReentrantMonitorAutoEnter enter(mMonitor);
|
|
|
|
if (mCallback && (mAvailable > sizeof(nsRawVideoHeader))) {
|
|
nsCOMPtr<nsIInputStreamCallback> callback;
|
|
if (mCallbackTarget) {
|
|
NS_NewInputStreamReadyEvent(getter_AddRefs(callback), mCallback, mCallbackTarget);
|
|
} else {
|
|
callback = mCallback;
|
|
}
|
|
|
|
NS_ASSERTION(callback, "Shouldn't fail to make the callback!");
|
|
|
|
// Null the callback first because OnInputStreamReady may reenter AsyncWait
|
|
mCallback = nsnull;
|
|
mCallbackTarget = nsnull;
|
|
|
|
callback->OnInputStreamReady(this);
|
|
}
|
|
}
|
|
|
|
NS_IMETHODIMP GonkCameraInputStream::AsyncWait(nsIInputStreamCallback *aCallback, PRUint32 aFlags, PRUint32 aRequestedCount, nsIEventTarget *aTarget)
|
|
{
|
|
if (aFlags != 0)
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
|
|
if (mCallback || mCallbackTarget)
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
mCallbackTarget = aTarget;
|
|
mCallback = aCallback;
|
|
|
|
// What we are being asked for may be present already
|
|
NotifyListeners();
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP GonkCameraInputStream::CloseWithStatus(PRUint32 status)
|
|
{
|
|
GonkCameraInputStream::doClose();
|
|
return NS_OK;
|
|
}
|
|
|
|
/**
|
|
* GonkCaptureProvider implementation
|
|
*/
|
|
NS_IMPL_ISUPPORTS0(GonkCaptureProvider)
|
|
|
|
GonkCaptureProvider* GonkCaptureProvider::sInstance = NULL;
|
|
|
|
GonkCaptureProvider::GonkCaptureProvider() {
|
|
}
|
|
|
|
GonkCaptureProvider::~GonkCaptureProvider() {
|
|
GonkCaptureProvider::sInstance = NULL;
|
|
}
|
|
|
|
nsresult GonkCaptureProvider::Init(nsACString& aContentType,
|
|
nsCaptureParams* aParams,
|
|
nsIInputStream** aStream) {
|
|
|
|
NS_ENSURE_ARG_POINTER(aParams);
|
|
|
|
NS_ASSERTION(aParams->frameLimit == 0 || aParams->timeLimit == 0,
|
|
"Cannot set both a frame limit and a time limit!");
|
|
|
|
nsRefPtr<GonkCameraInputStream> stream;
|
|
|
|
if (aContentType.EqualsLiteral("video/x-raw-yuv")) {
|
|
stream = new GonkCameraInputStream();
|
|
if (stream) {
|
|
nsresult rv = stream->Init(aContentType, aParams);
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
}
|
|
else {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
} else {
|
|
NS_NOTREACHED("Should not have asked Gonk for this type!");
|
|
}
|
|
return CallQueryInterface(stream, aStream);
|
|
}
|
|
|
|
already_AddRefed<GonkCaptureProvider> GetGonkCaptureProvider() {
|
|
if (!GonkCaptureProvider::sInstance) {
|
|
GonkCaptureProvider::sInstance = new GonkCaptureProvider();
|
|
}
|
|
GonkCaptureProvider::sInstance->AddRef();
|
|
return GonkCaptureProvider::sInstance;
|
|
}
|