video controls are working and can be set

test16bit
Steffen Pohle 4 years ago
parent d0d0b305b6
commit 32ef6e729f

@ -1,4 +1,8 @@
2021-09-21:
- scaling the video to screensize works.
- controls can be set via gui
2021-09-08:
- grab video from old type of cameras is working

@ -36,6 +36,7 @@ void cb_window_show (GtkWidget *widget, gpointer data) {
gtk_widget_set_sensitive(btnstart, true);
gtk_widget_set_sensitive(btnstop, false);
g_timeout_add(2000, videoctrl_update, NULL);
};

@ -42,6 +42,9 @@ 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 gboolean video_display(gpointer data);
G_MODULE_EXPORT void cb_vidctrl_entry_change (GtkWidget *widget, gpointer data);
G_MODULE_EXPORT void cb_vidctrl_scale_change (GtkRange *range, gpointer data);
gboolean videoctrl_update(gpointer data);
//

@ -12,7 +12,7 @@
<object class="GtkPaned">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="position">700</property>
<property name="position">800</property>
<property name="position-set">True</property>
<property name="wide-handle">True</property>
<child>
@ -20,7 +20,7 @@
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="orientation">vertical</property>
<property name="position">250</property>
<property name="position">480</property>
<property name="position-set">True</property>
<property name="wide-handle">True</property>
<child>
@ -147,7 +147,56 @@
</packing>
</child>
<child>
<placeholder/>
<object class="GtkScrolledWindow" id="vidctrl-scr">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="shadow-type">in</property>
<child>
<object class="GtkViewport" id="vidctrl-vp">
<property name="visible">True</property>
<property name="can-focus">False</property>
<child>
<!-- n-columns=3 n-rows=3 -->
<object class="GtkGrid" id="vidctrl-grid">
<property name="visible">True</property>
<property name="can-focus">False</property>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
</object>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<placeholder/>
@ -158,20 +207,27 @@
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">page 1</property>
<property name="label" translatable="yes">Video Device</property>
</object>
<packing>
<property name="tab-fill">False</property>
</packing>
</child>
<child>
<placeholder/>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">nix da - page 2</property>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
<child type="tab">
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">page 2</property>
<property name="label" translatable="yes">Detection</property>
</object>
<packing>
<property name="position">1</property>
@ -179,13 +235,20 @@
</packing>
</child>
<child>
<placeholder/>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">nix da</property>
</object>
<packing>
<property name="position">2</property>
</packing>
</child>
<child type="tab">
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">page 3</property>
<property name="label" translatable="yes">Output</property>
</object>
<packing>
<property name="position">2</property>

