mjpeg seem to work

test16bit
Steffen Pohle 4 years ago
parent 258494c571
commit 7f768518a1

@ -5,5 +5,5 @@ CPP = c++
CPPFLAGS = -ggdb -Wall -O0 `pkg-config --cflags gtk+-3.0 gmodule-export-2.0` -Wl,--export-dynamic -I/usr/include -DBUILD_LINUX=1 CPPFLAGS = -ggdb -Wall -O0 `pkg-config --cflags gtk+-3.0 gmodule-export-2.0` -Wl,--export-dynamic -I/usr/include -DBUILD_LINUX=1
INCLUDES = INCLUDES =
LDFLAGS = LDFLAGS =
LIBS = `pkg-config --libs gtk+-3.0 gmodule-export-2.0` -L/usr/lib LIBS = `pkg-config --libs gtk+-3.0 gmodule-export-2.0` -L/usr/lib -ljpeg

@ -35,6 +35,7 @@ void cb_window_show (GtkWidget *widget, gpointer data) {
gtk_widget_set_sensitive(btnstart, true); gtk_widget_set_sensitive(btnstart, true);
gtk_widget_set_sensitive(btnstop, false); gtk_widget_set_sensitive(btnstop, false);
}; };

@ -40,6 +40,9 @@ G_MODULE_EXPORT void cb_video_btnrefreshlist (GtkWidget *widget, gpointer data)
G_MODULE_EXPORT void cb_video_btnrec (GtkWidget *widget, gpointer data); G_MODULE_EXPORT void cb_video_btnrec (GtkWidget *widget, gpointer data);
G_MODULE_EXPORT void cb_video_btnstop (GtkWidget *widget, gpointer data); G_MODULE_EXPORT void cb_video_btnstop (GtkWidget *widget, gpointer data);
G_MODULE_EXPORT void cb_videoda_draw(GtkWidget *area, cairo_t *cr, int w, int h, gpointer data); G_MODULE_EXPORT void cb_videoda_draw(GtkWidget *area, cairo_t *cr, int w, int h, gpointer data);
G_MODULE_EXPORT gboolean video_display(gpointer data);
// //
// thread handles // thread handles

@ -7,6 +7,7 @@
<property name="default-width">1024</property> <property name="default-width">1024</property>
<property name="default-height">786</property> <property name="default-height">786</property>
<signal name="delete-event" handler="cb_window_delete_event" swapped="no"/> <signal name="delete-event" handler="cb_window_delete_event" swapped="no"/>
<signal name="show" handler="cb_window_show" swapped="no"/>
<child> <child>
<object class="GtkPaned"> <object class="GtkPaned">
<property name="visible">True</property> <property name="visible">True</property>

