Support multiple cameras in framework.

Change-Id: I081f0fbdca4b633715ea7c3b3d42f8662d27598a
This commit is contained in:
Chih-Chung Chang
2010-05-06 16:36:58 +08:00
parent a0a126a060
commit e25cc65639
15 changed files with 1303 additions and 1353 deletions

View File

@@ -1,15 +1,24 @@
LOCAL_PATH:= $(call my-dir)
#
# Set USE_CAMERA_STUB for non-emulator and non-simulator builds, if you want
# the camera service to use the fake camera. For emulator or simulator builds,
# we always use the fake camera.
# Set USE_CAMERA_STUB if you want to use the fake camera.
# Set USE_CAMERA_HARDWARE if you want to use the hardware camera.
# For emulator or simulator builds, we use the fake camera only by default.
ifeq ($(USE_CAMERA_STUB),)
USE_CAMERA_STUB:=false
ifneq ($(filter sooner generic sim,$(TARGET_DEVICE)),)
USE_CAMERA_STUB:=true
endif #libcamerastub
ifeq ($(USE_CAMERA_STUB),)
USE_CAMERA_STUB:=true
endif
ifeq ($(USE_CAMERA_HARDWARE),)
USE_CAMERA_HARDWARE:=false
endif
else
# force USE_CAMERA_STUB for testing temporarily
# ifeq ($(USE_CAMERA_STUB),)
USE_CAMERA_STUB:=true
# endif
ifeq ($(USE_CAMERA_HARDWARE),)
USE_CAMERA_HARDWARE:=true
endif
endif
ifeq ($(USE_CAMERA_STUB),true)
@@ -54,18 +63,18 @@ LOCAL_SHARED_LIBRARIES:= \
LOCAL_MODULE:= libcameraservice
LOCAL_CFLAGS += -DLOG_TAG=\"CameraService\"
ifeq ($(TARGET_SIMULATOR),true)
LOCAL_CFLAGS += -DSINGLE_PROCESS
endif
ifeq ($(USE_CAMERA_STUB), true)
LOCAL_STATIC_LIBRARIES += libcamerastub
LOCAL_CFLAGS += -include CameraHardwareStub.h
else
LOCAL_CFLAGS += -DUSE_CAMERA_STUB
endif
ifeq ($(USE_CAMERA_HARDWARE),true)
LOCAL_CFLAGS += -DUSE_CAMERA_HARDWARE
LOCAL_SHARED_LIBRARIES += libcamera
endif
include $(BUILD_SHARED_LIBRARY)

View File

