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.
340 lines
8.8 KiB
340 lines
8.8 KiB
/***************************************************************************************
|
|
*
|
|
* video.cc is part of SimpleSkyCam.
|
|
*
|
|
*****************************************************************************************/
|
|
|
|
#include <list>
|
|
#include <string>
|
|
#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<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)
|
|
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;
|
|
};
|
|
|