mjpeg stream is finally working. framerate increased to 25Hz

test16bit
Steffen Pohle 4 years ago
parent 7f768518a1
commit d0d0b305b6

@ -2,3 +2,8 @@
2021-09-08: 2021-09-08:
- grab video from old type of cameras is working - grab video from old type of cameras is working
2021-09-15:
- grab video from cammeras only supporting mjpeg stream

@ -11,7 +11,7 @@
VideoDev videodev; VideoDev videodev;
GtkWidget *video_da = NULL; GtkWidget *video_da = NULL;
GdkPixbuf *video_pixbuf; GdkPixbuf *video_pixbuf = NULL;
@ -32,7 +32,6 @@ void videoframe_to_pixbuf(GdkPixbuf* dest, VideoFrame *src) {
} }
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) {
if (video_da == NULL) return; if (video_da == NULL) return;
@ -98,7 +97,7 @@ void video_draw_image (VideoFrame *vf) {
if (video_pixbuf == NULL || pix_h != vf->h || pix_w != vf->w) { if (video_pixbuf == NULL || pix_h != vf->h || pix_w != vf->w) {
if (video_pixbuf != NULL) g_object_unref (video_pixbuf); if (video_pixbuf != NULL) g_object_unref (video_pixbuf);
printf ("%s:%d new pixbuf\n", __FILE__, __LINE__); printf ("%s:%d %s New Pixbuf: %d x %d\n", __FILE__, __LINE__, __FUNCTION__, vf->w, vf->h);
video_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, false, 8, vf->w, vf->h); video_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, false, 8, vf->w, vf->h);
pix_w = vf->w; pix_w = vf->w;
pix_h = vf->h; pix_h = vf->h;
@ -172,18 +171,13 @@ void cb_video_btnstop (GtkWidget *widget, gpointer data) {
gboolean cb_thread_video (gpointer data) { gboolean cb_thread_video (gpointer data) {
struct timeval tv;
float f;
GtkWidget *btnstart; GtkWidget *btnstart;
GtkWidget *btnstop; GtkWidget *btnstop;
VideoFrame *vf = (VideoFrame *) data; VideoFrame *vf = (VideoFrame *) data;
// get_cycletime(&tv);
btnstop = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "btn-video-stop")); 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")); btnstart = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "btn-video-rec"));
if (vf == 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();
@ -191,9 +185,9 @@ gboolean cb_thread_video (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);
} }
videodev.LockMutex();
video_draw_image(vf); video_draw_image(vf);
videodev.UnLockMutex();
// printf ("%s:%d Time for %s ... %f ms\n", __FILE__, __LINE__, __FUNCTION__, get_cycletime(&tv));
return false; return false;
}; };

@ -63,7 +63,7 @@ struct {
unsigned char *data; unsigned char *data;
} typedef VideoFrame; } typedef VideoFrame;
#define VDEV_INBUFFERS 2
class VideoDev { class VideoDev {
private: private:
std::string conf_device; std::string conf_device;
@ -73,10 +73,12 @@ private:
GMutex mutex; GMutex mutex;
GThread *thread; GThread *thread;
VideoInBuffer inbuffer; int inbuffer_idx;
VideoInBuffer inbuffer[VDEV_INBUFFERS];
struct v4l2_cropcap cropcap; struct v4l2_cropcap cropcap;
struct v4l2_crop crop; struct v4l2_crop crop;
struct v4l2_format fmt; struct v4l2_format fmt;
VideoFrame vf;
struct jpg_error_mgr jerr; struct jpg_error_mgr jerr;
struct jpeg_decompress_struct cinfo; struct jpeg_decompress_struct cinfo;
@ -112,6 +114,9 @@ public:
void PrintCaps (uint32_t caps); void PrintCaps (uint32_t caps);
void PrintFmt(struct v4l2_format *f); void PrintFmt(struct v4l2_format *f);
void LockMutex();
void UnLockMutex();
}; };