@@ -47,14 +47,14 @@ void CameraHardwareStub::initDefaultParameters()
{
CameraParameters p;
p.set("preview-size-values","320x240");
p.set(CameraParameters::KEY_SUPPORTED_PREVIEW_SIZES, "320x240");
p.setPreviewSize(320, 240);
p.setPreviewFrameRate(15);
p.setPreviewFormat("yuv422sp");
p.setPreviewFormat(CameraParameters::PIXEL_FORMAT_YUV420SP);
p.set("picture-size-values", "320x240");
p.set(CameraParameters::KEY_SUPPORTED_PICTURE_SIZES, "320x240");
p.setPictureSize(320, 240);
p.setPictureFormat("jpeg");
p.setPictureFormat(CameraParameters::PIXEL_FORMAT_JPEG);
if (setParameters(p) != NO_ERROR) {
LOGE("Failed to set default parameters?!");
@@ -66,14 +66,14 @@ void CameraHardwareStub::initHeapLocked()
// Create raw heap.
int picture_width, picture_height;
mParameters.getPictureSize(&picture_width, &picture_height);
mRawHeap = new MemoryHeapBase(picture_width * 2 * picture_height);
mRawHeap = new MemoryHeapBase(picture_width * picture_height * 3 / 2);
int preview_width, preview_height;
mParameters.getPreviewSize(&preview_width, &preview_height);
LOGD("initHeapLocked: preview size=%dx%d", preview_width, preview_height);
// Note that we enforce yuv422 in setParameters().
int how_big = preview_width * preview_height * 2;
// Note that we enforce yuv420sp in setParameters().
int how_big = preview_width * preview_height * 3 / 2;
// If we are being reinitialized to the same size as before, no
// work needs to be done.
@@ -99,7 +99,6 @@ CameraHardwareStub::~CameraHardwareStub()
{
delete mFakeCamera;
mFakeCamera = 0; // paranoia
singleton.clear();
}
sp<IMemoryHeap> CameraHardwareStub::getPreviewHeap() const
@@ -175,7 +174,7 @@ int CameraHardwareStub::previewThread()
// Fill the current frame with the fake camera.
uint8_t *frame = ((uint8_t *)base) + offset;
fakeCamera->getNextFrameAsYuv422(frame);
fakeCamera->getNextFrameAsYuv420(frame);
//LOGV("previewThread: generated frame to buffer %d", mCurrentPreviewFrame);
@@ -288,9 +287,9 @@ int CameraHardwareStub::pictureThread()
// In the meantime just make another fake camera picture.
int w, h;
mParameters.getPictureSize(&w, &h);
sp<MemoryBase> mem = new MemoryBase(mRawHeap, 0, w * 2 * h);
sp<MemoryBase> mem = new MemoryBase(mRawHeap, 0, w * h * 3 / 2);
FakeCamera cam(w, h);
cam.getNextFrameAsYuv422((uint8_t *)mRawHeap->base());
cam.getNextFrameAsYuv420((uint8_t *)mRawHeap->base());
mDataCb(CAMERA_MSG_RAW_IMAGE, mem, mCallbackCookie);
}
@@ -307,7 +306,7 @@ status_t CameraHardwareStub::takePicture()
{
stopPreview();
if (createThread(beginPictureThread, this) == false)
return -1;
return UNKNOWN_ERROR;
return NO_ERROR;
}
@@ -339,12 +338,14 @@ status_t CameraHardwareStub::setParameters(const CameraParameters& params)
Mutex::Autolock lock(mLock);
// XXX verify params
if (strcmp(params.getPreviewFormat(), "yuv422sp") != 0) {
LOGE("Only yuv422sp preview is supported");
if (strcmp(params.getPreviewFormat(),
CameraParameters::PIXEL_FORMAT_YUV420SP) != 0) {
LOGE("Only yuv420sp preview is supported");
return -1;
}
if (strcmp(params.getPictureFormat(), "jpeg") != 0) {
if (strcmp(params.getPictureFormat(),
CameraParameters::PIXEL_FORMAT_JPEG) != 0) {
LOGE("Only jpeg still pictures are supported");
return -1;
}
@@ -379,22 +380,12 @@ void CameraHardwareStub::release()
{
}
wp<CameraHardwareInterface> CameraHardwareStub::singleton;
sp<CameraHardwareInterface> CameraHardwareStub::createInstance()
{
if (singleton != 0) {
sp<CameraHardwareInterface> hardware = singleton.promote();
if (hardware != 0) {
return hardware;
}
}
sp<CameraHardwareInterface> hardware(new CameraHardwareStub());
singleton = hardware;
return hardware;
return new CameraHardwareStub();
}
extern "C" sp<CameraHardwareInterface> openCameraHardware()
extern "C" sp<CameraHardwareInterface> openCameraHardwareStub()
{
return CameraHardwareStub::createInstance();
}

View File

@@ -67,8 +67,6 @@ private:
CameraHardwareStub();
virtual ~CameraHardwareStub();
static wp<CameraHardwareInterface> singleton;
static const int kBufferCount = 4;
class PreviewThread : public Thread {
@@ -130,6 +128,8 @@ private:
int mCurrentPreviewFrame;
};
extern "C" sp<CameraHardwareInterface> openCameraHardwareStub();
}; // namespace android
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -21,207 +21,171 @@
#include <camera/ICameraService.h>
#include <camera/CameraHardwareInterface.h>
#include <camera/Camera.h>
/* This needs to be increased if we can have more cameras */
#define MAX_CAMERAS 2
namespace android {
class MemoryHeapBase;
class MediaPlayer;
// ----------------------------------------------------------------------------
#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true ))
#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false ))
// When enabled, this feature allows you to send an event to the CameraService
// so that you can cause all references to the heap object gWeakHeap, defined
// below, to be printed. You will also need to set DEBUG_REFS=1 and
// DEBUG_REFS_ENABLED_BY_DEFAULT=0 in libutils/RefBase.cpp. You just have to
// set gWeakHeap to the appropriate heap you want to track.
#define DEBUG_HEAP_LEAKS 0
// ----------------------------------------------------------------------------
class CameraService : public BnCameraService
class CameraService: public BnCameraService
{
class Client;
public:
static void instantiate();
static void instantiate();
// ICameraService interface
virtual sp<ICamera> connect(const sp<ICameraClient>& cameraClient);
CameraService();
virtual ~CameraService();
virtual status_t dump(int fd, const Vector<String16>& args);
virtual int32_t getNumberOfCameras();
virtual sp<ICamera> connect(const sp<ICameraClient>& cameraClient, int cameraId);
virtual void removeClient(const sp<ICameraClient>& cameraClient);
virtual sp<Client> getClientById(int cameraId);
void removeClient(const sp<ICameraClient>& cameraClient);
virtual status_t dump(int fd, const Vector<String16>& args);
virtual status_t onTransact(uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags);
virtual status_t onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags);
enum sound_kind {
SOUND_SHUTTER = 0,
SOUND_RECORDING = 1,
NUM_SOUNDS
};
void loadSound();
void playSound(sound_kind kind);
void releaseSound();
private:
Mutex mServiceLock;
wp<Client> mClient[MAX_CAMERAS]; // protected by mServiceLock
// ----------------------------------------------------------------------------
// atomics to record whether the hardware is allocated to some client.
volatile int32_t mBusy[MAX_CAMERAS];
void setCameraBusy(int cameraId);
void setCameraFree(int cameraId);
class Client : public BnCamera {
// sounds
Mutex mSoundLock;
sp<MediaPlayer> mSoundPlayer[NUM_SOUNDS];
int mSoundRef; // reference count (release all MediaPlayer when 0)
class Client : public BnCamera
{
public:
// ICamera interface (see ICamera for details)
virtual void disconnect();
// connect new client with existing camera remote
virtual status_t connect(const sp<ICameraClient>& client);
// prevent other processes from using this ICamera interface
virtual status_t lock();
// allow other processes to use this ICamera interface
virtual status_t unlock();
// pass the buffered ISurface to the camera service
virtual status_t setPreviewDisplay(const sp<ISurface>& surface);
// set the preview callback flag to affect how the received frames from
// preview are handled.
virtual void setPreviewCallbackFlag(int callback_flag);
// start preview mode, must call setPreviewDisplay first
virtual void setPreviewCallbackFlag(int flag);
virtual status_t startPreview();
// stop preview mode
virtual void stopPreview();
// get preview state
virtual bool previewEnabled();
// start recording mode
virtual status_t startRecording();
// stop recording mode
virtual void stopRecording();
// get recording state
virtual bool recordingEnabled();
// release a recording frame
virtual void releaseRecordingFrame(const sp<IMemory>& mem);
// auto focus
virtual status_t autoFocus();
// cancel auto focus
virtual status_t cancelAutoFocus();
// take a picture - returns an IMemory (ref-counted mmap)
virtual status_t takePicture();
// set preview/capture parameters - key/value pairs
virtual status_t setParameters(const String8& params);
// get preview/capture parameters - key/value pairs
virtual String8 getParameters() const;
// send command to camera driver
virtual status_t sendCommand(int32_t cmd, int32_t arg1, int32_t arg2);
// our client...
const sp<ICameraClient>& getCameraClient() const { return mCameraClient; }
private:
friend class CameraService;
Client(const sp<CameraService>& cameraService,
const sp<ICameraClient>& cameraClient,
pid_t clientPid);
Client();
virtual ~Client();
const sp<ICameraClient>& cameraClient,
int cameraId,
int clientPid);
~Client();
status_t checkPid();
// return our camera client
const sp<ICameraClient>& getCameraClient() { return mCameraClient; }
static void notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2, void* user);
static void dataCallback(int32_t msgType, const sp<IMemory>& dataPtr, void* user);
static void dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType,
const sp<IMemory>& dataPtr, void* user);
// check whether the calling process matches mClientPid.
status_t checkPid() const;
status_t checkPidAndHardware() const; // also check mHardware != 0
static sp<Client> getClientFromCookie(void* user);
void handlePreviewData(const sp<IMemory>&);
void handleShutter(image_rect_type *image);
void handlePostview(const sp<IMemory>&);
void handleRawPicture(const sp<IMemory>&);
void handleCompressedPicture(const sp<IMemory>&);
void copyFrameAndPostCopiedFrame(const sp<ICameraClient>& client,
const sp<IMemoryHeap>& heap, size_t offset, size_t size);
// these are internal functions used to set up preview buffers
status_t registerPreviewBuffers();
status_t setOverlay();
// camera operation mode
enum camera_mode {
CAMERA_PREVIEW_MODE = 0, // frame automatically released
CAMERA_RECORDING_MODE = 1, // frame has to be explicitly released by releaseRecordingFrame()
};
// these are internal functions used for preview/recording
status_t startCameraMode(camera_mode mode);
status_t startPreviewMode();
status_t startRecordingMode();
status_t setOverlay();
status_t registerPreviewBuffers();
// these are static callback functions
static void notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2, void* user);
static void dataCallback(int32_t msgType, const sp<IMemory>& dataPtr, void* user);
static void dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr, void* user);
// convert client from cookie
static sp<Client> getClientFromCookie(void* user);
// handlers for messages
void handleShutter(image_rect_type *size);
void handlePreviewData(const sp<IMemory>& mem);
void handlePostview(const sp<IMemory>& mem);
void handleRawPicture(const sp<IMemory>& mem);
void handleCompressedPicture(const sp<IMemory>& mem);
void handleGenericNotify(int32_t msgType, int32_t ext1, int32_t ext2);
void handleGenericData(int32_t msgType, const sp<IMemory>& dataPtr);
void handleGenericDataTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr);
void copyFrameAndPostCopiedFrame(
const sp<ICameraClient>& client,
const sp<IMemoryHeap>& heap,
size_t offset, size_t size);
// these are initialized in the constructor.
sp<CameraService> mCameraService; // immutable after constructor
sp<ICameraClient> mCameraClient;
int mCameraId; // immutable after constructor
pid_t mClientPid;
sp<CameraHardwareInterface> mHardware; // cleared after disconnect()
bool mUseOverlay; // immutable after constructor
sp<OverlayRef> mOverlayRef;
int mOverlayW;
int mOverlayH;
int mPreviewCallbackFlag;
int mOrientation;
// Ensures atomicity among the public methods
mutable Mutex mLock;
mutable Mutex mLock;
sp<ISurface> mSurface;
// mSurfaceLock synchronizes access to mSurface between
// setPreviewSurface() and postPreviewFrame(). Note that among
// the public methods, all accesses to mSurface are
// syncrhonized by mLock. However, postPreviewFrame() is called
// by the CameraHardwareInterface callback, and needs to
// access mSurface. It cannot hold mLock, however, because
// stopPreview() may be holding that lock while attempting
// to stop preview, and stopPreview itself will block waiting
// for a callback from CameraHardwareInterface. If this
// happens, it will cause a deadlock.
mutable Mutex mSurfaceLock;
mutable Condition mReady;
sp<CameraService> mCameraService;
sp<ISurface> mSurface;
int mPreviewCallbackFlag;
int mOrientation;
// If the user want us to return a copy of the preview frame (instead
// of the original one), we allocate mPreviewBuffer and reuse it if possible.
sp<MemoryHeapBase> mPreviewBuffer;
sp<MediaPlayer> mMediaPlayerClick;
sp<MediaPlayer> mMediaPlayerBeep;
// We need to avoid the deadlock when the incoming command thread and
// the CameraHardwareInterface callback thread both want to grab mLock.
// An extra flag is used to tell the callback thread that it should stop
// trying to deliver the callback messages if the client is not
// interested in it anymore. For example, if the client is calling
// stopPreview(), the preview frame messages do not need to be delivered
// anymore.
// these are immutable once the object is created,
// they don't need to be protected by a lock
sp<ICameraClient> mCameraClient;
sp<CameraHardwareInterface> mHardware;
pid_t mClientPid;
bool mUseOverlay;
// This function takes the same parameter as the enableMsgType() and
// disableMsgType() functions in CameraHardwareInterface.
void enableMsgType(int32_t msgType);
void disableMsgType(int32_t msgType);
volatile int32_t mMsgEnabled;
sp<OverlayRef> mOverlayRef;
int mOverlayW;
int mOverlayH;
mutable Mutex mPreviewLock;
sp<MemoryHeapBase> mPreviewBuffer;
// This function keeps trying to grab mLock, or give up if the message
// is found to be disabled. It returns true if mLock is grabbed.
bool lockIfMessageWanted(int32_t msgType);
};
// ----------------------------------------------------------------------------
CameraService();
virtual ~CameraService();
// We use a count for number of clients (shoule only be 0 or 1).
volatile int32_t mUsers;
virtual void incUsers();
virtual void decUsers();
mutable Mutex mServiceLock;
wp<Client> mClient;
#if DEBUG_HEAP_LEAKS
wp<IMemoryHeap> gWeakHeap;
#endif
};
// ----------------------------------------------------------------------------
}; // namespace android
} // namespace android
#endif

