/*************************************************************************************** * * video.cc is part of SimpleSkyCam. * *****************************************************************************************/ #include #include #include "gui.h" #include "video.h" VideoDev videodev; GtkWidget *video_da = NULL; GdkPixbuf *video_pixbuf; // // clamp and convert2rgb is build on the sample of the v4l2 api documentation // inline unsigned char clamp (double x) { int r = (int)x; if (r < 0) return 0; else if (r > 255) return 255; else return r; }; inline void convert2rgb (unsigned char Y1, unsigned char Cb, unsigned char Cr, unsigned char *ER, unsigned char *EB, unsigned char *EG) { register int y1, pb, pr; y1 = Y1 - 16; pb = Cb - 128; pr = Cr - 128; *ER = clamp (y1 + 1.402 * pr); *EB = clamp (y1 - 0.344 * pb - 0.714 * pr); *EG = clamp (y1 + 1.772 * pb); }; void video_convert (VideoFrame *vf, GdkPixbuf *pixbuf) { int xs, ys; int xd, yd; int dst_w, dst_h; unsigned char r,g,b; unsigned char cb, cr, y1; unsigned char *ptrdst = gdk_pixbuf_get_pixels(pixbuf); unsigned char *ptrsrc = vf->data; /* check if there is a format and buffer for the current frame.. */ if (vf == NULL || vf->data == NULL || ptrdst == NULL) return; dst_w = gdk_pixbuf_get_width(pixbuf); dst_h = gdk_pixbuf_get_height(pixbuf); switch (vf->format) { case (V4L2_PIX_FMT_RGB32): for (ys = 0, yd = 0; ys < (signed int)vf->h; ys++) { for (xs = 0, xd = 0; xs < (signed int)vf->w; xs++) { /* read the pixel */ ptrsrc++; r = *(ptrsrc++); g = *(ptrsrc++); b = *(ptrsrc++); /* only paint the image if the source is within the destination */ if (xd < dst_w) { /* set the pixel */ *(ptrdst++) = b; *(ptrdst++) = g; *(ptrdst++) = r; xd++; } } /* if the source image is too small ignore the other places.. */ if (xd < dst_w) ptrdst += 3 * (dst_w - xd); yd++; } break; case (V4L2_PIX_FMT_BGR32): for (ys = 0, yd = 0; ys < (signed int)vf->h; ys++) { for (xs = 0, xd = 0; xs < (signed int)vf->w; xs++) { /* read the pixel */ b = *(ptrsrc++); g = *(ptrsrc++); r = *(ptrsrc++); ptrsrc++; /* only paint the image if the source is within the destination */ if (xd < dst_w) { /* set the pixel */ *(ptrdst++) = b; *(ptrdst++) = g; *(ptrdst++) = r; xd++; } } /* if the source image is too small ignore the other places.. */ if (xd < dst_w) ptrdst += 3 * (dst_w - xd); yd++; } break; case (V4L2_PIX_FMT_UYVY): for (ys = 0, yd = 0; ys < (signed int)vf->h; ys++) { for (xs = 0, xd = 0; xs < (signed int)vf->w; xs++) { /* read the pixel */ if (xs & 1) { y1 = (unsigned char)*(ptrsrc + 1); cr = (unsigned char)*(ptrsrc); cb = (unsigned char)*(ptrsrc - 2); } else { y1 = (unsigned char)*(ptrsrc + 1); cr = (unsigned char)*(ptrsrc + 2); cb = (unsigned char)*(ptrsrc); } convert2rgb (y1, cr, cb, &r, &g, &b); ptrsrc += 2; /* only paint the image if the source is within the destination */ if (xd < dst_w) { /* set the pixel */ *(ptrdst++) = b; *(ptrdst++) = g; *(ptrdst++) = r; xd++; } } /* if the source image is too small ignore the other places.. */ if (xd < dst_w) ptrdst += 3 * (dst_w - xd); yd++; } break; case (V4L2_PIX_FMT_YUYV): for (ys = 0, yd = 0; ys < (signed int)vf->h; ys++) { if (yd < dst_h) { for (xs = 0, xd = 0; xs < (signed int)vf->w; xs++) { /* read the pixel */ if (xs & 1) { y1 = *(ptrsrc); cb = *(ptrsrc + 1); cr = *(ptrsrc - 1); } else { y1 = *(ptrsrc); cb = *(ptrsrc + 3); cr = *(ptrsrc + 1); } convert2rgb (y1, cr, cb, &r, &g, &b); ptrsrc += 2; /* only paint the image if the source is within the destination */ if (xd < dst_w) { /* set the pixel */ *(ptrdst++) = r; *(ptrdst++) = g; *(ptrdst++) = b; xd++; } } /* if the source image is too small ignore the other places.. */ if (xd < dst_w) ptrdst += 3 * (dst_w - xd); yd++; } } break; default: break; } }; void video_draw_image (VideoFrame *vf) { int pix_w; int pix_h; 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; // // 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 new pixbuf\n", __FILE__, __LINE__); video_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, false, 8, vf->w, vf->h); pix_w = vf->w; pix_h = vf->h; // int channels = gdk_pixbuf_get_n_channels (video_pixbuf); // int rowstride = gdk_pixbuf_get_rowstride (video_pixbuf); } video_convert(vf, video_pixbuf); GdkWindow* window = gtk_widget_get_window(video_da); cairo_t *cr = gdk_cairo_create(window); gdk_cairo_set_source_pixbuf(cr, video_pixbuf, 0, 0); cairo_paint(cr); cairo_fill (cr); // cairo_move_to(cr, 30, 30); // cairo_set_font_size(cr,15); // cairo_show_text(cr, "hello world"); cairo_destroy (cr); /* // // "convert" the G*t*kWidget to G*d*kWindow (no, it's not a GtkWindow!) GdkWindow* window = gtk_widget_get_window(video_da); cairo_region_t * cairoRegion = cairo_region_create(); GdkDrawingContext * drawingContext = gdk_window_begin_draw_frame (window,cairoRegion); cairo_t * cr = gdk_drawing_context_get_cairo_context (drawingContext); gdk_window_end_draw_frame(window,drawingContext); cairo_region_destroy(cairoRegion); */ }; // // 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) void cb_video_btnrec (GtkWidget *widget, gpointer data) { 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)); 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); }; // // 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); }; 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")); VideoFrame *vf; if (data == NULL) { printf ("%s:%d %s something went wrong\n", __FILE__, __LINE__, __FUNCTION__); videodev.Stop(); gtk_widget_set_sensitive(btnstart, true); gtk_widget_set_sensitive(btnstop, false); } else { // printf ("%s:%d %s got video data\n", __FILE__, __LINE__, __FUNCTION__); vf = videodev.FrameGet(); if (vf != NULL) video_draw_image(vf); videodev.FrameNext(); } return false; };