@ -14,187 +14,26 @@ GtkWidget *video_da = NULL;
GdkPixbuf *video_pixbuf; GdkPixbuf *video_pixbuf;
//
// 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;
};
void videoframe_to_pixbuf(GdkPixbuf* dest, VideoFrame *src) {
int destw, desth;
unsigned char *destpixel;
inline void convert2rgb (unsigned char Y1, unsigned char Cb, unsigned char Cr, if (dest == NULL || src == NULL) return;
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);
};
void video_convert (VideoFrame *vf, GdkPixbuf *pixbuf) {
int xs, ys;
int xd, yd;
int dst_w, dst_h;
unsigned char r,g,b;
unsigned char cb, cr, y1;
unsigned char *ptrdst = gdk_pixbuf_get_pixels(pixbuf);
unsigned char *ptrsrc = vf->data;
/* check if there is a format and buffer for the current frame.. */
if (vf == NULL || vf->data == NULL || ptrdst == NULL)
return;
dst_w = gdk_pixbuf_get_width(pixbuf);
dst_h = gdk_pixbuf_get_height(pixbuf);
switch (vf->format) {
case (V4L2_PIX_FMT_RGB32):
for (ys = 0, yd = 0; ys < (signed int)vf->h; ys++) {
for (xs = 0, xd = 0; xs < (signed int)vf->w; xs++) {
/* read the pixel */
ptrsrc++;
r = *(ptrsrc++);
g = *(ptrsrc++);
b = *(ptrsrc++);
/* only paint the image if the source is within the destination */
if (xd < dst_w) {
/* set the pixel */
*(ptrdst++) = b;
*(ptrdst++) = g;
*(ptrdst++) = r;
xd++;
}
}
/* if the source image is too small ignore the other places.. */ destw = gdk_pixbuf_get_height(dest);
if (xd < dst_w) desth = gdk_pixbuf_get_width(dest);
ptrdst += 3 * (dst_w - xd); destpixel = gdk_pixbuf_get_pixels(dest);
yd++;
}
break;
case (V4L2_PIX_FMT_BGR32):
for (ys = 0, yd = 0; ys < (signed int)vf->h; ys++) {
for (xs = 0, xd = 0; xs < (signed int)vf->w; xs++) {
/* read the pixel */
b = *(ptrsrc++);
g = *(ptrsrc++);
r = *(ptrsrc++);
ptrsrc++;
/* only paint the image if the source is within the destination */
if (xd < dst_w) {
/* set the pixel */
*(ptrdst++) = b;
*(ptrdst++) = g;
*(ptrdst++) = r;
xd++;
}
}
/* if the source image is too small ignore the other places.. */ if (destw > src->w) destw = src->w;
if (xd < dst_w) if (desth > src->h) destw = src->h;
ptrdst += 3 * (dst_w - xd);
yd++;
}
break;
case (V4L2_PIX_FMT_UYVY):
for (ys = 0, yd = 0; ys < (signed int)vf->h; ys++) {
for (xs = 0, xd = 0; xs < (signed int)vf->w; 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); memcpy (destpixel, src->data, 3*destw*desth);
ptrsrc += 2; }
/* only paint the image if the source is within the destination */
if (xd < dst_w) {
/* set the pixel */
*(ptrdst++) = b;
*(ptrdst++) = g;
*(ptrdst++) = r;
xd++;
}
}
/* if the source image is too small ignore the other places.. */
if (xd < dst_w)
ptrdst += 3 * (dst_w - xd);
yd++;
}
break;
case (V4L2_PIX_FMT_YUYV):
for (ys = 0, yd = 0; ys < (signed int)vf->h; ys++) {
if (yd < dst_h) {
for (xs = 0, xd = 0; xs < (signed int)vf->w; 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 < dst_w) {
/* set the pixel */
*(ptrdst++) = r;
*(ptrdst++) = g;
*(ptrdst++) = b;
xd++;
}
}
/* if the source image is too small ignore the other places.. */
if (xd < dst_w)
ptrdst += 3 * (dst_w - xd);
yd++;
}
}
break;
default:
break;
}
};
void cb_videoda_draw(GtkWidget *area, cairo_t *cr, int w, int h, gpointer data) { void cb_videoda_draw(GtkWidget *area, cairo_t *cr, int w, int h, gpointer data) {
GdkRGBA color;
GtkStyleContext *context;
context = gtk_widget_get_style_context (area);
if (video_da == NULL) return; if (video_da == NULL) return;
//cairo_move_to(cr, 30, 30); //cairo_move_to(cr, 30, 30);
@ -226,6 +65,34 @@ void video_draw_image (VideoFrame *vf) {
} }
else pix_h = 0; else pix_h = 0;
// display error screen?
if (vf == NULL) {
unsigned char *pixels;
int x, y;
// need to allocate?
if (video_pixbuf == NULL) {
printf ("%s:%d new pixbuf\n", __FILE__, __LINE__);
video_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, false, 8, 640, 480);
pix_w = 640;
pix_h = 480;
}
pixels = gdk_pixbuf_get_pixels(video_pixbuf);
for (y = 0; y < pix_h; y++) for (x = 0; x < pix_w; x++) {
if ((x & 1) && (y & 1)) {
*(++pixels) = 0xFF;
*(++pixels) = 0xFF;
*(++pixels) = 0xFF;
}
else {
*(++pixels) = 0x0;
*(++pixels) = 0x0;
*(++pixels) = 0x0;
}
}
}
else {
// //
// changes in resolution? // changes in resolution?
if (video_pixbuf == NULL || pix_h != vf->h || pix_w != vf->w) { if (video_pixbuf == NULL || pix_h != vf->h || pix_w != vf->w) {
@ -236,8 +103,8 @@ void video_draw_image (VideoFrame *vf) {
pix_w = vf->w; pix_w = vf->w;
pix_h = vf->h; pix_h = vf->h;
} }
videoframe_to_pixbuf(video_pixbuf, vf);
video_convert(vf, video_pixbuf); }
gdk_window_invalidate_rect(gtk_widget_get_window(video_da), NULL, true); gdk_window_invalidate_rect(gtk_widget_get_window(video_da), NULL, true);
}; };
@ -305,22 +172,28 @@ void cb_video_btnstop (GtkWidget *widget, gpointer data) {
gboolean cb_thread_video (gpointer data) { gboolean cb_thread_video (gpointer data) {
GtkWidget *btnstart = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "btn-video-rec")); struct timeval tv;
GtkWidget *btnstop = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "btn-video-stop")); float f;
VideoFrame *vf; GtkWidget *btnstart;
GtkWidget *btnstop;
VideoFrame *vf = (VideoFrame *) data;
// get_cycletime(&tv);
btnstop = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "btn-video-stop"));
btnstart = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "btn-video-rec"));
if (data == NULL) { if (vf == NULL) {
printf ("%s:%d %s something went wrong\n", __FILE__, __LINE__, __FUNCTION__); printf ("%s:%d %s something went wrong\n", __FILE__, __LINE__, __FUNCTION__);
videodev.Stop(); videodev.Stop();
video_draw_image(NULL);
gtk_widget_set_sensitive(btnstart, true); gtk_widget_set_sensitive(btnstart, true);
gtk_widget_set_sensitive(btnstop, false); gtk_widget_set_sensitive(btnstop, false);
} }
else { video_draw_image(vf);
vf = videodev.FrameGet();
if (vf != NULL) video_draw_image(vf); // printf ("%s:%d Time for %s ... %f ms\n", __FILE__, __LINE__, __FUNCTION__, get_cycletime(&tv));
else printf ("%s:%d %s could not get image data\n", __FILE__, __LINE__, __FUNCTION__);
videodev.FrameNext();
}
return false; return false;
}; };