View File

@@ -198,10 +198,11 @@ static const int SHIFT2 = 16;
static const int DELTA = kYb*(1 << SHIFT2);
static const int GAMMA = kYr*(1 << SHIFT2);
int32_t ccrgb16toyuv_wo_colorkey(uint8_t *rgb16,uint8_t *yuv422,uint32_t *param,uint8_t *table[])
int32_t ccrgb16toyuv_wo_colorkey(uint8_t *rgb16, uint8_t *yuv420,
uint32_t *param, uint8_t *table[])
{
uint16_t *inputRGB = (uint16_t*)rgb16;
uint8_t *outYUV = yuv422;
uint8_t *outYUV = yuv420;
int32_t width_dst = param[0];
int32_t height_dst = param[1];
int32_t pitch_dst = param[2];
@@ -260,12 +261,14 @@ uint32_t temp;
tempY[0] = y0;
tempY[1] = y1;
tempU[0] = u;
tempV[0] = v;
tempY += 2;
tempU += 2;
tempV += 2;
if ((j&1) == 0) {
tempU[0] = u;
tempV[0] = v;
tempU += 2;
tempV += 2;
}
}
inputRGB += pitch_src;
@@ -277,7 +280,7 @@ uint32_t temp;
#define min(a,b) ((a)<(b)?(a):(b))
#define max(a,b) ((a)>(b)?(a):(b))
static void convert_rgb16_to_yuv422(uint8_t *rgb, uint8_t *yuv, int width, int height)
static void convert_rgb16_to_yuv420(uint8_t *rgb, uint8_t *yuv, int width, int height)
{
if (!tables_initialized) {
initYtab();
@@ -326,7 +329,7 @@ void FakeCamera::setSize(int width, int height)
mCheckY = 0;
// This will cause it to be reallocated on the next call
// to getNextFrameAsYuv422().
// to getNextFrameAsYuv420().
delete[] mTmpRgb16Buffer;
mTmpRgb16Buffer = 0;
}
@@ -347,13 +350,13 @@ void FakeCamera::getNextFrameAsRgb565(uint16_t *buffer)
mCounter++;
}
void FakeCamera::getNextFrameAsYuv422(uint8_t *buffer)
void FakeCamera::getNextFrameAsYuv420(uint8_t *buffer)
{
if (mTmpRgb16Buffer == 0)
mTmpRgb16Buffer = new uint16_t[mWidth * mHeight];
getNextFrameAsRgb565(mTmpRgb16Buffer);
convert_rgb16_to_yuv422((uint8_t*)mTmpRgb16Buffer, buffer, mWidth, mHeight);
convert_rgb16_to_yuv420((uint8_t*)mTmpRgb16Buffer, buffer, mWidth, mHeight);
}
void FakeCamera::drawSquare(uint16_t *dst, int x, int y, int size, int color, int shadow)

