Added framerate decimation

Former-commit-id: 8f7eb09978aef5364cbab1f5dfe12afbf60e6cac
This commit is contained in:
johan 2014-01-12 14:18:27 +01:00
parent 558ad6a11f
commit 421ec6926f
3 changed files with 163 additions and 149 deletions

View File

@ -19,22 +19,25 @@
#define CLEAR(x) memset(&(x), 0, sizeof(x)) #define CLEAR(x) memset(&(x), 0, sizeof(x))
V4L2Grabber::V4L2Grabber(const std::string &device, int input, VideoStandard videoStandard, double fps) : V4L2Grabber::V4L2Grabber(const std::string &device, int input, VideoStandard videoStandard, int frameDecimation, int pixelDecimation) :
_deviceName(device), _deviceName(device),
_ioMethod(IO_METHOD_MMAP), _ioMethod(IO_METHOD_MMAP),
_fileDescriptor(-1), _fileDescriptor(-1),
_buffers(), _buffers(),
_width(0), _width(0),
_height(0) _height(0),
_frameDecimation(frameDecimation),
_pixelDecimation(pixelDecimation),
_currentFrame(0)
{ {
open_device(); open_device();
init_device(videoStandard, input); init_device(videoStandard, input);
} }
V4L2Grabber::~V4L2Grabber() V4L2Grabber::~V4L2Grabber()
{ {
uninit_device(); uninit_device();
close_device(); close_device();
} }
void V4L2Grabber::start() void V4L2Grabber::start()
@ -42,47 +45,48 @@ void V4L2Grabber::start()
start_capturing(); start_capturing();
} }
void V4L2Grabber::capture() void V4L2Grabber::capture(int frameCount)
{ {
int count = 1; for (int count = 0; count < frameCount || frameCount < 0; ++count)
while (count-- > 0) { {
for (;;) { for (;;)
fd_set fds; {
struct timeval tv; // the set of file descriptors for select
int r; fd_set fds;
FD_ZERO(&fds);
FD_SET(_fileDescriptor, &fds);
FD_ZERO(&fds); // timeout
FD_SET(_fileDescriptor, &fds); struct timeval tv;
tv.tv_sec = 2;
tv.tv_usec = 0;
/* Timeout. */ // block until data is available
tv.tv_sec = 2; int r = select(_fileDescriptor + 1, &fds, NULL, NULL, &tv);
tv.tv_usec = 0;
r = select(_fileDescriptor + 1, &fds, NULL, NULL, &tv); if (-1 == r)
{
if (EINTR == errno)
continue;
errno_exit("select");
}
if (-1 == r) if (0 == r)
{ {
if (EINTR == errno) fprintf(stderr, "select timeout\n");
continue; exit(EXIT_FAILURE);
errno_exit("select"); }
}
if (0 == r) if (read_frame())
{ break;
fprintf(stderr, "select timeout\n"); /* EAGAIN - continue select loop. */
exit(EXIT_FAILURE); }
} }
if (read_frame())
break;
/* EAGAIN - continue select loop. */
}
}
} }
void V4L2Grabber::stop() void V4L2Grabber::stop()
{ {
stop_capturing(); stop_capturing();
} }
void V4L2Grabber::open_device() void V4L2Grabber::open_device()
@ -218,7 +222,7 @@ void V4L2Grabber::init_userp(unsigned int buffer_size)
void V4L2Grabber::init_device(VideoStandard videoStandard, int input) void V4L2Grabber::init_device(VideoStandard videoStandard, int input)
{ {
struct v4l2_capability cap; struct v4l2_capability cap;
if (-1 == xioctl(VIDIOC_QUERYCAP, &cap)) if (-1 == xioctl(VIDIOC_QUERYCAP, &cap))
{ {
if (EINVAL == errno) { if (EINVAL == errno) {
fprintf(stderr, "%s is no V4L2 device\n", _deviceName.c_str()); fprintf(stderr, "%s is no V4L2 device\n", _deviceName.c_str());
@ -256,14 +260,14 @@ void V4L2Grabber::init_device(VideoStandard videoStandard, int input)
/* Select video input, video standard and tune here. */ /* Select video input, video standard and tune here. */
struct v4l2_cropcap cropcap; struct v4l2_cropcap cropcap;
CLEAR(cropcap); CLEAR(cropcap);
cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (0 == xioctl(VIDIOC_CROPCAP, &cropcap)) { if (0 == xioctl(VIDIOC_CROPCAP, &cropcap)) {
struct v4l2_crop crop; struct v4l2_crop crop;
crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
crop.c = cropcap.defrect; /* reset to default */ crop.c = cropcap.defrect; /* reset to default */
if (-1 == xioctl(VIDIOC_S_CROP, &crop)) { if (-1 == xioctl(VIDIOC_S_CROP, &crop)) {
@ -280,61 +284,61 @@ void V4L2Grabber::init_device(VideoStandard videoStandard, int input)
/* Errors ignored. */ /* Errors ignored. */
} }
// set input if needed // set input if needed
if (input >= 0) if (input >= 0)
{ {
if (-1 == xioctl(VIDIOC_S_INPUT, &input)) if (-1 == xioctl(VIDIOC_S_INPUT, &input))
{ {
errno_exit("VIDIOC_S_INPUT"); errno_exit("VIDIOC_S_INPUT");
} }
} }
// set the video standard if needed // set the video standard if needed
switch (videoStandard) switch (videoStandard)
{ {
case PAL: case PAL:
{ {
v4l2_std_id std_id = V4L2_STD_PAL; v4l2_std_id std_id = V4L2_STD_PAL;
if (-1 == xioctl(VIDIOC_S_STD, &std_id)) if (-1 == xioctl(VIDIOC_S_STD, &std_id))
{ {
errno_exit("VIDIOC_S_STD"); errno_exit("VIDIOC_S_STD");
} }
} }
break; break;
case NTSC: case NTSC:
{ {
v4l2_std_id std_id = V4L2_STD_NTSC; v4l2_std_id std_id = V4L2_STD_NTSC;
if (-1 == xioctl(VIDIOC_S_STD, &std_id)) if (-1 == xioctl(VIDIOC_S_STD, &std_id))
{ {
errno_exit("VIDIOC_S_STD"); errno_exit("VIDIOC_S_STD");
} }
} }
break; break;
case NO_CHANGE: case NO_CHANGE:
default: default:
// No change to device settings // No change to device settings
break; break;
} }
// get the current settings // get the current settings
struct v4l2_format fmt; struct v4l2_format fmt;
CLEAR(fmt); CLEAR(fmt);
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (-1 == xioctl(VIDIOC_G_FMT, &fmt)) if (-1 == xioctl(VIDIOC_G_FMT, &fmt))
{ {
errno_exit("VIDIOC_G_FMT"); errno_exit("VIDIOC_G_FMT");
} }
// check pixel format // check pixel format
if (fmt.fmt.pix.pixelformat != V4L2_PIX_FMT_UYVY) if (fmt.fmt.pix.pixelformat != V4L2_PIX_FMT_UYVY)
{ {
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
// store width & height // store width & height
_width = fmt.fmt.pix.width; _width = fmt.fmt.pix.width;
_height = fmt.fmt.pix.height; _height = fmt.fmt.pix.height;
std::cout << "V4L2 width=" << _width << " height=" << _height << std::endl; std::cout << "V4L2 width=" << _width << " height=" << _height << std::endl;
switch (_ioMethod) { switch (_ioMethod) {
case IO_METHOD_READ: case IO_METHOD_READ:
@ -445,11 +449,11 @@ int V4L2Grabber::read_frame()
switch (_ioMethod) { switch (_ioMethod) {
case IO_METHOD_READ: case IO_METHOD_READ:
int size; int size;
if ((size = read(_fileDescriptor, _buffers[0].start, _buffers[0].length)) == -1) if ((size = read(_fileDescriptor, _buffers[0].start, _buffers[0].length)) == -1)
{ {
switch (errno) switch (errno)
{ {
case EAGAIN: case EAGAIN:
return 0; return 0;
@ -463,7 +467,7 @@ int V4L2Grabber::read_frame()
} }
} }
process_image(_buffers[0].start, size); process_image(_buffers[0].start, size);
break; break;
case IO_METHOD_MMAP: case IO_METHOD_MMAP:
@ -472,10 +476,10 @@ int V4L2Grabber::read_frame()
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP; buf.memory = V4L2_MEMORY_MMAP;
if (-1 == xioctl(VIDIOC_DQBUF, &buf)) if (-1 == xioctl(VIDIOC_DQBUF, &buf))
{ {
switch (errno) switch (errno)
{ {
case EAGAIN: case EAGAIN:
return 0; return 0;
@ -491,12 +495,12 @@ int V4L2Grabber::read_frame()
assert(buf.index < _buffers.size()); assert(buf.index < _buffers.size());
process_image(_buffers[buf.index].start, buf.bytesused); process_image(_buffers[buf.index].start, buf.bytesused);
if (-1 == xioctl(VIDIOC_QBUF, &buf)) if (-1 == xioctl(VIDIOC_QBUF, &buf))
{ {
errno_exit("VIDIOC_QBUF"); errno_exit("VIDIOC_QBUF");
} }
break; break;
@ -506,10 +510,10 @@ int V4L2Grabber::read_frame()
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_USERPTR; buf.memory = V4L2_MEMORY_USERPTR;
if (-1 == xioctl(VIDIOC_DQBUF, &buf)) if (-1 == xioctl(VIDIOC_DQBUF, &buf))
{ {
switch (errno) switch (errno)
{ {
case EAGAIN: case EAGAIN:
return 0; return 0;
@ -524,19 +528,19 @@ int V4L2Grabber::read_frame()
} }
for (size_t i = 0; i < _buffers.size(); ++i) for (size_t i = 0; i < _buffers.size(); ++i)
{ {
if (buf.m.userptr == (unsigned long)_buffers[i].start && buf.length == _buffers[i].length) if (buf.m.userptr == (unsigned long)_buffers[i].start && buf.length == _buffers[i].length)
{ {
break; break;
} }
} }
process_image((void *)buf.m.userptr, buf.bytesused); process_image((void *)buf.m.userptr, buf.bytesused);
if (-1 == xioctl(VIDIOC_QBUF, &buf)) if (-1 == xioctl(VIDIOC_QBUF, &buf))
{ {
errno_exit("VIDIOC_QBUF"); errno_exit("VIDIOC_QBUF");
} }
break; break;
} }
@ -545,44 +549,50 @@ int V4L2Grabber::read_frame()
void V4L2Grabber::process_image(const void *p, int size) void V4L2Grabber::process_image(const void *p, int size)
{ {
if (size != 2*_width*_height) if (++_currentFrame >= _frameDecimation)
{ {
std::cout << "Frame too small: " << size << " != " << (2*_width*_height) << std::endl; // We do want a new frame...
}
else if (size != 2*_width*_height)
{ {
process_image(reinterpret_cast<const uint8_t *>(p)); std::cout << "Frame too small: " << size << " != " << (2*_width*_height) << std::endl;
} }
else
{
process_image(reinterpret_cast<const uint8_t *>(p));
_currentFrame = 0; // restart counting
}
}
} }
void V4L2Grabber::process_image(const uint8_t * data) void V4L2Grabber::process_image(const uint8_t * data)
{ {
std::cout << "process image" << std::endl; std::cout << "process image" << std::endl;
QImage image(_width, _height, QImage::Format_RGB888); QImage image(_width, _height, QImage::Format_RGB888);
for (int y = 0; y < image.height(); ++y) for (int y = 0; y < image.height(); ++y)
{ {
for (int x = 0; x < image.width(); ++x) for (int x = 0; x < image.width(); ++x)
{ {
uint8_t value = data[(image.width() * y + x) * 2 + 1]; uint8_t value = data[(image.width() * y + x) * 2 + 1];
//std::cout << "data = " << int(value) << std::endl; //std::cout << "data = " << int(value) << std::endl;
image.setPixel(x, y, qRgb(value, value, value)); image.setPixel(x, y, qRgb(value, value, value));
} }
} }
image.save("screenshot.png"); image.save("screenshot.png");
} }
int V4L2Grabber::xioctl(int request, void *arg) int V4L2Grabber::xioctl(int request, void *arg)
{ {
int r; int r;
do do
{ {
r = ioctl(_fileDescriptor, request, arg); r = ioctl(_fileDescriptor, request, arg);
} }
while (-1 == r && EINTR == errno); while (-1 == r && EINTR == errno);
return r; return r;
} }

View File

@ -10,19 +10,19 @@
class V4L2Grabber class V4L2Grabber
{ {
public: public:
enum VideoStandard { enum VideoStandard {
PAL, NTSC, NO_CHANGE PAL, NTSC, NO_CHANGE
}; };
public: public:
V4L2Grabber(const std::string & device, int input, VideoStandard videoStandard, double fps); V4L2Grabber(const std::string & device, int input, VideoStandard videoStandard, int frameDecimation, int pixelDecimation);
virtual ~V4L2Grabber(); virtual ~V4L2Grabber();
void start(); void start();
void capture(); void capture(int frameCount = -1);
void stop(); void stop();
private: private:
void open_device(); void open_device();
@ -35,7 +35,7 @@ private:
void init_userp(unsigned int buffer_size); void init_userp(unsigned int buffer_size);
void init_device(VideoStandard videoStandard, int input); void init_device(VideoStandard videoStandard, int input);
void uninit_device(); void uninit_device();
@ -45,9 +45,9 @@ private:
int read_frame(); int read_frame();
void process_image(const void *p, int size); void process_image(const void *p, int size);
void process_image(const uint8_t *p); void process_image(const uint8_t *p);
int xioctl(int request, void *arg); int xioctl(int request, void *arg);
@ -71,6 +71,10 @@ private:
int _fileDescriptor; int _fileDescriptor;
std::vector<buffer> _buffers; std::vector<buffer> _buffers;
int _width; int _width;
int _height; int _height;
const int _frameDecimation;
const int _pixelDecimation;
int _currentFrame;
}; };

View File

@ -40,10 +40,10 @@ int main(int argc, char** argv)
return 1; return 1;
} }
V4L2Grabber grabber("/dev/video0", 0, V4L2Grabber::PAL, 10.0); V4L2Grabber grabber("/dev/video0", 0, V4L2Grabber::PAL, 25, 8);
grabber.start(); grabber.start();
grabber.capture(); grabber.capture(250);
grabber.stop(); grabber.stop();
return 0; return 0;
} }