blob: 510464a628d5f350be8c828e025fbd3fb3e10b44 [file] [log] [blame]
/*
**
** Copyright 2009, The Android-x86 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.
**
** Author: Niels Keeman <nielskeeman@gmail.com>
**
*/
#define LOG_TAG "CameraHardware"
#include <utils/Log.h>
#include "CameraHardware.h"
#include <fcntl.h>
#include <sys/mman.h>
#include <cutils/native_handle.h>
#include <hal_public.h>
#include <ui/GraphicBufferMapper.h>
#include <gui/ISurfaceTexture.h>
#define MAX_VIDEONODES 20
#define MIN_WIDTH 320
#define MIN_HEIGHT 240
#define CAM_SIZE "320x240"
#define PIXEL_FORMAT V4L2_PIX_FMT_YUYV
#define CAMHAL_GRALLOC_USAGE GRALLOC_USAGE_HW_TEXTURE | \
GRALLOC_USAGE_HW_RENDER | \
GRALLOC_USAGE_SW_READ_RARELY | \
GRALLOC_USAGE_SW_WRITE_NEVER
extern "C" {
void yuyv422_to_yuv420sp(unsigned char*,unsigned char*,int,int);
void convertYUYVtoRGB565(unsigned char *buf, unsigned char *rgb, int width, int height);
}
namespace android {
const char supportedFpsRanges [] = "(8000,8000),(8000,10000),(10000,10000),(8000,15000),(15000,15000),(8000,20000),(20000,20000),(24000,24000),(25000,25000),(8000,30000),(30000,30000)";
CameraHardware::CameraHardware(int cameraId)
: mCameraId(cameraId),
mParameters(),
mHeap(0),
mPreviewHeap(0),
mRecordHeap(0),
mRawHeap(0),
mPreviewFrameSize(0),
mCurrentPreviewFrame(0),
mRecordRunning(false),
previewStopped(true),
nQueued(0),
nDequeued(0),
mNotifyFn(NULL),
mDataFn(NULL),
mTimestampFn(NULL),
mUser(NULL),
mMsgEnabled(0)
{
initDefaultParameters();
mNativeWindow=NULL;
}
void CameraHardware::initDefaultParameters()
{
CameraParameters p;
p.setPreviewSize(MIN_WIDTH, MIN_HEIGHT);
p.setPreviewFrameRate(30);
p.setPreviewFormat("yuv422sp");
p.set(p.KEY_SUPPORTED_PREVIEW_SIZES, CAM_SIZE);
p.set(p.KEY_SUPPORTED_PREVIEW_SIZES, "640x480");
p.set(CameraParameters::KEY_VIDEO_FRAME_FORMAT,CameraParameters::PIXEL_FORMAT_YUV420SP);
p.set(CameraParameters::KEY_FOCUS_MODE,0);
p.setPictureSize(MIN_WIDTH, MIN_HEIGHT);
p.setPictureFormat("jpeg");
p.set(p.KEY_SUPPORTED_PICTURE_SIZES, CAM_SIZE);
if (setParameters(p) != NO_ERROR) {
ALOGE("Failed to set default parameters?!");
}
}
CameraHardware::~CameraHardware()
{
}
sp<IMemoryHeap> CameraHardware::getPreviewHeap() const
{
return mHeap;
}
sp<IMemoryHeap> CameraHardware::getRawHeap() const
{
return mRawHeap;
}
// ---------------------------------------------------------------------------
void CameraHardware::setCallbacks(camera_notify_callback notify_cb,
camera_data_callback data_cb,
camera_data_timestamp_callback data_cb_timestamp,
camera_request_memory get_memory,
void *arg)
{
Mutex::Autolock lock(mLock);
mNotifyFn = notify_cb;
mDataFn = data_cb;
mRequestMemory = get_memory;
mTimestampFn = data_cb_timestamp;
mUser = arg;
}
int CameraHardware::setPreviewWindow( preview_stream_ops_t *window)
{
int err;
Mutex::Autolock lock(mLock);
if(mNativeWindow)
mNativeWindow=NULL;
if(window==NULL)
{
ALOGW("Window is Null");
return 0;
}
int width, height;
mParameters.getPreviewSize(&width, &height);
mNativeWindow=window;
mNativeWindow->set_usage(mNativeWindow,CAMHAL_GRALLOC_USAGE);
mNativeWindow->set_buffers_geometry(
mNativeWindow,
width,
height,
HAL_PIXEL_FORMAT_RGB_565);
err = mNativeWindow->set_buffer_count(mNativeWindow, 3);
if (err != 0) {
ALOGE("native_window_set_buffer_count failed: %s (%d)", strerror(-err), -err);
if ( ENODEV == err ) {
ALOGE("Preview surface abandoned!");
mNativeWindow = NULL;
}
}
return 0;
}
void CameraHardware::enableMsgType(int32_t msgType)
{
Mutex::Autolock lock(mLock);
mMsgEnabled |= msgType;
}
void CameraHardware::disableMsgType(int32_t msgType)
{
Mutex::Autolock lock(mLock);
mMsgEnabled &= ~msgType;
}
bool CameraHardware::msgTypeEnabled(int32_t msgType)
{
Mutex::Autolock lock(mLock);
return (mMsgEnabled & msgType);
}
//-------------------------------------------------------------
int CameraHardware::previewThread()
{
int width, height;
int err;
IMG_native_handle_t** hndl2hndl;
IMG_native_handle_t* handle;
int stride;
mParameters.getPreviewSize(&width, &height);
int framesize= width * height * 1.5 ; //yuv420sp
if (!previewStopped) {
mLock.lock();
if (mNativeWindow != NULL)
{
if ((err = mNativeWindow->dequeue_buffer(mNativeWindow,(buffer_handle_t**) &hndl2hndl,&stride)) != 0) {
ALOGW("Surface::dequeueBuffer returned error %d", err);
return -1;
}
mNativeWindow->lock_buffer(mNativeWindow, (buffer_handle_t*) hndl2hndl);
GraphicBufferMapper &mapper = GraphicBufferMapper::get();
Rect bounds(width, height);
void *tempbuf;
void *dst;
if(0 == mapper.lock((buffer_handle_t)*hndl2hndl,CAMHAL_GRALLOC_USAGE, bounds, &dst));
{
// Get preview frame
tempbuf=camera.GrabPreviewFrame();
convertYUYVtoRGB565((unsigned char *)tempbuf,(unsigned char *)dst, width, height);
mapper.unlock((buffer_handle_t)*hndl2hndl);
mNativeWindow->enqueue_buffer(mNativeWindow,(buffer_handle_t*) hndl2hndl);
if ((mMsgEnabled & CAMERA_MSG_PREVIEW_FRAME) ||
(mMsgEnabled & CAMERA_MSG_VIDEO_FRAME)) {
camera_memory_t* picture = mRequestMemory(-1, framesize, 1, NULL);
yuyv422_to_yuv420sp((unsigned char *)tempbuf,(unsigned char *) picture->data, width, height);
if ((mMsgEnabled & CAMERA_MSG_VIDEO_FRAME ) && mRecordRunning ) {
nsecs_t timeStamp = systemTime(SYSTEM_TIME_MONOTONIC);
//mTimestampFn(timeStamp, CAMERA_MSG_VIDEO_FRAME,mRecordBuffer, mUser);
}
mDataFn(CAMERA_MSG_PREVIEW_FRAME,picture,0,NULL,mUser);
picture->release(picture);
}
camera.ReleasePreviewFrame();
}
}
mLock.unlock();
}
return NO_ERROR;
}
status_t CameraHardware::startPreview()
{
int ret;
int width, height;
int i;
IMG_native_handle_t** hndl2hndl;
IMG_native_handle_t* handle;
int stride;
char devnode[15];
Mutex::Autolock lock(mLock);
if (mPreviewThread != 0) {
//already running
return INVALID_OPERATION;
}
#if 1
ALOGI("startPreview: in startpreview \n");
mParameters.getPreviewSize(&width, &height);
for( i=MAX_VIDEONODES; i>=0; i--) {
sprintf(devnode,"/dev/video%d",i);
ALOGI("trying the node %s width=%d height=%d \n",devnode,width,height);
ret = camera.Open(devnode, width, height, PIXEL_FORMAT);
if( ret >= 0)
break;
}
if( ret < 0)
return -1;
mPreviewFrameSize = width * height * 2;
mHeap = new MemoryHeapBase(mPreviewFrameSize);
mBuffer = new MemoryBase(mHeap, 0, mPreviewFrameSize);
ret = camera.Init();
if (ret != 0) {
ALOGI("startPreview: Camera.Init failed\n");
camera.Close();
return ret;
}
ret = camera.StartStreaming();
if (ret != 0) {
ALOGI("startPreview: Camera.StartStreaming failed\n");
camera.Uninit();
camera.Close();
return ret;
}
previewStopped = false;
mPreviewThread = new PreviewThread(this);
#endif
return NO_ERROR;
}
void CameraHardware::stopPreview()
{
sp<PreviewThread> previewThread;
{ // scope for the lock
Mutex::Autolock lock(mLock);
previewStopped = true;
}
{
Mutex::Autolock lock(mLock);
previewThread = mPreviewThread;
}
if (previewThread != 0) {
previewThread->requestExitAndWait();
}
if (mPreviewThread != 0) {
camera.Uninit();
camera.StopStreaming();
camera.Close();
}
Mutex::Autolock lock(mLock);
mPreviewThread.clear();
}
bool CameraHardware::previewEnabled()
{
Mutex::Autolock lock(mLock);
return ((mPreviewThread != 0) );
}
status_t CameraHardware::startRecording()
{
Mutex::Autolock lock(mLock);
mRecordHeap = new MemoryHeapBase(mPreviewFrameSize*3/4);
mRecordBuffer = new MemoryBase(mRecordHeap, 0, mPreviewFrameSize*3/4);
mRecordRunning = true;
return NO_ERROR;
}
void CameraHardware::stopRecording()
{
mRecordRunning = false;
}
bool CameraHardware::recordingEnabled()
{
return mRecordRunning;
}
void CameraHardware::releaseRecordingFrame(const void *opaque)
{
}
// ---------------------------------------------------------------------------
int CameraHardware::beginAutoFocusThread(void *cookie)
{
CameraHardware *c = (CameraHardware *)cookie;
return c->autoFocusThread();
}
int CameraHardware::autoFocusThread()
{
if (mMsgEnabled & CAMERA_MSG_FOCUS)
mNotifyFn(CAMERA_MSG_FOCUS, true, 0, mUser);
return NO_ERROR;
}
status_t CameraHardware::autoFocus()
{
Mutex::Autolock lock(mLock);
if (createThread(beginAutoFocusThread, this) == false)
return UNKNOWN_ERROR;
return NO_ERROR;
}
status_t CameraHardware::cancelAutoFocus()
{
return NO_ERROR;
}
/*static*/ int CameraHardware::beginPictureThread(void *cookie)
{
CameraHardware *c = (CameraHardware *)cookie;
return c->pictureThread();
}
int CameraHardware::pictureThread()
{
unsigned char *frame;
int bufferSize;
int w,h;
int ret;
struct v4l2_buffer buffer;
struct v4l2_format format;
struct v4l2_buffer cfilledbuffer;
struct v4l2_requestbuffers creqbuf;
struct v4l2_capability cap;
int i;
char devnode[15];
camera_memory_t* picture = NULL;
if (mMsgEnabled & CAMERA_MSG_SHUTTER)
mNotifyFn(CAMERA_MSG_SHUTTER, 0, 0, mUser);
mParameters.getPictureSize(&w, &h);
ALOGD("Picture Size: Width = %d \t Height = %d", w, h);
int width, height;
mParameters.getPictureSize(&width, &height);
mParameters.getPreviewSize(&width, &height);
for(i=MAX_VIDEONODES; i>=0; i--) {
sprintf(devnode,"/dev/video%d",i);
ALOGI("trying the node %s \n",devnode);
ret = camera.Open(devnode, width, height, PIXEL_FORMAT);
if( ret >= 0)
break;
}
if( ret < 0)
return -1;
camera.Init();
camera.StartStreaming();
//TODO xxx : Optimize the memory capture call. Too many memcpy
if (mMsgEnabled & CAMERA_MSG_COMPRESSED_IMAGE) {
ALOGD ("mJpegPictureCallback");
picture = camera.GrabJpegFrame(mRequestMemory);
mDataFn(CAMERA_MSG_COMPRESSED_IMAGE,picture,0,NULL ,mUser);
}
camera.Uninit();
camera.StopStreaming();
camera.Close();
return NO_ERROR;
}
status_t CameraHardware::takePicture()
{
ALOGD ("takepicture");
stopPreview();
pictureThread();
return NO_ERROR;
}
status_t CameraHardware::cancelPicture()
{
return NO_ERROR;
}
status_t CameraHardware::dump(int fd, const Vector<String16>& args) const
{
return NO_ERROR;
}
status_t CameraHardware::setParameters(const CameraParameters& params)
{
Mutex::Autolock lock(mLock);
if (strcmp(params.getPictureFormat(), "jpeg") != 0) {
ALOGE("Only jpeg still pictures are supported");
return -1;
}
int w, h;
int framerate;
mParameters = params;
params.getPictureSize(&w, &h);
mParameters.setPictureSize(w,h);
params.getPreviewSize(&w, &h);
mParameters.setPreviewSize(w,h);
framerate = params.getPreviewFrameRate();
ALOGD("PREVIEW SIZE: w=%d h=%d framerate=%d", w, h, framerate);
mParameters = params;
mParameters.setPreviewSize(w,h);
mParameters.set(CameraParameters::KEY_SUPPORTED_PREVIEW_FPS_RANGE, supportedFpsRanges);
mParameters.set(CameraParameters::KEY_SUPPORTED_PREVIEW_SIZES, "320x240,352x288,640x480,720x480,720x576,848x480");
return NO_ERROR;
}
status_t CameraHardware::sendCommand(int32_t command, int32_t arg1, int32_t arg2)
{
return BAD_VALUE;
}
CameraParameters CameraHardware::getParameters() const
{
CameraParameters params;
Mutex::Autolock lock(mLock);
params = mParameters;
return params;
}
void CameraHardware::release()
{
close(camera_device);
}
}; // namespace android