/*
**
** Copyright 2008, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
#define LOG_TAG "CameraHardwareStub"
#include <utils/Log.h>
#include "CameraHardwareStub.h"
#include <utils/threads.h>
#include <fcntl.h>
#include <sys/mman.h>
#include "CannedJpeg.h"
namespace android {
CameraHardwareStub::CameraHardwareStub()
: mParameters(),
mPreviewHeap(0),
mRawHeap(0),
mFakeCamera(0),
mPreviewFrameSize(0),
mNotifyCb(0),
mDataCb(0),
mDataCbTimestamp(0),
mCallbackCookie(0),
mMsgEnabled(0),
mCurrentPreviewFrame(0)
{
initDefaultParameters();
}
void CameraHardwareStub::initDefaultParameters()
{
CameraParameters p;
p.set(CameraParameters::KEY_SUPPORTED_PREVIEW_SIZES, "320x240");
p.setPreviewSize(320, 240);
p.setPreviewFrameRate(15);
p.setPreviewFormat(CameraParameters::PIXEL_FORMAT_YUV420SP);
p.set(CameraParameters::KEY_SUPPORTED_PICTURE_SIZES, "320x240");
p.setPictureSize(320, 240);
p.setPictureFormat(CameraParameters::PIXEL_FORMAT_JPEG);
if (setParameters(p) != NO_ERROR) {
ALOGE("Failed to set default parameters?!");
}
}
void CameraHardwareStub::initHeapLocked()
{
// Create raw heap.
int picture_width, picture_height;
mParameters.getPictureSize(&picture_width, &picture_height);
mRawHeap = new MemoryHeapBase(picture_width * picture_height * 3 / 2);
int preview_width, preview_height;
mParameters.getPreviewSize(&preview_width, &preview_height);
ALOGD("initHeapLocked: preview size=%dx%d", preview_width, preview_height);
// 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.
if (how_big == mPreviewFrameSize)
return;
mPreviewFrameSize = how_big;
// Make a new mmap'ed heap that can be shared across processes.
// use code below to test with pmem
mPreviewHeap = new MemoryHeapBase(mPreviewFrameSize * kBufferCount);
// Make an IMemory for each frame so that we can reuse them in callbacks.
for (int i = 0; i < kBufferCount; i++) {
mBuffers[i] = new MemoryBase(mPreviewHeap, i * mPreviewFrameSize, mPreviewFrameSize);
}
// Recreate the fake camera to reflect the current size.
delete mFakeCamera;
mFakeCamera = new FakeCamera(preview_width, preview_height);
}
CameraHardwareStub::~CameraHardwareStub()
{
delete mFakeCamera;
mFakeCamera = 0; // paranoia
}
status_t CameraHardwareStub::setPreviewWindow(const sp<ANativeWindow>& buf)
{
return NO_ERROR;
}
sp<IMemoryHeap> CameraHardwareStub::getRawHeap() const
{
return mRawHeap;
}
void CameraHardwareStub::setCallbacks(notify_callback notify_cb,
data_callback data_cb,
data_callback_timestamp data_cb_timestamp,
void* user)
{
Mutex::Autolock lock(mLock);
mNotifyCb = notify_cb;
mDataCb = data_cb;
mDataCbTimestamp = data_cb_timestamp;
mCallbackCookie = user;
}
void CameraHardwareStub::enableMsgType(int32_t msgType)
{
Mutex::Autolock lock(mLock);
mMsgEnabled |= msgType;
}
void CameraHardwareStub::disableMsgType(int32_t msgType)
{
Mutex::Autolock lock(mLock);
mMsgEnabled &= ~msgType;
}
bool CameraHardwareStub::msgTypeEnabled(int32_t msgType)
{
Mutex::Autolock lock(mLock);
return (mMsgEnabled & msgType);
}
// ---------------------------------------------------------------------------
int CameraHardwareStub::previewThread()
{
mLock.lock();
// the attributes below can change under our feet...
int previewFrameRate = mParameters.getPreviewFrameRate();
// Find the offset within the heap of the current buffer.
ssize_t offset = mCurrentPreviewFrame * mPreviewFrameSize;
sp<MemoryHeapBase> heap = mPreviewHeap;
// this assumes the internal state of fake camera doesn't change
// (or is thread safe)
FakeCamera* fakeCamera = mFakeCamera;
sp<MemoryBase> buffer = mBuffers[mCurrentPreviewFrame];
mLock.unlock();
// TODO: here check all the conditions that could go wrong
if (buffer != 0) {
// Calculate how long to wait between frames.
int delay = (int)(1000000.0f / float(previewFrameRate));
// This is always valid, even if the client died -- the memory
// is still mapped in our process.
void *base = heap->base();
// Fill the current frame with the fake camera.
uint8_t *frame = ((uint8_t *)base) + offset;
fakeCamera->getNextFrameAsYuv420(frame);
//ALOGV("previewThread: generated frame to buffer %d", mCurrentPreviewFrame);
// Notify the client of a new frame.
if (mMsgEnabled & CAMERA_MSG_PREVIEW_FRAME)
mDataCb(CAMERA_MSG_PREVIEW_FRAME, buffer, NULL, mCallbackCookie);
// Advance the buffer pointer.
mCurrentPreviewFrame = (mCurrentPreviewFrame + 1) % kBufferCount;
// Wait for it...
usleep(delay);
}
return NO_ERROR;
}
status_t CameraHardwareStub::startPreview()
{
Mutex::Autolock lock(mLock);
if (mPreviewThread != 0) {
// already running
return INVALID_OPERATION;
}
mPreviewThread = new PreviewThread(this);
return NO_ERROR;
}
void CameraHardwareStub::stopPreview()
{
sp<PreviewThread> previewThread;
{ // scope for the lock
Mutex::Autolock lock(mLock);
previewThread = mPreviewThread;
}
// don't hold the lock while waiting for the thread to quit
if (previewThread != 0) {
previewThread->requestExitAndWait();
}
Mutex::Autolock lock(mLock);
mPreviewThread.clear();
}
bool CameraHardwareStub::previewEnabled() {
return mPreviewThread != 0;
}
status_t CameraHardwareStub::startRecording()
{
return UNKNOWN_ERROR;
}
void CameraHardwareStub::stopRecording()
{
}
bool CameraHardwareStub::recordingEnabled()
{
return false;
}
void CameraHardwareStub::releaseRecordingFrame(const sp<IMemory>& mem)
{
}
// ---------------------------------------------------------------------------
int CameraHardwareStub::beginAutoFocusThread(void *cookie)
{
CameraHardwareStub *c = (CameraHardwareStub *)cookie;
return c->autoFocusThread();
}
int CameraHardwareStub::autoFocusThread()
{
if (mMsgEnabled & CAMERA_MSG_FOCUS)
mNotifyCb(CAMERA_MSG_FOCUS, true, 0, mCallbackCookie);
return NO_ERROR;
}
status_t CameraHardwareStub::autoFocus()
{
Mutex::Autolock lock(mLock);
if (createThread(beginAutoFocusThread, this) == false)
return UNKNOWN_ERROR;
return NO_ERROR;
}
status_t CameraHardwareStub::cancelAutoFocus()
{
return NO_ERROR;
}
/*static*/ int CameraHardwareStub::beginPictureThread(void *cookie)
{
CameraHardwareStub *c = (CameraHardwareStub *)cookie;
return c->pictureThread();
}
int CameraHardwareStub::pictureThread()
{
if (mMsgEnabled & CAMERA_MSG_SHUTTER)
mNotifyCb(CAMERA_MSG_SHUTTER, 0, 0, mCallbackCookie);
if (mMsgEnabled & CAMERA_MSG_RAW_IMAGE) {
//FIXME: use a canned YUV image!
// In the meantime just make another fake camera picture.
int w, h;
mParameters.getPictureSize(&w, &h);
sp<MemoryBase> mem = new MemoryBase(mRawHeap, 0, w * h * 3 / 2);
FakeCamera cam(w, h);
cam.getNextFrameAsYuv420((uint8_t *)mRawHeap->base());
mDataCb(CAMERA_MSG_RAW_IMAGE, mem, NULL, mCallbackCookie);
}
if (mMsgEnabled & CAMERA_MSG_COMPRESSED_IMAGE) {
sp<MemoryHeapBase> heap = new MemoryHeapBase(kCannedJpegSize);
sp<MemoryBase> mem = new MemoryBase(heap, 0, kCannedJpegSize);
memcpy(heap->base(), kCannedJpeg, kCannedJpegSize);
mDataCb(CAMERA_MSG_COMPRESSED_IMAGE, mem, NULL, mCallbackCookie);
}
return NO_ERROR;
}
status_t CameraHardwareStub::takePicture()
{
stopPreview();
if (createThread(beginPictureThread, this) == false)
return UNKNOWN_ERROR;
return NO_ERROR;
}
status_t CameraHardwareStub::cancelPicture()
{
return NO_ERROR;
}
status_t CameraHardwareStub::dump(int fd, const Vector<String16>& args) const
{
const size_t SIZE = 256;
char buffer[SIZE];
String8 result;
AutoMutex lock(&mLock);
if (mFakeCamera != 0) {
mFakeCamera->dump(fd);
mParameters.dump(fd, args);
snprintf(buffer, 255, " preview frame(%d), size (%d), running(%s)\n", mCurrentPreviewFrame, mPreviewFrameSize, mPreviewRunning?"true": "false");
result.append(buffer);
} else {
result.append("No camera client yet.\n");
}
write(fd, result.string(), result.size());
return NO_ERROR;
}
status_t CameraHardwareStub::setParameters(const CameraParameters& params)
{
Mutex::Autolock lock(mLock);
// XXX verify params
if (strcmp(params.getPreviewFormat(),
CameraParameters::PIXEL_FORMAT_YUV420SP) != 0) {
ALOGE("Only yuv420sp preview is supported");
return -1;
}
if (strcmp(params.getPictureFormat(),
CameraParameters::PIXEL_FORMAT_JPEG) != 0) {
ALOGE("Only jpeg still pictures are supported");
return -1;
}
int w, h;
params.getPictureSize(&w, &h);
if (w != kCannedJpegWidth && h != kCannedJpegHeight) {
ALOGE("Still picture size must be size of canned JPEG (%dx%d)",
kCannedJpegWidth, kCannedJpegHeight);
return -1;
}
mParameters = params;
initHeapLocked();
return NO_ERROR;
}
CameraParameters CameraHardwareStub::getParameters() const
{
Mutex::Autolock lock(mLock);
return mParameters;
}
status_t CameraHardwareStub::sendCommand(int32_t command, int32_t arg1,
int32_t arg2)
{
return BAD_VALUE;
}
void CameraHardwareStub::release()
{
}
sp<CameraHardwareInterface> CameraHardwareStub::createInstance()
{
return new CameraHardwareStub();
}
static CameraInfo sCameraInfo[] = {
{
CAMERA_FACING_BACK,
90, /* orientation */
}
};
extern "C" int HAL_getNumberOfCameras()
{
return sizeof(sCameraInfo) / sizeof(sCameraInfo[0]);
}
extern "C" void HAL_getCameraInfo(int cameraId, struct CameraInfo* cameraInfo)
{
memcpy(cameraInfo, &sCameraInfo[cameraId], sizeof(CameraInfo));
}
extern "C" sp<CameraHardwareInterface> HAL_openCameraHardware(int cameraId)
{
return CameraHardwareStub::createInstance();
}
}; // namespace android