/*************************************************************************************** * * video.cc is part of SimpleSkyCam. * *****************************************************************************************/ #include #include #include #include "gui.h" #include "video.h" VideoDev videodev; GtkWidget *video_da = NULL; 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; if (dest == NULL || src == NULL) return; destw = gdk_pixbuf_get_height(dest); desth = gdk_pixbuf_get_width(dest); destpixel = gdk_pixbuf_get_pixels(dest); if (destw > src->w) destw = src->w; if (desth > src->h) destw = src->h; memcpy (destpixel, src->data, 3*destw*desth); } 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; 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); }; 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")); 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; // display error screen? if (vf == NULL) { unsigned char *pixels; // need to allocate? if (video_pixbuf == NULL) { printf ("%s:%d new pixbuf\n", __FILE__, __LINE__); video_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, false, 8, 640, 480); pix_w = 640; pix_h = 480; } pixels = gdk_pixbuf_get_pixels(video_pixbuf); for (y = 0; y < pix_h; y++) for (x = 0; x < pix_w; x++) { if ((x & 1) && (y & 1)) { *(++pixels) = 0xFF; *(++pixels) = 0xFF; *(++pixels) = 0xFF; } else { *(++pixels) = 0x0; *(++pixels) = 0x0; *(++pixels) = 0x0; } } } 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); 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); } gdk_window_invalidate_rect(gtk_widget_get_window(video_da), NULL, true); }; // // 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 devlist; std::list::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 list; std::list::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 (vf == NULL) { printf ("%s:%d %s something went wrong\n", __FILE__, __LINE__, __FUNCTION__); videodev.Stop(); video_draw_image(NULL); gtk_widget_set_sensitive(btnstart, true); gtk_widget_set_sensitive(btnstop, false); } videodev.LockMutex(); video_draw_image(vf); videodev.UnLockMutex(); 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); };