blob: 3fb33808a6f73e23b92f4eced339231ef5bff0f4 [file] [log] [blame]
/*
* Copyright (C) Texas Instruments - http://www.ti.com/
*
* 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.
*/
/**
* @file V4LCameraAdapter.cpp
*
* This file maps the Camera Hardware Interface to V4L2.
*
*/
#include "V4LCameraAdapter.h"
#include "CameraHal.h"
#include "TICameraParameters.h"
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/select.h>
#include <linux/videodev.h>
#include <cutils/properties.h>
#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false ))
static int mDebugFps = 0;
#define Q16_OFFSET 16
#define HERE(Msg) {CAMHAL_LOGEB("--===line %d, %s===--\n", __LINE__, Msg);}
namespace android {
#undef LOG_TAG
///Maintain a separate tag for V4LCameraAdapter logs to isolate issues OMX specific
#define LOG_TAG "CameraHAL"
//frames skipped before recalculating the framerate
#define FPS_PERIOD 30
Mutex gAdapterLock;
const char *device = DEVICE;
/*--------------------Camera Adapter Class STARTS here-----------------------------*/
status_t V4LCameraAdapter::initialize(CameraProperties::Properties* caps)
{
LOG_FUNCTION_NAME;
char value[PROPERTY_VALUE_MAX];
property_get("debug.camera.showfps", value, "0");
mDebugFps = atoi(value);
int ret = NO_ERROR;
// Allocate memory for video info structure
mVideoInfo = (struct VideoInfo *) calloc (1, sizeof (struct VideoInfo));
if(!mVideoInfo)
{
return NO_MEMORY;
}
if ((mCameraHandle = open(device, O_RDWR)) == -1)
{
CAMHAL_LOGEB("Error while opening handle to V4L2 Camera: %s", strerror(errno));
return -EINVAL;
}
ret = ioctl (mCameraHandle, VIDIOC_QUERYCAP, &mVideoInfo->cap);
if (ret < 0)
{
CAMHAL_LOGEA("Error when querying the capabilities of the V4L Camera");
return -EINVAL;
}
if ((mVideoInfo->cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == 0)
{
CAMHAL_LOGEA("Error while adapter initialization: video capture not supported.");
return -EINVAL;
}
if (!(mVideoInfo->cap.capabilities & V4L2_CAP_STREAMING))
{
CAMHAL_LOGEA("Error while adapter initialization: Capture device does not support streaming i/o");
return -EINVAL;
}
// Initialize flags
mPreviewing = false;
mVideoInfo->isStreaming = false;
mRecording = false;
LOG_FUNCTION_NAME_EXIT;
return ret;
}
status_t V4LCameraAdapter::fillThisBuffer(void* frameBuf, CameraFrame::FrameType frameType)
{
status_t ret = NO_ERROR;
if ( !mVideoInfo->isStreaming )
{
return NO_ERROR;
}
int i = mPreviewBufs.valueFor(( unsigned int )frameBuf);
if(i<0)
{
return BAD_VALUE;
}
mVideoInfo->buf.index = i;
mVideoInfo->buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
mVideoInfo->buf.memory = V4L2_MEMORY_MMAP;
ret = ioctl(mCameraHandle, VIDIOC_QBUF, &mVideoInfo->buf);
if (ret < 0) {
CAMHAL_LOGEA("Init: VIDIOC_QBUF Failed");
return -1;
}
nQueued++;
return ret;
}
status_t V4LCameraAdapter::setParameters(const CameraParameters &params)
{
LOG_FUNCTION_NAME;
status_t ret = NO_ERROR;
int width, height;
params.getPreviewSize(&width, &height);
CAMHAL_LOGDB("Width * Height %d x %d format 0x%x", width, height, DEFAULT_PIXEL_FORMAT);
mVideoInfo->width = width;
mVideoInfo->height = height;
mVideoInfo->framesizeIn = (width * height << 1);
mVideoInfo->formatIn = DEFAULT_PIXEL_FORMAT;
mVideoInfo->format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
mVideoInfo->format.fmt.pix.width = width;
mVideoInfo->format.fmt.pix.height = height;
mVideoInfo->format.fmt.pix.pixelformat = DEFAULT_PIXEL_FORMAT;
ret = ioctl(mCameraHandle, VIDIOC_S_FMT, &mVideoInfo->format);
if (ret < 0) {
CAMHAL_LOGEB("Open: VIDIOC_S_FMT Failed: %s", strerror(errno));
return ret;
}
// Udpate the current parameter set
mParams = params;
LOG_FUNCTION_NAME_EXIT;
return ret;
}
void V4LCameraAdapter::getParameters(CameraParameters& params)
{
LOG_FUNCTION_NAME;
// Return the current parameter set
params = mParams;
LOG_FUNCTION_NAME_EXIT;
}
///API to give the buffers to Adapter
status_t V4LCameraAdapter::useBuffers(CameraMode mode, void* bufArr, int num, size_t length, unsigned int queueable)
{
status_t ret = NO_ERROR;
LOG_FUNCTION_NAME;
Mutex::Autolock lock(mLock);
switch(mode)
{
case CAMERA_PREVIEW:
ret = UseBuffersPreview(bufArr, num);
break;
//@todo Insert Image capture case here
case CAMERA_VIDEO:
//@warn Video capture is not fully supported yet
ret = UseBuffersPreview(bufArr, num);
break;
}
LOG_FUNCTION_NAME_EXIT;
return ret;
}
status_t V4LCameraAdapter::UseBuffersPreview(void* bufArr, int num)
{
int ret = NO_ERROR;
if(NULL == bufArr)
{
return BAD_VALUE;
}
//First allocate adapter internal buffers at V4L level for USB Cam
//These are the buffers from which we will copy the data into overlay buffers
/* Check if camera can handle NB_BUFFER buffers */
mVideoInfo->rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
mVideoInfo->rb.memory = V4L2_MEMORY_MMAP;
mVideoInfo->rb.count = num;
ret = ioctl(mCameraHandle, VIDIOC_REQBUFS, &mVideoInfo->rb);
if (ret < 0) {
CAMHAL_LOGEB("VIDIOC_REQBUFS failed: %s", strerror(errno));
return ret;
}
for (int i = 0; i < num; i++) {
memset (&mVideoInfo->buf, 0, sizeof (struct v4l2_buffer));
mVideoInfo->buf.index = i;
mVideoInfo->buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
mVideoInfo->buf.memory = V4L2_MEMORY_MMAP;
ret = ioctl (mCameraHandle, VIDIOC_QUERYBUF, &mVideoInfo->buf);
if (ret < 0) {
CAMHAL_LOGEB("Unable to query buffer (%s)", strerror(errno));
return ret;
}
mVideoInfo->mem[i] = mmap (0,
mVideoInfo->buf.length,
PROT_READ | PROT_WRITE,
MAP_SHARED,
mCameraHandle,
mVideoInfo->buf.m.offset);
if (mVideoInfo->mem[i] == MAP_FAILED) {
CAMHAL_LOGEB("Unable to map buffer (%s)", strerror(errno));
return -1;
}
uint32_t *ptr = (uint32_t*) bufArr;
//Associate each Camera internal buffer with the one from Overlay
mPreviewBufs.add((int)ptr[i], i);
}
// Update the preview buffer count
mPreviewBufferCount = num;
return ret;
}
status_t V4LCameraAdapter::startPreview()
{
status_t ret = NO_ERROR;
Mutex::Autolock lock(mPreviewBufsLock);
if(mPreviewing)
{
return BAD_VALUE;
}
for (int i = 0; i < mPreviewBufferCount; i++) {
mVideoInfo->buf.index = i;
mVideoInfo->buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
mVideoInfo->buf.memory = V4L2_MEMORY_MMAP;
ret = ioctl(mCameraHandle, VIDIOC_QBUF, &mVideoInfo->buf);
if (ret < 0) {
CAMHAL_LOGEA("VIDIOC_QBUF Failed");
return -EINVAL;
}
nQueued++;
}
enum v4l2_buf_type bufType;
if (!mVideoInfo->isStreaming) {
bufType = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ret = ioctl (mCameraHandle, VIDIOC_STREAMON, &bufType);
if (ret < 0) {
CAMHAL_LOGEB("StartStreaming: Unable to start capture: %s", strerror(errno));
return ret;
}
mVideoInfo->isStreaming = true;
}
// Create and start preview thread for receiving buffers from V4L Camera
mPreviewThread = new PreviewThread(this);
CAMHAL_LOGDA("Created preview thread");
//Update the flag to indicate we are previewing
mPreviewing = true;
return ret;
}
status_t V4LCameraAdapter::stopPreview()
{
enum v4l2_buf_type bufType;
int ret = NO_ERROR;
Mutex::Autolock lock(mPreviewBufsLock);
if(!mPreviewing)
{
return NO_INIT;
}
if (mVideoInfo->isStreaming) {
bufType = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ret = ioctl (mCameraHandle, VIDIOC_STREAMOFF, &bufType);
if (ret < 0) {
CAMHAL_LOGEB("StopStreaming: Unable to stop capture: %s", strerror(errno));
return ret;
}
mVideoInfo->isStreaming = false;
}
mVideoInfo->buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
mVideoInfo->buf.memory = V4L2_MEMORY_MMAP;
nQueued = 0;
nDequeued = 0;
/* Unmap buffers */
for (int i = 0; i < mPreviewBufferCount; i++)
if (munmap(mVideoInfo->mem[i], mVideoInfo->buf.length) < 0)
CAMHAL_LOGEA("Unmap failed");
mPreviewBufs.clear();
mPreviewThread->requestExitAndWait();
mPreviewThread.clear();
return ret;
}
char * V4LCameraAdapter::GetFrame(int &index)
{
int ret;
mVideoInfo->buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
mVideoInfo->buf.memory = V4L2_MEMORY_MMAP;
/* DQ */
ret = ioctl(mCameraHandle, VIDIOC_DQBUF, &mVideoInfo->buf);
if (ret < 0) {
CAMHAL_LOGEA("GetFrame: VIDIOC_DQBUF Failed");
return NULL;
}
nDequeued++;
index = mVideoInfo->buf.index;
return (char *)mVideoInfo->mem[mVideoInfo->buf.index];
}
//API to get the frame size required to be allocated. This size is used to override the size passed
//by camera service when VSTAB/VNF is turned ON for example
status_t V4LCameraAdapter::getFrameSize(size_t &width, size_t &height)
{
status_t ret = NO_ERROR;
// Just return the current preview size, nothing more to do here.
mParams.getPreviewSize(( int * ) &width,
( int * ) &height);
LOG_FUNCTION_NAME_EXIT;
return ret;
}
status_t V4LCameraAdapter::getFrameDataSize(size_t &dataFrameSize, size_t bufferCount)
{
// We don't support meta data, so simply return
return NO_ERROR;
}
status_t V4LCameraAdapter::getPictureBufferSize(size_t &length, size_t bufferCount)
{
// We don't support image capture yet, safely return from here without messing up
return NO_ERROR;
}
static void debugShowFPS()
{
static int mFrameCount = 0;
static int mLastFrameCount = 0;
static nsecs_t mLastFpsTime = 0;
static float mFps = 0;
mFrameCount++;
if (!(mFrameCount & 0x1F)) {
nsecs_t now = systemTime();
nsecs_t diff = now - mLastFpsTime;
mFps = ((mFrameCount - mLastFrameCount) * float(s2ns(1))) / diff;
mLastFpsTime = now;
mLastFrameCount = mFrameCount;
LOGD("Camera %d Frames, %f FPS", mFrameCount, mFps);
}
// XXX: mFPS has the value we want
}
status_t V4LCameraAdapter::recalculateFPS()
{
float currentFPS;
mFrameCount++;
if ( ( mFrameCount % FPS_PERIOD ) == 0 )
{
nsecs_t now = systemTime();
nsecs_t diff = now - mLastFPSTime;
currentFPS = ((mFrameCount - mLastFrameCount) * float(s2ns(1))) / diff;
mLastFPSTime = now;
mLastFrameCount = mFrameCount;
if ( 1 == mIter )
{
mFPS = currentFPS;
}
else
{
//cumulative moving average
mFPS = mLastFPS + (currentFPS - mLastFPS)/mIter;
}
mLastFPS = mFPS;
mIter++;
}
return NO_ERROR;
}
void V4LCameraAdapter::onOrientationEvent(uint32_t orientation, uint32_t tilt)
{
LOG_FUNCTION_NAME;
LOG_FUNCTION_NAME_EXIT;
}
V4LCameraAdapter::V4LCameraAdapter(size_t sensor_index)
{
LOG_FUNCTION_NAME;
// Nothing useful to do in the constructor
LOG_FUNCTION_NAME_EXIT;
}
V4LCameraAdapter::~V4LCameraAdapter()
{
LOG_FUNCTION_NAME;
// Close the camera handle and free the video info structure
close(mCameraHandle);
if (mVideoInfo)
{
free(mVideoInfo);
mVideoInfo = NULL;
}
LOG_FUNCTION_NAME_EXIT;
}
/* Preview Thread */
// ---------------------------------------------------------------------------
int V4LCameraAdapter::previewThread()
{
status_t ret = NO_ERROR;
int width, height;
CameraFrame frame;
if (mPreviewing)
{
int index = 0;
char *fp = this->GetFrame(index);
if(!fp)
{
return BAD_VALUE;
}
uint8_t* ptr = (uint8_t*) mPreviewBufs.keyAt(index);
int width, height;
uint16_t* dest = (uint16_t*)ptr;
uint16_t* src = (uint16_t*) fp;
mParams.getPreviewSize(&width, &height);
for(int i=0;i<height;i++)
{
for(int j=0;j<width;j++)
{
//*dest = *src;
//convert from YUYV to UYVY supported in Camera service
*dest = (((*src & 0xFF000000)>>24)<<16)|(((*src & 0x00FF0000)>>16)<<24) |
(((*src & 0xFF00)>>8)<<0)|(((*src & 0x00FF)>>0)<<8);
src++;
dest++;
}
dest += 4096/2-width;
}
mParams.getPreviewSize(&width, &height);
frame.mFrameType = CameraFrame::PREVIEW_FRAME_SYNC;
frame.mBuffer = ptr;
frame.mLength = width*height*2;
frame.mAlignment = width*2;
frame.mOffset = 0;
frame.mTimestamp = systemTime(SYSTEM_TIME_MONOTONIC);;
ret = sendFrameToSubscribers(&frame);
}
return ret;
}
extern "C" CameraAdapter* CameraAdapter_Factory()
{
CameraAdapter *adapter = NULL;
Mutex::Autolock lock(gAdapterLock);
LOG_FUNCTION_NAME;
adapter = new V4LCameraAdapter(sensor_index);
if ( adapter ) {
CAMHAL_LOGDB("New OMX Camera adapter instance created for sensor %d",sensor_index);
} else {
CAMHAL_LOGEA("Camera adapter create failed!");
}
LOG_FUNCTION_NAME_EXIT;
return adapter;
}
extern "C" int CameraAdapter_Capabilities(CameraProperties::Properties* properties_array,
const unsigned int starting_camera,
const unsigned int max_camera) {
int num_cameras_supported = 0;
CameraProperties::Properties* properties = NULL;
LOG_FUNCTION_NAME;
if(!properties_array)
{
return -EINVAL;
}
// TODO: Need to tell camera properties what other cameras we can support
if (starting_camera + num_cameras_supported < max_camera) {
num_cameras_supported++;
properties = properties_array + starting_camera;
properties->set(CameraProperties::CAMERA_NAME, "USBCamera");
}
LOG_FUNCTION_NAME_EXIT;
return num_cameras_supported;
}
};
/*--------------------Camera Adapter Class ENDS here-----------------------------*/