@ -6,6 +6,7 @@
#include <list>
#include <string>
#include <stdlib.h>
#include "gui.h"
#include "video.h"
@ -15,6 +16,30 @@ GdkPixbuf *video_pixbuf = NULL;
gboolean videoctrl_update(gpointer data) {
GtkWidget *grid = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "vidctrl-grid"));
GtkWidget *scale = NULL;
GtkWidget *entry = NULL;
GtkWidget *label = NULL;
int value;
int min;
int max;
int i;
for (i = 0; (label = gtk_grid_get_child_at(GTK_GRID(grid), 0, i)) != NULL; i++) {
if (videodev.GetCtrlMinMaxValue(gtk_label_get_text(GTK_LABEL(label)), &min, &max, &value) == VDEV_STATUS_OK) {
scale = gtk_grid_get_child_at(GTK_GRID(grid), 1, i);
gtk_range_set_value(GTK_RANGE(scale), value);
entry = gtk_grid_get_child_at(GTK_GRID(grid), 2, i);
gtk_entry_set_text(GTK_ENTRY(entry), std::to_string(value).c_str());
}
}
return TRUE;
}
void videoframe_to_pixbuf(GdkPixbuf* dest, VideoFrame *src) {
int destw, desth;
unsigned char *destpixel;
@ -33,21 +58,43 @@ void videoframe_to_pixbuf(GdkPixbuf* dest, VideoFrame *src) {
void cb_videoda_draw(GtkWidget *area, cairo_t *cr, int w, int h, gpointer data) {
if (video_da == NULL) return;
int clientw, clienth, pixbufw, pixbufh;
float clientar, pixbufar;
GdkPixbuf *pixbuf = NULL;
if (video_da == NULL) return;
clienth = gtk_widget_get_allocated_height(video_da);
clientw = gtk_widget_get_allocated_width(video_da);
clientar = (float)clientw/(float)clienth;
pixbufh = gdk_pixbuf_get_height(video_pixbuf);
pixbufw = gdk_pixbuf_get_width(video_pixbuf);
pixbufar = (float)pixbufw/(float)pixbufh;
if (pixbufar < clientar) {
clientw = (pixbufar * (float) clienth);
}
else {
clienth = ((float) clientw / pixbufar);
}
pixbuf = gdk_pixbuf_scale_simple (video_pixbuf, clientw, clienth, GDK_INTERP_NEAREST);
//cairo_move_to(cr, 30, 30);
//cairo_set_font_size(cr,15);
//cairo_show_text(cr, "hello world");
gdk_cairo_set_source_pixbuf(cr, video_pixbuf, 0, 0);
gdk_cairo_set_source_pixbuf(cr, pixbuf, 0, 0);
cairo_paint(cr);
cairo_fill (cr);
g_object_unref (pixbuf);
};
void video_draw_image (VideoFrame *vf) {
int pix_w;
int pix_h;
int x, y;
if (video_da == NULL) {
video_da = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "video-da"));
@ -67,7 +114,6 @@ void video_draw_image (VideoFrame *vf) {
// display error screen?
if (vf == NULL) {
unsigned char *pixels;
int x, y;
// need to allocate?
if (video_pixbuf == NULL) {
@ -94,6 +140,7 @@ void video_draw_image (VideoFrame *vf) {
else {
//
// changes in resolution?
if (video_pixbuf == NULL || pix_h != vf->h || pix_w != vf->w) {
if (video_pixbuf != NULL) g_object_unref (video_pixbuf);
@ -138,12 +185,19 @@ void cb_video_btnrefreshlist (GtkWidget *widget, gpointer data) {
//
// start recording from the videodev (will start a new thread)
// load list of controls from the video device and create the GtkGrid
// with all elements. Also connect the signals to the callback functions.
void cb_video_btnrec (GtkWidget *widget, gpointer data) {
std::list<std::string> list;
std::list<std::string>::iterator iter;
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));
GtkWidget *grid = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "vidctrl-grid"));
GtkWidget *gridchild = NULL;
int i;
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(' '));
@ -152,6 +206,54 @@ void cb_video_btnrec (GtkWidget *widget, gpointer data) {
gtk_widget_set_sensitive(btnstop, true);
videodev.Start(device, cb_thread_video);
videodev.GetCtrlList(&list);
//
// clear grid
while ((gridchild = gtk_grid_get_child_at(GTK_GRID(grid), 0, 0)) != NULL) {
gtk_grid_remove_row (GTK_GRID(grid), 0);
}
//
// add elements
for (i = 0, iter = list.begin(); iter != list.end(); iter++, i++) {
int min = 0;
int max = 100;
int value = 50;
videodev.GetCtrlMinMaxValue((*iter), &min, &max, &value);
//
// label
GtkWidget *label = gtk_label_new(iter->c_str());
//
// scale/range
GtkWidget *scale = gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL, min, max, 1);
gtk_range_set_value(GTK_RANGE(scale),value);
gtk_widget_set_hexpand (scale,true);
gtk_scale_set_draw_value(GTK_SCALE(scale), false);
g_signal_connect (GTK_RANGE(scale), "value-changed", G_CALLBACK(cb_vidctrl_scale_change), (void*)(long int)i);
//
// entry field
GtkWidget *entry = gtk_entry_new();
gtk_entry_set_text(GTK_ENTRY(entry), std::to_string(value).c_str());
g_signal_connect (entry, "activate", G_CALLBACK(cb_vidctrl_entry_change), (void*)(long int)i);
gtk_grid_insert_row(GTK_GRID(grid), i);
if (i == 0) {
gtk_grid_insert_column(GTK_GRID(grid), 0);
gtk_grid_insert_column(GTK_GRID(grid), 0);
gtk_grid_insert_column(GTK_GRID(grid), 0);
}
gtk_grid_attach(GTK_GRID(grid), label, 0, i, 1, 1);
gtk_grid_attach(GTK_GRID(grid), scale, 1, i, 1, 1);
gtk_grid_attach(GTK_GRID(grid), entry, 2, i, 1, 1);
gtk_widget_show (label);
gtk_widget_show (scale);
gtk_widget_show (entry);
}
};
@ -170,6 +272,9 @@ void cb_video_btnstop (GtkWidget *widget, gpointer data) {
//
// callback from videodev thread. data will point to the latest VideoFrame.
// Access to this data must be Locked before use.
gboolean cb_thread_video (gpointer data) {
GtkWidget *btnstart;
GtkWidget *btnstop;
@ -192,3 +297,45 @@ gboolean cb_thread_video (gpointer data) {
return false;
};
//
// set ctrl on the device
void videoctrl_set(std::string name, int value) {
videodev.SetCtrlValue(name, value);
}
//
// callback video control scale change
void cb_vidctrl_scale_change (GtkRange *range, gpointer data) {
GtkWidget *grid = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "vidctrl-grid"));
GtkWidget *scale = NULL;
GtkWidget *label = NULL;
int idx = (long int)data;
double value;
label = gtk_grid_get_child_at(GTK_GRID(grid), 0, idx);
scale = gtk_grid_get_child_at(GTK_GRID(grid), 1, idx);
if (scale == NULL) return;
value = gtk_range_get_value(GTK_RANGE(scale));
videoctrl_set((std::string)gtk_label_get_text(GTK_LABEL(label)), (int)value);
};
//
// callback video control entry change
void cb_vidctrl_entry_change (GtkWidget *widget, gpointer data) {
GtkWidget *grid = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "vidctrl-grid"));
GtkWidget *label = NULL;
GtkWidget *entry = NULL;
int idx = (long int)data;
int value;
label = gtk_grid_get_child_at(GTK_GRID(grid), 0, idx);
entry = gtk_grid_get_child_at(GTK_GRID(grid), 2, idx);
if (entry == NULL) return;
value = atoi(gtk_entry_get_text(GTK_ENTRY(entry)));
videoctrl_set((std::string)gtk_label_get_text(GTK_LABEL(label)), (int)value);
};

@ -50,6 +50,15 @@ enum {
};
struct {
unsigned int id;
int min;
int max;
int value;
std::string name;
} typedef VideoDevCtrl;
struct {
unsigned int size;
unsigned char* data;
@ -79,6 +88,7 @@ private:
struct v4l2_crop crop;
struct v4l2_format fmt;
VideoFrame vf;
std::list<VideoDevCtrl> vidctrls;
struct jpg_error_mgr jerr;
struct jpeg_decompress_struct cinfo;
@ -99,6 +109,8 @@ private:
int OpenInit ();
int Close ();
int Grab(VideoFrame *vf);
int SetDevCtrl(unsigned int id, int value);
int GetDevCtrl(unsigned int id, int *value);
int xioctl (int fd, int request, void *arg);
public:
VideoDev();
@ -110,6 +122,9 @@ public:
int Stop(); // stop video processing and stop the pthread
int GetDeviceList(std::list<std::string> *list);
int GetCtrlList(std::list<std::string> *list);
int GetCtrlMinMaxValue(std::string name, int *min, int *max, int *value);
int SetCtrlValue(std::string name, int value);
int IsRunning() { return running; };
void PrintCaps (uint32_t caps);

@ -143,7 +143,66 @@ int VideoDev::GetDeviceList(std::list<std::string> *list) {
//
// start the video thread (on error, call cb_thread_videodev with NULL data)
// return a list of strings for controls
//
int VideoDev::GetCtrlList(std::list<std::string> *list) {
std::list<VideoDevCtrl>::iterator iter;
if (list == NULL) return VDEV_STATUS_ERROR;
list->clear();
LockMutex();
for (iter = vidctrls.begin(); iter != vidctrls.end(); iter++) {
list->push_back ((*iter).name);
}
UnLockMutex();
return VDEV_STATUS_OK;
};
//
// return the values for an control, on error VDEV_STATUS_UNKNOWN
//
int VideoDev::GetCtrlMinMaxValue(std::string name, int *min, int *max, int *value) {
std::list<VideoDevCtrl>::iterator iter;
LockMutex();
for (iter = vidctrls.begin(); iter != vidctrls.end(); iter++)
if (iter->name.compare(name) == 0) {
GetDevCtrl(iter->id, &(iter->value));
if (value != NULL) *value = iter->value;
if (min != NULL) *min = iter->min;
if (max != NULL) *max = iter->max;
break;
}
UnLockMutex();
if (iter == vidctrls.end()) return VDEV_STATUS_ERROR;
return VDEV_STATUS_OK;
};
//
// set the value for an control, on error VDEV_STATUS_UNKNOWN
//
int VideoDev::SetCtrlValue(std::string name, int value) {
std::list<VideoDevCtrl>::iterator iter;
LockMutex();
for (iter = vidctrls.begin(); iter != vidctrls.end(); iter++)
if (iter->name.compare(name) == 0) {
SetDevCtrl(iter->id, value);
break;
}
UnLockMutex();
if (iter == vidctrls.end()) return VDEV_STATUS_ERROR;
return VDEV_STATUS_OK;
};
//
// start the video, start capturing, start thread
// after return of this function we can call the Ctrl thread
//
int VideoDev::Start(std::string dev, gboolean (*callback_func)(gpointer data)) {
if (running != 0 || thread != NULL) return VDEV_STATUS_ERROR;
@ -151,9 +210,24 @@ int VideoDev::Start(std::string dev, gboolean (*callback_func)(gpointer data)) {
running = 1;
conf_device = dev;
callback = callback_func;
//
// open and init device buffers device
if (OpenInit() != VDEV_STATUS_OK) {
Close();
return VDEV_STATUS_ERROR;
}
//
// start capturing
if (CaptureStart() != VDEV_STATUS_OK) {
Close();
return VDEV_STATUS_ERROR;
}
thread = g_thread_new("VideoDev", _VideoDevThread, NULL);
return VDEV_STATUS_ERROR;
return VDEV_STATUS_OK;
};
@ -188,14 +262,6 @@ void VideoDev::Thread() {
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) running = 0;
//
// start capturing
if (CaptureStart() != VDEV_STATUS_OK) running = 0;
//
// read untill something bad happens..
while (running) {
@ -318,8 +384,7 @@ int VideoDev::Grab(VideoFrame *vf) {
if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) {
printf ( "%s:%d error on VIDIOC_QBUF %s\n", __FILE__, __LINE__, strerror(errno));
exit (1);
}
return VDEV_STATUS_ERROR; }
if (++inbuffer_idx >= VDEV_INBUFFERS) inbuffer_idx = 0;
break;
@ -337,6 +402,7 @@ int VideoDev::Grab(VideoFrame *vf) {
int VideoDev::OpenInit() {
int i;
struct v4l2_capability vcap;
VideoDevCtrl vctl;
printf ("%s:%d %s Device: '%s'\n", __FILE__, __LINE__, __FUNCTION__, conf_device.c_str());
if (fd != -1) return VDEV_STATUS_ERROR;
@ -365,33 +431,21 @@ int VideoDev::OpenInit() {
//
// query controls
struct v4l2_queryctrl queryctrl;
struct v4l2_control control;
uint32_t min;
printf (" Controls: \n");
vidctrls.clear();
memset (&queryctrl, 0, sizeof (queryctrl));
for (i = V4L2_CID_BASE; i < V4L2_CID_LASTP1; i++) {
for (i = V4L2_CID_BASE; i < V4L2_CID_DETECT_CLASS_BASE+0x1000; 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;
vctl.name = (char*)queryctrl.name;
vctl.id = queryctrl.id;
vctl.min = queryctrl.minimum;
vctl.max = queryctrl.maximum;
GetDevCtrl(queryctrl.id, &vctl.value);
vidctrls.push_back(vctl);
}
}
printf ("\n");
//
// check for cropping.. if we have it setup default
@ -406,7 +460,6 @@ int VideoDev::OpenInit() {
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;
@ -446,6 +499,39 @@ int VideoDev::OpenInit() {
};
//
// set video control identified by id
int VideoDev::SetDevCtrl(unsigned int id, int value) {
struct v4l2_control ctrl;
CLEAR(ctrl);
ctrl.id = id;
ctrl.value = value;
if (-1 == xioctl (fd, VIDIOC_S_CTRL, &ctrl)) {
return VDEV_STATUS_ERROR;
}
return VDEV_STATUS_OK;
};
//
// get video control identified by id
int VideoDev::GetDevCtrl(unsigned int id, int *value) {
struct v4l2_control ctrl;
CLEAR(ctrl);
ctrl.id = id;
if (-1 == xioctl (fd, VIDIOC_G_CTRL, &ctrl)) {
return VDEV_STATUS_ERROR;
}
*value = ctrl.value;
return VDEV_STATUS_OK;
};
//
// prepare memory mapped buffers
int VideoDev::InitMMap() {

Loading…
Cancel
Save