blob: 92520fa4df24b4e358d78cca3a2b34a0253f6b8c [file] [log] [blame]
/*
* Copyright (C) 2013-2017 Intel Corporation
* Copyright (c) 2017, Fuzhou Rockchip Electronics Co., Ltd
*
* 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 "V4L2VideoNode"
#include "LogHelper.h"
#include "v4l2device.h"
#include <linux/media.h>
#include "Camera3V4l2Format.h"
#include <limits.h>
#include <fcntl.h>
#include "UtilityMacros.h"
////////////////////////////////////////////////////////////////////
// PUBLIC METHODS
////////////////////////////////////////////////////////////////////
#define MAX_CAMERA_BUFFERS_NUM 32 //MAX_CAMERA_BUFFERS_NUM
NAMESPACE_DECLARATION {
V4L2Buffer::V4L2Buffer()
{
LOG1("@%s", __FUNCTION__);
CLEAR(vbuf);
}
V4L2Buffer::V4L2Buffer(const struct v4l2_buffer &buf)
{
LOG1("@%s", __FUNCTION__);
memset(&vbuf, 0, sizeof(vbuf));
}
void V4L2Buffer::setType(uint32_t type)
{
CheckError(!V4L2_TYPE_IS_VALID(type), VOID_VALUE, \
"@%s: invalid buffer type: %d.", __FUNCTION__, type);
vbuf.type = type;
}
uint32_t V4L2Buffer::offset(int plane)
{
CheckError(!V4L2_TYPE_IS_VALID(vbuf.type), BAD_VALUE, \
"@%s: invalid buffer type: %d.", __FUNCTION__, vbuf.type);
bool mp = V4L2_TYPE_IS_MULTIPLANAR(vbuf.type);
CheckError(((!mp && plane) || (mp && plane >= planes.size())), 0,
"@%s: invalid plane %d", __FUNCTION__, plane);
return mp ? vbuf.m.planes[plane].m.mem_offset : vbuf.m.offset;
}
void V4L2Buffer::setOffset(uint32_t offset, int plane)
{
CheckError(!V4L2_TYPE_IS_VALID(vbuf.type), VOID_VALUE, \
"@%s: invalid buffer type: %d.", __FUNCTION__, vbuf.type);
bool mp = V4L2_TYPE_IS_MULTIPLANAR(vbuf.type);
CheckError(((!mp && plane) || (mp && plane >= planes.size())), VOID_VALUE,
"@%s: invalid plane %d", __FUNCTION__, plane);
if (mp)
vbuf.m.planes[plane].m.mem_offset = offset;
else
vbuf.m.offset = offset;
}
unsigned long V4L2Buffer::userptr(int plane)
{
CheckError(!V4L2_TYPE_IS_VALID(vbuf.type), BAD_VALUE, \
"@%s: invalid buffer type: %d.", __FUNCTION__, vbuf.type);
bool mp = V4L2_TYPE_IS_MULTIPLANAR(vbuf.type);
CheckError(((!mp && plane) || (mp && plane >= planes.size())), 0,
"@%s: invalid plane %d", __FUNCTION__, plane);
return mp ? vbuf.m.planes[plane].m.userptr : vbuf.m.userptr;
}
void V4L2Buffer::setUserptr(unsigned long userptr, int plane)
{
CheckError(!V4L2_TYPE_IS_VALID(vbuf.type), VOID_VALUE, \
"@%s: invalid buffer type: %d.", __FUNCTION__, vbuf.type);
bool mp = V4L2_TYPE_IS_MULTIPLANAR(vbuf.type);
CheckError(((!mp && plane) || (mp && plane >= planes.size())), VOID_VALUE,
"@%s: invalid plane %d", __FUNCTION__, plane);
if (mp)
vbuf.m.planes[plane].m.userptr = userptr;
else
vbuf.m.userptr = userptr;
}
int V4L2Buffer::fd(int plane)
{
CheckError(!V4L2_TYPE_IS_VALID(vbuf.type), BAD_VALUE, \
"@%s: invalid buffer type: %d.", __FUNCTION__, vbuf.type);
bool mp = V4L2_TYPE_IS_MULTIPLANAR(vbuf.type);
CheckError(((!mp && plane) || (mp && plane >= planes.size())), -1,
"@%s: invalid plane %d", __FUNCTION__, plane);
return mp ? vbuf.m.planes[plane].m.fd : vbuf.m.fd;
}
void V4L2Buffer::setFd(int fd, int plane)
{
CheckError(!V4L2_TYPE_IS_VALID(vbuf.type), VOID_VALUE, \
"@%s: invalid buffer type: %d.", __FUNCTION__, vbuf.type);
bool mp = V4L2_TYPE_IS_MULTIPLANAR(vbuf.type);
CheckError(((!mp && plane) || (mp && plane >= planes.size())), VOID_VALUE,
"@%s: invalid plane %d", __FUNCTION__, plane);
if (mp)
vbuf.m.planes[plane].m.fd = fd;
else
vbuf.m.fd = fd;
}
uint32_t V4L2Buffer::bytesused(int plane)
{
CheckError(!V4L2_TYPE_IS_VALID(vbuf.type), BAD_VALUE, \
"@%s: invalid buffer type: %d.", __FUNCTION__, vbuf.type);
bool mp = V4L2_TYPE_IS_MULTIPLANAR(vbuf.type);
CheckError(((!mp && plane) || (mp && plane >= planes.size())), 0,
"@%s: invalid plane %d", __FUNCTION__, plane);
return mp ? vbuf.m.planes[plane].bytesused : vbuf.bytesused;
}
void V4L2Buffer::setBytesused(uint32_t bytesused, int plane)
{
CheckError(!V4L2_TYPE_IS_VALID(vbuf.type), VOID_VALUE, \
"@%s: invalid buffer type: %d.", __FUNCTION__, vbuf.type);
bool mp = V4L2_TYPE_IS_MULTIPLANAR(vbuf.type);
CheckError(((!mp && plane) || (mp && plane >= planes.size())), VOID_VALUE,
"@%s: invalid plane %d", __FUNCTION__, plane);
if (mp)
vbuf.m.planes[plane].bytesused = bytesused;
else
vbuf.bytesused = bytesused;
}
uint32_t V4L2Buffer::length(int plane)
{
CheckError(!V4L2_TYPE_IS_VALID(vbuf.type), BAD_VALUE, \
"@%s: invalid buffer type: %d.", __FUNCTION__, vbuf.type);
bool mp = V4L2_TYPE_IS_MULTIPLANAR(vbuf.type);
CheckError(((!mp && plane) || (mp && plane >= planes.size())), 0,
"@%s: invalid plane %d", __FUNCTION__, plane);
return mp ? vbuf.m.planes[plane].length : vbuf.length;
}
void V4L2Buffer::setLength(uint32_t length, int plane)
{
CheckError(!V4L2_TYPE_IS_VALID(vbuf.type), VOID_VALUE, \
"@%s: invalid buffer type: %d.", __FUNCTION__, vbuf.type);
bool mp = V4L2_TYPE_IS_MULTIPLANAR(vbuf.type);
CheckError(((!mp && plane) || (mp && plane >= planes.size())), VOID_VALUE,
"@%s: invalid plane %d", __FUNCTION__, plane);
if (mp)
vbuf.m.planes[plane].length = length;
else
vbuf.length = length;
}
uint32_t V4L2Buffer::getNumPlanes()
{
CheckError(!V4L2_TYPE_IS_VALID(vbuf.type), BAD_VALUE, \
"@%s: invalid buffer type: %d.", __FUNCTION__, vbuf.type);
if (V4L2_TYPE_IS_MULTIPLANAR(vbuf.type))
return planes.size();
else
return 1;
}
void V4L2Buffer::setNumPlanes(int numPlanes)
{
CheckError(!V4L2_TYPE_IS_VALID(vbuf.type), VOID_VALUE, \
"@%s: invalid buffer type: %d.", __FUNCTION__, vbuf.type);
CheckError(!V4L2_TYPE_IS_MULTIPLANAR(vbuf.type), VOID_VALUE,
"@%s: setting plane number for single plane buffer is not allowed", __FUNCTION__);
if (numPlanes != planes.size()) {
planes.clear();
for (int i = 0; i < numPlanes; i++) {
struct v4l2_plane plane;
CLEAR(plane);
planes.push_back(plane);
}
}
vbuf.m.planes = planes.data();
vbuf.length = numPlanes;
}
const V4L2Buffer &V4L2Buffer::operator=(const V4L2Buffer &buf)
{
vbuf = buf.vbuf;
if (V4L2_TYPE_IS_MULTIPLANAR(vbuf.type)) {
planes = buf.planes;
vbuf.m.planes = planes.data();
}
return *this;
}
void V4L2Format::setType(uint32_t type)
{
CheckError(!V4L2_TYPE_IS_VALID(type), VOID_VALUE, \
"@%s: invalid buffer type: %d.", __FUNCTION__, type);
vfmt.type = type;
}
uint32_t V4L2Format::numPlanes()
{
CheckError(!V4L2_TYPE_IS_VALID(vfmt.type), BAD_VALUE, \
"@%s: invalid buffer type: %d.", __FUNCTION__, vfmt.type);
if (!V4L2_TYPE_IS_MULTIPLANAR(vfmt.type)) {
LOGE("@%s: reading number of planes non multi-plane format is not allowed.", __FUNCTION__);
return 0;
}
return vfmt.fmt.pix_mp.num_planes;
}
void V4L2Format::setNumPlanes(int numPlanes)
{
CheckError(!V4L2_TYPE_IS_VALID(vfmt.type), VOID_VALUE, \
"@%s: invalid buffer type: %d.", __FUNCTION__, vfmt.type);
if (!V4L2_TYPE_IS_MULTIPLANAR(vfmt.type)) {
LOGE("@%s: setting number of planes for non multi-plane format is not allowed.", __FUNCTION__);
return;
}
vfmt.fmt.pix_mp.num_planes = numPlanes;
}
uint32_t V4L2Format::width()
{
CheckError(!V4L2_TYPE_IS_VALID(vfmt.type), BAD_VALUE, \
"@%s: invalid buffer type: %d.", __FUNCTION__, vfmt.type);
if (V4L2_TYPE_IS_META(vfmt.type))
return vfmt.fmt.meta.buffersize;
else
return V4L2_TYPE_IS_MULTIPLANAR(vfmt.type) ? vfmt.fmt.pix_mp.width : vfmt.fmt.pix.width;
}
void V4L2Format::setWidth(uint32_t width)
{
CheckError(!V4L2_TYPE_IS_VALID(vfmt.type), VOID_VALUE, \
"@%s: invalid buffer type: %d.", __FUNCTION__, vfmt.type);
if (V4L2_TYPE_IS_META(vfmt.type))
LOGE("@%s: setting width for meta format is not allowed.", __FUNCTION__);
else if (V4L2_TYPE_IS_MULTIPLANAR(vfmt.type))
vfmt.fmt.pix_mp.width = width;
else
vfmt.fmt.pix.width = width;
}
uint32_t V4L2Format::height()
{
CheckError(!V4L2_TYPE_IS_VALID(vfmt.type), BAD_VALUE, \
"@%s: invalid buffer type: %d.", __FUNCTION__, vfmt.type);
if (V4L2_TYPE_IS_META(vfmt.type))
return 1;
else
return V4L2_TYPE_IS_MULTIPLANAR(vfmt.type) ? vfmt.fmt.pix_mp.height : vfmt.fmt.pix.height;
}
void V4L2Format::setHeight(uint32_t height)
{
CheckError(!V4L2_TYPE_IS_VALID(vfmt.type), VOID_VALUE, \
"@%s: invalid buffer type: %d.", __FUNCTION__, vfmt.type);
if (V4L2_TYPE_IS_META(vfmt.type))
LOGE("@%s: setting height for meta format is not allowed.", __FUNCTION__);
else if (V4L2_TYPE_IS_MULTIPLANAR(vfmt.type))
vfmt.fmt.pix_mp.height = height;
else
vfmt.fmt.pix.height = height;
}
uint32_t V4L2Format::pixelformat()
{
CheckError(!V4L2_TYPE_IS_VALID(vfmt.type), BAD_VALUE, \
"@%s: invalid buffer type: %d.", __FUNCTION__, vfmt.type);
if (V4L2_TYPE_IS_META(vfmt.type))
return vfmt.fmt.meta.dataformat;
else
return V4L2_TYPE_IS_MULTIPLANAR(vfmt.type) ?
vfmt.fmt.pix_mp.pixelformat : vfmt.fmt.pix.pixelformat;
}
void V4L2Format::setPixelformat(uint32_t format)
{
CheckError(!V4L2_TYPE_IS_VALID(vfmt.type), VOID_VALUE, \
"@%s: invalid buffer type: %d.", __FUNCTION__, vfmt.type);
if (V4L2_TYPE_IS_META(vfmt.type))
vfmt.fmt.meta.dataformat = format;
else if (V4L2_TYPE_IS_MULTIPLANAR(vfmt.type))
vfmt.fmt.pix_mp.pixelformat = format;
else
vfmt.fmt.pix.pixelformat = format;
}
uint32_t V4L2Format::field()
{
CheckError(!V4L2_TYPE_IS_VALID(vfmt.type), BAD_VALUE, \
"@%s: invalid buffer type: %d.", __FUNCTION__, vfmt.type);
if (V4L2_TYPE_IS_META(vfmt.type))
return V4L2_FIELD_NONE;
else
return V4L2_TYPE_IS_MULTIPLANAR(vfmt.type) ? vfmt.fmt.pix_mp.field : vfmt.fmt.pix.field;
}
void V4L2Format::setField(uint32_t field)
{
CheckError(!V4L2_TYPE_IS_VALID(vfmt.type), VOID_VALUE, \
"@%s: invalid buffer type: %d.", __FUNCTION__, vfmt.type);
if (V4L2_TYPE_IS_META(vfmt.type))
LOGE("@%s: setting field for meta format is not allowed.", __FUNCTION__);
else if (V4L2_TYPE_IS_MULTIPLANAR(vfmt.type))
vfmt.fmt.pix_mp.field = field;
else
vfmt.fmt.pix.field = field;
}
uint32_t V4L2Format::bytesperline(int plane)
{
CheckError(!V4L2_TYPE_IS_VALID(vfmt.type), BAD_VALUE, \
"@%s: invalid buffer type: %d.", __FUNCTION__, vfmt.type);
if (V4L2_TYPE_IS_META(vfmt.type))
return vfmt.fmt.meta.buffersize;
bool mp = V4L2_TYPE_IS_MULTIPLANAR(vfmt.type);
if ((!mp && plane) ||
(mp && plane >= vfmt.fmt.pix_mp.num_planes)) {
LOGE("@%s: invalid plane %d", __FUNCTION__, plane);
plane = 0;
}
return mp ? vfmt.fmt.pix_mp.plane_fmt[plane].bytesperline : vfmt.fmt.pix.bytesperline;
}
void V4L2Format::setBytesperline(uint32_t bytesperline, int plane)
{
CheckError(!V4L2_TYPE_IS_VALID(vfmt.type), VOID_VALUE, \
"@%s: invalid buffer type: %d.", __FUNCTION__, vfmt.type);
if (V4L2_TYPE_IS_META(vfmt.type))
LOGE("@%s: setting bytesperline for meta format is not allowed.", __FUNCTION__);
else if (V4L2_TYPE_IS_MULTIPLANAR(vfmt.type))
vfmt.fmt.pix_mp.plane_fmt[plane].bytesperline = bytesperline;
else
vfmt.fmt.pix.bytesperline = bytesperline;
}
uint32_t V4L2Format::sizeimage(int plane)
{
CheckError(!V4L2_TYPE_IS_VALID(vfmt.type), BAD_VALUE, \
"@%s: invalid buffer type: %d.", __FUNCTION__, vfmt.type);
if (V4L2_TYPE_IS_META(vfmt.type))
return vfmt.fmt.meta.buffersize;
bool mp = V4L2_TYPE_IS_MULTIPLANAR(vfmt.type);
if ((!mp && plane) ||
(mp && plane && plane >= vfmt.fmt.pix_mp.num_planes)) {
LOGE("@%s: invalid plane %d", __FUNCTION__, plane);
plane = 0;
}
return mp ? vfmt.fmt.pix_mp.plane_fmt[plane].sizeimage : vfmt.fmt.pix.sizeimage;
}
void V4L2Format::setSizeimage(uint32_t size, int plane)
{
CheckError(!V4L2_TYPE_IS_VALID(vfmt.type), VOID_VALUE, \
"@%s: invalid buffer type: %d.", __FUNCTION__, vfmt.type);
if (V4L2_TYPE_IS_META(vfmt.type))
vfmt.fmt.meta.buffersize = size;
else if (V4L2_TYPE_IS_MULTIPLANAR(vfmt.type))
vfmt.fmt.pix_mp.plane_fmt[plane].sizeimage = size;
else
vfmt.fmt.pix.sizeimage = size;
}
const V4L2Format &V4L2Format::operator=(const V4L2Format &fmt)
{
vfmt = fmt.vfmt;
return *this;
}
V4L2BufferInfo::V4L2BufferInfo():
data(NULL),
length(0),
width(0),
height(0),
format(0),
cache_flags(0)
{
}
V4L2VideoNode::V4L2VideoNode(const char *name):
V4L2DeviceBase(name),
mState(DEVICE_CLOSED),
mBuffersInDevice(0),
mFrameCounter(0),
mInitialSkips(0),
mBufType(V4L2_BUF_TYPE_VIDEO_CAPTURE),
mMemoryType(V4L2_MEMORY_USERPTR)
{
LOG1("@%s: device: %s", __FUNCTION__, name);
mBufferPool.reserve(MAX_CAMERA_BUFFERS_NUM);
mSetBufferPool.reserve(MAX_CAMERA_BUFFERS_NUM);
CLEAR(mConfig);
}
V4L2VideoNode::~V4L2VideoNode()
{
LOG1("@%s device : %s", __FUNCTION__, mName.c_str());
/**
* Do something for the buffer pool ?
*/
}
status_t V4L2VideoNode::open()
{
struct v4l2_capability cap;
status_t status(NO_ERROR);
status = V4L2DeviceBase::open();
CheckError((status != NO_ERROR), status, "@%s: failed to open video device node", __FUNCTION__);
mState = DEVICE_OPEN;
status = queryCap(&cap);
CheckError((status != NO_ERROR), status, "@%s: query device caps failed", __FUNCTION__);
if (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)
mBufType = V4L2_BUF_TYPE_VIDEO_CAPTURE;
else if (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE_MPLANE)
mBufType = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
else if (cap.capabilities & V4L2_CAP_VIDEO_OUTPUT)
mBufType = V4L2_BUF_TYPE_VIDEO_OUTPUT;
else if (cap.capabilities & V4L2_CAP_VIDEO_OUTPUT_MPLANE)
mBufType = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
else if (cap.capabilities & V4L2_CAP_META_CAPTURE)
mBufType = V4L2_BUF_TYPE_META_CAPTURE;
else if (cap.capabilities & V4L2_CAP_META_OUTPUT)
mBufType = V4L2_BUF_TYPE_META_OUTPUT;
else {
LOGE("@%s: unsupported buffer type.", __FUNCTION__);
return DEAD_OBJECT;
}
mBuffersInDevice = 0;
return status;
}
status_t V4L2VideoNode::close()
{
status_t status(NO_ERROR);
if (mState == DEVICE_STARTED) {
stop();
}
if (!mBufferPool.empty()) {
destroyBufferPool();
}
status = V4L2DeviceBase::close();
if (status == NO_ERROR)
mState = DEVICE_CLOSED;
mBuffersInDevice = 0;
return status;
}
status_t V4L2VideoNode::setBlock(bool block)
{
int ret, flags;
flags = fcntl(mFd, F_GETFL, 0);
if (flags < 0)
return UNKNOWN_ERROR;
if (block)
flags &= ~O_NONBLOCK;
else
flags |= O_NONBLOCK;
ret = fcntl(mFd, F_SETFL, flags);
if (ret < 0)
return UNKNOWN_ERROR;
return NO_ERROR;
}
/**
* queries the capabilities of the device and it does some basic consistency
* checks based on the direction of the video device node
*
* \param cap: [OUT] V4L2 capability structure
*
* \return NO_ERROR if everything went ok
* \return INVALID_OPERATION if the device was not in correct state
* \return UNKNOWN_ERROR if IOCTL operation failed
* \return DEAD_OBJECT if the basic checks for this object failed
*/
status_t V4L2VideoNode::queryCap(struct v4l2_capability *cap)
{
LOG1("@%s device : %s", __FUNCTION__, mName.c_str());
int ret(0);
if (mState != DEVICE_OPEN) {
LOGE("%s invalid device state %d",__FUNCTION__, mState);
return INVALID_OPERATION;
}
ret = pioctl(mFd, VIDIOC_QUERYCAP, cap, mName.c_str());
if (ret < 0) {
LOGE("VIDIOC_QUERYCAP returned: %d (%s)", ret, strerror(errno));
return UNKNOWN_ERROR;
}
LOG1( "driver: '%s'", cap->driver);
LOG1( "card: '%s'", cap->card);
LOG1( "bus_info: '%s'", cap->bus_info);
LOG1( "version: %x", cap->version);
LOG1( "capabilities: %x", cap->capabilities);
LOG1( "device caps: %x", cap->device_caps);
LOG1( "buffer type %d", mBufType);
return NO_ERROR;
}
status_t V4L2VideoNode::enumerateInputs(struct v4l2_input *anInput)
{
LOG1("@%s device : %s", __FUNCTION__, mName.c_str());
int ret(0);
if (mState == DEVICE_CLOSED) {
LOGE("%s invalid device state %d",__FUNCTION__, mState);
return INVALID_OPERATION;
}
ret = pioctl(mFd, VIDIOC_ENUMINPUT, anInput, mName.c_str());
int errno_copy = errno;
if (ret < 0) {
LOGE("VIDIOC_ENUMINPUT failed returned: %d (%s)", ret, strerror(errno_copy));
if (errno_copy == EINVAL)
return BAD_INDEX;
else
return UNKNOWN_ERROR;
}
return NO_ERROR;
}
status_t V4L2VideoNode::queryCapturePixelFormats(std::vector<v4l2_fmtdesc> &formats)
{
LOG1("@%s device : %s", __FUNCTION__, mName.c_str());
struct v4l2_fmtdesc aFormat;
if (mState == DEVICE_CLOSED) {
LOGE("%s invalid device state %d",__FUNCTION__, mState);
return INVALID_OPERATION;
}
formats.clear();
CLEAR(aFormat);
aFormat.index = 0;
aFormat.type = mBufType;
while (pioctl(mFd, VIDIOC_ENUM_FMT , &aFormat, mName.c_str()) == 0) {
formats.push_back(aFormat);
aFormat.index++;
};
aFormat.index = 0;
aFormat.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
while (pioctl(mFd, VIDIOC_ENUM_FMT , &aFormat, mName.c_str()) == 0) {
formats.push_back(aFormat);
aFormat.index++;
};
LOG1("@%s device : %s %zu format retrieved", __FUNCTION__, mName.c_str(), formats.size());
return NO_ERROR;
}
int V4L2VideoNode::getMemoryType()
{
return mMemoryType;
}
status_t V4L2VideoNode::setInput(int index)
{
LOG1("@%s %s", __FUNCTION__, mName.c_str());
struct v4l2_input input;
status_t status = NO_ERROR;
int ret(0);
if (mState == DEVICE_CLOSED) {
LOGE("%s invalid device state %d",__FUNCTION__, mState);
return INVALID_OPERATION;
}
input.index = index;
ret = pioctl(mFd, VIDIOC_S_INPUT, &input, mName.c_str());
if (ret < 0) {
LOGE("VIDIOC_S_INPUT index %d returned: %d (%s)",
input.index, ret, strerror(errno));
status = UNKNOWN_ERROR;
}
return status;
}
/**
* Stop the streaming of buffers of a video device
* This method is basically a stream-off IOCTL but it has a parameter to
* stop and destroy the current active-buffer-pool
*
* After this method completes the device is in state DEVICE_PREPARED
*
* \param leavePopulated: boolean to control the state change
* \return 0 on success
* -1 on failure
*/
int V4L2VideoNode::stop(bool keepBuffers)
{
LOG1("@%s: device = %s", __FUNCTION__, mName.c_str());
int ret = 0;
if (mState == DEVICE_STARTED) {
//stream off
ret = pioctl(mFd, VIDIOC_STREAMOFF, &mBufType, mName.c_str());
if (ret < 0) {
LOGE("VIDIOC_STREAMOFF returned: %d (%s)", ret, strerror(errno));
return ret;
}
mState = DEVICE_PREPARED;
}
if (mState == DEVICE_PREPARED) {
if (!keepBuffers) {
destroyBufferPool();
mState = DEVICE_CONFIGURED;
}
} else {
LOGW("Trying to stop a device not started");
ret = -1;
}
return ret;
}
/**
* Start the streaming of buffers in a video device
*
* This called is allowed in the following states:
* - PREPARED
*
* This method just calls call the stream on IOCTL
*/
int V4L2VideoNode::start(int initialSkips)
{
LOG1("@%s, device = %s, initialSkips:%d", __FUNCTION__, mName.c_str(), initialSkips);
int ret(0);
if (mState != DEVICE_PREPARED) {
LOGE("%s: Invalid state to start %d", __FUNCTION__, mState);
return -1;
}
//stream on
ret = pioctl(mFd, VIDIOC_STREAMON, &mBufType, mName.c_str());
if (ret < 0) {
LOGE("VIDIOC_STREAMON returned: %d (%s)", ret, strerror(errno));
return ret;
}
mFrameCounter = 0;
mState = DEVICE_STARTED;
mInitialSkips = initialSkips;
return ret;
}
/**
* Update the current device node configuration
*
* This called is allowed in the following states:
* - OPEN
* - CONFIGURED
* - PREPARED
*
* This method is a convenience method for use in the context of video capture
* (INPUT_VIDEO_NODE)
* It makes use of the more detailed method that uses as input parameter the
* v4l2_format structure
* This method queries first the current format and updates capture format.
*
*
* \param aConfig:[IN/OUT] reference to the new configuration.
* This structure contains new values for width,height and format
* parameters, but the stride value is not known by the caller
* of this method. The stride value is retrieved from the ISP
* and the value updated, so aConfig.stride is an OUTPUT parameter
* The same applies for the expected size of the buffer
* aConfig.size is also an OUTPUT parameter
*
* \return NO_ERROR if everything went well
* INVALID_OPERATION if device is not in correct state (open)
* UNKNOW_ERROR if we get an error from the v4l2 ioctl's
*/
status_t V4L2VideoNode::setFormat(FrameInfo &aConfig)
{
LOG1("@%s device = %s", __FUNCTION__, mName.c_str());
int ret(0);
V4L2Format v4l2_fmt;
if ((mState != DEVICE_OPEN) &&
(mState != DEVICE_CONFIGURED) &&
(mState != DEVICE_PREPARED) ){
LOGE("%s invalid device state %d",__FUNCTION__, mState);
return INVALID_OPERATION;
}
LOG1("VIDIOC_G_FMT");
v4l2_fmt.setType(mBufType);
ret = pioctl (mFd, VIDIOC_G_FMT, v4l2_fmt.get(), mName.c_str());
if (ret < 0) {
LOGE("VIDIOC_G_FMT failed: %s", strerror(errno));
return UNKNOWN_ERROR;
}
if (V4L2_TYPE_IS_META(mBufType)) {
v4l2_fmt.setPixelformat(aConfig.format);
v4l2_fmt.setSizeimage(0);
LOG2("@%s, set meta format: %d", __FUNCTION__, v4l2_fmt.pixelformat());
ret = setMetaFormat(v4l2_fmt);
CheckError(ret != NO_ERROR, ret, "@%s set meta format failed", __FUNCTION__);
// update config info
aConfig.size = mConfig.size;
} else {
v4l2_fmt.setType(mBufType);
v4l2_fmt.setWidth(aConfig.width);
v4l2_fmt.setHeight(aConfig.height);
v4l2_fmt.setPixelformat(aConfig.format);
v4l2_fmt.setBytesperline((unsigned int)pixelsToBytes(aConfig.format, aConfig.stride));
v4l2_fmt.setSizeimage(0);
v4l2_fmt.setField(aConfig.field);
LOG2("@%s, set pixel format: %d", __FUNCTION__, v4l2_fmt.pixelformat());
// Update current configuration with the new one
ret = setPixFormat(v4l2_fmt);
CheckError(ret != NO_ERROR, ret, "@%s set pixel format failed", __FUNCTION__);
// update config info
aConfig.stride = mConfig.stride;
aConfig.width = mConfig.width;
aConfig.height = mConfig.height;
aConfig.field = mConfig.field;
aConfig.size = mConfig.size;
}
return NO_ERROR;
}
/**
* Update the current device node configuration (low-level)
*
* This called is allowed in the following states:
* - OPEN
* - CONFIGURED
* - PREPARED
*
* This methods allows more detailed control of the format than the previous one
* It updates the internal configuration used to check for discrepancies between
* configuration and buffer pool properties
*
* \param aFormat:[IN] reference to the new v4l2 format .
*
* \return NO_ERROR if everything went well
* INVALID_OPERATION if device is not in correct state (open)
* UNKNOW_ERROR if we get an error from the v4l2 ioctl's
*/
status_t V4L2VideoNode::setPixFormat(V4L2Format &aFormat)
{
LOG1("@%s device = %s", __FUNCTION__, mName.c_str());
int ret(0);
if ((mState != DEVICE_OPEN) &&
(mState != DEVICE_CONFIGURED) &&
(mState != DEVICE_PREPARED) ){
LOGE("%s invalid device state %d", __FUNCTION__, mState);
return INVALID_OPERATION;
}
aFormat.setType(mBufType);
if (V4L2_TYPE_IS_MULTIPLANAR(mBufType)) {
int num_planes = numOfNonContiguousPlanes(aFormat.pixelformat());
aFormat.setNumPlanes(num_planes);
}
LOG1("VIDIOC_S_FMT: %s width: %d, height: %d, bpl: %d, fourcc: %s, field: %d",
mName.c_str(),
aFormat.width(),
aFormat.height(),
aFormat.bytesperline(),
v4l2Fmt2Str(aFormat.pixelformat()),
aFormat.field());
ret = pioctl(mFd, VIDIOC_S_FMT, aFormat.get(), mName.c_str());
if (ret < 0) {
LOGE("VIDIOC_S_FMT failed: %s", strerror(errno));
return UNKNOWN_ERROR;
}
LOG2("after VIDIOC_S_FMT: %s width: %d, height: %d, bpl: %d, fourcc: %s, field: %d",
mName.c_str(),
aFormat.width(),
aFormat.height(),
aFormat.bytesperline(),
v4l2Fmt2Str(aFormat.pixelformat()),
aFormat.field());
// Update current configuration with the new one
mConfig.format = aFormat.pixelformat();
mConfig.width = aFormat.width();
mConfig.height = aFormat.height();
mConfig.stride = bytesToPixels(mConfig.format, aFormat.bytesperline());
mConfig.size = frameSize(mConfig.format, mConfig.stride, mConfig.height);
if (mConfig.stride != mConfig.width)
LOG1("stride: %d from ISP width: %d", mConfig.stride,mConfig.width);
mState = DEVICE_CONFIGURED;
mSetBufferPool.clear();
return NO_ERROR;
}
status_t V4L2VideoNode::setMetaFormat(V4L2Format &aFormat)
{
LOG1("@%s device = %s", __FUNCTION__, mName.c_str());
int ret(0);
if ((mState != DEVICE_OPEN) &&
(mState != DEVICE_CONFIGURED) &&
(mState != DEVICE_PREPARED) ){
LOGE("%s invalid device state %d", __FUNCTION__, mState);
return INVALID_OPERATION;
}
aFormat.setType(mBufType);
LOG1("VIDIOC_S_FMT: %s fourcc: %s, size: %d",
mName.c_str(),
v4l2Fmt2Str(aFormat.pixelformat()),
aFormat.sizeimage());
ret = pioctl(mFd, VIDIOC_S_FMT, aFormat.get(), mName.c_str());
if (ret < 0) {
LOGE("VIDIOC_S_FMT failed: %s", strerror(errno));
return UNKNOWN_ERROR;
}
LOG2("after VIDIOC_S_FMT: %s fourcc: %s, size: %d",
mName.c_str(),
v4l2Fmt2Str(aFormat.pixelformat()),
aFormat.sizeimage());
// Update current configuration with the new one
mConfig.format = aFormat.pixelformat();
mConfig.size = aFormat.sizeimage();
mState = DEVICE_CONFIGURED;
mSetBufferPool.clear();
return NO_ERROR;
}
status_t V4L2VideoNode::setSelection(const struct v4l2_selection &aSelection)
{
LOG1("@%s device = %s", __FUNCTION__, mName.c_str());
struct v4l2_selection *sel = const_cast<struct v4l2_selection *>(&aSelection);
int ret = 0;
if ((mState != DEVICE_OPEN) &&
(mState != DEVICE_CONFIGURED)){
LOGE("%s invalid device state %d",__FUNCTION__, mState);
return INVALID_OPERATION;
}
sel->type = mBufType;
LOG2("VIDIOC_S_SELECTION name %s, type: %u, target: 0x%x, flags: 0x%x, rect left: %d, rect top: %d, width: %d, height: %d",
mName.c_str(),
aSelection.type,
aSelection.target,
aSelection.flags,
aSelection.r.left,
aSelection.r.top,
aSelection.r.width,
aSelection.r.height);
ret = pbxioctl(VIDIOC_S_SELECTION, sel);
if (ret < 0) {
LOGE("VIDIOC_S_SELECTION failed: %s", strerror(errno));
return UNKNOWN_ERROR;
}
return NO_ERROR;
}
int V4L2VideoNode::grabFrame(V4L2BufferInfo *buf)
{
int ret(0);
LOG2("@%s %s enter", __FUNCTION__, mName.c_str());
CheckError((mState != DEVICE_STARTED), -1, "@%s %s invalid device state %d",
__FUNCTION__, mName.c_str(), mState);
CheckError((buf == nullptr), -1, "@%s %s invalid parameter buf is nullptr",
__FUNCTION__, mName.c_str());
ret = dqbuf(buf);
if (ret < 0)
return ret;
// inc frame counter but do no wrap to negative numbers
mFrameCounter++;
mFrameCounter &= INT_MAX;
printBufferInfo(__FUNCTION__, buf->vbuffer);
return buf->vbuffer.index();
}
/*
* In some cases like in timeout situation there is no need to add buffer to
* traced buffers list because it is already there.
*/
status_t V4L2VideoNode::putFrame(const V4L2Buffer &buf)
{
unsigned int index = buf.index();
CheckError((index >= mBufferPool.size()), BAD_INDEX, "@%s %s Invalid index %d pool size %zu",
__FUNCTION__, mName.c_str(), index, mBufferPool.size());
mBufferPool.at(index).vbuffer = buf;
if (putFrame(index) < 0)
return UNKNOWN_ERROR;
return NO_ERROR;
}
/*
* In some cases like in timeout situation there is no need to add buffer to
* traced buffers list because it is already there.
*/
int V4L2VideoNode::putFrame(unsigned int index)
{
int ret(0);
LOG2("@%s %s enter", __FUNCTION__, mName.c_str());
CheckError((index >= mBufferPool.size()), BAD_INDEX, "@%s %s Invalid index %d pool size %zu",
__FUNCTION__, mName.c_str(), index, mBufferPool.size());
V4L2BufferInfo vbuf = mBufferPool.at(index);
ret = qbuf(&vbuf);
printBufferInfo(__FUNCTION__, vbuf.vbuffer);
return ret;
}
int V4L2VideoNode::exportFrame(unsigned int index, int plane)
{
int ret(0);
if (mMemoryType != V4L2_MEMORY_MMAP) {
LOGE("@%s %s Cannot export non-mmap buffers", __FUNCTION__, mName.c_str());
return BAD_VALUE;
}
if (index >= mBufferPool.size()) {
LOGE("@%s %s Invalid index %d pool size %zu",
__FUNCTION__, mName.c_str(), index, mBufferPool.size());
return BAD_INDEX;
}
V4L2BufferInfo vbuf = mBufferPool.at(index);
struct v4l2_exportbuffer ebuf;
CLEAR(ebuf);
ebuf.type = vbuf.vbuffer.type();
ebuf.index = index;
ebuf.plane = plane;
ret = pioctl(mFd, VIDIOC_EXPBUF, &ebuf, mName.c_str());
if (ret < 0) {
LOGE("@%s %s VIDIOC_EXPBUF failed ret %d : %s",
__FUNCTION__, mName.c_str(), ret, strerror(errno));
return ret;
}
LOG2("@%s %s idx %d fd %d", __FUNCTION__, mName.c_str(), index, ebuf.fd);
return ebuf.fd;
}
status_t V4L2VideoNode::setParameter (struct v4l2_streamparm *aParam)
{
LOG2("@%s %s", __FUNCTION__, mName.c_str());
status_t ret = NO_ERROR;
if (mState == DEVICE_CLOSED)
return INVALID_OPERATION;
ret = pioctl(mFd, VIDIOC_S_PARM, aParam, mName.c_str());
if (ret < 0) {
LOGE("VIDIOC_S_PARM failed ret %d : %s", ret, strerror(errno));
ret = UNKNOWN_ERROR;
}
return ret;
}
/**
* Get the maximum rectangle for cropping.
*
* This call is allowed in all other states except in DEVICE_CLOSED.
*
* \param crop:[OUT] pointer to the maximum crop rectangle.
*
* \return NO_ERROR if everything went well
* INVALID_OPERATION if device is not in correct state (open)
* UNKNOW_ERROR if we get an error from the v4l2 ioctl's
*/
status_t V4L2VideoNode::getMaxCropRectangle(struct v4l2_rect *crop)
{
LOG1("@%s", __FUNCTION__);
int ret;
struct v4l2_cropcap cropcap;
if (!crop)
return UNKNOWN_ERROR;
if (mState == DEVICE_CLOSED)
return INVALID_OPERATION;
CLEAR(cropcap);
cropcap.type = mBufType;
ret = pioctl(mFd, VIDIOC_CROPCAP, &cropcap, mName.c_str());
if (ret != NO_ERROR)
return ret;
*crop = cropcap.defrect;
return NO_ERROR;
}
/**
* Update the current device node cropping settings.
*
* This call is allowed in all other states except in DEVICE_CLOSED.
*
* It makes use of the more detailed method that uses as input parameter the
* v4l2_format structure.
*
*
* \param crop:[IN] pointer to the new cropping settings.
*
* \return NO_ERROR if everything went well
* INVALID_OPERATION if device is not in correct state (open)
* UNKNOW_ERROR if we get an error from the v4l2 ioctl's
*/
status_t V4L2VideoNode::setCropRectangle(struct v4l2_rect *crop)
{
LOG2("@%s", __FUNCTION__);
int ret;
struct v4l2_crop v4l2_crop;
CLEAR(v4l2_crop);
if (!crop)
return UNKNOWN_ERROR;
if (mState == DEVICE_CLOSED)
return INVALID_OPERATION;
v4l2_crop.type = mBufType;
v4l2_crop.c.left = crop->left;
v4l2_crop.c.top = crop->top;
v4l2_crop.c.width = crop->width;
v4l2_crop.c.height = crop->height;
// Update current configuration with the new one
ret = pioctl(mFd, VIDIOC_S_CROP, &v4l2_crop, mName.c_str());
if (ret != NO_ERROR)
return ret;
return NO_ERROR;
}
/**
* Get the current device node cropping settings.
*
* This call is allowed in all other states except in DEVICE_CLOSED.
*
* \param[out] crop pointer to the cropping settings read from driver.
*
* \return NO_ERROR if everything went well
* INVALID_OPERATION if device is not in correct state (open)
* UNKNOWN_ERROR if we get an error from the v4l2 ioctl's
*/
status_t V4L2VideoNode::getCropRectangle(struct v4l2_rect *crop)
{
LOG2("@%s", __FUNCTION__);
int ret;
struct v4l2_crop v4l2_crop;
CLEAR(v4l2_crop);
if (!crop)
return BAD_VALUE;
if (mState == DEVICE_CLOSED)
return INVALID_OPERATION;
v4l2_crop.type = mBufType;
// Update current configuration with the new one
ret = pioctl(mFd, VIDIOC_G_CROP, &v4l2_crop, mName.c_str());
if (ret != NO_ERROR)
return ret;
crop->left = v4l2_crop.c.left;
crop->top = v4l2_crop.c.top;
crop->width = v4l2_crop.c.width;
crop->height = v4l2_crop.c.height;
return NO_ERROR;
}
int V4L2VideoNode::getFramerate(float * framerate, int width, int height, int pix_fmt)
{
LOG1("@%s %s", __FUNCTION__, mName.c_str());
int ret(0);
struct v4l2_frmivalenum frm_interval;
if (nullptr == framerate)
return BAD_VALUE;
if (mState == DEVICE_CLOSED) {
LOGE("Invalid state (%d) to set an attribute",mState);
return UNKNOWN_ERROR;
}
CLEAR(frm_interval);
frm_interval.pixel_format = pix_fmt;
frm_interval.width = width;
frm_interval.height = height;
*framerate = -1.0;
ret = pioctl(mFd, VIDIOC_ENUM_FRAMEINTERVALS, &frm_interval, mName.c_str());
if (ret < 0) {
LOGW("ioctl VIDIOC_ENUM_FRAMEINTERVALS failed: %s", strerror(errno));
return UNKNOWN_ERROR;
} else if (0 == frm_interval.discrete.denominator) {
LOGE("ioctl VIDIOC_ENUM_FRAMEINTERVALS get invalid denominator value");
*framerate = 0;
return UNKNOWN_ERROR;
}
*framerate = 1.0 / (1.0 * frm_interval.discrete.numerator / frm_interval.discrete.denominator);
return NO_ERROR;
}
/**
* setBufferPool
* updates the set buffer pool with externally allocated memory
*
* The device has to be at least in CONFIGURED state but once configured
* it the buffer pool can be reset in PREPARED state.
*
* This pool will become active after calling start()
* \param pool: array of void* where the memory is available
* \param poolSize: amount of buffers in the pool
* \param aFrameInfo: description of the properties of the buffers
* it should match the configuration passed during setFormat
* \param cached: boolean to detect whether the buffers are cached or not
* A cached buffer in this context means that the buffer
* memory may be accessed through some system caches, and
* the V4L2 driver must do cache invalidation in case
* the image data source is not updating system caches
* in hardware.
* When false (not cached), V4L2 driver can assume no cache
* invalidation/flushes are needed for this buffer.
*/
status_t V4L2VideoNode::setBufferPool(void **pool, unsigned int poolSize,
FrameInfo *aFrameInfo, bool cached)
{
LOG1("@%s: device = %s", __FUNCTION__, mName.c_str());
V4L2BufferInfo vinfo;
uint32_t cacheflags = V4L2_BUF_FLAG_NO_CACHE_INVALIDATE |
V4L2_BUF_FLAG_NO_CACHE_CLEAN;
if ((mState != DEVICE_CONFIGURED) && (mState != DEVICE_PREPARED)) {
LOGE("%s:Invalid operation, device %s not configured (state = %d)",
__FUNCTION__, mName.c_str(), mState);
return INVALID_OPERATION;
}
if (pool == nullptr || aFrameInfo == nullptr) {
LOGE("Invalid parameters, pool %p frameInfo %p",pool, aFrameInfo);
return BAD_TYPE;
}
/**
* check that the configuration of these buffers matches what we have already
* told the driver.
*/
if ((aFrameInfo->width != mConfig.width) ||
(aFrameInfo->height != mConfig.height) ||
(aFrameInfo->stride != mConfig.stride) ||
(aFrameInfo->format != mConfig.format) ) {
LOGE("Pool configuration does not match device configuration: (%dx%d) s:%d f:%s Pool is: (%dx%d) s:%d f:%s ",
mConfig.width, mConfig.height, mConfig.stride, v4l2Fmt2Str(mConfig.format),
aFrameInfo->width, aFrameInfo->height, aFrameInfo->stride, v4l2Fmt2Str(aFrameInfo->format));
return BAD_VALUE;
}
mSetBufferPool.clear();
for (unsigned int i = 0; i < poolSize; i++) {
vinfo.data = pool[i];
vinfo.width = aFrameInfo->stride;
vinfo.height = aFrameInfo->height;
vinfo.format = aFrameInfo->format;
vinfo.length = aFrameInfo->size;
if (cached)
vinfo.cache_flags = 0;
else
vinfo.cache_flags = cacheflags;
mSetBufferPool.push_back(vinfo);
}
mState = DEVICE_PREPARED;
return NO_ERROR;
}
/**
* setBufferPool
* Presents the pool of buffers to the device.
*
* The device has to be in CONFIGURED state.
*
* In this stage we request the buffer slots to the device and present
* them to the driver assigning one index to each buffer.
*
* After this method the device is PREPARED and ready to Q
* buffers
* The structures in the pool are empty.
* After this call the buffers have been presented to the device an index is assigned.
* The structure is updated with this index and other details (this is the output)
*
* \param pool: [IN/OUT]std::vector of v4l2_buffers structures
* \param cached: [IN]boolean to detect whether the buffers are cached or not
* A cached buffer in this context means that the buffer
* memory may be accessed through some system caches, and
* the V4L2 driver must do cache invalidation in case
* the image data source is not updating system caches
* in hardware.
* When false (not cached), V4L2 driver can assume no cache
* invalidation/flushes are needed for this buffer.
*\return INVALID_OPERATION if the device was not configured
*\return UNKNOWN_ERROR if any of the v4l2 commands fails
*\return NO_ERROR if everything went AOK
*/
status_t V4L2VideoNode::setBufferPool(std::vector<V4L2Buffer> &pool,
bool cached, int memType)
{
LOG1("@%s: device = %s memType = %d", __FUNCTION__, mName.c_str(), memType);
V4L2BufferInfo vinfo;
int ret;
uint32_t cacheflags = V4L2_BUF_FLAG_NO_CACHE_INVALIDATE |
V4L2_BUF_FLAG_NO_CACHE_CLEAN;
if ((mState != DEVICE_CONFIGURED)) {
LOGE("%s:Invalid operation, device %s not configured (state = %d)",
__FUNCTION__, mName.c_str(), mState);
return INVALID_OPERATION;
}
mBufferPool.clear();
int num_buffers = requestBuffers(pool.size(), memType);
if (num_buffers <= 0) {
LOGE("%s: Could not complete buffer request",__FUNCTION__);
return UNKNOWN_ERROR;
}
vinfo.width = mConfig.stride;
vinfo.height = mConfig.height;
vinfo.format = mConfig.format;
vinfo.length = mConfig.size;
for (unsigned int i = 0; i < pool.size(); i++) {
if (cached)
vinfo.cache_flags = 0;
else
vinfo.cache_flags = cacheflags;
vinfo.vbuffer = pool.at(i);
if (memType == V4L2_MEMORY_USERPTR) {
vinfo.data = (void*)(pool[i].userptr());
}
ret = newBuffer(i, vinfo, memType);
if (ret < 0) {
LOGE("Error querying buffers status");
mBufferPool.clear();
mState = DEVICE_ERROR;
return UNKNOWN_ERROR;
}
pool.at(i) = vinfo.vbuffer;
mBufferPool.push_back(vinfo);
}
mMemoryType = memType;
mState = DEVICE_PREPARED;
return NO_ERROR;
}
status_t V4L2VideoNode::enumModes(std::vector<struct v4l2_sensor_mode> &modes)
{
static const int MAX_ENUMS = 100000;
static const __u32 type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
struct v4l2_sensor_mode mode;
int r, id, is, ii;
for (id = 0; id < MAX_ENUMS; id++) {
CLEAR(mode.fmt);
mode.fmt.index = id;
mode.fmt.type = type;
r = pioctl(mFd, VIDIOC_ENUM_FMT, &mode.fmt, mName.c_str());
if (r < 0 && errno == EINVAL)
break;
if (r < 0)
return UNKNOWN_ERROR;
for (is = 0; is < MAX_ENUMS; is++) {
int width = 0, height = 0;
CLEAR(mode.size);
mode.size.index = is;
mode.size.pixel_format = mode.fmt.pixelformat;
r = pioctl(mFd, VIDIOC_ENUM_FRAMESIZES, &mode.size, mName.c_str());
if (r < 0 && errno == EINVAL)
break;
if (r < 0)
return UNKNOWN_ERROR;
switch (mode.size.type) {
case V4L2_FRMSIZE_TYPE_DISCRETE:
width = mode.size.discrete.width;
height = mode.size.discrete.height;
break;
case V4L2_FRMSIZE_TYPE_CONTINUOUS:
case V4L2_FRMSIZE_TYPE_STEPWISE:
width = mode.size.stepwise.min_width;
height = mode.size.stepwise.min_height;
break;
}
for (ii = 0; ii < MAX_ENUMS; ii++) {
CLEAR(mode.ival);
mode.ival.index = ii;
mode.ival.pixel_format = mode.fmt.pixelformat;
mode.ival.width = width;
mode.ival.height = height;
r = pioctl(mFd, VIDIOC_ENUM_FRAMEINTERVALS, &mode.ival, mName.c_str());
if (r < 0 && errno == EINVAL)
break;
if (r < 0)
return UNKNOWN_ERROR;
modes.push_back(mode);
}
if (ii >= MAX_ENUMS)
LOGE("%s too many frame intervals", __FUNCTION__);
}
if (is >= MAX_ENUMS)
LOGE("%s too many frame sizes", __FUNCTION__);
}
if (id >= MAX_ENUMS)
LOGE("%s too many frame formats", __FUNCTION__);
return NO_ERROR;
}
////////////////////////////////////////////////////////////////////
// PRIVATE METHODS
////////////////////////////////////////////////////////////////////
void V4L2VideoNode::destroyBufferPool()
{
LOG1("@%s: device = %s", __FUNCTION__, mName.c_str());
mBufferPool.clear();
requestBuffers(0, mMemoryType);
}
int V4L2VideoNode::requestBuffers(size_t num_buffers, int memType)
{
LOG1("@%s, num_buffers:%zu %s", __FUNCTION__, num_buffers, mName.c_str());
struct v4l2_requestbuffers req_buf;
int ret;
CLEAR(req_buf);
if (mState == DEVICE_CLOSED)
return 0;
req_buf.memory = memType;
req_buf.count = num_buffers;
req_buf.type = mBufType;
LOG1("VIDIOC_REQBUFS, count=%u, memory=%u, type=%u", req_buf.count, req_buf.memory, req_buf.type);
ret = pioctl(mFd, VIDIOC_REQBUFS, &req_buf, mName.c_str());
if (ret < 0) {
LOGE("VIDIOC_REQBUFS(%zu) returned: %d (%s)",
num_buffers, ret, strerror(errno));
return ret;
}
if (req_buf.count < num_buffers)
LOGW("Got less buffers than requested! %u < %zu",req_buf.count, num_buffers);
return req_buf.count;
}
void V4L2VideoNode::printBufferInfo(const char *func, V4L2Buffer &buf)
{
switch (mMemoryType) {
case V4L2_MEMORY_USERPTR:
LOG2("@%s %s idx:%d addr:%p", func, mName.c_str(), buf.index(), (void *)buf.userptr());
break;
case V4L2_MEMORY_MMAP:
LOG2("@%s %s idx:%d offset:0x%x", func, mName.c_str(), buf.index(), buf.offset());
break;
case V4L2_MEMORY_DMABUF:
LOG2("@%s %s idx:%d fd:%d", func, mName.c_str(), buf.index(), buf.fd());
break;
default:
LOG2("@%s %s unknown memory type %d", func, mName.c_str(), mMemoryType);
break;
}
}
int V4L2VideoNode::qbuf(V4L2BufferInfo *buf)
{
LOG2("@%s %s", __FUNCTION__, mName.c_str());
int ret = 0;
buf->vbuffer.setFlags(buf->cache_flags);
buf->vbuffer.setMemory(mMemoryType);
buf->vbuffer.setType(mBufType);
ret = pioctl(mFd, VIDIOC_QBUF, buf->vbuffer.get(), mName.c_str());
if (ret < 0) {
LOGE("VIDIOC_QBUF on %s failed: %s", mName.c_str(), strerror(errno));
return ret;
}
mBuffersInDevice++;
LOG2("VIDIOC_QBUF, index=%u, device(%s), mBuffersInDevice(%d)", buf->vbuffer.index(), mName.c_str(), mBuffersInDevice.load());
return ret;
}
int V4L2VideoNode::dqbuf(V4L2BufferInfo *buf)
{
LOG2("@%s %s", __FUNCTION__, mName.c_str());
V4L2Buffer &v4l2_buf = buf->vbuffer;
int ret = 0;
v4l2_buf.setMemory(mMemoryType);
v4l2_buf.setType(mBufType);
if (V4L2_TYPE_IS_MULTIPLANAR(mBufType)) {
int num_planes = numOfNonContiguousPlanes(mConfig.format);
v4l2_buf.setNumPlanes(num_planes);
}
ret = pioctl(mFd, VIDIOC_DQBUF, v4l2_buf.get(), mName.c_str());
if (ret < 0) {
if (errno != EAGAIN)
LOGE("VIDIOC_DQBUF failed: %s on %s for mBufType %d mMemoryType %d", strerror(errno), mName.c_str(), mBufType, mMemoryType);
else
LOG1("VIDIOC_DQBUF failed: %s on %s for mBufType %d mMemoryType %d", strerror(errno), mName.c_str(), mBufType, mMemoryType);
return ret;
}
mBuffersInDevice--;
LOG2("VIDIOC_DQBUF, index=%u, device(%s), mBuffersInDevice(%d)",
v4l2_buf.index(), mName.c_str(), mBuffersInDevice.load());
return ret;
}
/**
* creates an active buffer pool from the set-buffer-pool that is provided
* to the device by the call setBufferPool.
*
* We request to the V4L2 driver certain amount of buffer slots with the
* buffer configuration.
*
* Then copy the required number from the set-buffer-pool to the active-buffer-pool
*
* \param buffer_count: number of buffers that the active pool contains
* This number is at maximum the number of buffers in the set-buffer-pool
* \return 0 success
* -1 failure
*/
int V4L2VideoNode::createBufferPool(unsigned int buffer_count)
{
LOG1("@%s: device = %s buf count %d", __FUNCTION__, mName.c_str(), buffer_count);
int i(0);
int ret(0);
if (mState != DEVICE_PREPARED) {
LOGE("%s: Incorrect device state %d", __FUNCTION__, mState);
return -1;
}
if (buffer_count > mSetBufferPool.size()) {
LOGE("%s: Incorrect parameter requested %u, but only %zu provided",
__FUNCTION__, buffer_count, mSetBufferPool.size());
return -1;
}
int num_buffers = requestBuffers(buffer_count);
if (num_buffers <= 0) {
LOGE("%s: Could not complete buffer request",__FUNCTION__);
return -1;
}
mBufferPool.clear();
for (i = 0; i < num_buffers; i++) {
ret = newBuffer(i, mSetBufferPool.at(i));
if (ret < 0)
goto error;
mBufferPool.push_back(mSetBufferPool[i]);
}
return 0;
error:
LOGE("Failed to VIDIOC_QUERYBUF some of the buffers, clearing the active buffer pool");
mBufferPool.clear();
return ret;
}
int V4L2VideoNode::newBuffer(int index, V4L2BufferInfo &buf, int memType)
{
LOG1("@%s", __FUNCTION__);
int ret;
V4L2Buffer &vbuf = buf.vbuffer;
vbuf.setFlags(0x0);
vbuf.setMemory(memType);
vbuf.setType(mBufType);
vbuf.setIndex(index);
if (V4L2_TYPE_IS_MULTIPLANAR(mBufType)) {
int num_planes = numOfNonContiguousPlanes(mConfig.format);
vbuf.setNumPlanes(num_planes);
}
ret = pioctl(mFd , VIDIOC_QUERYBUF, vbuf.get(), mName.c_str());
if (ret < 0) {
LOGE("VIDIOC_QUERYBUF failed: %s", strerror(errno));
return ret;
}
if (memType == V4L2_MEMORY_USERPTR) {
vbuf.userptr((unsigned long)(buf.data));
}
buf.length = vbuf.length();
LOG1("index %u", vbuf.index());
LOG1("type %d", vbuf.type());
LOG1("bytesused %u", vbuf.bytesused());
LOG1("flags %08x", vbuf.flags());
if (memType == V4L2_MEMORY_MMAP) {
LOG1("memory MMAP: offset 0x%X", vbuf.offset());
} else if (memType == V4L2_MEMORY_USERPTR) {
LOG1("memory USRPTR: %p", (void*)vbuf.userptr());
} else if (memType == V4L2_MEMORY_DMABUF) {
LOG1("memory DMABUF: %d", vbuf.fd());
} else {
LOGE("not support memory type %d", memType);
}
LOG1("length %u", vbuf.length());
return ret;
}
int V4L2VideoNode::freeBuffer(V4L2BufferInfo *buf_info)
{
/**
* For devices using usr ptr as data this method is a no-op
*/
UNUSED(buf_info);
return 0;
}
status_t V4L2VideoNode::getFormat(V4L2Format &aFormat)
{
LOG1("@%s device = %s", __FUNCTION__, mName.c_str());
int ret = 0;
if ((mState != DEVICE_OPEN) &&
(mState != DEVICE_CONFIGURED)){
LOGE("%s invalid device state %d",__FUNCTION__, mState);
return INVALID_OPERATION;
}
aFormat.setType(mBufType);
ret = pioctl(mFd, VIDIOC_G_FMT, aFormat.get(), mName.c_str());
if (ret < 0) {
LOGE("VIDIOC_G_FMT failed: %s", strerror(errno));
return UNKNOWN_ERROR;
}
if (V4L2_TYPE_IS_META(mBufType)) {
LOG1("Get Meta format: %s format: %d, size: %d",
mName.c_str(),
aFormat.pixelformat(),
aFormat.sizeimage());
} else {
LOG1("Get pixel format: %s width: %d, height: %d, bpl: %d, fourcc: %s, field: %d",
mName.c_str(),
aFormat.width(),
aFormat.height(),
aFormat.bytesperline(),
v4l2Fmt2Str(aFormat.pixelformat()),
aFormat.field());
}
return NO_ERROR;
}
} NAMESPACE_DECLARATION_END