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.
SimpleSkyCam/video.cc

362 lines
11 KiB

/***************************************************************************************
*
* video.cc is part of SimpleSkyCam.
*
*****************************************************************************************/
#include <list>
#include <string>
#include <stdlib.h>
#include "gui.h"
#include "video.h"
#include "filter.h"
#include "detect.h"
VideoDev videodev;
GtkWidget *video_da = NULL;
GdkPixbuf *video_pixbuf = NULL;
extern GtkBuilder *_builder_; // work around for threads
extern Detect detect;
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);
if (!gtk_widget_has_focus(scale))
gtk_range_set_value(GTK_RANGE(scale), value);
entry = gtk_grid_get_child_at(GTK_GRID(grid), 2, i);
if (!gtk_widget_has_focus(entry))
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;
if (dest == NULL || src == NULL) return;
desth = gdk_pixbuf_get_height(dest);
destw = gdk_pixbuf_get_width(dest);
destpixel = gdk_pixbuf_get_pixels(dest);
if (destw * desth < src->h * src->w) {
destw = src->w;
desth = src->h;
}
memcpy (destpixel, src->data, 3*destw*desth);
}
#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;
GdkRGBA color;
GtkWidget *e_x = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "detect-entry-posx"));
GtkWidget *e_y = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "detect-entry-posy"));
GtkWidget *e_w = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "detect-entry-w"));
GtkWidget *e_h = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "detect-entry-h"));
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);
//cairo_move_to(cr, 30, 30);
//cairo_set_font_size(cr,15);
//cairo_show_text(cr, "hello world");
gdk_cairo_set_source_pixbuf(cr, pixbuf, 0, 0);
cairo_paint(cr);
cairo_fill (cr);
g_object_unref (pixbuf);
//
// 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);
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)));
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);
};
//
// 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));
if (videodev.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);
}
}
};
//
// 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 *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(' '));
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);
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);
}
};
//
// 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);
};
//
// 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;
VideoFrame *vf = (VideoFrame *) data;
btnstop = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "btn-video-stop"));
btnstart = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "btn-video-rec"));
if (video_da == NULL)
video_da = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "video-da"));
if (vf == NULL) {
printf ("%s:%d %s something went wrong\n", __FILE__, __LINE__, __FUNCTION__);
videodev.Stop();
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));
gtk_widget_set_sensitive(btnstart, true);
gtk_widget_set_sensitive(btnstop, false);
}
else {
int pix_h, pix_w;
videodev.LockMutex();
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;
}
videoframe_to_pixbuf(video_pixbuf, vf);
detect.NewFrame(vf);
videodev.UnLockMutex();
}
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) {
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);
};