You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
908 lines
28 KiB
908 lines
28 KiB
/***************************************************************************************
|
|
*
|
|
* video.cc is part of SimpleSkyCam.
|
|
*
|
|
*****************************************************************************************/
|
|
|
|
#include <list>
|
|
#include <string>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include "config.h"
|
|
#include "gui.h"
|
|
#include "output.h"
|
|
#include "detect.h"
|
|
#include "configuration.h"
|
|
#include "video.h"
|
|
#include "videodev.h"
|
|
#include "histogram.h"
|
|
#include "simpleskycam.h"
|
|
|
|
VideoDev *videodev = NULL;
|
|
GThread *videodev_thread = NULL;
|
|
GtkWidget *video_da = NULL;
|
|
GdkPixbuf *video_pixbuf = NULL;
|
|
extern GtkBuilder *_builder_; // work around for threads
|
|
extern Detect detect;
|
|
extern int video_enterdata;
|
|
extern position_2d video_enterdata_pos;
|
|
extern detect_movement detectedpos_data;
|
|
extern Configuration config;
|
|
|
|
void videoctrl_grid_delete ();
|
|
void videoctrl_grid_build ();
|
|
|
|
/*
|
|
* stop video recording
|
|
*/
|
|
void video_stoprecord() {
|
|
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"));
|
|
|
|
if (videodev == NULL) return;
|
|
|
|
videodev->Stop();
|
|
gtk_widget_set_sensitive(btnstart, true);
|
|
gtk_widget_set_sensitive(btnstop, false);
|
|
|
|
if (videodev_thread != NULL) g_thread_join (videodev_thread);
|
|
videodev_thread = NULL;
|
|
|
|
delete videodev;
|
|
|
|
videoctrl_grid_delete();
|
|
|
|
videodev = NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
* return selected driver and device from GUI
|
|
*/
|
|
void video_get_driverdevice(std::string *driver, std::string *device) {
|
|
GtkWidget *cbox = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "cb-videodev"));
|
|
GtkWidget *cbdevice = gtk_bin_get_child(GTK_BIN(cbox));
|
|
|
|
if (driver == NULL || device == NULL) return;
|
|
|
|
// read values from GUI elements, and start videodev with the correct driver
|
|
int i;
|
|
*device = gtk_entry_get_text(GTK_ENTRY(cbdevice));
|
|
|
|
i = device->find(' ');
|
|
*driver = device->substr (0, i);
|
|
*device = device->substr (i+1, std::string::npos);
|
|
*device = device->substr (0, device->find(' '));
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* will be called every 2000ms hardcoded @gui.cc function cb_window_show
|
|
*/
|
|
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;
|
|
|
|
if (videodev == NULL) return TRUE;
|
|
|
|
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) {
|
|
if ((scale = gtk_grid_get_child_at(GTK_GRID(grid), 1, i)) != NULL) {
|
|
if (!gtk_widget_has_focus(scale))
|
|
gtk_range_set_value(GTK_RANGE(scale), value);
|
|
}
|
|
if ((entry = gtk_grid_get_child_at(GTK_GRID(grid), 2, i)) != NULL) {
|
|
if (!gtk_widget_has_focus(entry))
|
|
gtk_entry_set_text(GTK_ENTRY(entry), std::to_string(value).c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
#define S_X(_x_) (((_x_) * clientw / pixbufw))
|
|
#define S_Y(_y_) (((_y_) * clienth / pixbufh))
|
|
void cb_videoda_draw(GtkWidget *area, cairo_t *cr, int w, int h, gpointer data) {
|
|
int clientw, clienth, pixbufw, pixbufh;
|
|
float clientar, pixbufar;
|
|
GdkPixbuf *pixbuf = NULL;
|
|
char txt1[255];
|
|
char txt2[255];
|
|
|
|
GdkRGBA color;
|
|
static GtkWidget *e_x = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "detect-entry-posx"));
|
|
static GtkWidget *e_y = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "detect-entry-posy"));
|
|
static GtkWidget *e_w = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "detect-entry-w"));
|
|
static GtkWidget *e_h = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "detect-entry-h"));
|
|
static GtkWidget *lbX = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "lb_input_xdelta"));
|
|
static GtkWidget *lbY = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "lb_input_ydelta"));
|
|
|
|
int x, y, w1, h1;
|
|
|
|
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);
|
|
|
|
gdk_cairo_set_source_pixbuf(cr, pixbuf, 0, 0);
|
|
cairo_paint(cr);
|
|
cairo_fill (cr);
|
|
|
|
g_object_unref (pixbuf);
|
|
|
|
//
|
|
// need to draw the new destination?
|
|
if (video_enterdata == VID_ENTERDATA_SETDEST) {
|
|
color.blue = 1.0;
|
|
color.red = 0.0;
|
|
color.green = 1.0;
|
|
color.alpha = 1.0;
|
|
gdk_cairo_set_source_rgba(cr, &color);
|
|
|
|
cairo_move_to(cr, S_X(video_enterdata_pos.x)-10, S_Y(video_enterdata_pos.y)-10);
|
|
cairo_line_to(cr, S_X(video_enterdata_pos.x)+10, S_Y(video_enterdata_pos.y)+10);
|
|
cairo_move_to(cr, S_X(video_enterdata_pos.x)+10, S_Y(video_enterdata_pos.y)-10);
|
|
cairo_line_to(cr, S_X(video_enterdata_pos.x)-10, S_Y(video_enterdata_pos.y)+10);
|
|
|
|
cairo_stroke(cr);
|
|
}
|
|
else if (posctl_threaddata.target_pos.x != -1 and posctl_threaddata.target_pos.y != -1) {
|
|
color.blue = 0.0;
|
|
color.red = 0.0;
|
|
color.green = 0.5;
|
|
color.alpha = 1.0;
|
|
gdk_cairo_set_source_rgba(cr, &color);
|
|
|
|
cairo_move_to(cr, S_X(posctl_threaddata.target_pos.x), S_Y(posctl_threaddata.target_pos.y)-5);
|
|
cairo_line_to(cr, S_X(posctl_threaddata.target_pos.x), S_Y(posctl_threaddata.target_pos.y)+5);
|
|
cairo_move_to(cr, S_X(posctl_threaddata.target_pos.x)-5, S_Y(posctl_threaddata.target_pos.y));
|
|
cairo_line_to(cr, S_X(posctl_threaddata.target_pos.x)+5, S_Y(posctl_threaddata.target_pos.y));
|
|
|
|
cairo_stroke(cr);
|
|
}
|
|
|
|
|
|
//
|
|
// need to draw the select new position cross
|
|
if (video_enterdata == VID_ENTERDATA_POS) {
|
|
color.blue = 1.0;
|
|
color.red = 0.0;
|
|
color.green = 1.0;
|
|
color.alpha = 1.0;
|
|
gdk_cairo_set_source_rgba(cr, &color);
|
|
|
|
cairo_move_to(cr, S_X(video_enterdata_pos.x)-10, S_Y(video_enterdata_pos.y)-10);
|
|
cairo_line_to(cr, S_X(video_enterdata_pos.x)+10, S_Y(video_enterdata_pos.y)+10);
|
|
cairo_move_to(cr, S_X(video_enterdata_pos.x)+10, S_Y(video_enterdata_pos.y)-10);
|
|
cairo_line_to(cr, S_X(video_enterdata_pos.x)-10, S_Y(video_enterdata_pos.y)+10);
|
|
|
|
cairo_move_to(cr, S_X(video_enterdata_pos.x)-15, S_Y(video_enterdata_pos.y)-20);
|
|
cairo_line_to(cr, S_X(video_enterdata_pos.x)-20, S_Y(video_enterdata_pos.y)-20);
|
|
cairo_line_to(cr, S_X(video_enterdata_pos.x)-20, S_Y(video_enterdata_pos.y)-15);
|
|
|
|
cairo_move_to(cr, S_X(video_enterdata_pos.x)+15, S_Y(video_enterdata_pos.y)-20);
|
|
cairo_line_to(cr, S_X(video_enterdata_pos.x)+20, S_Y(video_enterdata_pos.y)-20);
|
|
cairo_line_to(cr, S_X(video_enterdata_pos.x)+20, S_Y(video_enterdata_pos.y)-15);
|
|
|
|
cairo_move_to(cr, S_X(video_enterdata_pos.x)-15, S_Y(video_enterdata_pos.y)+20);
|
|
cairo_line_to(cr, S_X(video_enterdata_pos.x)-20, S_Y(video_enterdata_pos.y)+20);
|
|
cairo_line_to(cr, S_X(video_enterdata_pos.x)-20, S_Y(video_enterdata_pos.y)+15);
|
|
|
|
cairo_move_to(cr, S_X(video_enterdata_pos.x)+15, S_Y(video_enterdata_pos.y)+20);
|
|
cairo_line_to(cr, S_X(video_enterdata_pos.x)+20, S_Y(video_enterdata_pos.y)+20);
|
|
cairo_line_to(cr, S_X(video_enterdata_pos.x)+20, S_Y(video_enterdata_pos.y)+15);
|
|
|
|
cairo_stroke(cr);
|
|
}
|
|
else {
|
|
x = atoi (gtk_entry_get_text(GTK_ENTRY(e_x)));
|
|
y = atoi (gtk_entry_get_text(GTK_ENTRY(e_y)));
|
|
w1 = atoi (gtk_entry_get_text(GTK_ENTRY(e_w)));
|
|
h1 = atoi (gtk_entry_get_text(GTK_ENTRY(e_h)));
|
|
|
|
if (x >= (w1/2) && x < (pixbufw-w1/2)
|
|
&& y >= (h1/2) && y < (pixbufh-h1/2)) {
|
|
//
|
|
// draw red cross
|
|
cairo_set_line_width(cr, 1.0);
|
|
color.blue = 0.0;
|
|
color.red = 1.0;
|
|
color.green = 0.0;
|
|
color.alpha = 1.0;
|
|
gdk_cairo_set_source_rgba(cr, &color);
|
|
|
|
cairo_move_to(cr, S_X(x), S_Y(y)-10);
|
|
cairo_line_to(cr, S_X(x), S_Y(y)+10);
|
|
cairo_move_to(cr, S_X(x)-10, S_Y(y));
|
|
cairo_line_to(cr, S_X(x)+10, S_Y(y));
|
|
cairo_stroke(cr);
|
|
|
|
//
|
|
// green width border
|
|
color.blue = 0.0;
|
|
color.red = 0.0;
|
|
color.green = 1.0;
|
|
color.alpha = 1.0;
|
|
gdk_cairo_set_source_rgba(cr, &color);
|
|
|
|
cairo_move_to(cr, S_X(x-w1/2), S_Y(y-h1/2)+10);
|
|
cairo_line_to(cr, S_X(x-w1/2), S_Y(y-h1/2));
|
|
cairo_line_to(cr, S_X(x+w1/2), S_Y(y-h1/2));
|
|
cairo_line_to(cr, S_X(x+w1/2), S_Y(y-h1/2)+10);
|
|
|
|
cairo_move_to(cr, S_X(x-w1/2), S_Y(y+h1/2)-10);
|
|
cairo_line_to(cr, S_X(x-w1/2), S_Y(y+h1/2));
|
|
cairo_line_to(cr, S_X(x+w1/2), S_Y(y+h1/2));
|
|
cairo_line_to(cr, S_X(x+w1/2), S_Y(y+h1/2)-10);
|
|
|
|
cairo_stroke(cr);
|
|
}
|
|
}
|
|
|
|
// draw movement vector to screen
|
|
snprintf (txt1, 255, "X: %f", detectedpos_data.dx);
|
|
snprintf (txt2, 255, "Y: %f", detectedpos_data.dy);
|
|
gtk_label_set_text(GTK_LABEL(lbX), txt1);
|
|
gtk_label_set_text(GTK_LABEL(lbY), txt2);
|
|
|
|
if (config.GetCalibrationShowData()) {
|
|
// printf ("%s:%d posctl_threaddata.c : %d \t %d -> \t %d \t %d\n", __FILE__, __LINE__,
|
|
// posctl_threaddata.c[0].x, posctl_threaddata.c[0].y,
|
|
// posctl_threaddata.c[1].x, posctl_threaddata.c[1].y);
|
|
// printf ("%s:%d posctl_threaddata.a1: %d \t %d -> \t %d \t %d\n", __FILE__, __LINE__,
|
|
// posctl_threaddata.a1[0].x, posctl_threaddata.a1[0].y,
|
|
// posctl_threaddata.a1[1].x, posctl_threaddata.a1[1].y);
|
|
// printf ("%s:%d posctl_threaddata.a2: %d \t %d -> \t %d \t %d\n", __FILE__, __LINE__,
|
|
// posctl_threaddata.a2[0].x, posctl_threaddata.a2[0].y,
|
|
// posctl_threaddata.a2[1].x, posctl_threaddata.a2[1].y);
|
|
if (posctl_threaddata.c[0].x != -1 && posctl_threaddata.c[0].y != -1 &&
|
|
posctl_threaddata.c[1].x != -1 && posctl_threaddata.c[1].y != -1) {
|
|
cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
|
|
cairo_set_line_width(cr, 1.0);
|
|
cairo_move_to(cr, S_X(posctl_threaddata.c[0].x), S_Y(posctl_threaddata.c[0].y));
|
|
cairo_line_to(cr, S_X(posctl_threaddata.c[1].x), S_Y(posctl_threaddata.c[1].y));
|
|
cairo_stroke(cr);
|
|
}
|
|
if (posctl_threaddata.a1[0].x != -1 && posctl_threaddata.a1[0].y != -1 &&
|
|
posctl_threaddata.a1[1].x != -1 && posctl_threaddata.a1[1].y != -1) {
|
|
cairo_set_source_rgb(cr, 1.0, 0.5, 1.0);
|
|
cairo_set_line_width(cr, 1.0);
|
|
cairo_move_to(cr, S_X(posctl_threaddata.a1[0].x), S_Y(posctl_threaddata.a1[0].y));
|
|
cairo_line_to(cr, S_X(posctl_threaddata.a1[1].x), S_Y(posctl_threaddata.a1[1].y));
|
|
cairo_stroke(cr);
|
|
}
|
|
if (posctl_threaddata.a2[0].x != -1 && posctl_threaddata.a2[0].y != -1 &&
|
|
posctl_threaddata.a2[1].x != -1 && posctl_threaddata.a2[1].y != -1) {
|
|
cairo_set_source_rgb(cr, 1.0, 0.5, 1.0);
|
|
cairo_set_line_width(cr, 1.0);
|
|
cairo_move_to(cr, S_X(posctl_threaddata.a2[0].x), S_Y(posctl_threaddata.a2[0].y));
|
|
cairo_line_to(cr, S_X(posctl_threaddata.a2[1].x), S_Y(posctl_threaddata.a2[1].y));
|
|
cairo_stroke(cr);
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG_ANGLES
|
|
debug_angles_draw(cr);
|
|
#endif
|
|
};
|
|
|
|
|
|
|
|
// fixme: refresh format list.
|
|
/*
|
|
* 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)));
|
|
|
|
std::list<std::string> devlist;
|
|
std::list<std::string>::iterator iter;
|
|
|
|
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
|
|
|
|
if (model == NULL) {
|
|
model = gtk_list_store_new(1, G_TYPE_STRING);
|
|
gtk_combo_box_set_model(GTK_COMBO_BOX(cbox),GTK_TREE_MODEL(model));
|
|
}
|
|
|
|
gtk_list_store_clear(GTK_LIST_STORE(model));
|
|
|
|
devlist.clear();
|
|
|
|
VideoDev_Dumpfile vdef1; vdef1.GetDeviceList(&devlist);
|
|
#ifdef USE_V4L2
|
|
VideoDev_V4L2 vdef2; vdef2.GetDeviceList(&devlist);
|
|
#endif
|
|
#ifdef USE_SVBONY
|
|
VideoDev_SVBCam vdef3; vdef3.GetDeviceList(&devlist);
|
|
#endif
|
|
#ifdef USE_VFW
|
|
VideoDev_VFW vdef4; vdef4.GetDeviceList(&devlist);
|
|
#endif
|
|
VideoDev_Simulation vdef5; vdef5.GetDeviceList(&devlist);
|
|
|
|
for (iter = devlist.begin(); iter != devlist.end(); iter++) {
|
|
gtk_list_store_insert_with_values(GTK_LIST_STORE(model), NULL, -1,
|
|
0, iter->c_str(),
|
|
-1);
|
|
}
|
|
};
|
|
|
|
|
|
gpointer videodev_threadprocess_wrapper (gpointer data) {
|
|
if (videodev == NULL) return NULL;
|
|
videodev->ThreadProcess();
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
* 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) {
|
|
GtkWidget *cbres = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "cb-videores"));
|
|
GtkWidget *cbresentry = gtk_bin_get_child(GTK_BIN(cbres));
|
|
GtkWidget *cbfmt = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "cb-videofmt"));
|
|
GtkWidget *cbfmtentry = gtk_bin_get_child(GTK_BIN(cbfmt));
|
|
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"));
|
|
int w, h;
|
|
|
|
// get device and driver information
|
|
std::string device, driver;
|
|
std::string parameter;
|
|
video_get_driverdevice(&driver, &device);
|
|
|
|
// stop recording
|
|
video_stoprecord();
|
|
|
|
//
|
|
// load the selected driver
|
|
if (driver.compare("VIDEODUMP") == 0) videodev = new VideoDev_Dumpfile;
|
|
#ifdef USE_V4L2
|
|
else if (driver.compare("V4L2") == 0) videodev = new VideoDev_V4L2;
|
|
#endif
|
|
#ifdef USE_SVBONY
|
|
else if (driver.compare("SVBCAM") == 0) videodev = new VideoDev_SVBCam;
|
|
#endif
|
|
#ifdef USE_VFW
|
|
else if (driver.compare("VFW") == 0) videodev = new VideoDev_VFW;
|
|
#endif
|
|
else if (driver.compare("SIMULATION") == 0) videodev = new VideoDev_Simulation;
|
|
else videodev = new VideoDev;
|
|
|
|
std::string format = gtk_entry_get_text(GTK_ENTRY(cbfmtentry));
|
|
std::string resolution = gtk_entry_get_text(GTK_ENTRY(cbresentry));
|
|
w = h = -1;
|
|
if (resolution.compare ("MAX") != 0) sscanf (resolution.c_str(), "%dx%d", &w, &h);
|
|
|
|
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->SetConfig(device, w, h, format, parameter, cb_thread_video);
|
|
videodev_thread = g_thread_new(driver.c_str(), videodev_threadprocess_wrapper, NULL);
|
|
// FIXME: workaround, soltuion should be: create a timer and request all controls
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
* clear all elements in the controls grid
|
|
*/
|
|
void videoctrl_grid_delete () {
|
|
GtkWidget *grid = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "vidctrl-grid"));
|
|
GtkWidget *gridchild = NULL;
|
|
|
|
//
|
|
// clear grid
|
|
while ((gridchild = gtk_grid_get_child_at(GTK_GRID(grid), 0, 0)) != NULL) {
|
|
gtk_grid_remove_row (GTK_GRID(grid), 0);
|
|
}
|
|
};
|
|
|
|
|
|
/*
|
|
* after the video is started and we get the first callback from the VideoDev thread
|
|
* we will call this function to read all ctrls and creade the grid table.
|
|
* function gets called if: threaddata.runnin == 1
|
|
*/
|
|
void videoctrl_grid_build () {
|
|
std::list<std::string> list;
|
|
std::list<std::string>::iterator iter;
|
|
GtkWidget *cbres = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "cb-videores"));
|
|
GtkWidget *cbresentry = gtk_bin_get_child(GTK_BIN(cbres));
|
|
GtkWidget *cbfmt = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "cb-videofmt"));
|
|
GtkWidget *cbfmtentry = gtk_bin_get_child(GTK_BIN(cbfmt));
|
|
GtkWidget *grid = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "vidctrl-grid"));
|
|
int i, w, h;
|
|
std::string format;
|
|
std::string resolution;
|
|
|
|
if (videodev == NULL) return;
|
|
|
|
videodev->GetCtrlList(&list);
|
|
videoctrl_grid_delete();
|
|
|
|
//
|
|
// 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());
|
|
GtkWidget *scale = NULL;
|
|
|
|
//
|
|
// scale/range
|
|
if (min < max) {
|
|
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*)(size_t)i);
|
|
}
|
|
else {
|
|
printf ("%s:%d control %s check not valid min[%d] < max[%d]\n", __FILE__, __LINE__, iter->c_str(), min, max);
|
|
}
|
|
|
|
//
|
|
// 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*)(size_t)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);
|
|
|
|
if (scale) 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);
|
|
if (scale) gtk_widget_show (scale);
|
|
gtk_widget_show (entry);
|
|
}
|
|
|
|
//
|
|
// read video information
|
|
videodev->GetVideoInfo(&w, &h, &format);
|
|
resolution = std::to_string(w) + "x" + std::to_string(h);
|
|
gtk_entry_set_text (GTK_ENTRY(cbresentry), resolution.c_str());
|
|
gtk_entry_set_text (GTK_ENTRY(cbfmtentry), format.c_str());
|
|
};
|
|
|
|
/*
|
|
* stop recording from the videodev (will stop the running thread)
|
|
* we need to wait for the thread to complete and then we can delete this object.
|
|
*/
|
|
void cb_video_btnstop (GtkWidget *widget, gpointer data) {
|
|
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
|
|
video_stoprecord();
|
|
};
|
|
|
|
|
|
/*
|
|
* 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 = 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"));
|
|
VideoDevThreadData *cbdata = (VideoDevThreadData*) data;
|
|
VideoFrame *vf = NULL;
|
|
VideoFrameRaw *vfr = NULL;
|
|
|
|
if (videodev == NULL) return false;
|
|
|
|
//
|
|
// after video starts, on the first frame
|
|
// read out all controls
|
|
if (cbdata != NULL) {
|
|
vf = &cbdata->vf;
|
|
vfr = &cbdata->vfr;
|
|
if (cbdata->running == 1) {
|
|
videoctrl_grid_build();
|
|
cbdata->running = 2;
|
|
}
|
|
if (vf->w <= 0 || vf->h <= 0 || vf->data == NULL) vf = NULL;
|
|
if (vfr->w <= 0 || vfr->h <= 0 || vfr->data == NULL) vfr = NULL;
|
|
}
|
|
|
|
//
|
|
// create video drawarea if needed
|
|
if (video_da == NULL)
|
|
video_da = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "video-da"));
|
|
|
|
if (video_pixbuf == NULL) {
|
|
video_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, false, 8, 100, 100);
|
|
memset (gdk_pixbuf_get_pixels(video_pixbuf), 0, 3 * gdk_pixbuf_get_height(video_pixbuf) * gdk_pixbuf_get_width(video_pixbuf));
|
|
}
|
|
|
|
//
|
|
// if cbdata not set, we have a error on the video stream
|
|
// stop recording. Else push framedata into the detect thread.
|
|
if (cbdata == NULL) {
|
|
printf ("%s:%d %s something went wrong CBData == NULL\n", __FILE__, __LINE__, __FUNCTION__);
|
|
videodev->Stop();
|
|
memset (gdk_pixbuf_get_pixels(video_pixbuf), 0, 3 * gdk_pixbuf_get_height(video_pixbuf) * gdk_pixbuf_get_width(video_pixbuf));
|
|
|
|
gtk_widget_set_sensitive(btnstart, true);
|
|
gtk_widget_set_sensitive(btnstop, false);
|
|
gdk_window_invalidate_rect(gtk_widget_get_window(video_da), NULL, true);
|
|
}
|
|
else if (vf != NULL) {
|
|
int pix_h, pix_w;
|
|
|
|
videodev->LockMutex();
|
|
|
|
//
|
|
// check and resize video_drawarea if needed
|
|
if (video_pixbuf) {
|
|
pix_h = gdk_pixbuf_get_height(video_pixbuf);
|
|
pix_w = gdk_pixbuf_get_width(video_pixbuf);
|
|
}
|
|
else pix_h = 0;
|
|
|
|
if (video_pixbuf == NULL || pix_h != vf->h || pix_w != vf->w) {
|
|
if (video_pixbuf != NULL) g_object_unref (video_pixbuf);
|
|
|
|
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);
|
|
pix_w = vf->w;
|
|
pix_h = vf->h;
|
|
}
|
|
|
|
vf->ToPixbuf(video_pixbuf); // convert Frame to pixeldata
|
|
histogram_update(vf); // update histogram
|
|
detect.CopyNewFrame(vf, vfr); // push new data to detect object
|
|
|
|
videodev->UnLockMutex();
|
|
|
|
// redraw drawarea on screen.
|
|
gdk_window_invalidate_rect(gtk_widget_get_window(video_da), NULL, true);
|
|
}
|
|
|
|
return false;
|
|
};
|
|
|
|
|
|
/*
|
|
* set ctrl on the device
|
|
*/
|
|
void videoctrl_set(std::string name, int value) {
|
|
if (videodev == NULL) return;
|
|
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;
|
|
int64_t idx = (int64_t)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;
|
|
int64_t idx = (int64_t)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);
|
|
};
|
|
|
|
|
|
/*
|
|
*
|
|
*/
|
|
void cb_videoda_motionevent (GtkWidget *widget, GdkEvent *event, gpointer data) {
|
|
int dw, dh, sw, sh;
|
|
float sar, dar;
|
|
|
|
if (video_pixbuf != NULL) {
|
|
sh = gtk_widget_get_allocated_height(video_da);
|
|
sw = gtk_widget_get_allocated_width(video_da);
|
|
|
|
dw = gdk_pixbuf_get_width(video_pixbuf);
|
|
dh = gdk_pixbuf_get_height(video_pixbuf);
|
|
|
|
sar = (float)sw/(float)sh;
|
|
dar = (float)dw/(float)dh;
|
|
|
|
if (dar < sar) {
|
|
sw = (dar * (float) sh);
|
|
}
|
|
else {
|
|
sh = ((float) sw / dar);
|
|
}
|
|
|
|
video_enterdata_pos.x = event->motion.x * dw / sw;
|
|
video_enterdata_pos.y = event->motion.y * dh / sh;
|
|
|
|
gdk_window_invalidate_rect(gtk_widget_get_window(video_da), NULL, true);
|
|
}
|
|
|
|
#ifdef DEBUG_ANGLES
|
|
debug_angles_motionevent(event);
|
|
#endif
|
|
};
|
|
|
|
|
|
void cb_videoda_btnpress (GtkWidget *widget, GdkEvent *event, gpointer data) {
|
|
int x = event->motion.x;
|
|
int y = event->motion.y;
|
|
|
|
#ifdef DEBUG_ANGLES
|
|
debug_angles_btnpress(event);
|
|
#endif
|
|
|
|
printf ("%s:%d %s %d, %d\n", __FILE__, __LINE__, __FUNCTION__, x, y);
|
|
};
|
|
|
|
|
|
void cb_videoda_btnrelease (GtkWidget *widget, GdkEvent *event, gpointer data) {
|
|
if (video_enterdata == VID_ENTERDATA_POS) {
|
|
detect.SetObjectPos(video_enterdata_pos.x, video_enterdata_pos.y);
|
|
video_enterdata = VID_ENTERDATA_NONE;
|
|
}
|
|
};
|
|
|
|
|
|
/*
|
|
* cb_video_pre_click/cb_video_pre_pressed, are needed to detect is we press
|
|
* the button longer as two seconds. If it is, all settings will be saved.
|
|
*/
|
|
#define BTNDOWN_TIME 1.0
|
|
struct {
|
|
int idx;
|
|
struct timeval tv;
|
|
guint timer;
|
|
} presetbtn;
|
|
|
|
void cb_video_pre_click (GtkWidget *widget, gpointer data) {
|
|
float pushtime = get_cycletime(&presetbtn.tv);
|
|
std::string s;
|
|
int idx;
|
|
GtkWidget *btn;
|
|
|
|
if (videodev == NULL) return;
|
|
|
|
if (pushtime > BTNDOWN_TIME && presetbtn.idx >= 0 && presetbtn.idx < BTN_PRESET_MAX) {
|
|
//
|
|
// save the settings
|
|
//
|
|
std::list<VideoDevCtrl> ctrls = videodev->GetCtrlsMinMaxValue();
|
|
config.SetPresetButton(presetbtn.idx, &ctrls);
|
|
|
|
//
|
|
// reset button label
|
|
//
|
|
for (idx = 0; idx < BTN_PRESET_MAX; idx++) {
|
|
s = "btn-video-pre"+std::to_string(idx+1);
|
|
if ((btn = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), s.c_str()))) != NULL) {
|
|
gtk_button_set_label(GTK_BUTTON(btn), std::to_string(idx+1).c_str());
|
|
}
|
|
}
|
|
}
|
|
else if (presetbtn.idx >= 0 && presetbtn.idx < BTN_PRESET_MAX) {
|
|
//
|
|
// callup settings
|
|
//
|
|
int i, retry = 5, again = 1;
|
|
std::list<VideoDevCtrl> ctrls = config.GetPresetButton(presetbtn.idx);
|
|
std::list<VideoDevCtrl>::iterator iter;
|
|
presetbtn.idx = -1;
|
|
|
|
for (retry = 5; retry > 0 && again; retry--) {
|
|
again = 0;
|
|
for (iter = ctrls.begin(); iter != ctrls.end(); iter++) {
|
|
videodev->SetCtrlValue(iter->name, iter->value);
|
|
usleep(10000);
|
|
videodev->GetCtrlMinMaxValue(iter->name, NULL, NULL, &i);
|
|
if (i != iter->value) again = 1;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
/*
|
|
* should determine the pressed button and save it together with the current
|
|
* time in structure presetbtn.
|
|
*/
|
|
void cb_video_pre_pressed (GtkWidget *widget, gpointer data) {
|
|
int idx = -1;
|
|
std::string s;
|
|
GtkWidget *btn;
|
|
|
|
for (idx = 0; idx < BTN_PRESET_MAX; idx++) {
|
|
s = "btn-video-pre"+std::to_string(idx+1);
|
|
if ((btn = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), s.c_str()))) == widget) {
|
|
if (presetbtn.timer <= 0)
|
|
presetbtn.timer = g_timeout_add((int)(BTNDOWN_TIME*1000.0), video_pre_long, NULL);
|
|
break;
|
|
}
|
|
}
|
|
|
|
presetbtn.idx = idx;
|
|
gettimeofday (&presetbtn.tv, NULL);
|
|
};
|
|
|
|
|
|
/*
|
|
* gnome timer callback if any presetbutton was pressed down
|
|
* in case the button is still pressed, we write SAVED in the buttons label.
|
|
*/
|
|
gboolean video_pre_long(gpointer data) {
|
|
std::string s = "btn-video-pre"+std::to_string(presetbtn.idx+1);
|
|
GtkWidget *btn = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), s.c_str()));
|
|
|
|
if (btn && presetbtn.idx >= 0 && presetbtn.idx < BTN_PRESET_MAX)
|
|
gtk_button_set_label(GTK_BUTTON(btn), "Saved");
|
|
|
|
presetbtn.timer = 0;
|
|
return FALSE;
|
|
};
|
|
|
|
|
|
/*
|
|
* videodevice changed
|
|
*/
|
|
void cb_video_cbox_videodev (GtkWidget *widget, gpointer data) {
|
|
|
|
// GtkWidget *cbres = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "cb-videores"));
|
|
// GtkWidget *cbresentry = gtk_bin_get_child(GTK_BIN(cbres));
|
|
GtkWidget *cbfmt = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "cb-videofmt"));
|
|
GtkWidget *cbfmtentry = gtk_bin_get_child(GTK_BIN(cbfmt));
|
|
GtkListStore *model;
|
|
std::list<string> lst_format;
|
|
std::list<string> lst_resolution;
|
|
std::list<string>::iterator iter;
|
|
std::string device, driver;
|
|
std::string parameter;
|
|
|
|
|
|
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
|
|
|
|
video_stoprecord();
|
|
|
|
//
|
|
// open device
|
|
video_get_driverdevice(&driver, &device);
|
|
|
|
//
|
|
// load the selected driver
|
|
if (driver.compare("DUMMY") == 0) videodev = new VideoDev;
|
|
#ifdef USE_V4L2
|
|
else if (driver.compare("V4L2") == 0) videodev = new VideoDev_V4L2;
|
|
#endif
|
|
#ifdef USE_SVBONY
|
|
else if (driver.compare("SVBCAM") == 0) videodev = new VideoDev_SVBCam;
|
|
#endif
|
|
#ifdef USE_VFW
|
|
else if (driver.compare("VFW") == 0) videodev = new VideoDev_VFW;
|
|
#endif
|
|
else videodev = new VideoDev;
|
|
|
|
videodev->GetDeviceFormats(device, &lst_format);
|
|
model = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(cbfmt)));
|
|
gtk_list_store_clear(GTK_LIST_STORE(model));
|
|
gtk_entry_set_text (GTK_ENTRY(cbfmtentry), "");
|
|
for (iter = lst_format.begin(); iter != lst_format.end(); iter++) {
|
|
gtk_list_store_insert_with_values(GTK_LIST_STORE(model), NULL, -1,
|
|
0, iter->c_str(),
|
|
-1);
|
|
}
|
|
|
|
videodev->GetDeviceResolutions(device, &lst_format);
|
|
|
|
delete videodev;
|
|
videodev = NULL;
|
|
};
|
|
|
|
|
|
void cb_input_btncapture (GtkWidget *widget, gpointer data) {
|
|
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
|
|
};
|
|
|
|
|
|
void cb_input_btnsnapshot (GtkWidget *widget, gpointer data) {
|
|
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
|
|
};
|
|
|
|
|
|
/*
|
|
* resize windows depending on the button which was pressed
|
|
*/
|
|
void cb_input_btnscale (GtkWidget *widget, gpointer data) {
|
|
int dw, dh, ww, wh;
|
|
float scale = 1.0;
|
|
|
|
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
|
|
if (videodev == NULL || video_da == NULL) return;
|
|
|
|
//
|
|
// what scale button was pressed
|
|
GtkWidget *btn11 = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "btn_inputscale11"));
|
|
GtkWidget *btn12 = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "btn_inputscale12"));
|
|
GtkWidget *btn14 = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "btn_inputscale14"));
|
|
if (widget == btn11) scale = 1.0;
|
|
if (widget == btn12) scale = 0.5;
|
|
if (widget == btn14) scale = 0.25;
|
|
|
|
//
|
|
// read our video size from gui and calculate size of gui elements
|
|
GtkWidget *win = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "window-input"));
|
|
gtk_window_get_size(GTK_WINDOW(win), &ww, &wh);
|
|
dh = gtk_widget_get_allocated_height(video_da);
|
|
dw = gtk_widget_get_allocated_width(video_da);
|
|
dw = ww-dw;
|
|
dh = wh-dh;
|
|
|
|
videodev->GetVideoInfo(&ww, &wh, NULL);
|
|
|
|
ww = dw + (scale * ww) + 1;
|
|
wh = dh + (scale * wh);
|
|
|
|
gtk_window_resize(GTK_WINDOW(win), ww, wh);
|
|
};
|