mirror of
https://github.com/opencv/opencv.git
synced 2025-06-13 13:13:26 +08:00
V4L2: Add multi-planar capture support
Devices which only support multi-planar capture cannot be processed as single-planar. Add multi-planar support to v4l driver.
This commit is contained in:
parent
925ff6241f
commit
a001ab3a44
@ -286,6 +286,12 @@ typedef uint32_t __u32;
|
|||||||
#define MAX_V4L_BUFFERS 10
|
#define MAX_V4L_BUFFERS 10
|
||||||
#define DEFAULT_V4L_BUFFERS 4
|
#define DEFAULT_V4L_BUFFERS 4
|
||||||
|
|
||||||
|
// types of memory in 'special' buffer
|
||||||
|
enum {
|
||||||
|
MEMORY_ORIG = 0, // Image data in original format.
|
||||||
|
MEMORY_RGB = 1, // Image data converted to RGB format.
|
||||||
|
};
|
||||||
|
|
||||||
// if enabled, then bad JPEG warnings become errors and cause NULL returned instead of image
|
// if enabled, then bad JPEG warnings become errors and cause NULL returned instead of image
|
||||||
#define V4L_ABORT_BADJPEG
|
#define V4L_ABORT_BADJPEG
|
||||||
|
|
||||||
@ -317,17 +323,27 @@ static const char* decode_ioctl_code(unsigned long ioctlCode)
|
|||||||
return "unknown";
|
return "unknown";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Memory
|
||||||
|
{
|
||||||
|
void * start;
|
||||||
|
size_t length;
|
||||||
|
|
||||||
|
Memory() : start(NULL), length(0) {}
|
||||||
|
};
|
||||||
|
|
||||||
/* Device Capture Objects */
|
/* Device Capture Objects */
|
||||||
/* V4L2 structure */
|
/* V4L2 structure */
|
||||||
struct Buffer
|
struct Buffer
|
||||||
{
|
{
|
||||||
void * start;
|
Memory memories[VIDEO_MAX_PLANES];
|
||||||
size_t length;
|
v4l2_plane planes[VIDEO_MAX_PLANES] = {};
|
||||||
|
// Total number of bytes occupied by data in the all planes (payload)
|
||||||
|
__u32 bytesused;
|
||||||
// This is dequeued buffer. It used for to put it back in the queue.
|
// This is dequeued buffer. It used for to put it back in the queue.
|
||||||
// The buffer is valid only if capture->bufferIndex >= 0
|
// The buffer is valid only if capture->bufferIndex >= 0
|
||||||
v4l2_buffer buffer;
|
v4l2_buffer buffer;
|
||||||
|
|
||||||
Buffer() : start(NULL), length(0)
|
Buffer()
|
||||||
{
|
{
|
||||||
buffer = v4l2_buffer();
|
buffer = v4l2_buffer();
|
||||||
}
|
}
|
||||||
@ -374,6 +390,7 @@ struct CvCaptureCAM_V4L CV_FINAL : public CvCapture
|
|||||||
v4l2_format form;
|
v4l2_format form;
|
||||||
v4l2_requestbuffers req;
|
v4l2_requestbuffers req;
|
||||||
v4l2_buf_type type;
|
v4l2_buf_type type;
|
||||||
|
unsigned char num_planes;
|
||||||
|
|
||||||
timeval timestamp;
|
timeval timestamp;
|
||||||
|
|
||||||
@ -430,6 +447,7 @@ CvCaptureCAM_V4L::CvCaptureCAM_V4L() :
|
|||||||
fps(0), convert_rgb(0), frame_allocated(false), returnFrame(false),
|
fps(0), convert_rgb(0), frame_allocated(false), returnFrame(false),
|
||||||
channelNumber(-1), normalizePropRange(false),
|
channelNumber(-1), normalizePropRange(false),
|
||||||
type(V4L2_BUF_TYPE_VIDEO_CAPTURE),
|
type(V4L2_BUF_TYPE_VIDEO_CAPTURE),
|
||||||
|
num_planes(0),
|
||||||
havePendingFrame(false)
|
havePendingFrame(false)
|
||||||
{
|
{
|
||||||
frame = cvIplImage();
|
frame = cvIplImage();
|
||||||
@ -472,15 +490,24 @@ bool CvCaptureCAM_V4L::isOpened() const
|
|||||||
bool CvCaptureCAM_V4L::try_palette_v4l2()
|
bool CvCaptureCAM_V4L::try_palette_v4l2()
|
||||||
{
|
{
|
||||||
form = v4l2_format();
|
form = v4l2_format();
|
||||||
form.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
form.type = type;
|
||||||
form.fmt.pix.pixelformat = palette;
|
if (V4L2_TYPE_IS_MULTIPLANAR(type)) {
|
||||||
form.fmt.pix.field = V4L2_FIELD_ANY;
|
form.fmt.pix_mp.pixelformat = palette;
|
||||||
form.fmt.pix.width = width;
|
form.fmt.pix_mp.field = V4L2_FIELD_ANY;
|
||||||
form.fmt.pix.height = height;
|
form.fmt.pix_mp.width = width;
|
||||||
|
form.fmt.pix_mp.height = height;
|
||||||
|
} else {
|
||||||
|
form.fmt.pix.pixelformat = palette;
|
||||||
|
form.fmt.pix.field = V4L2_FIELD_ANY;
|
||||||
|
form.fmt.pix.width = width;
|
||||||
|
form.fmt.pix.height = height;
|
||||||
|
}
|
||||||
if (!tryIoctl(VIDIOC_S_FMT, &form, true))
|
if (!tryIoctl(VIDIOC_S_FMT, &form, true))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (V4L2_TYPE_IS_MULTIPLANAR(type))
|
||||||
|
return palette == form.fmt.pix_mp.pixelformat;
|
||||||
return palette == form.fmt.pix.pixelformat;
|
return palette == form.fmt.pix.pixelformat;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -534,12 +561,15 @@ bool CvCaptureCAM_V4L::try_init_v4l2()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((capability.capabilities & V4L2_CAP_VIDEO_CAPTURE) == 0)
|
if ((capability.capabilities & (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_CAPTURE_MPLANE)) == 0)
|
||||||
{
|
{
|
||||||
/* Nope. */
|
/* Nope. */
|
||||||
CV_LOG_INFO(NULL, "VIDEOIO(V4L2:" << deviceName << "): not supported - device is unable to capture video (missing V4L2_CAP_VIDEO_CAPTURE)");
|
CV_LOG_INFO(NULL, "VIDEOIO(V4L2:" << deviceName << "): not supported - device is unable to capture video (missing V4L2_CAP_VIDEO_CAPTURE or V4L2_CAP_VIDEO_CAPTURE_MPLANE)");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (capability.capabilities & V4L2_CAP_VIDEO_CAPTURE_MPLANE)
|
||||||
|
type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -603,7 +633,7 @@ bool CvCaptureCAM_V4L::setFps(int value)
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
v4l2_streamparm streamparm = v4l2_streamparm();
|
v4l2_streamparm streamparm = v4l2_streamparm();
|
||||||
streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
streamparm.type = type;
|
||||||
streamparm.parm.capture.timeperframe.numerator = 1;
|
streamparm.parm.capture.timeperframe.numerator = 1;
|
||||||
streamparm.parm.capture.timeperframe.denominator = __u32(value);
|
streamparm.parm.capture.timeperframe.denominator = __u32(value);
|
||||||
if (!tryIoctl(VIDIOC_S_PARM, &streamparm) || !tryIoctl(VIDIOC_G_PARM, &streamparm))
|
if (!tryIoctl(VIDIOC_S_PARM, &streamparm) || !tryIoctl(VIDIOC_G_PARM, &streamparm))
|
||||||
@ -652,12 +682,21 @@ bool CvCaptureCAM_V4L::convertableToRgb() const
|
|||||||
|
|
||||||
void CvCaptureCAM_V4L::v4l2_create_frame()
|
void CvCaptureCAM_V4L::v4l2_create_frame()
|
||||||
{
|
{
|
||||||
CV_Assert(form.fmt.pix.width <= (uint)std::numeric_limits<int>::max());
|
CvSize size;
|
||||||
CV_Assert(form.fmt.pix.height <= (uint)std::numeric_limits<int>::max());
|
|
||||||
CvSize size = {(int)form.fmt.pix.width, (int)form.fmt.pix.height};
|
|
||||||
int channels = 3;
|
int channels = 3;
|
||||||
int depth = IPL_DEPTH_8U;
|
int depth = IPL_DEPTH_8U;
|
||||||
|
|
||||||
|
|
||||||
|
if (V4L2_TYPE_IS_MULTIPLANAR(type)) {
|
||||||
|
CV_Assert(form.fmt.pix_mp.width <= (uint)std::numeric_limits<int>::max());
|
||||||
|
CV_Assert(form.fmt.pix_mp.height <= (uint)std::numeric_limits<int>::max());
|
||||||
|
size = {(int)form.fmt.pix_mp.width, (int)form.fmt.pix_mp.height};
|
||||||
|
} else {
|
||||||
|
CV_Assert(form.fmt.pix.width <= (uint)std::numeric_limits<int>::max());
|
||||||
|
CV_Assert(form.fmt.pix.height <= (uint)std::numeric_limits<int>::max());
|
||||||
|
size = {(int)form.fmt.pix.width, (int)form.fmt.pix.height};
|
||||||
|
}
|
||||||
|
|
||||||
if (!convert_rgb) {
|
if (!convert_rgb) {
|
||||||
switch (palette) {
|
switch (palette) {
|
||||||
case V4L2_PIX_FMT_BGR24:
|
case V4L2_PIX_FMT_BGR24:
|
||||||
@ -689,9 +728,19 @@ void CvCaptureCAM_V4L::v4l2_create_frame()
|
|||||||
default:
|
default:
|
||||||
channels = 1;
|
channels = 1;
|
||||||
if(bufferIndex < 0)
|
if(bufferIndex < 0)
|
||||||
size = cvSize(buffers[MAX_V4L_BUFFERS].length, 1);
|
size = cvSize(buffers[MAX_V4L_BUFFERS].memories[MEMORY_ORIG].length, 1);
|
||||||
else
|
else {
|
||||||
size = cvSize(buffers[bufferIndex].buffer.bytesused, 1);
|
__u32 bytesused = 0;
|
||||||
|
if (V4L2_TYPE_IS_MULTIPLANAR(type)) {
|
||||||
|
__u32 data_offset;
|
||||||
|
for (unsigned char n_planes = 0; n_planes < num_planes; n_planes++) {
|
||||||
|
data_offset = buffers[bufferIndex].planes[n_planes].data_offset;
|
||||||
|
bytesused += buffers[bufferIndex].planes[n_planes].bytesused - data_offset;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
bytesused = buffers[bufferIndex].buffer.bytesused;
|
||||||
|
size = cvSize(bytesused, 1);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -723,7 +772,7 @@ bool CvCaptureCAM_V4L::initCapture()
|
|||||||
|
|
||||||
/* Find Window info */
|
/* Find Window info */
|
||||||
form = v4l2_format();
|
form = v4l2_format();
|
||||||
form.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
form.type = type;
|
||||||
|
|
||||||
if (!tryIoctl(VIDIOC_G_FMT, &form))
|
if (!tryIoctl(VIDIOC_G_FMT, &form))
|
||||||
{
|
{
|
||||||
@ -743,18 +792,27 @@ bool CvCaptureCAM_V4L::initCapture()
|
|||||||
/* try to set framerate */
|
/* try to set framerate */
|
||||||
setFps(fps);
|
setFps(fps);
|
||||||
|
|
||||||
unsigned int min;
|
|
||||||
|
|
||||||
/* Buggy driver paranoia. */
|
/* Buggy driver paranoia. */
|
||||||
min = form.fmt.pix.width * 2;
|
if (V4L2_TYPE_IS_MULTIPLANAR(type)) {
|
||||||
|
// TODO: add size adjustment if needed
|
||||||
|
} else {
|
||||||
|
unsigned int min;
|
||||||
|
|
||||||
if (form.fmt.pix.bytesperline < min)
|
min = form.fmt.pix.width * 2;
|
||||||
form.fmt.pix.bytesperline = min;
|
|
||||||
|
|
||||||
min = form.fmt.pix.bytesperline * form.fmt.pix.height;
|
if (form.fmt.pix.bytesperline < min)
|
||||||
|
form.fmt.pix.bytesperline = min;
|
||||||
|
|
||||||
if (form.fmt.pix.sizeimage < min)
|
min = form.fmt.pix.bytesperline * form.fmt.pix.height;
|
||||||
form.fmt.pix.sizeimage = min;
|
|
||||||
|
if (form.fmt.pix.sizeimage < min)
|
||||||
|
form.fmt.pix.sizeimage = min;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (V4L2_TYPE_IS_MULTIPLANAR(type))
|
||||||
|
num_planes = form.fmt.pix_mp.num_planes;
|
||||||
|
else
|
||||||
|
num_planes = 1;
|
||||||
|
|
||||||
if (!requestBuffers())
|
if (!requestBuffers())
|
||||||
return false;
|
return false;
|
||||||
@ -800,7 +858,7 @@ bool CvCaptureCAM_V4L::requestBuffers(unsigned int buffer_number)
|
|||||||
|
|
||||||
req = v4l2_requestbuffers();
|
req = v4l2_requestbuffers();
|
||||||
req.count = buffer_number;
|
req.count = buffer_number;
|
||||||
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
req.type = type;
|
||||||
req.memory = V4L2_MEMORY_MMAP;
|
req.memory = V4L2_MEMORY_MMAP;
|
||||||
|
|
||||||
if (!tryIoctl(VIDIOC_REQBUFS, &req)) {
|
if (!tryIoctl(VIDIOC_REQBUFS, &req)) {
|
||||||
@ -824,34 +882,56 @@ bool CvCaptureCAM_V4L::createBuffers()
|
|||||||
size_t maxLength = 0;
|
size_t maxLength = 0;
|
||||||
for (unsigned int n_buffers = 0; n_buffers < req.count; ++n_buffers) {
|
for (unsigned int n_buffers = 0; n_buffers < req.count; ++n_buffers) {
|
||||||
v4l2_buffer buf = v4l2_buffer();
|
v4l2_buffer buf = v4l2_buffer();
|
||||||
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
v4l2_plane mplanes[VIDEO_MAX_PLANES];
|
||||||
|
size_t length;
|
||||||
|
off_t offset;
|
||||||
|
buf.type = type;
|
||||||
buf.memory = V4L2_MEMORY_MMAP;
|
buf.memory = V4L2_MEMORY_MMAP;
|
||||||
buf.index = n_buffers;
|
buf.index = n_buffers;
|
||||||
|
if (V4L2_TYPE_IS_MULTIPLANAR(type)) {
|
||||||
|
buf.m.planes = mplanes;
|
||||||
|
buf.length = VIDEO_MAX_PLANES;
|
||||||
|
}
|
||||||
|
|
||||||
if (!tryIoctl(VIDIOC_QUERYBUF, &buf)) {
|
if (!tryIoctl(VIDIOC_QUERYBUF, &buf)) {
|
||||||
CV_LOG_WARNING(NULL, "VIDEOIO(V4L2:" << deviceName << "): failed VIDIOC_QUERYBUF: errno=" << errno << " (" << strerror(errno) << ")");
|
CV_LOG_WARNING(NULL, "VIDEOIO(V4L2:" << deviceName << "): failed VIDIOC_QUERYBUF: errno=" << errno << " (" << strerror(errno) << ")");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
buffers[n_buffers].length = buf.length;
|
CV_Assert(1 <= num_planes && num_planes <= VIDEO_MAX_PLANES);
|
||||||
buffers[n_buffers].start =
|
for (unsigned char n_planes = 0; n_planes < num_planes; n_planes++) {
|
||||||
mmap(NULL /* start anywhere */,
|
if (V4L2_TYPE_IS_MULTIPLANAR(type)) {
|
||||||
buf.length,
|
length = buf.m.planes[n_planes].length;
|
||||||
PROT_READ /* required */,
|
offset = buf.m.planes[n_planes].m.mem_offset;
|
||||||
MAP_SHARED /* recommended */,
|
} else {
|
||||||
deviceHandle, buf.m.offset);
|
length = buf.length;
|
||||||
|
offset = buf.m.offset;
|
||||||
|
}
|
||||||
|
|
||||||
if (MAP_FAILED == buffers[n_buffers].start) {
|
buffers[n_buffers].memories[n_planes].length = length;
|
||||||
CV_LOG_WARNING(NULL, "VIDEOIO(V4L2:" << deviceName << "): failed mmap(" << buf.length << "): errno=" << errno << " (" << strerror(errno) << ")");
|
buffers[n_buffers].memories[n_planes].start =
|
||||||
return false;
|
mmap(NULL /* start anywhere */,
|
||||||
|
length,
|
||||||
|
PROT_READ /* required */,
|
||||||
|
MAP_SHARED /* recommended */,
|
||||||
|
deviceHandle, offset);
|
||||||
|
if (MAP_FAILED == buffers[n_buffers].memories[n_planes].start) {
|
||||||
|
CV_LOG_WARNING(NULL, "VIDEOIO(V4L2:" << deviceName << "): failed mmap(" << length << "): errno=" << errno << " (" << strerror(errno) << ")");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
maxLength = maxLength > buf.length ? maxLength : buf.length;
|
|
||||||
|
maxLength = maxLength > length ? maxLength : length;
|
||||||
}
|
}
|
||||||
if (maxLength > 0) {
|
if (maxLength > 0) {
|
||||||
buffers[MAX_V4L_BUFFERS].start = malloc(maxLength);
|
maxLength *= num_planes;
|
||||||
buffers[MAX_V4L_BUFFERS].length = maxLength;
|
buffers[MAX_V4L_BUFFERS].memories[MEMORY_ORIG].start = malloc(maxLength);
|
||||||
|
buffers[MAX_V4L_BUFFERS].memories[MEMORY_ORIG].length = maxLength;
|
||||||
|
buffers[MAX_V4L_BUFFERS].memories[MEMORY_RGB].start = malloc(maxLength);
|
||||||
|
buffers[MAX_V4L_BUFFERS].memories[MEMORY_RGB].length = maxLength;
|
||||||
}
|
}
|
||||||
return buffers[MAX_V4L_BUFFERS].start != 0;
|
return (buffers[MAX_V4L_BUFFERS].memories[MEMORY_ORIG].start != 0) &&
|
||||||
|
(buffers[MAX_V4L_BUFFERS].memories[MEMORY_RGB].start != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -933,8 +1013,13 @@ bool CvCaptureCAM_V4L::open(const char* _deviceName)
|
|||||||
bool CvCaptureCAM_V4L::read_frame_v4l2()
|
bool CvCaptureCAM_V4L::read_frame_v4l2()
|
||||||
{
|
{
|
||||||
v4l2_buffer buf = v4l2_buffer();
|
v4l2_buffer buf = v4l2_buffer();
|
||||||
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
v4l2_plane mplanes[VIDEO_MAX_PLANES];
|
||||||
|
buf.type = type;
|
||||||
buf.memory = V4L2_MEMORY_MMAP;
|
buf.memory = V4L2_MEMORY_MMAP;
|
||||||
|
if (V4L2_TYPE_IS_MULTIPLANAR(type)) {
|
||||||
|
buf.m.planes = mplanes;
|
||||||
|
buf.length = VIDEO_MAX_PLANES;
|
||||||
|
}
|
||||||
|
|
||||||
while (!tryIoctl(VIDIOC_DQBUF, &buf)) {
|
while (!tryIoctl(VIDIOC_DQBUF, &buf)) {
|
||||||
int err = errno;
|
int err = errno;
|
||||||
@ -951,12 +1036,33 @@ bool CvCaptureCAM_V4L::read_frame_v4l2()
|
|||||||
}
|
}
|
||||||
|
|
||||||
CV_Assert(buf.index < req.count);
|
CV_Assert(buf.index < req.count);
|
||||||
CV_Assert(buffers[buf.index].length == buf.length);
|
|
||||||
|
if (V4L2_TYPE_IS_MULTIPLANAR(type)) {
|
||||||
|
for (unsigned char n_planes = 0; n_planes < num_planes; n_planes++)
|
||||||
|
CV_Assert(buffers[buf.index].memories[n_planes].length == buf.m.planes[n_planes].length);
|
||||||
|
} else
|
||||||
|
CV_Assert(buffers[buf.index].memories[MEMORY_ORIG].length == buf.length);
|
||||||
|
|
||||||
//We shouldn't use this buffer in the queue while not retrieve frame from it.
|
//We shouldn't use this buffer in the queue while not retrieve frame from it.
|
||||||
buffers[buf.index].buffer = buf;
|
buffers[buf.index].buffer = buf;
|
||||||
bufferIndex = buf.index;
|
bufferIndex = buf.index;
|
||||||
|
|
||||||
|
if (V4L2_TYPE_IS_MULTIPLANAR(type)) {
|
||||||
|
__u32 offset = 0;
|
||||||
|
|
||||||
|
buffers[buf.index].buffer.m.planes = buffers[buf.index].planes;
|
||||||
|
memcpy(buffers[buf.index].planes, buf.m.planes, sizeof(mplanes));
|
||||||
|
|
||||||
|
for (unsigned char n_planes = 0; n_planes < num_planes; n_planes++) {
|
||||||
|
__u32 bytesused;
|
||||||
|
bytesused = buffers[buf.index].planes[n_planes].bytesused -
|
||||||
|
buffers[buf.index].planes[n_planes].data_offset;
|
||||||
|
offset += bytesused;
|
||||||
|
}
|
||||||
|
buffers[buf.index].bytesused = offset;
|
||||||
|
} else
|
||||||
|
buffers[buf.index].bytesused = buffers[buf.index].buffer.bytesused;
|
||||||
|
|
||||||
//set timestamp in capture struct to be timestamp of most recent frame
|
//set timestamp in capture struct to be timestamp of most recent frame
|
||||||
timestamp = buf.timestamp;
|
timestamp = buf.timestamp;
|
||||||
return true;
|
return true;
|
||||||
@ -1042,10 +1148,15 @@ bool CvCaptureCAM_V4L::grabFrame()
|
|||||||
bufferIndex = -1;
|
bufferIndex = -1;
|
||||||
for (__u32 index = 0; index < req.count; ++index) {
|
for (__u32 index = 0; index < req.count; ++index) {
|
||||||
v4l2_buffer buf = v4l2_buffer();
|
v4l2_buffer buf = v4l2_buffer();
|
||||||
|
v4l2_plane mplanes[VIDEO_MAX_PLANES];
|
||||||
|
|
||||||
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
buf.type = type;
|
||||||
buf.memory = V4L2_MEMORY_MMAP;
|
buf.memory = V4L2_MEMORY_MMAP;
|
||||||
buf.index = index;
|
buf.index = index;
|
||||||
|
if (V4L2_TYPE_IS_MULTIPLANAR(type)) {
|
||||||
|
buf.m.planes = mplanes;
|
||||||
|
buf.length = VIDEO_MAX_PLANES;
|
||||||
|
}
|
||||||
|
|
||||||
if (!tryIoctl(VIDIOC_QBUF, &buf)) {
|
if (!tryIoctl(VIDIOC_QBUF, &buf)) {
|
||||||
CV_LOG_DEBUG(NULL, "VIDEOIO(V4L2:" << deviceName << "): failed VIDIOC_QBUF (buffer=" << index << "): errno=" << errno << " (" << strerror(errno) << ")");
|
CV_LOG_DEBUG(NULL, "VIDEOIO(V4L2:" << deviceName << "): failed VIDIOC_QBUF (buffer=" << index << "): errno=" << errno << " (" << strerror(errno) << ")");
|
||||||
@ -1534,35 +1645,51 @@ static int sonix_decompress(int width, int height, unsigned char *inp, unsigned
|
|||||||
|
|
||||||
void CvCaptureCAM_V4L::convertToRgb(const Buffer ¤tBuffer)
|
void CvCaptureCAM_V4L::convertToRgb(const Buffer ¤tBuffer)
|
||||||
{
|
{
|
||||||
cv::Size imageSize(form.fmt.pix.width, form.fmt.pix.height);
|
cv::Size imageSize;
|
||||||
|
unsigned char *start;
|
||||||
|
|
||||||
|
if (V4L2_TYPE_IS_MULTIPLANAR(type)) {
|
||||||
|
__u32 offset = 0;
|
||||||
|
start = (unsigned char*)buffers[MAX_V4L_BUFFERS].memories[MEMORY_ORIG].start;
|
||||||
|
for (unsigned char n_planes = 0; n_planes < num_planes; n_planes++) {
|
||||||
|
__u32 data_offset, bytesused;
|
||||||
|
data_offset = currentBuffer.planes[n_planes].data_offset;
|
||||||
|
bytesused = currentBuffer.planes[n_planes].bytesused - data_offset;
|
||||||
|
memcpy(start + offset, (char *)currentBuffer.memories[n_planes].start + data_offset,
|
||||||
|
std::min(currentBuffer.memories[n_planes].length, (size_t)bytesused));
|
||||||
|
offset += bytesused;
|
||||||
|
}
|
||||||
|
|
||||||
|
imageSize = cv::Size(form.fmt.pix_mp.width, form.fmt.pix_mp.height);
|
||||||
|
} else {
|
||||||
|
start = (unsigned char*)currentBuffer.memories[MEMORY_ORIG].start;
|
||||||
|
|
||||||
|
imageSize = cv::Size(form.fmt.pix.width, form.fmt.pix.height);
|
||||||
|
}
|
||||||
// Not found conversion
|
// Not found conversion
|
||||||
switch (palette)
|
switch (palette)
|
||||||
{
|
{
|
||||||
case V4L2_PIX_FMT_YUV411P:
|
case V4L2_PIX_FMT_YUV411P:
|
||||||
yuv411p_to_rgb24(imageSize.width, imageSize.height,
|
yuv411p_to_rgb24(imageSize.width, imageSize.height,
|
||||||
(unsigned char*)(currentBuffer.start),
|
start, (unsigned char*)frame.imageData);
|
||||||
(unsigned char*)frame.imageData);
|
|
||||||
return;
|
return;
|
||||||
case V4L2_PIX_FMT_SBGGR8:
|
case V4L2_PIX_FMT_SBGGR8:
|
||||||
bayer2rgb24(imageSize.width, imageSize.height,
|
bayer2rgb24(imageSize.width, imageSize.height,
|
||||||
(unsigned char*)currentBuffer.start,
|
start, (unsigned char*)frame.imageData);
|
||||||
(unsigned char*)frame.imageData);
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case V4L2_PIX_FMT_SN9C10X:
|
case V4L2_PIX_FMT_SN9C10X:
|
||||||
sonix_decompress_init();
|
sonix_decompress_init();
|
||||||
sonix_decompress(imageSize.width, imageSize.height,
|
sonix_decompress(imageSize.width, imageSize.height,
|
||||||
(unsigned char*)currentBuffer.start,
|
start, (unsigned char*)buffers[MAX_V4L_BUFFERS].memories[MEMORY_RGB].start);
|
||||||
(unsigned char*)buffers[MAX_V4L_BUFFERS].start);
|
|
||||||
|
|
||||||
bayer2rgb24(imageSize.width, imageSize.height,
|
bayer2rgb24(imageSize.width, imageSize.height,
|
||||||
(unsigned char*)buffers[MAX_V4L_BUFFERS].start,
|
(unsigned char*)buffers[MAX_V4L_BUFFERS].memories[MEMORY_RGB].start,
|
||||||
(unsigned char*)frame.imageData);
|
(unsigned char*)frame.imageData);
|
||||||
return;
|
return;
|
||||||
case V4L2_PIX_FMT_SGBRG8:
|
case V4L2_PIX_FMT_SGBRG8:
|
||||||
sgbrg2rgb24(imageSize.width, imageSize.height,
|
sgbrg2rgb24(imageSize.width, imageSize.height,
|
||||||
(unsigned char*)currentBuffer.start,
|
start, (unsigned char*)frame.imageData);
|
||||||
(unsigned char*)frame.imageData);
|
|
||||||
return;
|
return;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@ -1571,69 +1698,69 @@ void CvCaptureCAM_V4L::convertToRgb(const Buffer ¤tBuffer)
|
|||||||
cv::Mat destination(imageSize, CV_8UC3, frame.imageData);
|
cv::Mat destination(imageSize, CV_8UC3, frame.imageData);
|
||||||
switch (palette) {
|
switch (palette) {
|
||||||
case V4L2_PIX_FMT_YVU420:
|
case V4L2_PIX_FMT_YVU420:
|
||||||
cv::cvtColor(cv::Mat(imageSize.height * 3 / 2, imageSize.width, CV_8U, currentBuffer.start), destination,
|
cv::cvtColor(cv::Mat(imageSize.height * 3 / 2, imageSize.width, CV_8U, start), destination,
|
||||||
COLOR_YUV2BGR_YV12);
|
COLOR_YUV2BGR_YV12);
|
||||||
return;
|
return;
|
||||||
case V4L2_PIX_FMT_YUV420:
|
case V4L2_PIX_FMT_YUV420:
|
||||||
cv::cvtColor(cv::Mat(imageSize.height * 3 / 2, imageSize.width, CV_8U, currentBuffer.start), destination,
|
cv::cvtColor(cv::Mat(imageSize.height * 3 / 2, imageSize.width, CV_8U, start), destination,
|
||||||
COLOR_YUV2BGR_IYUV);
|
COLOR_YUV2BGR_IYUV);
|
||||||
return;
|
return;
|
||||||
case V4L2_PIX_FMT_NV12:
|
case V4L2_PIX_FMT_NV12:
|
||||||
cv::cvtColor(cv::Mat(imageSize.height * 3 / 2, imageSize.width, CV_8U, currentBuffer.start), destination,
|
cv::cvtColor(cv::Mat(imageSize.height * 3 / 2, imageSize.width, CV_8U, start), destination,
|
||||||
COLOR_YUV2RGB_NV12);
|
COLOR_YUV2RGB_NV12);
|
||||||
return;
|
return;
|
||||||
case V4L2_PIX_FMT_NV21:
|
case V4L2_PIX_FMT_NV21:
|
||||||
cv::cvtColor(cv::Mat(imageSize.height * 3 / 2, imageSize.width, CV_8U, currentBuffer.start), destination,
|
cv::cvtColor(cv::Mat(imageSize.height * 3 / 2, imageSize.width, CV_8U, start), destination,
|
||||||
COLOR_YUV2RGB_NV21);
|
COLOR_YUV2RGB_NV21);
|
||||||
return;
|
return;
|
||||||
#ifdef HAVE_JPEG
|
#ifdef HAVE_JPEG
|
||||||
case V4L2_PIX_FMT_MJPEG:
|
case V4L2_PIX_FMT_MJPEG:
|
||||||
case V4L2_PIX_FMT_JPEG:
|
case V4L2_PIX_FMT_JPEG:
|
||||||
CV_LOG_DEBUG(NULL, "VIDEOIO(V4L2:" << deviceName << "): decoding JPEG frame: size=" << currentBuffer.buffer.bytesused);
|
CV_LOG_DEBUG(NULL, "VIDEOIO(V4L2:" << deviceName << "): decoding JPEG frame: size=" << currentBuffer.bytesused);
|
||||||
cv::imdecode(Mat(1, currentBuffer.buffer.bytesused, CV_8U, currentBuffer.start), IMREAD_COLOR, &destination);
|
cv::imdecode(Mat(1, currentBuffer.bytesused, CV_8U, start), IMREAD_COLOR, &destination);
|
||||||
return;
|
return;
|
||||||
#endif
|
#endif
|
||||||
case V4L2_PIX_FMT_YUYV:
|
case V4L2_PIX_FMT_YUYV:
|
||||||
cv::cvtColor(cv::Mat(imageSize, CV_8UC2, currentBuffer.start), destination, COLOR_YUV2BGR_YUYV);
|
cv::cvtColor(cv::Mat(imageSize, CV_8UC2, start), destination, COLOR_YUV2BGR_YUYV);
|
||||||
return;
|
return;
|
||||||
case V4L2_PIX_FMT_UYVY:
|
case V4L2_PIX_FMT_UYVY:
|
||||||
cv::cvtColor(cv::Mat(imageSize, CV_8UC2, currentBuffer.start), destination, COLOR_YUV2BGR_UYVY);
|
cv::cvtColor(cv::Mat(imageSize, CV_8UC2, start), destination, COLOR_YUV2BGR_UYVY);
|
||||||
return;
|
return;
|
||||||
case V4L2_PIX_FMT_RGB24:
|
case V4L2_PIX_FMT_RGB24:
|
||||||
cv::cvtColor(cv::Mat(imageSize, CV_8UC3, currentBuffer.start), destination, COLOR_RGB2BGR);
|
cv::cvtColor(cv::Mat(imageSize, CV_8UC3, start), destination, COLOR_RGB2BGR);
|
||||||
return;
|
return;
|
||||||
case V4L2_PIX_FMT_Y16:
|
case V4L2_PIX_FMT_Y16:
|
||||||
{
|
{
|
||||||
cv::Mat temp(imageSize, CV_8UC1, buffers[MAX_V4L_BUFFERS].start);
|
cv::Mat temp(imageSize, CV_8UC1, buffers[MAX_V4L_BUFFERS].memories[MEMORY_RGB].start);
|
||||||
cv::Mat(imageSize, CV_16UC1, currentBuffer.start).convertTo(temp, CV_8U, 1.0 / 256);
|
cv::Mat(imageSize, CV_16UC1, start).convertTo(temp, CV_8U, 1.0 / 256);
|
||||||
cv::cvtColor(temp, destination, COLOR_GRAY2BGR);
|
cv::cvtColor(temp, destination, COLOR_GRAY2BGR);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case V4L2_PIX_FMT_Y12:
|
case V4L2_PIX_FMT_Y12:
|
||||||
{
|
{
|
||||||
cv::Mat temp(imageSize, CV_8UC1, buffers[MAX_V4L_BUFFERS].start);
|
cv::Mat temp(imageSize, CV_8UC1, buffers[MAX_V4L_BUFFERS].memories[MEMORY_RGB].start);
|
||||||
cv::Mat(imageSize, CV_16UC1, currentBuffer.start).convertTo(temp, CV_8U, 1.0 / 16);
|
cv::Mat(imageSize, CV_16UC1, start).convertTo(temp, CV_8U, 1.0 / 16);
|
||||||
cv::cvtColor(temp, destination, COLOR_GRAY2BGR);
|
cv::cvtColor(temp, destination, COLOR_GRAY2BGR);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case V4L2_PIX_FMT_Y10:
|
case V4L2_PIX_FMT_Y10:
|
||||||
{
|
{
|
||||||
cv::Mat temp(imageSize, CV_8UC1, buffers[MAX_V4L_BUFFERS].start);
|
cv::Mat temp(imageSize, CV_8UC1, buffers[MAX_V4L_BUFFERS].memories[MEMORY_RGB].start);
|
||||||
cv::Mat(imageSize, CV_16UC1, currentBuffer.start).convertTo(temp, CV_8U, 1.0 / 4);
|
cv::Mat(imageSize, CV_16UC1, start).convertTo(temp, CV_8U, 1.0 / 4);
|
||||||
cv::cvtColor(temp, destination, COLOR_GRAY2BGR);
|
cv::cvtColor(temp, destination, COLOR_GRAY2BGR);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case V4L2_PIX_FMT_GREY:
|
case V4L2_PIX_FMT_GREY:
|
||||||
cv::cvtColor(cv::Mat(imageSize, CV_8UC1, currentBuffer.start), destination, COLOR_GRAY2BGR);
|
cv::cvtColor(cv::Mat(imageSize, CV_8UC1, start), destination, COLOR_GRAY2BGR);
|
||||||
break;
|
break;
|
||||||
case V4L2_PIX_FMT_XBGR32:
|
case V4L2_PIX_FMT_XBGR32:
|
||||||
case V4L2_PIX_FMT_ABGR32:
|
case V4L2_PIX_FMT_ABGR32:
|
||||||
cv::cvtColor(cv::Mat(imageSize, CV_8UC4, currentBuffer.start), destination, COLOR_BGRA2BGR);
|
cv::cvtColor(cv::Mat(imageSize, CV_8UC4, start), destination, COLOR_BGRA2BGR);
|
||||||
break;
|
break;
|
||||||
case V4L2_PIX_FMT_BGR24:
|
case V4L2_PIX_FMT_BGR24:
|
||||||
default:
|
default:
|
||||||
memcpy((char *)frame.imageData, (char *)currentBuffer.start,
|
memcpy((char *)frame.imageData, start,
|
||||||
std::min(frame.imageSize, (int)currentBuffer.buffer.bytesused));
|
std::min(frame.imageSize, (int)currentBuffer.bytesused));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1904,9 +2031,15 @@ double CvCaptureCAM_V4L::getProperty(int property_id) const
|
|||||||
{
|
{
|
||||||
switch (property_id) {
|
switch (property_id) {
|
||||||
case cv::CAP_PROP_FRAME_WIDTH:
|
case cv::CAP_PROP_FRAME_WIDTH:
|
||||||
return form.fmt.pix.width;
|
if (V4L2_TYPE_IS_MULTIPLANAR(type))
|
||||||
|
return form.fmt.pix_mp.width;
|
||||||
|
else
|
||||||
|
return form.fmt.pix.width;
|
||||||
case cv::CAP_PROP_FRAME_HEIGHT:
|
case cv::CAP_PROP_FRAME_HEIGHT:
|
||||||
return form.fmt.pix.height;
|
if (V4L2_TYPE_IS_MULTIPLANAR(type))
|
||||||
|
return form.fmt.pix_mp.height;
|
||||||
|
else
|
||||||
|
return form.fmt.pix.height;
|
||||||
case cv::CAP_PROP_FOURCC:
|
case cv::CAP_PROP_FOURCC:
|
||||||
return palette;
|
return palette;
|
||||||
case cv::CAP_PROP_FORMAT:
|
case cv::CAP_PROP_FORMAT:
|
||||||
@ -1922,7 +2055,7 @@ double CvCaptureCAM_V4L::getProperty(int property_id) const
|
|||||||
case cv::CAP_PROP_FPS:
|
case cv::CAP_PROP_FPS:
|
||||||
{
|
{
|
||||||
v4l2_streamparm sp = v4l2_streamparm();
|
v4l2_streamparm sp = v4l2_streamparm();
|
||||||
sp.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
sp.type = type;
|
||||||
if (!tryIoctl(VIDIOC_G_PARM, &sp)) {
|
if (!tryIoctl(VIDIOC_G_PARM, &sp)) {
|
||||||
CV_LOG_WARNING(NULL, "VIDEOIO(V4L2:" << deviceName << "): Unable to get camera FPS");
|
CV_LOG_WARNING(NULL, "VIDEOIO(V4L2:" << deviceName << "): Unable to get camera FPS");
|
||||||
return -1;
|
return -1;
|
||||||
@ -2063,9 +2196,14 @@ void CvCaptureCAM_V4L::releaseBuffers()
|
|||||||
{
|
{
|
||||||
releaseFrame();
|
releaseFrame();
|
||||||
|
|
||||||
if (buffers[MAX_V4L_BUFFERS].start) {
|
if (buffers[MAX_V4L_BUFFERS].memories[MEMORY_ORIG].start) {
|
||||||
free(buffers[MAX_V4L_BUFFERS].start);
|
free(buffers[MAX_V4L_BUFFERS].memories[MEMORY_ORIG].start);
|
||||||
buffers[MAX_V4L_BUFFERS].start = 0;
|
buffers[MAX_V4L_BUFFERS].memories[MEMORY_ORIG].start = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffers[MAX_V4L_BUFFERS].memories[MEMORY_RGB].start) {
|
||||||
|
free(buffers[MAX_V4L_BUFFERS].memories[MEMORY_RGB].start);
|
||||||
|
buffers[MAX_V4L_BUFFERS].memories[MEMORY_RGB].start = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bufferIndex = -1;
|
bufferIndex = -1;
|
||||||
@ -2076,11 +2214,14 @@ void CvCaptureCAM_V4L::releaseBuffers()
|
|||||||
v4l_buffersRequested = false;
|
v4l_buffersRequested = false;
|
||||||
|
|
||||||
for (unsigned int n_buffers = 0; n_buffers < MAX_V4L_BUFFERS; ++n_buffers) {
|
for (unsigned int n_buffers = 0; n_buffers < MAX_V4L_BUFFERS; ++n_buffers) {
|
||||||
if (buffers[n_buffers].start) {
|
for (unsigned char n_planes = 0; n_planes < num_planes; n_planes++) {
|
||||||
if (-1 == munmap(buffers[n_buffers].start, buffers[n_buffers].length)) {
|
if (buffers[n_buffers].memories[n_planes].start) {
|
||||||
CV_LOG_DEBUG(NULL, "VIDEOIO(V4L2:" << deviceName << "): failed munmap(): errno=" << errno << " (" << strerror(errno) << ")");
|
if (-1 == munmap(buffers[n_buffers].memories[n_planes].start,
|
||||||
} else {
|
buffers[n_buffers].memories[n_planes].length)) {
|
||||||
buffers[n_buffers].start = 0;
|
CV_LOG_DEBUG(NULL, "VIDEOIO(V4L2:" << deviceName << "): failed munmap(): errno=" << errno << " (" << strerror(errno) << ")");
|
||||||
|
} else {
|
||||||
|
buffers[n_buffers].memories[n_planes].start = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2100,7 +2241,6 @@ bool CvCaptureCAM_V4L::streaming(bool startStream)
|
|||||||
return !startStream;
|
return !startStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
||||||
bool result = tryIoctl(startStream ? VIDIOC_STREAMON : VIDIOC_STREAMOFF, &type);
|
bool result = tryIoctl(startStream ? VIDIOC_STREAMON : VIDIOC_STREAMOFF, &type);
|
||||||
if (result)
|
if (result)
|
||||||
{
|
{
|
||||||
@ -2133,13 +2273,26 @@ IplImage *CvCaptureCAM_V4L::retrieveFrame(int)
|
|||||||
} else {
|
} else {
|
||||||
// for mjpeg streams the size might change in between, so we have to change the header
|
// for mjpeg streams the size might change in between, so we have to change the header
|
||||||
// We didn't allocate memory when not convert_rgb, but we have to recreate the header
|
// We didn't allocate memory when not convert_rgb, but we have to recreate the header
|
||||||
CV_LOG_DEBUG(NULL, "VIDEOIO(V4L2:" << deviceName << "): buffer input size=" << currentBuffer.buffer.bytesused);
|
CV_LOG_DEBUG(NULL, "VIDEOIO(V4L2:" << deviceName << "): buffer input size=" << currentBuffer.bytesused);
|
||||||
if (frame.imageSize != (int)currentBuffer.buffer.bytesused)
|
if (frame.imageSize != (int)currentBuffer.bytesused)
|
||||||
v4l2_create_frame();
|
v4l2_create_frame();
|
||||||
|
|
||||||
frame.imageData = (char *)buffers[MAX_V4L_BUFFERS].start;
|
frame.imageData = (char *)buffers[MAX_V4L_BUFFERS].memories[MEMORY_ORIG].start;
|
||||||
memcpy(buffers[MAX_V4L_BUFFERS].start, currentBuffer.start,
|
if (V4L2_TYPE_IS_MULTIPLANAR(type)) {
|
||||||
std::min(buffers[MAX_V4L_BUFFERS].length, (size_t)currentBuffer.buffer.bytesused));
|
__u32 offset = 0;
|
||||||
|
for (unsigned char n_planes = 0; n_planes < num_planes; n_planes++) {
|
||||||
|
__u32 data_offset, bytesused;
|
||||||
|
data_offset = currentBuffer.planes[n_planes].data_offset;
|
||||||
|
bytesused = currentBuffer.planes[n_planes].bytesused - data_offset;
|
||||||
|
memcpy((unsigned char*)buffers[MAX_V4L_BUFFERS].memories[MEMORY_ORIG].start + offset,
|
||||||
|
(char *)currentBuffer.memories[n_planes].start + data_offset,
|
||||||
|
std::min(currentBuffer.memories[n_planes].length, (size_t)bytesused));
|
||||||
|
offset += bytesused;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
memcpy(buffers[MAX_V4L_BUFFERS].memories[MEMORY_ORIG].start, currentBuffer.memories[MEMORY_ORIG].start,
|
||||||
|
std::min(buffers[MAX_V4L_BUFFERS].memories[MEMORY_ORIG].length, (size_t)currentBuffer.buffer.bytesused));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
//Revert buffer to the queue
|
//Revert buffer to the queue
|
||||||
if (!tryIoctl(VIDIOC_QBUF, &buffers[bufferIndex].buffer))
|
if (!tryIoctl(VIDIOC_QBUF, &buffers[bufferIndex].buffer))
|
||||||
|
Loading…
Reference in New Issue
Block a user