@ -9,8 +9,9 @@
#include <string> #include <string>
#include <list> #include <list>
#include <jpeglib.h>
#include <linux/videodev2.h> #include <linux/videodev2.h>
#include <setjmp.h>
#include "gui.h" #include "gui.h"
@ -19,8 +20,7 @@
enum { enum {
IOMODE_READ, IOMODE_READ,
IOMODE_MMAP, IOMODE_MMAP
IOMODE_USERPTR
}; };
@ -31,6 +31,16 @@ enum {
VDEV_STATUS_ERROR VDEV_STATUS_ERROR
}; };
//
// jpeg error handling
//
struct jpg_error_mgr {
struct jpeg_error_mgr pub; /* "public" fields */
jmp_buf setjmp_buffer; /* for return to caller */
};
typedef struct jpg_error_mgr *jpg_error_ptr;
// callback status // callback status
enum { enum {
@ -49,14 +59,11 @@ struct {
struct { struct {
int w; int w;
int h; int h;
uint32_t format;
uint32_t size; uint32_t size;
uint32_t maxsize;
unsigned char *data; unsigned char *data;
} typedef VideoFrame; } typedef VideoFrame;
#define VIDEOBUFFERS 3
class VideoDev { class VideoDev {
private: private:
std::string conf_device; std::string conf_device;
@ -66,13 +73,13 @@ private:
GMutex mutex; GMutex mutex;
GThread *thread; GThread *thread;
VideoFrame vf[VIDEOBUFFERS]; VideoInBuffer inbuffer;
VideoInBuffer *inbuffers;
unsigned int inbuffers_cnt;
struct v4l2_cropcap cropcap; struct v4l2_cropcap cropcap;
struct v4l2_crop crop; struct v4l2_crop crop;
struct v4l2_format fmt; struct v4l2_format fmt;
struct jpg_error_mgr jerr;
struct jpeg_decompress_struct cinfo;
int vf_rec; int vf_rec;
int vf_get; int vf_get;
@ -85,9 +92,11 @@ private:
int CaptureStop(); int CaptureStop();
int UnInit(); int UnInit();
int Convert (VideoFrame *dest, unsigned char *ptrsrc, int srcsize, uint32_t pixelformat, int srcw, int srch);
int OpenInit (); int OpenInit ();
int Close (); int Close ();
int Grab(); int Grab(VideoFrame *vf);
int xioctl (int fd, int request, void *arg); int xioctl (int fd, int request, void *arg);
public: public:
VideoDev(); VideoDev();
@ -101,8 +110,6 @@ public:
int GetDeviceList(std::list<std::string> *list); int GetDeviceList(std::list<std::string> *list);
int IsRunning() { return running; }; int IsRunning() { return running; };
VideoFrame *FrameGet();
void FrameNext();
void PrintCaps (uint32_t caps); void PrintCaps (uint32_t caps);
void PrintFmt(struct v4l2_format *f); void PrintFmt(struct v4l2_format *f);
}; };

@ -16,17 +16,58 @@
#include <sys/types.h> #include <sys/types.h>
#include <sys/time.h> #include <sys/time.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <jpeglib.h>
#include <list> #include <list>
#include <string> #include <string>
#include <setjmp.h>
#include "gui.h" #include "gui.h"
#include "video.h" #include "video.h"
#define CLEAR(x) memset (&(x), 0, sizeof (x)) #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) { gpointer _VideoDevThread (gpointer data) {
videodev.Thread (); videodev.Thread ();
return NULL; return NULL;
@ -35,8 +76,8 @@ gpointer _VideoDevThread (gpointer data) {
VideoDev::VideoDev() { VideoDev::VideoDev() {
inbuffers = NULL; inbuffer.size = 0;
inbuffers_cnt = 0; inbuffer.data = NULL;
running = 0; running = 0;
callback = NULL; callback = NULL;
thread = NULL; thread = NULL;
@ -57,6 +98,7 @@ VideoDev::~VideoDev() {
// //
// return list of devices in form of /dev/videoYY [Name] // return list of devices in form of /dev/videoYY [Name]
//
int VideoDev::GetDeviceList(std::list<std::string> *list) { int VideoDev::GetDeviceList(std::list<std::string> *list) {
std::string device; std::string device;
int devnum; 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) // start the video thread (on error, call cb_thread_videodev with NULL data)
//
int VideoDev::Start(std::string dev, gboolean (*callback_func)(gpointer data)) { int VideoDev::Start(std::string dev, gboolean (*callback_func)(gpointer data)) {
if (running != 0 || thread != NULL) return VDEV_STATUS_ERROR; if (running != 0 || thread != NULL) return VDEV_STATUS_ERROR;
running = 1; running = 1;
conf_device = dev; conf_device = dev;
callback = callback_func; callback = callback_func;
thread = g_thread_new("network thread", _VideoDevThread, NULL); thread = g_thread_new("VideoDev", _VideoDevThread, NULL);
return VDEV_STATUS_ERROR; return VDEV_STATUS_ERROR;
}; };
@ -121,6 +164,7 @@ int VideoDev::Stop() {
// //
// try to read a video every 0.05ms (25hz) // try to read a video every 0.05ms (25hz)
//
#define CYCLETIME 0.050 #define CYCLETIME 0.050
void VideoDev::Thread() { void VideoDev::Thread() {
struct timeval cycle_timestamp; struct timeval cycle_timestamp;
@ -129,6 +173,7 @@ void VideoDev::Thread() {
int i; int i;
fd_set fds; fd_set fds;
struct timeval tv; struct timeval tv;
VideoFrame vf;
printf ("%s:%d %s Enter\n", __FILE__, __LINE__, __FUNCTION__); printf ("%s:%d %s Enter\n", __FILE__, __LINE__, __FUNCTION__);
cycle_time = get_cycletime(&cycle_timestamp); // just start counting cycle_time = get_cycletime(&cycle_timestamp); // just start counting
@ -142,36 +187,16 @@ void VideoDev::Thread() {
// //
// start capturing // start capturing
CaptureStart(); if (CaptureStart() != VDEV_STATUS_OK) running = 0;
// //
// read untill something bad happens.. // read untill something bad happens..
while (running > 0) { while (running > 0) {
// i = Grab(&vf);
// read data
g_mutex_lock(&mutex); switch (i) {
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()) {
case VDEV_STATUS_OK: case VDEV_STATUS_OK:
// got valid data if (callback) gdk_threads_add_idle(callback, &vf);
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);
break; break;
case VDEV_STATUS_AGAIN: case VDEV_STATUS_AGAIN:
@ -180,22 +205,16 @@ void VideoDev::Thread() {
default: default:
running = 0; running = 0;
} }
}
}
else
g_mutex_unlock(&mutex);
// //
// keep 25fps, write every second a message // keep 25fps, write every second a message
cycle_time = get_cycletime(&cycle_timestamp); cycle_time = get_cycletime(&cycle_timestamp);
cycle_wait = (CYCLETIME - cycle_time) + cycle_wait; cycle_wait = (CYCLETIME - cycle_time) + cycle_wait;
if (lastsec != cycle_timestamp.tv_sec) { 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; lastsec = cycle_timestamp.tv_sec;
} }
if (cycle_wait > 0.0 && cycle_wait < 1.0 ) usleep ((int)(cycle_wait * 1000000.0)); if (cycle_wait > 0.0 && cycle_wait < 1.0 ) usleep ((int)(cycle_wait * 1000000.0));
} }
// //
// stop capturing // stop capturing
CaptureStop(); CaptureStop();
@ -239,15 +258,15 @@ void VideoDev::PrintFmt(struct v4l2_format *f) {
int VideoDev::Grab() { int VideoDev::Grab(VideoFrame *vf) {
unsigned int i;
struct v4l2_buffer buf; 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) { switch (io) {
case IOMODE_READ: 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) { switch (errno) {
case EAGAIN: case EAGAIN:
return VDEV_STATUS_AGAIN; return VDEV_STATUS_AGAIN;
@ -257,6 +276,8 @@ int VideoDev::Grab() {
return VDEV_STATUS_ERROR; return VDEV_STATUS_ERROR;
} }
} }
else
Convert(vf, inbuffer.data, len, fmt.fmt.pix.pixelformat, fmt.fmt.pix.width, fmt.fmt.pix.height);
break; break;
case IOMODE_MMAP: case IOMODE_MMAP:
@ -264,66 +285,42 @@ int VideoDev::Grab() {
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP; buf.memory = V4L2_MEMORY_MMAP;
buf.index = 0;
if (-1 == xioctl(fd, VIDIOC_DQBUF, &buf)) { if (-1 == xioctl(fd, VIDIOC_DQBUF, &buf)) {
switch (errno) { switch (errno) {
case EAGAIN: case EAGAIN:
return VDEV_CBSTATUS_NOTHING; return VDEV_STATUS_AGAIN;
case EIO: case EIO:
printf ( "%s:%d error on VIDIOC_DQBUF EIO %s\n", __FILE__, __LINE__, strerror(errno));
/* Could ignore EIO, see spec. */ /* Could ignore EIO, see spec. */
/* fall through */ /* fall through */
default: default:
printf ( "%s:%d error on VIDIOC_DQBUF %s\n", __FILE__, __LINE__, strerror(errno)); 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) { // 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,
memcpy (vf[vf_rec].data, inbuffers[buf.index].data, buf.bytesused); // buf.flags, buf.index, buf.length, buf.sequence, buf.memory);
vf[vf_rec].size = buf.bytesused;
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)) { if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) {
printf ( "%s:%d error on VIDIOC_QBUF %s\n", __FILE__, __LINE__, strerror(errno)); printf ( "%s:%d error on VIDIOC_QBUF %s\n", __FILE__, __LINE__, strerror(errno));
exit (1); 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) // 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,
if (buf.m.userptr == (unsigned long)inbuffers[i].data && buf.length == inbuffers[i].size) // buf.flags, buf.index, buf.length, buf.sequence, buf.memory);
break; break;
if (i < inbuffers_cnt) { default:
memcpy (vf[vf_rec].data, inbuffers[buf.index].data, buf.bytesused); printf ("%s:%d %s wrong IOMODE?\n", __FILE__, __LINE__, __FUNCTION__);
vf[vf_rec].size = buf.bytesused; return VDEV_STATUS_ERROR;
}
if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) {
printf ( "%s:%d error on VIDIOC_QBUF %s\n", __FILE__, __LINE__, strerror(errno));
exit (1);
}
break;
} }
return VDEV_STATUS_OK; return VDEV_STATUS_OK;
@ -339,8 +336,8 @@ int VideoDev::OpenInit() {
if (fd != -1) return VDEV_STATUS_ERROR; if (fd != -1) return VDEV_STATUS_ERROR;
// //
// open device and get device name and capabilities // open device and get device name and capabilities | O_NONBLOCK
if((fd = open(conf_device.c_str(), O_RDWR | O_NONBLOCK)) == -1){ if((fd = open(conf_device.c_str(), O_RDWR)) == -1){
return VDEV_STATUS_ERROR; 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); 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); CLEAR (fmt);
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width = cropcap.defrect.width; // fmt.fmt.pix.width = cropcap.defrect.width;
fmt.fmt.pix.height = cropcap.defrect.height; // fmt.fmt.pix.height = cropcap.defrect.height;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB32; 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; fmt.fmt.pix.field = V4L2_FIELD_NONE;
if (-1 == xioctl (fd, VIDIOC_S_FMT, &fmt)) { if (-1 == xioctl (fd, VIDIOC_S_FMT, &fmt)) {
@ -428,26 +427,12 @@ int VideoDev::OpenInit() {
fmt.fmt.pix.sizeimage = min; fmt.fmt.pix.sizeimage = min;
PrintFmt (&fmt); 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 // init buffers
switch (io) { switch (io) {
case IOMODE_MMAP: case IOMODE_MMAP:
if (InitMMap() == VDEV_STATUS_ERROR) if (InitMMap() == VDEV_STATUS_ERROR)
Close(); Close();
break; break;
case IOMODE_USERPTR:
if (InitUserPtr() == VDEV_STATUS_ERROR)
Close();
break;
case IOMODE_READ: case IOMODE_READ:
default: default:
break; break;
@ -460,16 +445,18 @@ int VideoDev::OpenInit() {
// //
// prepare memory mapped buffers // prepare memory mapped buffers
int VideoDev::InitMMap() { int VideoDev::InitMMap() {
struct v4l2_requestbuffers req; struct v4l2_requestbuffers bufreq;
struct v4l2_buffer bufinfo;
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__); printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
CLEAR(req); CLEAR(bufreq);
CLEAR(bufinfo);
req.count = 4; bufreq.count = 1;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; bufreq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP; bufreq.memory = V4L2_MEMORY_MMAP;
if (-1 == xioctl(fd, VIDIOC_REQBUFS, &req)) { if (-1 == xioctl(fd, VIDIOC_REQBUFS, &bufreq)) {
if (EINVAL == errno) { if (EINVAL == errno) {
printf("%s does not support " printf("%s does not support "
"memory mappingn", conf_device.c_str()); "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()); printf ( "Insufficient buffer memory on %s\n", conf_device.c_str());
return VDEV_STATUS_ERROR; return VDEV_STATUS_ERROR;
} }
inbuffers = (VideoInBuffer*) calloc(req.count, sizeof(*inbuffers)); CLEAR(bufinfo);
if (!inbuffers) {
fprintf(stderr, "Out of memory\\n");
exit(1);
}
for (inbuffers_cnt = 0; inbuffers_cnt < req.count; ++inbuffers_cnt) { bufinfo.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
struct v4l2_buffer buf; bufinfo.memory = V4L2_MEMORY_MMAP;
bufinfo.index = 0;
CLEAR(buf); if(ioctl(fd, VIDIOC_QUERYBUF, &bufinfo) < 0){
perror("VIDIOC_QUERYBUF");
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; exit(1);
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);
} }
inbuffers[inbuffers_cnt].size = buf.length; inbuffer.size = bufinfo.length;
inbuffers[inbuffers_cnt].data = (unsigned char*)mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset); inbuffer.data = (unsigned char*)mmap(NULL, bufinfo.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, bufinfo.m.offset);
if (inbuffer.data == MAP_FAILED) {
if (MAP_FAILED == inbuffers[inbuffers_cnt].data) {
printf ( "%s:%d error on mmap %s\n", __FILE__, __LINE__, strerror(errno)); printf ( "%s:%d error on mmap %s\n", __FILE__, __LINE__, strerror(errno));
exit (1); 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; return VDEV_STATUS_OK;
} }
@ -565,8 +497,8 @@ int VideoDev::InitUserPtr() {
// //
// send the start capture signal to the cam // send the start capture signal to the cam
int VideoDev::CaptureStart() { int VideoDev::CaptureStart() {
unsigned int i;
enum v4l2_buf_type type; enum v4l2_buf_type type;
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__); printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
switch (io) { switch (io) {
@ -575,19 +507,18 @@ int VideoDev::CaptureStart() {
break; break;
case IOMODE_MMAP: case IOMODE_MMAP:
for (i = 0; i < inbuffers_cnt; ++i) {
struct v4l2_buffer buf; struct v4l2_buffer buf;
CLEAR(buf); CLEAR(buf);
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP; buf.memory = V4L2_MEMORY_MMAP;
buf.index = i; buf.index = 0;
if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) { if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) {
printf ( "%s:%d VIDIOC_QBUF %s\n", __FILE__, __LINE__, strerror(errno)); printf ( "%s:%d error on VIDIOC_QBUF %s\n", __FILE__, __LINE__, strerror(errno));
return VDEV_STATUS_ERROR; exit (1);
}
} }
type = V4L2_BUF_TYPE_VIDEO_CAPTURE; type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (-1 == xioctl(fd, VIDIOC_STREAMON, &type)) { if (-1 == xioctl(fd, VIDIOC_STREAMON, &type)) {
printf ( "%s:%d VIDIOC_STREAMON %s\n", __FILE__, __LINE__, strerror(errno)); printf ( "%s:%d VIDIOC_STREAMON %s\n", __FILE__, __LINE__, strerror(errno));
@ -595,28 +526,23 @@ int VideoDev::CaptureStart() {
} }
break; break;
case IOMODE_USERPTR: default:
for (i = 0; i < inbuffers_cnt; ++i) { printf ("%s:%d %s wrong IOMODE?\n", __FILE__, __LINE__, __FUNCTION__);
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));
return VDEV_STATUS_ERROR; return VDEV_STATUS_ERROR;
} }
}
type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if (fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG) {
if (-1 == xioctl(fd, VIDIOC_STREAMON, &type)) { cinfo.err = jpeg_std_error(&jerr.pub);
printf ( "%s:%d VIDIOC_STREAMON %s\n", __FILE__, __LINE__, strerror(errno)); 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; return VDEV_STATUS_ERROR;
} }
break; jpeg_create_decompress(&cinfo);
} }
return VDEV_STATUS_OK; return VDEV_STATUS_OK;
@ -627,26 +553,30 @@ int VideoDev::CaptureStop() {
enum v4l2_buf_type type; enum v4l2_buf_type type;
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__); printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
if (fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG) {
jpeg_destroy_decompress(&cinfo);
}
switch (io) { switch (io) {
case IOMODE_READ: case IOMODE_READ:
/* Nothing to do. */ /* Nothing to do. */
break; break;
case IOMODE_MMAP: case IOMODE_MMAP:
case IOMODE_USERPTR:
type = V4L2_BUF_TYPE_VIDEO_CAPTURE; type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (-1 == xioctl(fd, VIDIOC_STREAMOFF, &type)) { if (-1 == xioctl(fd, VIDIOC_STREAMOFF, &type)) {
fprintf(stderr, "%s:%d VIDIOC_STREAMOFF Error:%s\n", __FILE__, __LINE__, strerror(errno)); fprintf(stderr, "%s:%d VIDIOC_STREAMOFF Error:%s\n", __FILE__, __LINE__, strerror(errno));
return VDEV_STATUS_ERROR; return VDEV_STATUS_ERROR;
} }
break; break;
default:
printf ("%s:%d %s wrong IOMODE?\n", __FILE__, __LINE__, __FUNCTION__);
return VDEV_STATUS_ERROR;
} }
return VDEV_STATUS_OK; return VDEV_STATUS_OK;
}; };
int VideoDev::UnInit() { int VideoDev::UnInit() {
unsigned int i;
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__); printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
switch (io) { switch (io) {
@ -654,22 +584,18 @@ int VideoDev::UnInit() {
break; break;
case IOMODE_MMAP: case IOMODE_MMAP:
for (i = 0; i < inbuffers_cnt; ++i) if (-1 == munmap(inbuffer.data, inbuffer.size)){
if (-1 == munmap(inbuffers[i].data, inbuffers[i].size)){
fprintf(stderr, "Fatal Error @ %s:%d munmap Error:%s\n", __FILE__, __LINE__, strerror(errno)); fprintf(stderr, "Fatal Error @ %s:%d munmap Error:%s\n", __FILE__, __LINE__, strerror(errno));
exit(1); exit(1);
} }
break; break;
case IOMODE_USERPTR: default:
for (i = 0; i < inbuffers_cnt; ++i) printf ("%s:%d %s wrong IOMODE?\n", __FILE__, __LINE__, __FUNCTION__);
free(inbuffers[i].data); return VDEV_STATUS_ERROR;
break;
} }
free(inbuffers);
inbuffers = NULL;
inbuffers_cnt = 0;
return VDEV_STATUS_OK; return VDEV_STATUS_OK;
}; };
@ -677,6 +603,7 @@ int VideoDev::UnInit() {
// //
// Close Device // Close Device
// Free videobuffer // Free videobuffer
//
int VideoDev::Close() { int VideoDev::Close() {
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__); printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
@ -690,46 +617,14 @@ int VideoDev::Close() {
vf_rec = -1; vf_rec = -1;
vf_get = -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; 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. // 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 VideoDev::xioctl(int fd, int request, void *arg) {
int r; int r;
int errnoioctl; int errnoioctl;
@ -748,3 +643,178 @@ int VideoDev::xioctl(int fd, int request, void *arg) {
return r; 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;
};

Loading…
Cancel
Save