@ -76,13 +76,22 @@ gpointer _VideoDevThread (gpointer data) {
VideoDev::VideoDev() { VideoDev::VideoDev() {
inbuffer.size = 0; int i;
inbuffer.data = NULL;
for (i = 0; i < VDEV_INBUFFERS; i++) {
inbuffer[i].size = 0;
inbuffer[i].data = NULL;
}
vf.data = NULL;
vf.h = 0;
vf.w = 0;
vf.size = 0;
running = 0; running = 0;
callback = NULL; callback = NULL;
thread = NULL; thread = NULL;
fd = -1; fd = -1;
io = IOMODE_MMAP; io = IOMODE_MMAP;
inbuffer_idx = 0;
CLEAR(cropcap); CLEAR(cropcap);
CLEAR(crop); CLEAR(crop);
CLEAR(fmt); CLEAR(fmt);
@ -150,7 +159,7 @@ int VideoDev::Start(std::string dev, gboolean (*callback_func)(gpointer data)) {
int VideoDev::Stop() { int VideoDev::Stop() {
if (running == 1) { if (running == 1) {
running = 0; running = 0; // we can jump directly to 0
} }
if (thread) { if (thread) {
@ -164,26 +173,24 @@ int VideoDev::Stop() {
// //
// try to read a video every 0.05ms (25hz) // try to read a video every 0.05ms (25hz)
// running = 2 ... thread is running normaly
// running = 1 ... thread need to close wait for GTK to set running to 0
// running = 0 ... thread stopped and all handlers can be freed
// //
#define CYCLETIME 0.050 #define CYCLETIME 0.050
void VideoDev::Thread() { void VideoDev::Thread() {
struct timeval cycle_timestamp; struct timeval cycle_timestamp;
int lastsec = 0; int lastsec = 0;
float cycle_time, cycle_wait; float cycle_time = 0.0;
float cycle_wait = 0.0;
int i; int i;
fd_set fds;
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
// //
// open and init device buffers device // open and init device buffers device
if (OpenInit() != VDEV_STATUS_OK) { if (OpenInit() != VDEV_STATUS_OK) running = 0;
printf ("%s:%d %s something went wrong on Open()\n", __FILE__, __LINE__, __FUNCTION__);
running = 0;
}
// //
// start capturing // start capturing
@ -191,7 +198,7 @@ void VideoDev::Thread() {
// //
// read untill something bad happens.. // read untill something bad happens..
while (running > 0) { while (running) {
i = Grab(&vf); i = Grab(&vf);
switch (i) { switch (i) {
@ -215,12 +222,13 @@ void VideoDev::Thread() {
} }
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
if (callback) gdk_threads_add_idle(callback, NULL);
CaptureStop(); CaptureStop();
UnInit(); UnInit();
if (callback) gdk_threads_add_idle(callback, NULL);
Close(); Close();
printf ("%s:%d %s Exit\n", __FILE__, __LINE__, __FUNCTION__); printf ("%s:%d %s Exit\n", __FILE__, __LINE__, __FUNCTION__);
@ -266,7 +274,7 @@ int VideoDev::Grab(VideoFrame *vf) {
switch (io) { switch (io) {
case IOMODE_READ: case IOMODE_READ:
if ((len = read (fd, inbuffer.data, fmt.fmt.pix.sizeimage)) == -1) { if ((len = read (fd, inbuffer[0].data, fmt.fmt.pix.sizeimage)) == -1) {
switch (errno) { switch (errno) {
case EAGAIN: case EAGAIN:
return VDEV_STATUS_AGAIN; return VDEV_STATUS_AGAIN;
@ -276,8 +284,11 @@ int VideoDev::Grab(VideoFrame *vf) {
return VDEV_STATUS_ERROR; return VDEV_STATUS_ERROR;
} }
} }
else else {
Convert(vf, inbuffer.data, len, fmt.fmt.pix.pixelformat, fmt.fmt.pix.width, fmt.fmt.pix.height); LockMutex();
Convert(vf, inbuffer[0].data, len, fmt.fmt.pix.pixelformat, fmt.fmt.pix.width, fmt.fmt.pix.height);
UnLockMutex();
}
break; break;
case IOMODE_MMAP: case IOMODE_MMAP:
@ -285,7 +296,6 @@ int VideoDev::Grab(VideoFrame *vf) {
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) {
@ -294,19 +304,16 @@ int VideoDev::Grab(VideoFrame *vf) {
case EIO: case EIO:
printf ( "%s:%d error on VIDIOC_DQBUF EIO %s\n", __FILE__, __LINE__, strerror(errno)); printf ( "%s:%d error on VIDIOC_DQBUF EIO %s\n", __FILE__, __LINE__, strerror(errno));
/* Could ignore EIO, see spec. */
/* 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_STATUS_ERROR; return VDEV_STATUS_ERROR;
} }
} }
// 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, if (buf.index >= 0 && buf.index < VDEV_INBUFFERS) {
// buf.flags, buf.index, buf.length, buf.sequence, buf.memory); LockMutex();
Convert(vf, inbuffer[buf.index].data, buf.bytesused, fmt.fmt.pix.pixelformat, fmt.fmt.pix.width, fmt.fmt.pix.height);
if (buf.index == 0) { UnLockMutex();
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)) {
@ -314,8 +321,7 @@ int VideoDev::Grab(VideoFrame *vf) {
exit (1); exit (1);
} }
// 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 (++inbuffer_idx >= VDEV_INBUFFERS) inbuffer_idx = 0;
// buf.flags, buf.index, buf.length, buf.sequence, buf.memory);
break; break;
default: default:
@ -404,12 +410,10 @@ int VideoDev::OpenInit() {
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.height = cropcap.defrect.height;
fmt.fmt.pix.width = 1920; fmt.fmt.pix.width = 1920;
fmt.fmt.pix.height = 1080; fmt.fmt.pix.height = 1080;
// fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB32; fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB32;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG; // 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)) {
@ -449,10 +453,12 @@ int VideoDev::InitMMap() {
struct v4l2_buffer bufinfo; struct v4l2_buffer bufinfo;
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__); printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
int i;
CLEAR(bufreq); CLEAR(bufreq);
CLEAR(bufinfo); CLEAR(bufinfo);
bufreq.count = 1; bufreq.count = VDEV_INBUFFERS;
bufreq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; bufreq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
bufreq.memory = V4L2_MEMORY_MMAP; bufreq.memory = V4L2_MEMORY_MMAP;
@ -472,24 +478,25 @@ int VideoDev::InitMMap() {
return VDEV_STATUS_ERROR; return VDEV_STATUS_ERROR;
} }
for (i = 0; i < VDEV_INBUFFERS; i++) {
CLEAR(bufinfo); CLEAR(bufinfo);
bufinfo.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; bufinfo.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
bufinfo.memory = V4L2_MEMORY_MMAP; bufinfo.memory = V4L2_MEMORY_MMAP;
bufinfo.index = 0; bufinfo.index = i;
if(ioctl(fd, VIDIOC_QUERYBUF, &bufinfo) < 0){ if(ioctl(fd, VIDIOC_QUERYBUF, &bufinfo) < 0){
perror("VIDIOC_QUERYBUF"); perror("VIDIOC_QUERYBUF");
exit(1); exit(1);
} }
inbuffer.size = bufinfo.length; inbuffer[i].size = bufinfo.length;
inbuffer.data = (unsigned char*)mmap(NULL, bufinfo.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, bufinfo.m.offset); inbuffer[i].data = (unsigned char*)mmap(NULL, bufinfo.length, PROT_READ | PROT_WRITE,
if (inbuffer.data == MAP_FAILED) { MAP_SHARED, fd, bufinfo.m.offset);
if (inbuffer[i].data == MAP_FAILED) {
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; return VDEV_STATUS_OK;
} }
@ -508,17 +515,20 @@ int VideoDev::CaptureStart() {
case IOMODE_MMAP: case IOMODE_MMAP:
struct v4l2_buffer buf; struct v4l2_buffer buf;
CLEAR(buf);
for (inbuffer_idx = 0; inbuffer_idx < VDEV_INBUFFERS; inbuffer_idx++) {
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 = 0; buf.index = inbuffer_idx;
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); return VDEV_STATUS_ERROR;
}
} }
inbuffer_idx = 0;
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));
@ -532,16 +542,6 @@ int VideoDev::CaptureStart() {
} }
if (fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG) { 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;
}
jpeg_create_decompress(&cinfo); jpeg_create_decompress(&cinfo);
} }
@ -584,10 +584,15 @@ int VideoDev::UnInit() {
break; break;
case IOMODE_MMAP: case IOMODE_MMAP:
if (-1 == munmap(inbuffer.data, inbuffer.size)){ for (inbuffer_idx = 0; inbuffer_idx < VDEV_INBUFFERS; inbuffer_idx++)
if (inbuffer[inbuffer_idx].data) {
if (-1 == munmap(inbuffer[inbuffer_idx].data, inbuffer[inbuffer_idx].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);
} }
inbuffer[inbuffer_idx].data = NULL;
inbuffer[inbuffer_idx].size = 0;
}
break; break;
default: default:
@ -661,7 +666,7 @@ int VideoDev::Convert (VideoFrame *dest, unsigned char *ptrsrc, int srcsize, uin
if (dest->data != NULL && dest->w != srcw && dest->h != srch) { if (dest->data != NULL && dest->w != srcw && dest->h != srch) {
free (dest->data); free (dest->data);
dest->data == NULL; dest->data = NULL;
} }
if (dest->data == NULL) { if (dest->data == NULL) {
@ -799,6 +804,14 @@ int VideoDev::Convert (VideoFrame *dest, unsigned char *ptrsrc, int srcsize, uin
break; break;
case (V4L2_PIX_FMT_MJPEG): case (V4L2_PIX_FMT_MJPEG):
cinfo.err = jpeg_std_error(&jerr.pub);
jerr.pub.error_exit = jpg_error_exit;
if (setjmp(jerr.setjmp_buffer)) {
// jpeg data and allocations will be destroyed via StopCapture.
printf ("%s:%d %s JPEG Error\n", __FILE__, __LINE__, __FUNCTION__);
return VDEV_STATUS_ERROR;
}
jpeg_mem_src(&cinfo, ptrsrc, srcsize); jpeg_mem_src(&cinfo, ptrsrc, srcsize);
jpeg_read_header(&cinfo, TRUE); jpeg_read_header(&cinfo, TRUE);
jpeg_start_decompress(&cinfo); jpeg_start_decompress(&cinfo);
@ -818,3 +831,15 @@ int VideoDev::Convert (VideoFrame *dest, unsigned char *ptrsrc, int srcsize, uin
return VDEV_STATUS_OK; return VDEV_STATUS_OK;
}; };
void VideoDev::LockMutex() {
g_mutex_lock(&mutex);
};
void VideoDev::UnLockMutex() {
g_mutex_unlock(&mutex);
};

Loading…
Cancel
Save