grab video image is working

test16bit
Steffen Pohle 4 years ago
parent ea797bf912
commit cd83c80237

@ -0,0 +1,4 @@
2021-09-08:
- grab video from old type of cameras is working

@ -25,11 +25,19 @@ gboolean cb_window_delete_event (GtkWidget *widget, GdkEvent *event, gpointer
return FALSE;
};
void cb_window_show (GtkWidget *widget, gpointer data) {
// GtkBuilder *builder = (GtkBuilder *) data;
// g_timeout_add(100, gui_loop, NULL);
GtkWidget *btnstart = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "btn-video-rec"));
GtkWidget *btnstop = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "btn-video-stop"));
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
gtk_widget_set_sensitive(btnstart, true);
gtk_widget_set_sensitive(btnstop, false);
};
void displayerror (std::string error) {
GtkWidget *dialog;
GtkWidget *window = GTK_WIDGET (gtk_builder_get_object (_builder_, "main-window"));
@ -44,3 +52,4 @@ void displayerror (std::string error) {
};

@ -33,8 +33,12 @@ G_MODULE_EXPORT void cb_window_show (GtkWidget *widget, gpointer data);
G_MODULE_EXPORT gboolean cb_window_delete_event (GtkWidget *widget,
GdkEvent *event, gpointer data);
G_MODULE_EXPORT void cb_video_refreshlist (GtkWidget *widget, gpointer data);
//
// video and video devices
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_btnstop (GtkWidget *widget, gpointer data);
//
// thread handles
@ -45,5 +49,7 @@ G_MODULE_EXPORT gboolean cb_thread_video (gpointer data);
#endif
extern float get_cycletime(struct timeval *t);
#endif // _GUI_H_

@ -23,7 +23,7 @@
<property name="position-set">True</property>
<property name="wide-handle">True</property>
<child>
<object class="GtkDrawingArea">
<object class="GtkDrawingArea" id="video-da">
<property name="visible">True</property>
<property name="can-focus">False</property>
</object>
@ -41,10 +41,13 @@
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">1</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="margin-start">5</property>
<property name="margin-end">5</property>
<property name="spacing">7</property>
<child>
<object class="GtkLabel">
@ -64,6 +67,11 @@
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="has-entry">True</property>
<child internal-child="entry">
<object class="GtkEntry">
<property name="can-focus">False</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
@ -80,8 +88,8 @@
<property name="receives-default">True</property>
<property name="use-stock">True</property>
<property name="always-show-image">True</property>
<signal name="activate" handler="cb_video_refreshlist" swapped="no"/>
<signal name="pressed" handler="cb_video_refreshlist" swapped="no"/>
<signal name="activate" handler="cb_video_btnrefreshlist" swapped="no"/>
<signal name="pressed" handler="cb_video_btnrefreshlist" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
@ -90,21 +98,44 @@
</packing>
</child>
<child>
<object class="GtkButton" id="btn_device_connect">
<property name="label">gtk-apply</property>
<property name="name">btn-videodev-ok</property>
<object class="GtkButton" id="btn-video-stop">
<property name="label">gtk-stop</property>
<property name="name">btn-videodev-stop</property>
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="use-stock">True</property>
<property name="always-show-image">True</property>
<signal name="activate" handler="cb_video_btnstop" swapped="no"/>
<signal name="pressed" handler="cb_video_btnstop" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack-type">end</property>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkButton" id="btn-video-rec">
<property name="label">gtk-media-record</property>
<property name="name">btn-videodev-rec</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="use-stock">True</property>
<property name="always-show-image">True</property>
<signal name="activate" handler="cb_video_btnrec" swapped="no"/>
<signal name="pressed" handler="cb_video_btnrec" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack-type">end</property>
<property name="position">4</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>

@ -10,8 +10,253 @@
#include "video.h"
VideoDev videodev;
GtkWidget *video_da = NULL;
GdkPixbuf *video_pixbuf;
void cb_video_refreshlist (GtkWidget *widget, gpointer data) {
//
// 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);
};
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.. */
if (xd < dst_w)
ptrdst += 3 * (dst_w - xd);
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 (xd < dst_w)
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);
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 video_draw_image (VideoFrame *vf) {
int pix_w;
int pix_h;
if (video_da == NULL) {
video_da = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "video-da"));
if (video_da == NULL) {
printf ("%s:%d could not find drawingarea.\n", __FILE__, __LINE__);
return;
}
}
// check if the pixbuf is already allocated?
if (video_pixbuf) {
pix_h = gdk_pixbuf_get_height(video_pixbuf);
pix_w = gdk_pixbuf_get_width(video_pixbuf);
}
else pix_h = 0;
//
// changes in resolution?
if (video_pixbuf == NULL || pix_h != vf->h || pix_w != vf->w) {
if (video_pixbuf != NULL) g_object_unref (video_pixbuf);
printf ("%s:%d new pixbuf\n", __FILE__, __LINE__);
video_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, false, 8, vf->w, vf->h);
pix_w = vf->w;
pix_h = vf->h;
// int channels = gdk_pixbuf_get_n_channels (video_pixbuf);
// int rowstride = gdk_pixbuf_get_rowstride (video_pixbuf);
}
video_convert(vf, video_pixbuf);
GdkWindow* window = gtk_widget_get_window(video_da);
cairo_t *cr = gdk_cairo_create(window);
gdk_cairo_set_source_pixbuf(cr, video_pixbuf, 0, 0);
cairo_paint(cr);
cairo_fill (cr);
// cairo_move_to(cr, 30, 30);
// cairo_set_font_size(cr,15);
// cairo_show_text(cr, "hello world");
cairo_destroy (cr);
/*
//
// "convert" the G*t*kWidget to G*d*kWindow (no, it's not a GtkWindow!)
GdkWindow* window = gtk_widget_get_window(video_da);
cairo_region_t * cairoRegion = cairo_region_create();
GdkDrawingContext * drawingContext = gdk_window_begin_draw_frame (window,cairoRegion);
cairo_t * cr = gdk_drawing_context_get_cairo_context (drawingContext);
gdk_window_end_draw_frame(window,drawingContext);
cairo_region_destroy(cairoRegion);
*/
};
//
// refresh the possible devices
void cb_video_btnrefreshlist (GtkWidget *widget, gpointer data) {
GtkWidget *cbox = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "cb-videodev"));
GtkListStore *model = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(cbox)));
@ -34,4 +279,61 @@ void cb_video_refreshlist (GtkWidget *widget, gpointer data) {
-1);
}
}
}
};
//
// start recording from the videodev (will start a new thread)
void cb_video_btnrec (GtkWidget *widget, gpointer data) {
GtkWidget *cbox = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "cb-videodev"));
GtkWidget *btnstart = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "btn-video-rec"));
GtkWidget *btnstop = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "btn-video-stop"));
GtkWidget *cbdevice = gtk_bin_get_child(GTK_BIN(cbox));
std::string device = gtk_entry_get_text(GTK_ENTRY(cbdevice));
device = device.substr (0, device.find(' '));
printf ("%s:%d %s open device: '%s'\n", __FILE__, __LINE__, __FUNCTION__, device.c_str());
gtk_widget_set_sensitive(btnstart, false);
gtk_widget_set_sensitive(btnstop, true);
videodev.Start(device, cb_thread_video);
};
//
// stop recording from the videodev (will stop the running thread)
void cb_video_btnstop (GtkWidget *widget, gpointer data) {
GtkWidget *btnstart = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "btn-video-rec"));
GtkWidget *btnstop = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "btn-video-stop"));
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
videodev.Stop();
gtk_widget_set_sensitive(btnstart, true);
gtk_widget_set_sensitive(btnstop, false);
};
gboolean cb_thread_video (gpointer data) {
GtkWidget *btnstart = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "btn-video-rec"));
GtkWidget *btnstop = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "btn-video-stop"));
VideoFrame *vf;
if (data == NULL) {
printf ("%s:%d %s something went wrong\n", __FILE__, __LINE__, __FUNCTION__);
videodev.Stop();
gtk_widget_set_sensitive(btnstart, true);
gtk_widget_set_sensitive(btnstop, false);
}
else {
// printf ("%s:%d %s got video data\n", __FILE__, __LINE__, __FUNCTION__);
vf = videodev.FrameGet();
if (vf != NULL) video_draw_image(vf);
videodev.FrameNext();
}
return false;
};