View File

@@ -40,7 +40,7 @@ public:
~FakeCamera();
void setSize(int width, int height);
void getNextFrameAsYuv422(uint8_t *buffer);
void getNextFrameAsYuv420(uint8_t *buffer);
// Write to the fd a string representing the current state.
void dump(int fd) const;

View File

@@ -38,7 +38,7 @@ void assert_fail(const char *file, int line, const char *func, const char *expr)
INFO("assertion failed at file %s, line %d, function %s:",
file, line, func);
INFO("%s", expr);
exit(1);
abort();
}
void assert_eq_fail(const char *file, int line, const char *func,
@@ -46,7 +46,7 @@ void assert_eq_fail(const char *file, int line, const char *func,
INFO("assertion failed at file %s, line %d, function %s:",
file, line, func);
INFO("(expected) %s != (actual) %d", expr, actual);
exit(1);
abort();
}
#define ASSERT(e) \
@@ -155,7 +155,7 @@ public:
virtual void notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2);
virtual void dataCallback(int32_t msgType, const sp<IMemory>& data);
virtual void dataCallbackTimestamp(nsecs_t timestamp,
int32_t msgType, const sp<IMemory>& data) {}
int32_t msgType, const sp<IMemory>& data);
// new functions
void clearStat();
@@ -176,6 +176,7 @@ private:
DefaultKeyedVector<int32_t, int> mDataCount;
DefaultKeyedVector<int32_t, int> mDataSize;
bool test(OP op, int v1, int v2);
void assertTest(OP op, int v1, int v2);
ICamera *mReleaser;
};
@@ -199,22 +200,29 @@ bool MCameraClient::test(OP op, int v1, int v2) {
return false;
}
void MCameraClient::assertTest(OP op, int v1, int v2) {
if (!test(op, v1, v2)) {
LOGE("assertTest failed: op=%d, v1=%d, v2=%d", op, v1, v2);
ASSERT(0);
}
}
void MCameraClient::assertNotify(int32_t msgType, OP op, int count) {
Mutex::Autolock _l(mLock);
int v = mNotifyCount.valueFor(msgType);
ASSERT(test(op, v, count));
assertTest(op, v, count);
}
void MCameraClient::assertData(int32_t msgType, OP op, int count) {
Mutex::Autolock _l(mLock);
int v = mDataCount.valueFor(msgType);
ASSERT(test(op, v, count));
assertTest(op, v, count);
}
void MCameraClient::assertDataSize(int32_t msgType, OP op, int dataSize) {
Mutex::Autolock _l(mLock);
int v = mDataSize.valueFor(msgType);
ASSERT(test(op, v, dataSize));
assertTest(op, v, dataSize);
}
void MCameraClient::notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2) {
@@ -250,6 +258,11 @@ void MCameraClient::dataCallback(int32_t msgType, const sp<IMemory>& data) {
}
}
void MCameraClient::dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType,
const sp<IMemory>& data) {
dataCallback(msgType, data);
}
void MCameraClient::waitNotify(int32_t msgType, OP op, int count) {
INFO("waitNotify: %d, %d, %d", msgType, op, count);
Mutex::Autolock _l(mLock);
@@ -348,10 +361,9 @@ void MSurface::waitUntil(int c0, int c1, int c2) {
sp<OverlayRef> MSurface::createOverlay(uint32_t w, uint32_t h, int32_t format,
int32_t orientation) {
// We don't expect this to be called in current hardware.
// Not implemented.
ASSERT(0);
sp<OverlayRef> dummy;
return dummy;
return NULL;
}
//
@@ -395,38 +407,43 @@ sp<ICameraService> getCameraService() {
return cs;
}
int getNumberOfCameras() {
sp<ICameraService> cs = getCameraService();
return cs->getNumberOfCameras();
}
//
// Various Connect Tests
//
void testConnect() {
void testConnect(int cameraId) {
INFO(__func__);
sp<ICameraService> cs = getCameraService();
sp<MCameraClient> cc = new MCameraClient();
sp<ICamera> c = cs->connect(cc);
sp<ICamera> c = cs->connect(cc, cameraId);
ASSERT(c != 0);
c->disconnect();
}
void testAllowConnectOnceOnly() {
void testAllowConnectOnceOnly(int cameraId) {
INFO(__func__);
sp<ICameraService> cs = getCameraService();
// Connect the first client.
sp<MCameraClient> cc = new MCameraClient();
sp<ICamera> c = cs->connect(cc);
sp<ICamera> c = cs->connect(cc, cameraId);
ASSERT(c != 0);
// Same client -- ok.
ASSERT(cs->connect(cc) != 0);
ASSERT(cs->connect(cc, cameraId) != 0);
// Different client -- not ok.
sp<MCameraClient> cc2 = new MCameraClient();
ASSERT(cs->connect(cc2) == 0);
ASSERT(cs->connect(cc2, cameraId) == 0);
c->disconnect();
}
void testReconnectFailed() {
INFO(__func__);
sp<ICamera> c = interface_cast<ICamera>(getTempObject());
sp<MCameraClient> cc2 = new MCameraClient();
ASSERT(c->connect(cc2) != NO_ERROR);
sp<MCameraClient> cc = new MCameraClient();
ASSERT(c->connect(cc) != NO_ERROR);
}
void testReconnectSuccess() {
@@ -434,6 +451,7 @@ void testReconnectSuccess() {
sp<ICamera> c = interface_cast<ICamera>(getTempObject());
sp<MCameraClient> cc = new MCameraClient();
ASSERT(c->connect(cc) == NO_ERROR);
c->disconnect();
}
void testLockFailed() {
@@ -453,6 +471,7 @@ void testLockSuccess() {
INFO(__func__);
sp<ICamera> c = interface_cast<ICamera>(getTempObject());
ASSERT(c->lock() == NO_ERROR);
c->disconnect();
}
//
@@ -499,11 +518,11 @@ void runInAnotherProcess(const char *tag) {
}
}
void testReconnect() {
void testReconnect(int cameraId) {
INFO(__func__);
sp<ICameraService> cs = getCameraService();
sp<MCameraClient> cc = new MCameraClient();
sp<ICamera> c = cs->connect(cc);
sp<ICamera> c = cs->connect(cc, cameraId);
ASSERT(c != 0);
// Reconnect to the same client -- ok.
ASSERT(c->connect(cc) == NO_ERROR);
@@ -514,10 +533,10 @@ void testReconnect() {
cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0);
}
void testLockUnlock() {
void testLockUnlock(int cameraId) {
sp<ICameraService> cs = getCameraService();
sp<MCameraClient> cc = new MCameraClient();
sp<ICamera> c = cs->connect(cc);
sp<ICamera> c = cs->connect(cc, cameraId);
ASSERT(c != 0);
// We can lock as many times as we want.
ASSERT(c->lock() == NO_ERROR);
@@ -530,16 +549,15 @@ void testLockUnlock() {
runInAnotherProcess("testLockUnlockSuccess");
// Unlock then lock from a different process -- ok.
runInAnotherProcess("testLockSuccess");
c->disconnect();
clearTempObject();
}
void testReconnectFromAnotherProcess() {
void testReconnectFromAnotherProcess(int cameraId) {
INFO(__func__);
sp<ICameraService> cs = getCameraService();
sp<MCameraClient> cc = new MCameraClient();
sp<ICamera> c = cs->connect(cc);
sp<ICamera> c = cs->connect(cc, cameraId);
ASSERT(c != 0);
// Reconnect from a different process -- not ok.
putTempObject(c->asBinder());
@@ -547,7 +565,6 @@ void testReconnectFromAnotherProcess() {
// Unlock then reconnect from a different process -- ok.
ASSERT(c->unlock() == NO_ERROR);
runInAnotherProcess("testReconnectSuccess");
c->disconnect();
clearTempObject();
}
@@ -560,10 +577,11 @@ static void flushCommands() {
}
// Run a test case
#define RUN(class_name) do { \
#define RUN(class_name, cameraId) do { \
{ \
INFO(#class_name); \
class_name instance; \
instance.init(cameraId); \
instance.run(); \
} \
flushCommands(); \
@@ -571,19 +589,21 @@ static void flushCommands() {
// Base test case after the the camera is connected.
class AfterConnect {
public:
void init(int cameraId) {
cs = getCameraService();
cc = new MCameraClient();
c = cs->connect(cc, cameraId);
ASSERT(c != 0);
}
protected:
sp<ICameraService> cs;
sp<MCameraClient> cc;
sp<ICamera> c;
AfterConnect() {
cs = getCameraService();
cc = new MCameraClient();
c = cs->connect(cc);
ASSERT(c != 0);
}
~AfterConnect() {
c->disconnect();
c.clear();
cc.clear();
cs.clear();
@@ -612,19 +632,16 @@ public:
surface->waitUntil(1, 10, 0); // needs 1 registerBuffers and 10 postBuffer
surface->clearStat();
c->disconnect();
// TODO: CameraService crashes for this. Fix it.
#if 0
sp<MSurface> another_surface = new MSurface();
c->setPreviewDisplay(another_surface); // just to make sure unregisterBuffers
// is called.
surface->waitUntil(0, 0, 1); // needs unregisterBuffers
#endif
cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0);
}
};
class TestStartPreviewWithoutDisplay : AfterConnect {
class TestStartPreviewWithoutDisplay : public AfterConnect {
public:
void run() {
ASSERT(c->startPreview() == NO_ERROR);
@@ -636,15 +653,17 @@ public:
// Base test case after the the camera is connected and the preview is started.
class AfterStartPreview : public AfterConnect {
protected:
sp<MSurface> surface;
AfterStartPreview() {
public:
void init(int cameraId) {
AfterConnect::init(cameraId);
surface = new MSurface();
ASSERT(c->setPreviewDisplay(surface) == NO_ERROR);
ASSERT(c->startPreview() == NO_ERROR);
}
protected:
sp<MSurface> surface;
~AfterStartPreview() {
surface.clear();
}
@@ -680,9 +699,6 @@ public:
cc->waitData(CAMERA_MSG_RAW_IMAGE, MCameraClient::EQ, 1);
cc->waitData(CAMERA_MSG_COMPRESSED_IMAGE, MCameraClient::EQ, 1);
c->stopPreview();
#if 1 // TODO: It crashes if we don't have this. Fix it.
usleep(100000);
#endif
c->disconnect();
cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0);
}
@@ -697,7 +713,6 @@ public:
cc->waitNotify(CAMERA_MSG_SHUTTER, MCameraClient::EQ, 1);
cc->waitData(CAMERA_MSG_RAW_IMAGE, MCameraClient::EQ, 1);
cc->waitData(CAMERA_MSG_COMPRESSED_IMAGE, MCameraClient::EQ, 1);
usleep(100000); // 100ms
}
c->disconnect();
cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0);
@@ -712,32 +727,67 @@ public:
}
};
static bool getNextSize(const char **ptrS, int *w, int *h) {
const char *s = *ptrS;
// skip over ','
if (*s == ',') s++;
// remember start position in p
const char *p = s;
while (*s != '\0' && *s != 'x') {
s++;
}
if (*s == '\0') return false;
// get the width
*w = atoi(p);
// skip over 'x'
ASSERT(*s == 'x');
p = s + 1;
while (*s != '\0' && *s != ',') {
s++;
}
// get the height
*h = atoi(p);
*ptrS = s;
return true;
}
class TestPictureSize : public AfterStartPreview {
public:
void checkOnePicture(int w, int h) {
const float rate = 0.5; // byte per pixel limit
const float rate = 0.9; // byte per pixel limit
int pixels = w * h;
CameraParameters param(c->getParameters());
param.setPictureSize(w, h);
// disable thumbnail to get more accurate size.
param.set(CameraParameters::KEY_JPEG_THUMBNAIL_WIDTH, 0);
param.set(CameraParameters::KEY_JPEG_THUMBNAIL_HEIGHT, 0);
c->setParameters(param.flatten());
cc->clearStat();
ASSERT(c->takePicture() == NO_ERROR);
cc->waitData(CAMERA_MSG_RAW_IMAGE, MCameraClient::EQ, 1);
cc->assertDataSize(CAMERA_MSG_RAW_IMAGE, MCameraClient::EQ, pixels*3/2);
//cc->assertDataSize(CAMERA_MSG_RAW_IMAGE, MCameraClient::EQ, pixels*3/2);
cc->waitData(CAMERA_MSG_COMPRESSED_IMAGE, MCameraClient::EQ, 1);
cc->assertDataSize(CAMERA_MSG_COMPRESSED_IMAGE, MCameraClient::LT,
int(pixels * rate));
cc->assertDataSize(CAMERA_MSG_COMPRESSED_IMAGE, MCameraClient::GT, 0);
cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0);
usleep(100000); // 100ms
}
void run() {
checkOnePicture(2048, 1536);
checkOnePicture(1600, 1200);
checkOnePicture(1024, 768);
CameraParameters param(c->getParameters());
int w, h;
const char *s = param.get(CameraParameters::KEY_SUPPORTED_PICTURE_SIZES);
while (getNextSize(&s, &w, &h)) {
LOGD("checking picture size %dx%d", w, h);
checkOnePicture(w, h);
}
}
};
@@ -749,6 +799,8 @@ public:
// Try all flag combinations.
for (int v = 0; v < 8; v++) {
LOGD("TestPreviewCallbackFlag: flag=%d", v);
usleep(100000); // sleep a while to clear the in-flight callbacks.
cc->clearStat();
c->setPreviewCallbackFlag(v);
ASSERT(c->previewEnabled() == false);
@@ -781,6 +833,7 @@ public:
ASSERT(c->recordingEnabled() == true);
sleep(2);
c->stopRecording();
usleep(100000); // sleep a while to clear the in-flight callbacks.
cc->setReleaser(NULL);
cc->assertData(CAMERA_MSG_VIDEO_FRAME, MCameraClient::GE, 10);
}
@@ -806,9 +859,13 @@ public:
}
void run() {
checkOnePicture(480, 320);
checkOnePicture(352, 288);
checkOnePicture(176, 144);
CameraParameters param(c->getParameters());
int w, h;
const char *s = param.get(CameraParameters::KEY_SUPPORTED_PREVIEW_SIZES);
while (getNextSize(&s, &w, &h)) {
LOGD("checking preview size %dx%d", w, h);
checkOnePicture(w, h);
}
}
};
@@ -827,23 +884,30 @@ int main(int argc, char **argv)
INFO("CameraServiceTest start");
gExecutable = argv[0];
runHolderService();
int n = getNumberOfCameras();
INFO("%d Cameras available", n);
testConnect(); flushCommands();
testAllowConnectOnceOnly(); flushCommands();
testReconnect(); flushCommands();
testLockUnlock(); flushCommands();
testReconnectFromAnotherProcess(); flushCommands();
for (int id = 0; id < n; id++) {
INFO("Testing camera %d", id);
testConnect(id); flushCommands();
testAllowConnectOnceOnly(id); flushCommands();
testReconnect(id); flushCommands();
testLockUnlock(id); flushCommands();
testReconnectFromAnotherProcess(id); flushCommands();
RUN(TestSetPreviewDisplay);
RUN(TestStartPreview);
RUN(TestStartPreviewWithoutDisplay);
RUN(TestAutoFocus);
RUN(TestStopPreview);
RUN(TestTakePicture);
RUN(TestTakeMultiplePictures);
RUN(TestGetParameters);
RUN(TestPictureSize);
RUN(TestPreviewCallbackFlag);
RUN(TestRecording);
RUN(TestPreviewSize);
RUN(TestSetPreviewDisplay, id);
RUN(TestStartPreview, id);
RUN(TestStartPreviewWithoutDisplay, id);
RUN(TestAutoFocus, id);
RUN(TestStopPreview, id);
RUN(TestTakePicture, id);
RUN(TestTakeMultiplePictures, id);
RUN(TestGetParameters, id);
RUN(TestPictureSize, id);
RUN(TestPreviewCallbackFlag, id);
RUN(TestRecording, id);
RUN(TestPreviewSize, id);
}
INFO("CameraServiceTest finished");
}

View File

@@ -84,13 +84,29 @@ public class Camera {
private boolean mWithBuffer;
/**
* Returns a new Camera object.
* Returns the number of Cameras available.
* @hide
*/
public static Camera open() {
return new Camera();
public native static int getNumberOfCameras();
/**
* Returns a new Camera object.
* If {@link #getNumberOfCameras()} returns N, the valid is is 0 to N-1.
* The id 0 is the default camera.
* @hide
*/
public static Camera open(int cameraId) {
return new Camera(cameraId);
}
Camera() {
/**
* Returns a new Camera object. This returns the default camera.
*/
public static Camera open() {
return new Camera(0);
}
Camera(int cameraId) {
mShutterCallback = null;
mRawImageCallback = null;
mJpegCallback = null;
@@ -107,14 +123,14 @@ public class Camera {
mEventHandler = null;
}
native_setup(new WeakReference<Camera>(this));
native_setup(new WeakReference<Camera>(this), cameraId);
}
protected void finalize() {
native_release();
}
private native final void native_setup(Object camera_this);
private native final void native_setup(Object camera_this, int cameraId);
private native final void native_release();

View File

@@ -288,10 +288,16 @@ void JNICameraContext::clearCallbackBuffers_l(JNIEnv *env)
}
}
// connect to camera service
static void android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
static jint android_hardware_Camera_getNumberOfCameras(JNIEnv *env, jobject thiz)
{
sp<Camera> camera = Camera::connect();
return Camera::getNumberOfCameras();
}
// connect to camera service
static void android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz,
jobject weak_this, jint cameraId)
{
sp<Camera> camera = Camera::connect(cameraId);
if (camera == NULL) {
jniThrowException(env, "java/lang/RuntimeException",
@@ -566,8 +572,11 @@ static void android_hardware_Camera_setDisplayOrientation(JNIEnv *env, jobject t
//-------------------------------------------------
static JNINativeMethod camMethods[] = {
{ "getNumberOfCameras",
"()I",
(void *)android_hardware_Camera_getNumberOfCameras },
{ "native_setup",
"(Ljava/lang/Object;)V",
"(Ljava/lang/Object;I)V",
(void*)android_hardware_Camera_native_setup },
{ "native_release",
"()V",

View File

@@ -113,7 +113,8 @@ class Camera : public BnCameraClient, public IBinder::DeathRecipient
public:
// construct a camera client from an existing remote
static sp<Camera> create(const sp<ICamera>& camera);
static sp<Camera> connect();
static int32_t getNumberOfCameras();
static sp<Camera> connect(int cameraId);
~Camera();
void init();

View File

@@ -30,13 +30,16 @@ class ICameraService : public IInterface
{
public:
enum {
CONNECT = IBinder::FIRST_CALL_TRANSACTION,
GET_NUMBER_OF_CAMERAS = IBinder::FIRST_CALL_TRANSACTION,
CONNECT
};
public:
DECLARE_META_INTERFACE(CameraService);
virtual sp<ICamera> connect(const sp<ICameraClient>& cameraClient) = 0;
virtual int32_t getNumberOfCameras() = 0;
virtual sp<ICamera> connect(const sp<ICameraClient>& cameraClient,
int cameraId) = 0;
};
// ----------------------------------------------------------------------------

View File

@@ -95,13 +95,20 @@ Camera::~Camera()
disconnect();
}
sp<Camera> Camera::connect()
int32_t Camera::getNumberOfCameras()
{
const sp<ICameraService>& cs = getCameraService();
if (cs == 0) return 0;
return cs->getNumberOfCameras();
}
sp<Camera> Camera::connect(int cameraId)
{
LOGV("connect");
sp<Camera> c = new Camera();
const sp<ICameraService>& cs = getCameraService();
if (cs != 0) {
c->mCamera = cs->connect(c);
c->mCamera = cs->connect(c, cameraId);
}
if (c->mCamera != 0) {
c->mCamera->asBinder()->linkToDeath(c);

View File

@@ -34,12 +34,22 @@ public:
{
}
// get number of cameras available
virtual int32_t getNumberOfCameras()
{
Parcel data, reply;
data.writeInterfaceToken(ICameraService::getInterfaceDescriptor());
remote()->transact(BnCameraService::GET_NUMBER_OF_CAMERAS, data, &reply);
return reply.readInt32();
}
// connect to camera service
virtual sp<ICamera> connect(const sp<ICameraClient>& cameraClient)
virtual sp<ICamera> connect(const sp<ICameraClient>& cameraClient, int cameraId)
{
Parcel data, reply;
data.writeInterfaceToken(ICameraService::getInterfaceDescriptor());
data.writeStrongBinder(cameraClient->asBinder());
data.writeInt32(cameraId);
remote()->transact(BnCameraService::CONNECT, data, &reply);
return interface_cast<ICamera>(reply.readStrongBinder());
}
@@ -53,10 +63,15 @@ status_t BnCameraService::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
switch(code) {
case GET_NUMBER_OF_CAMERAS: {
CHECK_INTERFACE(ICameraService, data, reply);
reply->writeInt32(getNumberOfCameras());
return NO_ERROR;
} break;
case CONNECT: {
CHECK_INTERFACE(ICameraService, data, reply);
sp<ICameraClient> cameraClient = interface_cast<ICameraClient>(data.readStrongBinder());
sp<ICamera> camera = connect(cameraClient);
sp<ICamera> camera = connect(cameraClient, data.readInt32());
reply->writeStrongBinder(camera->asBinder());
return NO_ERROR;
} break;

View File

@@ -106,7 +106,7 @@ void CameraSourceListener::postDataTimestamp(
// static
CameraSource *CameraSource::Create() {
sp<Camera> camera = Camera::connect();
sp<Camera> camera = Camera::connect(0);
if (camera.get() == NULL) {
return NULL;