|
|
|
@ -16,17 +16,58 @@
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
#include <sys/time.h>
|
|
|
|
|
#include <sys/mman.h>
|
|
|
|
|
#include <jpeglib.h>
|
|
|
|
|
|
|
|
|
|
#include <list>
|
|
|
|
|
#include <string>
|
|
|
|
|
#include <setjmp.h>
|
|
|
|
|
|
|
|
|
|
#include "gui.h"
|
|
|
|
|
#include "video.h"
|
|
|
|
|
|
|
|
|
|
#define CLEAR(x) memset (&(x), 0, sizeof (x))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// jpeg: replacement for error_exit
|
|
|
|
|
//
|
|
|
|
|
METHODDEF(void) jpg_error_exit (j_common_ptr cinfo) {
|
|
|
|
|
jpg_error_ptr myerr = (jpg_error_ptr) cinfo->err;
|
|
|
|
|
(*cinfo->err->output_message) (cinfo);
|
|
|
|
|
longjmp(myerr->setjmp_buffer, 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// clamp and convert2rgb is build on the sample of the v4l2 api documentation
|
|
|
|
|
//
|
|
|
|
|
inline unsigned char clamp (double x) {
|
|
|
|
|
int r = (int)x;
|
|
|
|
|
|
|
|
|
|
if (r < 0) return 0;
|
|
|
|
|
else if (r > 255) return 255;
|
|
|
|
|
else return r;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
inline void convert2rgb (unsigned char Y1, unsigned char Cb, unsigned char Cr,
|
|
|
|
|
unsigned char *ER, unsigned char *EB, unsigned char *EG) {
|
|
|
|
|
register int y1, pb, pr;
|
|
|
|
|
|
|
|
|
|
y1 = Y1 - 16;
|
|
|
|
|
pb = Cb - 128;
|
|
|
|
|
pr = Cr - 128;
|
|
|
|
|
|
|
|
|
|
*ER = clamp (y1 + 1.402 * pr);
|
|
|
|
|
*EB = clamp (y1 - 0.344 * pb - 0.714 * pr);
|
|
|
|
|
*EG = clamp (y1 + 1.772 * pb);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// C / C++ Wrapper for the thread function
|
|
|
|
|
//
|
|
|
|
|
// C / C++ Wrapper
|
|
|
|
|
gpointer _VideoDevThread (gpointer data) {
|
|
|
|
|
videodev.Thread ();
|
|
|
|
|
return NULL;
|
|
|
|
@ -35,8 +76,8 @@ gpointer _VideoDevThread (gpointer data) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VideoDev::VideoDev() {
|
|
|
|
|
inbuffers = NULL;
|
|
|
|
|
inbuffers_cnt = 0;
|
|
|
|
|
inbuffer.size = 0;
|
|
|
|
|
inbuffer.data = NULL;
|
|
|
|
|
running = 0;
|
|
|
|
|
callback = NULL;
|
|
|
|
|
thread = NULL;
|
|
|
|
@ -57,6 +98,7 @@ VideoDev::~VideoDev() {
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// return list of devices in form of /dev/videoYY [Name]
|
|
|
|
|
//
|
|
|
|
|
int VideoDev::GetDeviceList(std::list<std::string> *list) {
|
|
|
|
|
std::string device;
|
|
|
|
|
int devnum;
|
|
|
|
@ -93,13 +135,14 @@ int VideoDev::GetDeviceList(std::list<std::string> *list) {
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// start the video thread (on error, call cb_thread_videodev with NULL data)
|
|
|
|
|
//
|
|
|
|
|
int VideoDev::Start(std::string dev, gboolean (*callback_func)(gpointer data)) {
|
|
|
|
|
if (running != 0 || thread != NULL) return VDEV_STATUS_ERROR;
|
|
|
|
|
|
|
|
|
|
running = 1;
|
|
|
|
|
conf_device = dev;
|
|
|
|
|
callback = callback_func;
|
|
|
|
|
thread = g_thread_new("network thread", _VideoDevThread, NULL);
|
|
|
|
|
thread = g_thread_new("VideoDev", _VideoDevThread, NULL);
|
|
|
|
|
|
|
|
|
|
return VDEV_STATUS_ERROR;
|
|
|
|
|
};
|
|
|
|
@ -121,6 +164,7 @@ int VideoDev::Stop() {
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// try to read a video every 0.05ms (25hz)
|
|
|
|
|
//
|
|
|
|
|
#define CYCLETIME 0.050
|
|
|
|
|
void VideoDev::Thread() {
|
|
|
|
|
struct timeval cycle_timestamp;
|
|
|
|
@ -129,6 +173,7 @@ void VideoDev::Thread() {
|
|
|
|
|
int i;
|
|
|
|
|
fd_set fds;
|
|
|
|
|
struct timeval tv;
|
|
|
|
|
VideoFrame vf;
|
|
|
|
|
|
|
|
|
|
printf ("%s:%d %s Enter\n", __FILE__, __LINE__, __FUNCTION__);
|
|
|
|
|
cycle_time = get_cycletime(&cycle_timestamp); // just start counting
|
|
|
|
@ -142,36 +187,16 @@ void VideoDev::Thread() {
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// start capturing
|
|
|
|
|
CaptureStart();
|
|
|
|
|
if (CaptureStart() != VDEV_STATUS_OK) running = 0;
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// read untill something bad happens..
|
|
|
|
|
while (running > 0) {
|
|
|
|
|
//
|
|
|
|
|
// read data
|
|
|
|
|
g_mutex_lock(&mutex);
|
|
|
|
|
if (vf_rec == -1) vf_rec = 0;
|
|
|
|
|
if (vf_rec != vf_get) {
|
|
|
|
|
g_mutex_unlock(&mutex);
|
|
|
|
|
|
|
|
|
|
FD_ZERO (&fds);
|
|
|
|
|
FD_SET (fd, &fds);
|
|
|
|
|
tv.tv_sec = 2;
|
|
|
|
|
tv.tv_usec = 0;
|
|
|
|
|
i = select (fd + 1, &fds, NULL, NULL, &tv);
|
|
|
|
|
|
|
|
|
|
/* error while select */
|
|
|
|
|
if (i == -1 && EINTR == errno) // system interrupt. something went wrong
|
|
|
|
|
running = 0;
|
|
|
|
|
else if (i == 1) {
|
|
|
|
|
switch (Grab()) {
|
|
|
|
|
i = Grab(&vf);
|
|
|
|
|
|
|
|
|
|
switch (i) {
|
|
|
|
|
case VDEV_STATUS_OK:
|
|
|
|
|
// got valid data
|
|
|
|
|
g_mutex_lock(&mutex);
|
|
|
|
|
if (vf_get == -1) vf_get = vf_rec;
|
|
|
|
|
if ((++vf_rec) >= VIDEOBUFFERS) vf_rec = 0;
|
|
|
|
|
if (callback) gdk_threads_add_idle(callback, (void*)VDEV_CBSTATUS_NEWFRAME);
|
|
|
|
|
g_mutex_unlock(&mutex);
|
|
|
|
|
if (callback) gdk_threads_add_idle(callback, &vf);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case VDEV_STATUS_AGAIN:
|
|
|
|
@ -180,22 +205,16 @@ void VideoDev::Thread() {
|
|
|
|
|
default:
|
|
|
|
|
running = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
g_mutex_unlock(&mutex);
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// keep 25fps, write every second a message
|
|
|
|
|
cycle_time = get_cycletime(&cycle_timestamp);
|
|
|
|
|
cycle_wait = (CYCLETIME - cycle_time) + cycle_wait;
|
|
|
|
|
if (lastsec != cycle_timestamp.tv_sec) {
|
|
|
|
|
printf ("%s:%d %s cycle_time:%f \r", __FILE__, __LINE__, __FUNCTION__, cycle_time);
|
|
|
|
|
printf ("%s:%d %s cycle_time:%f Freq:%f Hz \r", __FILE__, __LINE__, __FUNCTION__, cycle_time, (1.0/cycle_time));
|
|
|
|
|
lastsec = cycle_timestamp.tv_sec;
|
|
|
|
|
}
|
|
|
|
|
if (cycle_wait > 0.0 && cycle_wait < 1.0 ) usleep ((int)(cycle_wait * 1000000.0));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// stop capturing
|
|
|
|
|
CaptureStop();
|
|
|
|
@ -239,15 +258,15 @@ void VideoDev::PrintFmt(struct v4l2_format *f) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int VideoDev::Grab() {
|
|
|
|
|
unsigned int i;
|
|
|
|
|
int VideoDev::Grab(VideoFrame *vf) {
|
|
|
|
|
struct v4l2_buffer buf;
|
|
|
|
|
int len;
|
|
|
|
|
|
|
|
|
|
if (vf_rec < 0 || vf_rec >= VIDEOBUFFERS) return VDEV_CBSTATUS_NOTHING;
|
|
|
|
|
if (vf == NULL) return VDEV_STATUS_ERROR;
|
|
|
|
|
|
|
|
|
|
switch (io) {
|
|
|
|
|
case IOMODE_READ:
|
|
|
|
|
if (-1 == read (fd, vf[vf_rec].data, fmt.fmt.pix.sizeimage)) {
|
|
|
|
|
if ((len = read (fd, inbuffer.data, fmt.fmt.pix.sizeimage)) == -1) {
|
|
|
|
|
switch (errno) {
|
|
|
|
|
case EAGAIN:
|
|
|
|
|
return VDEV_STATUS_AGAIN;
|
|
|
|
@ -257,6 +276,8 @@ int VideoDev::Grab() {
|
|
|
|
|
return VDEV_STATUS_ERROR;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
Convert(vf, inbuffer.data, len, fmt.fmt.pix.pixelformat, fmt.fmt.pix.width, fmt.fmt.pix.height);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case IOMODE_MMAP:
|
|
|
|
@ -264,66 +285,42 @@ int VideoDev::Grab() {
|
|
|
|
|
|
|
|
|
|
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
|
|
|
buf.memory = V4L2_MEMORY_MMAP;
|
|
|
|
|
buf.index = 0;
|
|
|
|
|
|
|
|
|
|
if (-1 == xioctl(fd, VIDIOC_DQBUF, &buf)) {
|
|
|
|
|
switch (errno) {
|
|
|
|
|
case EAGAIN:
|
|
|
|
|
return VDEV_CBSTATUS_NOTHING;
|
|
|
|
|
return VDEV_STATUS_AGAIN;
|
|
|
|
|
|
|
|
|
|
case EIO:
|
|
|
|
|
printf ( "%s:%d error on VIDIOC_DQBUF EIO %s\n", __FILE__, __LINE__, strerror(errno));
|
|
|
|
|
/* Could ignore EIO, see spec. */
|
|
|
|
|
/* fall through */
|
|
|
|
|
default:
|
|
|
|
|
printf ( "%s:%d error on VIDIOC_DQBUF %s\n", __FILE__, __LINE__, strerror(errno));
|
|
|
|
|
return VDEV_CBSTATUS_ERROR;
|
|
|
|
|
return VDEV_STATUS_ERROR;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (buf.index < inbuffers_cnt) {
|
|
|
|
|
memcpy (vf[vf_rec].data, inbuffers[buf.index].data, buf.bytesused);
|
|
|
|
|
vf[vf_rec].size = buf.bytesused;
|
|
|
|
|
// printf ("%s:%d DQBUF used:%d field:%d flags:%d index:%d len:%d seq:%d mem:%d\n", __FILE__, __LINE__, buf.bytesused, buf.field,
|
|
|
|
|
// buf.flags, buf.index, buf.length, buf.sequence, buf.memory);
|
|
|
|
|
|
|
|
|
|
if (buf.index == 0) {
|
|
|
|
|
Convert(vf, inbuffer.data, buf.bytesused, fmt.fmt.pix.pixelformat, fmt.fmt.pix.width, fmt.fmt.pix.height);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) {
|
|
|
|
|
printf ( "%s:%d error on VIDIOC_QBUF %s\n", __FILE__, __LINE__, strerror(errno));
|
|
|
|
|
exit (1);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case IOMODE_USERPTR:
|
|
|
|
|
CLEAR(buf);
|
|
|
|
|
|
|
|
|
|
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
|
|
|
buf.memory = V4L2_MEMORY_USERPTR;
|
|
|
|
|
|
|
|
|
|
if (-1 == xioctl(fd, VIDIOC_DQBUF, &buf)) {
|
|
|
|
|
switch (errno) {
|
|
|
|
|
case EAGAIN:
|
|
|
|
|
return VDEV_CBSTATUS_NOTHING;
|
|
|
|
|
|
|
|
|
|
case EIO:
|
|
|
|
|
/* Could ignore EIO, see spec. */
|
|
|
|
|
/* fall through */
|
|
|
|
|
default:
|
|
|
|
|
printf ( "%s:%d error on VIDIOC_DQBUF %s\n", __FILE__, __LINE__, strerror(errno));
|
|
|
|
|
exit (1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < inbuffers_cnt; ++i)
|
|
|
|
|
if (buf.m.userptr == (unsigned long)inbuffers[i].data && buf.length == inbuffers[i].size)
|
|
|
|
|
// printf ("%s:%d QBUF used:%d field:%d flags:%d index:%d len:%d seq:%d mem:%d\n", __FILE__, __LINE__, buf.bytesused, buf.field,
|
|
|
|
|
// buf.flags, buf.index, buf.length, buf.sequence, buf.memory);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
if (i < inbuffers_cnt) {
|
|
|
|
|
memcpy (vf[vf_rec].data, inbuffers[buf.index].data, buf.bytesused);
|
|
|
|
|
vf[vf_rec].size = buf.bytesused;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) {
|
|
|
|
|
printf ( "%s:%d error on VIDIOC_QBUF %s\n", __FILE__, __LINE__, strerror(errno));
|
|
|
|
|
exit (1);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
printf ("%s:%d %s wrong IOMODE?\n", __FILE__, __LINE__, __FUNCTION__);
|
|
|
|
|
return VDEV_STATUS_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return VDEV_STATUS_OK;
|
|
|
|
@ -339,8 +336,8 @@ int VideoDev::OpenInit() {
|
|
|
|
|
if (fd != -1) return VDEV_STATUS_ERROR;
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// open device and get device name and capabilities
|
|
|
|
|
if((fd = open(conf_device.c_str(), O_RDWR | O_NONBLOCK)) == -1){
|
|
|
|
|
// open device and get device name and capabilities | O_NONBLOCK
|
|
|
|
|
if((fd = open(conf_device.c_str(), O_RDWR)) == -1){
|
|
|
|
|
return VDEV_STATUS_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -406,11 +403,13 @@ int VideoDev::OpenInit() {
|
|
|
|
|
printf (" max image size: %d + %d : %d x %d fmt size:%d\n", cropcap.defrect.left, cropcap.defrect.top, cropcap.defrect.width, cropcap.defrect.height, fmt.fmt.pix.sizeimage);
|
|
|
|
|
|
|
|
|
|
CLEAR (fmt);
|
|
|
|
|
|
|
|
|
|
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
|
|
|
fmt.fmt.pix.width = cropcap.defrect.width;
|
|
|
|
|
fmt.fmt.pix.height = cropcap.defrect.height;
|
|
|
|
|
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB32;
|
|
|
|
|
// fmt.fmt.pix.width = cropcap.defrect.width;
|
|
|
|
|
// fmt.fmt.pix.height = cropcap.defrect.height;
|
|
|
|
|
fmt.fmt.pix.width = 1920;
|
|
|
|
|
fmt.fmt.pix.height = 1080;
|
|
|
|
|
// fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB32;
|
|
|
|
|
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
|
|
|
|
|
fmt.fmt.pix.field = V4L2_FIELD_NONE;
|
|
|
|
|
|
|
|
|
|
if (-1 == xioctl (fd, VIDIOC_S_FMT, &fmt)) {
|
|
|
|
@ -428,26 +427,12 @@ int VideoDev::OpenInit() {
|
|
|
|
|
fmt.fmt.pix.sizeimage = min;
|
|
|
|
|
PrintFmt (&fmt);
|
|
|
|
|
|
|
|
|
|
// allocate buffers for readin from other threads
|
|
|
|
|
for (i = 0; i < VIDEOBUFFERS; i++) {
|
|
|
|
|
vf[i].maxsize = fmt.fmt.pix.sizeimage;
|
|
|
|
|
vf[i].size = fmt.fmt.pix.sizeimage;
|
|
|
|
|
vf[i].format = fmt.fmt.pix.pixelformat;
|
|
|
|
|
vf[i].h = fmt.fmt.pix.height;
|
|
|
|
|
vf[i].w = fmt.fmt.pix.width;
|
|
|
|
|
vf[i].data = (unsigned char*) malloc (vf[i].size);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// init buffers
|
|
|
|
|
switch (io) {
|
|
|
|
|
case IOMODE_MMAP:
|
|
|
|
|
if (InitMMap() == VDEV_STATUS_ERROR)
|
|
|
|
|
Close();
|
|
|
|
|
break;
|
|
|
|
|
case IOMODE_USERPTR:
|
|
|
|
|
if (InitUserPtr() == VDEV_STATUS_ERROR)
|
|
|
|
|
Close();
|
|
|
|
|
break;
|
|
|
|
|
case IOMODE_READ:
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
@ -460,16 +445,18 @@ int VideoDev::OpenInit() {
|
|
|
|
|
//
|
|
|
|
|
// prepare memory mapped buffers
|
|
|
|
|
int VideoDev::InitMMap() {
|
|
|
|
|
struct v4l2_requestbuffers req;
|
|
|
|
|
struct v4l2_requestbuffers bufreq;
|
|
|
|
|
struct v4l2_buffer bufinfo;
|
|
|
|
|
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
|
|
|
|
|
|
|
|
|
|
CLEAR(req);
|
|
|
|
|
CLEAR(bufreq);
|
|
|
|
|
CLEAR(bufinfo);
|
|
|
|
|
|
|
|
|
|
req.count = 4;
|
|
|
|
|
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
|
|
|
req.memory = V4L2_MEMORY_MMAP;
|
|
|
|
|
bufreq.count = 1;
|
|
|
|
|
bufreq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
|
|
|
bufreq.memory = V4L2_MEMORY_MMAP;
|
|
|
|
|
|
|
|
|
|
if (-1 == xioctl(fd, VIDIOC_REQBUFS, &req)) {
|
|
|
|
|
if (-1 == xioctl(fd, VIDIOC_REQBUFS, &bufreq)) {
|
|
|
|
|
if (EINVAL == errno) {
|
|
|
|
|
printf("%s does not support "
|
|
|
|
|
"memory mappingn", conf_device.c_str());
|
|
|
|
@ -480,83 +467,28 @@ int VideoDev::InitMMap() {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (req.count < 2) {
|
|
|
|
|
if (bufreq.count < 1) {
|
|
|
|
|
printf ( "Insufficient buffer memory on %s\n", conf_device.c_str());
|
|
|
|
|
return VDEV_STATUS_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inbuffers = (VideoInBuffer*) calloc(req.count, sizeof(*inbuffers));
|
|
|
|
|
if (!inbuffers) {
|
|
|
|
|
fprintf(stderr, "Out of memory\\n");
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
CLEAR(bufinfo);
|
|
|
|
|
|
|
|
|
|
for (inbuffers_cnt = 0; inbuffers_cnt < req.count; ++inbuffers_cnt) {
|
|
|
|
|
struct v4l2_buffer buf;
|
|
|
|
|
bufinfo.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
|
|
|
bufinfo.memory = V4L2_MEMORY_MMAP;
|
|
|
|
|
bufinfo.index = 0;
|
|
|
|
|
|
|
|
|
|
CLEAR(buf);
|
|
|
|
|
|
|
|
|
|
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
|
|
|
buf.memory = V4L2_MEMORY_MMAP;
|
|
|
|
|
buf.index = inbuffers_cnt;
|
|
|
|
|
|
|
|
|
|
if (-1 == xioctl(fd, VIDIOC_QUERYBUF, &buf)) {
|
|
|
|
|
printf ( "%s:%d Insufficient buffer memory on %s\n", __FILE__, __LINE__, conf_device.c_str());
|
|
|
|
|
exit (1);
|
|
|
|
|
if(ioctl(fd, VIDIOC_QUERYBUF, &bufinfo) < 0){
|
|
|
|
|
perror("VIDIOC_QUERYBUF");
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inbuffers[inbuffers_cnt].size = buf.length;
|
|
|
|
|
inbuffers[inbuffers_cnt].data = (unsigned char*)mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset);
|
|
|
|
|
|
|
|
|
|
if (MAP_FAILED == inbuffers[inbuffers_cnt].data) {
|
|
|
|
|
inbuffer.size = bufinfo.length;
|
|
|
|
|
inbuffer.data = (unsigned char*)mmap(NULL, bufinfo.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, bufinfo.m.offset);
|
|
|
|
|
if (inbuffer.data == MAP_FAILED) {
|
|
|
|
|
printf ( "%s:%d error on mmap %s\n", __FILE__, __LINE__, strerror(errno));
|
|
|
|
|
exit (1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return VDEV_STATUS_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// prepare memory mapped buffers
|
|
|
|
|
int VideoDev::InitUserPtr() {
|
|
|
|
|
struct v4l2_requestbuffers req;
|
|
|
|
|
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
|
|
|
|
|
|
|
|
|
|
CLEAR(req);
|
|
|
|
|
|
|
|
|
|
req.count = 4;
|
|
|
|
|
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
|
|
|
req.memory = V4L2_MEMORY_USERPTR;
|
|
|
|
|
|
|
|
|
|
if (-1 == xioctl(fd, VIDIOC_REQBUFS, &req)) {
|
|
|
|
|
if (EINVAL == errno) {
|
|
|
|
|
printf("%s does not support "
|
|
|
|
|
"memory mappingn", conf_device.c_str());
|
|
|
|
|
return VDEV_STATUS_ERROR;
|
|
|
|
|
} else {
|
|
|
|
|
printf ( "%s:%d error on UserPtr %s\n", __FILE__, __LINE__, strerror(errno));
|
|
|
|
|
exit (1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inbuffers = (VideoInBuffer*)calloc(4, sizeof(*inbuffers));
|
|
|
|
|
|
|
|
|
|
if (!inbuffers) {
|
|
|
|
|
fprintf(stderr, "Out of memory\\n");
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (inbuffers_cnt = 0; inbuffers_cnt < 4; ++inbuffers_cnt) {
|
|
|
|
|
inbuffers[inbuffers_cnt].size = vf[0].maxsize;
|
|
|
|
|
inbuffers[inbuffers_cnt].data = (unsigned char*)malloc(inbuffers[inbuffers_cnt].size);
|
|
|
|
|
|
|
|
|
|
if (!inbuffers[inbuffers_cnt].data) {
|
|
|
|
|
fprintf(stderr, "Out of memory\n");
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return VDEV_STATUS_OK;
|
|
|
|
|
}
|
|
|
|
@ -565,8 +497,8 @@ int VideoDev::InitUserPtr() {
|
|
|
|
|
//
|
|
|
|
|
// send the start capture signal to the cam
|
|
|
|
|
int VideoDev::CaptureStart() {
|
|
|
|
|
unsigned int i;
|
|
|
|
|
enum v4l2_buf_type type;
|
|
|
|
|
|
|
|
|
|
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
|
|
|
|
|
|
|
|
|
|
switch (io) {
|
|
|
|
@ -575,19 +507,18 @@ int VideoDev::CaptureStart() {
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case IOMODE_MMAP:
|
|
|
|
|
for (i = 0; i < inbuffers_cnt; ++i) {
|
|
|
|
|
struct v4l2_buffer buf;
|
|
|
|
|
|
|
|
|
|
CLEAR(buf);
|
|
|
|
|
|
|
|
|
|
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
|
|
|
buf.memory = V4L2_MEMORY_MMAP;
|
|
|
|
|
buf.index = i;
|
|
|
|
|
buf.index = 0;
|
|
|
|
|
|
|
|
|
|
if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) {
|
|
|
|
|
printf ( "%s:%d VIDIOC_QBUF %s\n", __FILE__, __LINE__, strerror(errno));
|
|
|
|
|
return VDEV_STATUS_ERROR;
|
|
|
|
|
}
|
|
|
|
|
printf ( "%s:%d error on VIDIOC_QBUF %s\n", __FILE__, __LINE__, strerror(errno));
|
|
|
|
|
exit (1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
|
|
|
if (-1 == xioctl(fd, VIDIOC_STREAMON, &type)) {
|
|
|
|
|
printf ( "%s:%d VIDIOC_STREAMON %s\n", __FILE__, __LINE__, strerror(errno));
|
|
|
|
@ -595,28 +526,23 @@ int VideoDev::CaptureStart() {
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case IOMODE_USERPTR:
|
|
|
|
|
for (i = 0; i < inbuffers_cnt; ++i) {
|
|
|
|
|
struct v4l2_buffer buf;
|
|
|
|
|
|
|
|
|
|
CLEAR(buf);
|
|
|
|
|
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
|
|
|
buf.memory = V4L2_MEMORY_USERPTR;
|
|
|
|
|
buf.index = i;
|
|
|
|
|
buf.m.userptr = (unsigned long)inbuffers[i].data;
|
|
|
|
|
buf.length = inbuffers[i].size;
|
|
|
|
|
|
|
|
|
|
if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) {
|
|
|
|
|
printf ( "%s:%d VIDIOC_QBUF %s\n", __FILE__, __LINE__, strerror(errno));
|
|
|
|
|
default:
|
|
|
|
|
printf ("%s:%d %s wrong IOMODE?\n", __FILE__, __LINE__, __FUNCTION__);
|
|
|
|
|
return VDEV_STATUS_ERROR;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
|
|
|
if (-1 == xioctl(fd, VIDIOC_STREAMON, &type)) {
|
|
|
|
|
printf ( "%s:%d VIDIOC_STREAMON %s\n", __FILE__, __LINE__, strerror(errno));
|
|
|
|
|
|
|
|
|
|
if (fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG) {
|
|
|
|
|
cinfo.err = jpeg_std_error(&jerr.pub);
|
|
|
|
|
jerr.pub.error_exit = jpg_error_exit;
|
|
|
|
|
if (setjmp(jerr.setjmp_buffer)) {
|
|
|
|
|
/* If we get here, the JPEG code has signaled an error.
|
|
|
|
|
* We need to clean up the JPEG object, close the input file, and return.
|
|
|
|
|
*/
|
|
|
|
|
printf ("%s:%d %s JPEG Error\n", __FILE__, __LINE__, __FUNCTION__);
|
|
|
|
|
jpeg_destroy_decompress(&cinfo);
|
|
|
|
|
return VDEV_STATUS_ERROR;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
jpeg_create_decompress(&cinfo);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return VDEV_STATUS_OK;
|
|
|
|
@ -627,26 +553,30 @@ int VideoDev::CaptureStop() {
|
|
|
|
|
enum v4l2_buf_type type;
|
|
|
|
|
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
|
|
|
|
|
|
|
|
|
|
if (fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG) {
|
|
|
|
|
jpeg_destroy_decompress(&cinfo);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (io) {
|
|
|
|
|
case IOMODE_READ:
|
|
|
|
|
/* Nothing to do. */
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case IOMODE_MMAP:
|
|
|
|
|
case IOMODE_USERPTR:
|
|
|
|
|
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
|
|
|
if (-1 == xioctl(fd, VIDIOC_STREAMOFF, &type)) {
|
|
|
|
|
fprintf(stderr, "%s:%d VIDIOC_STREAMOFF Error:%s\n", __FILE__, __LINE__, strerror(errno));
|
|
|
|
|
return VDEV_STATUS_ERROR;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
printf ("%s:%d %s wrong IOMODE?\n", __FILE__, __LINE__, __FUNCTION__);
|
|
|
|
|
return VDEV_STATUS_ERROR;
|
|
|
|
|
}
|
|
|
|
|
return VDEV_STATUS_OK;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int VideoDev::UnInit() {
|
|
|
|
|
unsigned int i;
|
|
|
|
|
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
|
|
|
|
|
|
|
|
|
|
switch (io) {
|
|
|
|
@ -654,22 +584,18 @@ int VideoDev::UnInit() {
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case IOMODE_MMAP:
|
|
|
|
|
for (i = 0; i < inbuffers_cnt; ++i)
|
|
|
|
|
if (-1 == munmap(inbuffers[i].data, inbuffers[i].size)){
|
|
|
|
|
if (-1 == munmap(inbuffer.data, inbuffer.size)){
|
|
|
|
|
fprintf(stderr, "Fatal Error @ %s:%d munmap Error:%s\n", __FILE__, __LINE__, strerror(errno));
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case IOMODE_USERPTR:
|
|
|
|
|
for (i = 0; i < inbuffers_cnt; ++i)
|
|
|
|
|
free(inbuffers[i].data);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
printf ("%s:%d %s wrong IOMODE?\n", __FILE__, __LINE__, __FUNCTION__);
|
|
|
|
|
return VDEV_STATUS_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
free(inbuffers);
|
|
|
|
|
inbuffers = NULL;
|
|
|
|
|
inbuffers_cnt = 0;
|
|
|
|
|
|
|
|
|
|
return VDEV_STATUS_OK;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
@ -677,6 +603,7 @@ int VideoDev::UnInit() {
|
|
|
|
|
//
|
|
|
|
|
// Close Device
|
|
|
|
|
// Free videobuffer
|
|
|
|
|
//
|
|
|
|
|
int VideoDev::Close() {
|
|
|
|
|
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
|
|
|
|
|
|
|
|
|
@ -690,46 +617,14 @@ int VideoDev::Close() {
|
|
|
|
|
|
|
|
|
|
vf_rec = -1;
|
|
|
|
|
vf_get = -1;
|
|
|
|
|
for (int i = 0; i < VIDEOBUFFERS; i++) {
|
|
|
|
|
vf[i].data = NULL;
|
|
|
|
|
vf[i].w = 0;
|
|
|
|
|
vf[i].h = 0;
|
|
|
|
|
vf[i].size = 0;
|
|
|
|
|
vf[i].format = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return VDEV_STATUS_OK;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// get current frame, if no new frame is avaiable return NULL
|
|
|
|
|
VideoFrame *VideoDev::FrameGet() {
|
|
|
|
|
VideoFrame *res = NULL;
|
|
|
|
|
|
|
|
|
|
g_mutex_lock(&mutex);
|
|
|
|
|
if (vf_get != -1)
|
|
|
|
|
res = &vf[vf_get];
|
|
|
|
|
g_mutex_unlock(&mutex);
|
|
|
|
|
|
|
|
|
|
return res;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// release frame, and prepare next frame
|
|
|
|
|
void VideoDev::FrameNext() {
|
|
|
|
|
g_mutex_lock(&mutex);
|
|
|
|
|
vf_get++;
|
|
|
|
|
if (vf_get >= VIDEOBUFFERS) vf_get = 0;
|
|
|
|
|
if (vf_get == vf_rec) vf_get = -1;
|
|
|
|
|
g_mutex_unlock(&mutex);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// try to send ioctl command as long as EINTR is valid. But abort after 2 seconds.
|
|
|
|
|
//
|
|
|
|
|
int VideoDev::xioctl(int fd, int request, void *arg) {
|
|
|
|
|
int r;
|
|
|
|
|
int errnoioctl;
|
|
|
|
@ -748,3 +643,178 @@ int VideoDev::xioctl(int fd, int request, void *arg) {
|
|
|
|
|
return r;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// converts the video from input type to RGB24 type - 24Bit
|
|
|
|
|
//
|
|
|
|
|
int VideoDev::Convert (VideoFrame *dest, unsigned char *ptrsrc, int srcsize, uint32_t pixelformat, int srcw, int srch) {
|
|
|
|
|
int xs, ys;
|
|
|
|
|
int xd, yd;
|
|
|
|
|
unsigned char r,g,b;
|
|
|
|
|
unsigned char cb, cr, y1;
|
|
|
|
|
unsigned char *ptrdst = NULL;
|
|
|
|
|
|
|
|
|
|
// check if there is a destination and that the destination is large to keep
|
|
|
|
|
// the full image
|
|
|
|
|
if (dest == NULL || ptrsrc == NULL)
|
|
|
|
|
return VDEV_STATUS_ERROR;
|
|
|
|
|
|
|
|
|
|
if (dest->data != NULL && dest->w != srcw && dest->h != srch) {
|
|
|
|
|
free (dest->data);
|
|
|
|
|
dest->data == NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (dest->data == NULL) {
|
|
|
|
|
dest->w = srcw;
|
|
|
|
|
dest->h = srch;
|
|
|
|
|
dest->size = srcw * srch * 3;
|
|
|
|
|
dest->data = (unsigned char*) malloc (dest->size);
|
|
|
|
|
}
|
|
|
|
|
ptrdst = dest->data;
|
|
|
|
|
|
|
|
|
|
switch (pixelformat) {
|
|
|
|
|
case (V4L2_PIX_FMT_RGB32):
|
|
|
|
|
for (ys = 0, yd = 0; ys < (signed int)srch; ys++) {
|
|
|
|
|
for (xs = 0, xd = 0; xs < (signed int)srcw; xs++) {
|
|
|
|
|
/* read the pixel */
|
|
|
|
|
|
|
|
|
|
ptrsrc++;
|
|
|
|
|
r = *(ptrsrc++);
|
|
|
|
|
g = *(ptrsrc++);
|
|
|
|
|
b = *(ptrsrc++);
|
|
|
|
|
|
|
|
|
|
/* only paint the image if the source is within the destination */
|
|
|
|
|
if (xd < dest->w) {
|
|
|
|
|
/* set the pixel */
|
|
|
|
|
*(ptrdst++) = b;
|
|
|
|
|
*(ptrdst++) = g;
|
|
|
|
|
*(ptrdst++) = r;
|
|
|
|
|
xd++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* if the source image is too small ignore the other places.. */
|
|
|
|
|
if (xd < dest->w)
|
|
|
|
|
ptrdst += 3 * (dest->w - xd);
|
|
|
|
|
yd++;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case (V4L2_PIX_FMT_BGR32):
|
|
|
|
|
for (ys = 0, yd = 0; ys < (signed int)srch; ys++) {
|
|
|
|
|
for (xs = 0, xd = 0; xs < (signed int)srcw; xs++) {
|
|
|
|
|
/* read the pixel */
|
|
|
|
|
|
|
|
|
|
b = *(ptrsrc++);
|
|
|
|
|
g = *(ptrsrc++);
|
|
|
|
|
r = *(ptrsrc++);
|
|
|
|
|
ptrsrc++;
|
|
|
|
|
|
|
|
|
|
/* only paint the image if the source is within the destination */
|
|
|
|
|
if (xd < dest->w) {
|
|
|
|
|
/* set the pixel */
|
|
|
|
|
*(ptrdst++) = b;
|
|
|
|
|
*(ptrdst++) = g;
|
|
|
|
|
*(ptrdst++) = r;
|
|
|
|
|
xd++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* if the source image is too small ignore the other places.. */
|
|
|
|
|
if (xd < dest->w)
|
|
|
|
|
ptrdst += 3 * (dest->w - xd);
|
|
|
|
|
yd++;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case (V4L2_PIX_FMT_UYVY):
|
|
|
|
|
for (ys = 0, yd = 0; ys < (signed int)srch; ys++) {
|
|
|
|
|
for (xs = 0, xd = 0; xs < (signed int)srcw; xs++) {
|
|
|
|
|
/* read the pixel */
|
|
|
|
|
if (xs & 1) {
|
|
|
|
|
y1 = (unsigned char)*(ptrsrc + 1);
|
|
|
|
|
cr = (unsigned char)*(ptrsrc);
|
|
|
|
|
cb = (unsigned char)*(ptrsrc - 2);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
y1 = (unsigned char)*(ptrsrc + 1);
|
|
|
|
|
cr = (unsigned char)*(ptrsrc + 2);
|
|
|
|
|
cb = (unsigned char)*(ptrsrc);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
convert2rgb (y1, cr, cb, &r, &g, &b);
|
|
|
|
|
ptrsrc += 2;
|
|
|
|
|
|
|
|
|
|
/* only paint the image if the source is within the destination */
|
|
|
|
|
if (xd < dest->w) {
|
|
|
|
|
/* set the pixel */
|
|
|
|
|
*(ptrdst++) = b;
|
|
|
|
|
*(ptrdst++) = g;
|
|
|
|
|
*(ptrdst++) = r;
|
|
|
|
|
xd++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* if the source image is too small ignore the other places.. */
|
|
|
|
|
if (xd < dest->w)
|
|
|
|
|
ptrdst += 3 * (dest->w - xd);
|
|
|
|
|
yd++;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case (V4L2_PIX_FMT_YUYV):
|
|
|
|
|
for (ys = 0, yd = 0; ys < (signed int)srch; ys++) {
|
|
|
|
|
if (yd < dest->h) {
|
|
|
|
|
for (xs = 0, xd = 0; xs < (signed int)srcw; xs++) {
|
|
|
|
|
/* read the pixel */
|
|
|
|
|
if (xs & 1) {
|
|
|
|
|
y1 = *(ptrsrc);
|
|
|
|
|
cb = *(ptrsrc + 1);
|
|
|
|
|
cr = *(ptrsrc - 1);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
y1 = *(ptrsrc);
|
|
|
|
|
cb = *(ptrsrc + 3);
|
|
|
|
|
cr = *(ptrsrc + 1);
|
|
|
|
|
}
|
|
|
|
|
convert2rgb (y1, cr, cb, &r, &g, &b);
|
|
|
|
|
ptrsrc += 2;
|
|
|
|
|
|
|
|
|
|
/* only paint the image if the source is within the destination */
|
|
|
|
|
if (xd < dest->w) {
|
|
|
|
|
/* set the pixel */
|
|
|
|
|
*(ptrdst++) = r;
|
|
|
|
|
*(ptrdst++) = g;
|
|
|
|
|
*(ptrdst++) = b;
|
|
|
|
|
xd++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* if the source image is too small ignore the other places.. */
|
|
|
|
|
if (xd < dest->w)
|
|
|
|
|
ptrdst += 3 * (dest->w - xd);
|
|
|
|
|
yd++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case (V4L2_PIX_FMT_MJPEG):
|
|
|
|
|
jpeg_mem_src(&cinfo, ptrsrc, srcsize);
|
|
|
|
|
jpeg_read_header(&cinfo, TRUE);
|
|
|
|
|
jpeg_start_decompress(&cinfo);
|
|
|
|
|
|
|
|
|
|
while (cinfo.output_scanline < cinfo.output_height) {
|
|
|
|
|
unsigned char *temp_array[] = {ptrdst + (cinfo.output_scanline) * srcw * 3};
|
|
|
|
|
jpeg_read_scanlines(&cinfo, temp_array, 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
jpeg_finish_decompress(&cinfo);
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return VDEV_STATUS_OK;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|