@ -9,32 +9,107 @@
#include <string>
#include <list>
#include <linux/videodev2.h>
#include "gui.h"
#include "config.h"
enum {
IOMODE_READ,
IOMODE_MMAP,
IOMODE_USERPTR
};
enum {
VDEV_STATUS_UNKNOWN,
VDEV_STATUS_OK,
VDEV_STATUS_AGAIN,
VDEV_STATUS_ERROR
};
// callback status
enum {
VDEV_CBSTATUS_NOTHING = 1,
VDEV_CBSTATUS_NEWFRAME,
VDEV_CBSTATUS_ERROR
};
struct {
unsigned int size;
unsigned char* data;
} typedef VideoInBuffer;
struct {
int w;
int h;
uint32_t format;
uint32_t size;
uint32_t maxsize;
unsigned char *data;
} typedef VideoFrame;
#define VIDEOBUFFERS 3
class VideoDev {
private:
std::string conf_device;
std::string conf_devicename;
gboolean (*callback)(gpointer data);
int status;
int running;
GMutex mutex;
GThread *thread;
VideoFrame vf[VIDEOBUFFERS];
VideoInBuffer *inbuffers;
unsigned int inbuffers_cnt;
struct v4l2_cropcap cropcap;
struct v4l2_crop crop;
struct v4l2_format fmt;
int vf_rec;
int vf_get;
int io; // IO Mode
int fd;
int InitMMap();
int InitUserPtr();
int CaptureStart();
int CaptureStop();
int UnInit();
int OpenInit ();
int Close ();
int Grab();
int xioctl (int fd, int request, void *arg);
public:
VideoDev();
~VideoDev();
void Thread();
int Start(std::string dev, gboolean (*callback_func)(gpointer data)); // will start a new thread
int Stop(); // stop video processing and stop the pthread
int GetDeviceList(std::list<std::string> *list);
int IsRunning() { return running; };
int GetStatus() { return status; };
VideoFrame *FrameGet();
void FrameNext();
void PrintCaps (uint32_t caps);
void PrintFmt(struct v4l2_format *f);
};
extern VideoDev videodev;
#endif // _VIDEO_H_

@ -4,23 +4,49 @@
*
*****************************************************************************************/
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>
#include <sys/time.h>
#include <errno.h>
#include <getopt.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/mman.h>
#include <list>
#include <string>
#include "gui.h"
#include "video.h"
#define CLEAR(x) memset (&(x), 0, sizeof (x))
//
// C / C++ Wrapper
gpointer _VideoDevThread (gpointer data) {
videodev.Thread ();
return NULL;
};
VideoDev::VideoDev() {
conf_device = "";
conf_devicename = "";
status = VDEV_STATUS_OK;
inbuffers = NULL;
inbuffers_cnt = 0;
running = 0;
callback = NULL;
thread = NULL;
fd = -1;
io = IOMODE_MMAP;
CLEAR(cropcap);
CLEAR(crop);
CLEAR(fmt);
g_mutex_init (&mutex);
Close(); // will reset almost everything
};
@ -65,11 +91,660 @@ 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);
return VDEV_STATUS_ERROR;
};
int VideoDev::Stop() {
if (running == 1) {
running = 0;
}
if (thread) {
g_thread_join (thread);
thread = NULL;
}
return VDEV_STATUS_OK;
};
//
// try to read a video every 0.05ms (25hz)
#define CYCLETIME 0.050
void VideoDev::Thread() {
struct timeval cycle_timestamp;
int lastsec = 0;
float cycle_time, cycle_wait;
int i;
fd_set fds;
struct timeval tv;
printf ("%s:%d %s Enter\n", __FILE__, __LINE__, __FUNCTION__);
cycle_time = get_cycletime(&cycle_timestamp); // just start counting
//
// open and init device buffers device
if (OpenInit() != VDEV_STATUS_OK) {
printf ("%s:%d %s something went wrong on Open()\n", __FILE__, __LINE__, __FUNCTION__);
running = 0;
}
//
// start capturing
CaptureStart();
//
// 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()) {
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);
break;
case VDEV_STATUS_AGAIN:
break;
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);
lastsec = cycle_timestamp.tv_sec;
}
if (cycle_wait > 0.0 && cycle_wait < 1.0 ) usleep ((int)(cycle_wait * 1000000.0));
}
//
// stop capturing
CaptureStop();
UnInit();
if (callback) gdk_threads_add_idle(callback, NULL);
Close();
printf ("%s:%d %s Exit\n", __FILE__, __LINE__, __FUNCTION__);
};
void VideoDev::PrintCaps(uint32_t caps) {
printf ("%s:%d %s Caps: %x\n", __FILE__, __LINE__, __FUNCTION__, caps);
if (caps & V4L2_CAP_VIDEO_CAPTURE) printf (" V4L2_CAP_VIDEO_CAPTURE\n");
if (caps & V4L2_CAP_EXT_PIX_FORMAT) printf (" V4L2_CAP_EXT_PIX_FORMAT\n");
if (caps & V4L2_CAP_META_CAPTURE) printf (" V4L2_CAP_META_CAPTURE\n");
if (caps & V4L2_CAP_STREAMING) printf (" V4L2_CAP_STREAMING\n");
if (caps & V4L2_CAP_DEVICE_CAPS) printf (" V4L2_CAP_DEVICE_CAPS\n");
if (caps & V4L2_CAP_TUNER) printf (" V4L2_CAP_TUNER\n");
if (caps & V4L2_CAP_MODULATOR) printf (" V4L2_CAP_MODULATOR\n");
if (caps & V4L2_CAP_READWRITE) printf (" V4L2_CAP_READWRITE\n");
if (caps & V4L2_CAP_ASYNCIO) printf (" V4L2_CAP_ASYNCIO\n");
}
void VideoDev::PrintFmt(struct v4l2_format *f) {
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
printf (" type : %u\n", f->type);
printf (" fmt.pix.width : %d\n", f->fmt.pix.width);
printf (" fmt.pix.height : %d\n", f->fmt.pix.height);
printf (" fmt.fmt.pix.pixelformat : %c%c%c%c\n", ((char*)&fmt.fmt.pix.pixelformat)[0],
((char*)&fmt.fmt.pix.pixelformat)[1],
((char*)&fmt.fmt.pix.pixelformat)[2],
((char*)&fmt.fmt.pix.pixelformat)[3]);
printf (" fmt.pix.field : %d\n", f->fmt.pix.field);
printf (" fmt.pix.bytesperline : %d\n", f->fmt.pix.bytesperline);
printf (" fmt.pix.sizeimage : %d\n", f->fmt.pix.sizeimage);
}
int VideoDev::Grab() {
unsigned int i;
struct v4l2_buffer buf;
if (vf_rec < 0 || vf_rec >= VIDEOBUFFERS) return VDEV_CBSTATUS_NOTHING;
switch (io) {
case IOMODE_READ:
if (-1 == read (fd, vf[vf_rec].data, fmt.fmt.pix.sizeimage)) {
switch (errno) {
case EAGAIN:
return VDEV_STATUS_AGAIN;
case EIO:
default:
printf ("v4l2_grab IOM_READ: %s dest:%p size:%d\n", strerror (errno), vf[vf_rec].data, fmt.fmt.pix.sizeimage);
return VDEV_STATUS_ERROR;
}
}
break;
case IOMODE_MMAP:
CLEAR(buf);
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
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));
return VDEV_CBSTATUS_ERROR;
}
}
if (buf.index < 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;
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)
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;
}
return VDEV_STATUS_OK;
}
//
// Open Device
int VideoDev::OpenInit() {
int i;
struct v4l2_capability vcap;
printf ("%s:%d %s Device: '%s'\n", __FILE__, __LINE__, __FUNCTION__, conf_device.c_str());
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){
return VDEV_STATUS_ERROR;
}
if(ioctl(fd, VIDIOC_QUERYCAP, &vcap) == -1)
strncpy ((char*)&vcap.card, "unknown", sizeof(vcap.card));
conf_devicename = (char*) vcap.card;
printf ("%s:%d %s Capabilities: %u\n", __FILE__, __LINE__, __FUNCTION__, vcap.capabilities);
PrintCaps(vcap.capabilities);
printf ("%s:%d %s Device Capabilities: %u\n", __FILE__, __LINE__, __FUNCTION__, vcap.device_caps);
PrintCaps(vcap.device_caps);
if (!(vcap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
printf ("%s:%d %s device has no video capture capabilities\n", __FILE__, __LINE__, __FUNCTION__);
return VDEV_STATUS_ERROR;
Close();
}
//
// query controls
struct v4l2_queryctrl queryctrl;
struct v4l2_control control;
uint32_t min;
printf (" Controls: \n");
memset (&queryctrl, 0, sizeof (queryctrl));
for (i = V4L2_CID_BASE; i < V4L2_CID_LASTP1; i++) {
queryctrl.id = i;
if (0 == ioctl (fd, VIDIOC_QUERYCTRL, &queryctrl)) {
if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED)
printf (" *");
memset (&control, 0, sizeof (control));
control.id = i;
ioctl (fd, VIDIOC_G_CTRL, &control);
printf (" [%d] %s:%d\n", i, queryctrl.name, control.value);
}
}
printf ("\n Private Controls: ");
for (i = V4L2_CID_PRIVATE_BASE;; i++) {
queryctrl.id = i;
if (0 == ioctl (fd, VIDIOC_QUERYCTRL, &queryctrl)) {
printf (" [%d] %s\n", i, queryctrl.name);
} else {
break;
}
}
printf ("\n");
//
// check for cropping.. if we have it setup default
CLEAR (cropcap);
cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (0 == xioctl (fd, VIDIOC_CROPCAP, &cropcap)) {
crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
crop.c = cropcap.defrect; // reset to default
if (-1 == xioctl (fd, VIDIOC_S_CROP, &crop)) {
printf ("%s:%d %s VIDEOC_S_CROP Errorcode: %s\n", __FILE__, __LINE__, __FUNCTION__, strerror(errno));
}
}
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.field = V4L2_FIELD_NONE;
if (-1 == xioctl (fd, VIDIOC_S_FMT, &fmt)) {
fprintf (stderr, "VIDIOC_S_FMT : %s", strerror (errno));
return VDEV_STATUS_ERROR;
}
// Note VIDIOC_S_FMT may change width and height.
// Buggy driver paranoia. - as written in the v4l2 api documentation
min = fmt.fmt.pix.width * 2;
if (fmt.fmt.pix.bytesperline < min)
fmt.fmt.pix.bytesperline = min;
min = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height;
if (fmt.fmt.pix.sizeimage < min)
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;
}
return VDEV_STATUS_OK;
};
//
// prepare memory mapped buffers
int VideoDev::InitMMap() {
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_MMAP;
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 %s Error %s\n", __FILE__, __LINE__, __FUNCTION__, strerror(errno));
return VDEV_STATUS_ERROR;
}
}
if (req.count < 2) {
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);
}
for (inbuffers_cnt = 0; inbuffers_cnt < req.count; ++inbuffers_cnt) {
struct v4l2_buffer buf;
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);
}
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) {
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;
}
//
// 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) {
case IOMODE_READ:
/* Nothing to do. */
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;
if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) {
printf ( "%s:%d VIDIOC_QBUF %s\n", __FILE__, __LINE__, strerror(errno));
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));
return VDEV_STATUS_ERROR;
}
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));
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));
return VDEV_STATUS_ERROR;
}
break;
}
return VDEV_STATUS_OK;
};
int VideoDev::CaptureStop() {
enum v4l2_buf_type type;
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
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;
}
return VDEV_STATUS_OK;
};
int VideoDev::UnInit() {
unsigned int i;
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
switch (io) {
case IOMODE_READ:
break;
case IOMODE_MMAP:
for (i = 0; i < inbuffers_cnt; ++i)
if (-1 == munmap(inbuffers[i].data, inbuffers[i].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;
}
free(inbuffers);
inbuffers = NULL;
inbuffers_cnt = 0;
return VDEV_STATUS_OK;
};
//
// Close Device
// Free videobuffer
int VideoDev::Close() {
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
if (fd >= 0) {
close (fd);
fd = -1;
}
conf_device = "";
conf_devicename = "";
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;
struct timeval to1;
struct timeval to2;
float to;
gettimeofday(&to1, NULL);
do {
r = ioctl(fd, request, arg);
errnoioctl = errno;
gettimeofday(&to2, NULL);
to = (float)(to2.tv_sec - to1.tv_sec) + ((to2.tv_usec - to1.tv_usec) / 1000000.0);
} while (r == -1 && errnoioctl == EINTR && to < 2.0);
return r;
};

Loading…
Cancel
Save