$ git clone http://tcclient.ion.nu/tc_client.git
commit 6bec5cc9bfc377a99f855b48623caa7ebe2e716b
Author: Alicia <...>
Date: Mon Sep 26 14:28:26 2016 +0200
tc_client-gtk: changed the greenscreen postprocessor to use libcamera for the background, allowing you to use either another camera or an image through the virtual "Image" camera as background.
diff --git a/ChangeLog b/ChangeLog
index b7ae2d8..232150f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -7,6 +7,7 @@ tc_client-gtk: added GTK+2 compatibility code related to the greenscreen postpro
tc_client-gtk: changed camera input for broadcasting from using a thread to using g_timeout.
tc_client-gtk: use the camera's own copy of its ID for g_timeout_add() rather than what camera_new() was called with. And in configure, include libavutil/mem.h if we fall back on av_freep()
tc_client-gtk: cleaned up leftover windows compatibility code that is no longer necessary.
+tc_client-gtk: changed the greenscreen postprocessor to use libcamera for the background, allowing you to use either another camera or an image through the virtual "Image" camera as background.
tc_client-gtk and camviewer: updated to libavcodec's avcodec_{send,receive}_{frame,packet} API.
0.39:
Added a /closecam command to stop receiving a cam stream.
diff --git a/gtkgui.glade b/gtkgui.glade
index 3018d71..141560f 100644
--- a/gtkgui.glade
+++ b/gtkgui.glade
@@ -1369,11 +1369,13 @@
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
- <object class="GtkFileChooserButton" id="greenscreen_filechooser">
+ <object class="GtkButton" id="greenscreenbutton">
<property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="create_folders">False</property>
- <property name="title" translatable="yes">Select background</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <child>
+ <placeholder/>
+ </child>
</object>
<packing>
<property name="expand">False</property>
diff --git a/utilities/gtk/camviewer.c b/utilities/gtk/camviewer.c
index d978bbe..90691e7 100644
--- a/utilities/gtk/camviewer.c
+++ b/utilities/gtk/camviewer.c
@@ -1046,7 +1046,7 @@ int main(int argc, char** argv)
g_signal_connect(gtk_builder_get_object(gui, "camcolors_auto"), "toggled", G_CALLBACK(camcolors_toggle_auto), 0);
g_signal_connect(gtk_builder_get_object(gui, "camcolors_flip_horizontal"), "toggled", G_CALLBACK(camcolors_toggle_flip), 0);
g_signal_connect(gtk_builder_get_object(gui, "camcolors_flip_vertical"), "toggled", G_CALLBACK(camcolors_toggle_flip), (void*)1);
- g_signal_connect(gtk_builder_get_object(gui, "greenscreen_filechooser"), "file-set", G_CALLBACK(gui_set_greenscreen_img), 0);
+ g_signal_connect(gtk_builder_get_object(gui, "greenscreenbutton"), "clicked", G_CALLBACK(gui_set_greenscreen_img), 0);
g_signal_connect(gtk_builder_get_object(gui, "greenscreen_colorpicker"), "color-set", G_CALLBACK(gui_set_greenscreen_color), 0);
g_signal_connect(gtk_builder_get_object(gui, "greenscreen_tolerance_h"), "value-changed", G_CALLBACK(gui_set_greenscreen_tolerance), 0);
g_signal_connect(gtk_builder_get_object(gui, "greenscreen_tolerance_s"), "value-changed", G_CALLBACK(gui_set_greenscreen_tolerance), (void*)1);
diff --git a/utilities/gtk/gui.c b/utilities/gtk/gui.c
index 0546891..ca4cbe8 100644
--- a/utilities/gtk/gui.c
+++ b/utilities/gtk/gui.c
@@ -28,6 +28,9 @@
extern void startsession(GtkButton* button, void* x);
extern int tc_client_in[2];
GtkBuilder* gui;
+GtkWidget* gui_greenscreen_preview_img=0;
+unsigned int gui_greenscreen_preview_event=0;
+extern gboolean gui_greenscreen_preview(void* x);
char autoscroll_before(GtkAdjustment* scroll)
{
@@ -484,7 +487,6 @@ void gui_show_camcolors(GtkMenuItem* menuitem, void* x)
{
struct camera* cam=camera_find(menu_context_cam);
if(!cam){return;}
- gtk_widget_show_all(GTK_WIDGET(gtk_builder_get_object(gui, "cam_colors")));
// Set brightness controls
GtkAdjustment* adjustment=GTK_ADJUSTMENT(gtk_builder_get_object(gui, "camcolors_min_brightness"));
gtk_adjustment_set_value(adjustment, cam->postproc.min_brightness);
@@ -495,7 +497,22 @@ void gui_show_camcolors(GtkMenuItem* menuitem, void* x)
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gtk_builder_get_object(gui, "camcolors_flip_horizontal")), cam->postproc.flip_horizontal);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gtk_builder_get_object(gui, "camcolors_flip_vertical")), cam->postproc.flip_vertical);
// Greenscreen controls
- gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(gtk_builder_get_object(gui, "greenscreen_filechooser")), cam->postproc.greenscreen_filename);
+ // Set the appropriate child widget for the greenscreenbutton and start the preview if applicable
+ GtkWidget* button=GTK_WIDGET(gtk_builder_get_object(gui, "greenscreenbutton"));
+ GtkWidget* child=gtk_bin_get_child(GTK_BIN(button));
+ if(child){gtk_widget_destroy(child);}
+ if(cam->postproc.greenscreen)
+ {
+ gui_greenscreen_preview_img=gtk_image_new();
+ gtk_container_add(GTK_CONTAINER(button), gui_greenscreen_preview_img);
+ if(!gui_greenscreen_preview_event)
+ {
+ gui_greenscreen_preview_event=g_timeout_add(100, gui_greenscreen_preview, 0);
+ }
+ }else{
+ gui_greenscreen_preview_img=0;
+ gtk_container_add(GTK_CONTAINER(button), gtk_label_new("Choose a greenscreen background"));
+ }
GdkRGBA color={.red=(double)cam->postproc.greenscreen_color[0]/255,
.green=(double)cam->postproc.greenscreen_color[1]/255,
.blue=(double)cam->postproc.greenscreen_color[2]/255,
@@ -504,6 +521,8 @@ void gui_show_camcolors(GtkMenuItem* menuitem, void* x)
gtk_adjustment_set_value(GTK_ADJUSTMENT(gtk_builder_get_object(gui, "greenscreen_tolerance_h")), cam->postproc.greenscreen_tolerance[0]);
gtk_adjustment_set_value(GTK_ADJUSTMENT(gtk_builder_get_object(gui, "greenscreen_tolerance_s")), cam->postproc.greenscreen_tolerance[1]);
gtk_adjustment_set_value(GTK_ADJUSTMENT(gtk_builder_get_object(gui, "greenscreen_tolerance_v")), cam->postproc.greenscreen_tolerance[2]);
+
+ gtk_widget_show_all(GTK_WIDGET(gtk_builder_get_object(gui, "cam_colors")));
}
void camcolors_adjust_min(GtkAdjustment* adjustment, void* x)
@@ -565,17 +584,76 @@ void gui_hide_cam(GtkMenuItem* menuitem, void* x)
camera_remove(menu_context_cam, 0);
}
-void gui_set_greenscreen_img(GtkFileChooserButton* button, void* x)
+gboolean gui_greenscreen_preview(void* x)
+{
+ struct camera* cam;
+ if(!gui_greenscreen_preview_img || !menu_context_cam ||
+ !(cam=camera_find(menu_context_cam)) || !cam->postproc.greenscreen)
+ {
+ gui_greenscreen_preview_event=0;
+ return G_SOURCE_REMOVE;
+ }
+ GdkPixbuf* oldpixbuf=gtk_image_get_pixbuf(GTK_IMAGE(gui_greenscreen_preview_img));
+ GdkPixbuf* gdkframe=scaled_gdk_pixbuf_from_cam(cam->postproc.greenscreen, cam->postproc.greenscreen_size.width, cam->postproc.greenscreen_size.height, 160, 120);
+ gtk_image_set_from_pixbuf(GTK_IMAGE(gui_greenscreen_preview_img), gdkframe);
+ if(oldpixbuf){g_object_unref(oldpixbuf);}
+ return G_SOURCE_CONTINUE;
+}
+
+void gui_set_greenscreen_img_accept(CAM* img)
+{
+ if(!menu_context_cam){cam_close(img); return;}
+ struct camera* cam=camera_find(menu_context_cam);
+ if(!cam){cam_close(img); return;}
+ if(cam->postproc.greenscreen){cam_close(cam->postproc.greenscreen);}
+ cam->postproc.greenscreen=img;
+ cam->postproc.greenscreen_size.width=340;
+ cam->postproc.greenscreen_size.height=240;
+ cam_resolution(img, &cam->postproc.greenscreen_size.width, &cam->postproc.greenscreen_size.height);
+ GtkWidget* button=GTK_WIDGET(gtk_builder_get_object(gui, "greenscreenbutton"));
+ GtkWidget* child=gtk_bin_get_child(GTK_BIN(button));
+ if(child){gtk_widget_destroy(child);}
+ // Add a GtkImage to the button and start a g_timeout to show a preview on it
+ gui_greenscreen_preview_img=gtk_image_new();
+ gtk_container_add(GTK_CONTAINER(button), gui_greenscreen_preview_img);
+ gtk_widget_show(gui_greenscreen_preview_img);
+ if(!gui_greenscreen_preview_event)
+ {
+ gui_greenscreen_preview_event=g_timeout_add(100, gui_greenscreen_preview, 0);
+ }
+}
+
+void gui_set_greenscreen_img_cancel(void)
+{
+ if(gui_greenscreen_preview_event)
+ {
+ g_source_remove(gui_greenscreen_preview_event);
+ gui_greenscreen_preview_event=0;
+ }
+ GtkWidget* button=GTK_WIDGET(gtk_builder_get_object(gui, "greenscreenbutton"));
+ GtkWidget* child=gtk_bin_get_child(GTK_BIN(button));
+ if(child){gtk_widget_destroy(child);}
+ gui_greenscreen_preview_img=0;
+ GtkWidget* label=gtk_label_new("Choose a greenscreen background");
+ gtk_container_add(GTK_CONTAINER(button), label);
+ gtk_widget_show(label);
+ // Remove from camera
+ if(!menu_context_cam){return;}
+ struct camera* cam=camera_find(menu_context_cam);
+ if(!cam){return;}
+ if(cam->postproc.greenscreen)
+ {
+ cam_close(cam->postproc.greenscreen);
+ cam->postproc.greenscreen=0;
+ }
+}
+
+void gui_set_greenscreen_img(GtkButton* button, void* x)
{
if(!menu_context_cam){return;}
struct camera* cam=camera_find(menu_context_cam);
if(!cam){return;}
- gchar* file=gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(button));
- if(cam->postproc.greenscreen){img_free(cam->postproc.greenscreen);}
- free((void*)cam->postproc.greenscreen_filename);
- cam->postproc.greenscreen=img_load(file);
- cam->postproc.greenscreen_filename=strdup(file);
- g_free(file);
+ camselect_open(gui_set_greenscreen_img_accept, gui_set_greenscreen_img_cancel);
}
void gui_set_greenscreen_color(GtkColorButton* button, void* x)
diff --git a/utilities/gtk/gui.h b/utilities/gtk/gui.h
index 281c189..3b63f62 100644
--- a/utilities/gtk/gui.h
+++ b/utilities/gtk/gui.h
@@ -45,7 +45,7 @@ extern void camcolors_adjust_max(GtkAdjustment* adjustment, void* x);
extern void camcolors_toggle_auto(GtkToggleButton* button, void* x);
extern void camcolors_toggle_flip(GtkToggleButton* button, void* vertical);
extern void gui_hide_cam(GtkMenuItem* menuitem, void* x);
-extern void gui_set_greenscreen_img(GtkFileChooserButton* button, void* x);
+extern void gui_set_greenscreen_img(GtkButton* button, void* x);
extern void gui_set_greenscreen_color(GtkColorButton* button, void* x);
extern void gui_set_greenscreen_tolerance(GtkAdjustment* adjustment, void* x);
diff --git a/utilities/gtk/media.c b/utilities/gtk/media.c
index 36b39ef..ff95847 100644
--- a/utilities/gtk/media.c
+++ b/utilities/gtk/media.c
@@ -270,21 +270,8 @@ struct
gboolean camselect_frame(void* x)
{
if(!camselect.current){return G_SOURCE_CONTINUE;}
- void* buf=malloc(camselect.size.width*camselect.size.height*3);
- cam_getframe(camselect.current, buf);
GdkPixbuf* oldpixbuf=gtk_image_get_pixbuf(GTK_IMAGE(campreview.cam));
- GdkPixbuf* gdkframe=gdk_pixbuf_new_from_data(buf, GDK_COLORSPACE_RGB, 0, 8, camselect.size.width, camselect.size.height, camselect.size.width*3, 0, freebuffer);
- if(gdk_pixbuf_get_height(gdkframe)>PREVIEW_MAX_HEIGHT || gdk_pixbuf_get_width(gdkframe)>PREVIEW_MAX_WIDTH) // Scale if the input is huge
- {
- unsigned int width=gdk_pixbuf_get_width(gdkframe);
- unsigned int height=gdk_pixbuf_get_height(gdkframe);
- if(height*PREVIEW_MAX_WIDTH/width>PREVIEW_MAX_HEIGHT)
- {
- gdkframe=gdk_pixbuf_scale_simple(gdkframe, width*PREVIEW_MAX_HEIGHT/height, PREVIEW_MAX_HEIGHT, GDK_INTERP_BILINEAR);
- }else{
- gdkframe=gdk_pixbuf_scale_simple(gdkframe, PREVIEW_MAX_WIDTH, height*PREVIEW_MAX_WIDTH/width, GDK_INTERP_BILINEAR);
- }
- }
+ GdkPixbuf* gdkframe=scaled_gdk_pixbuf_from_cam(camselect.current, camselect.size.width, camselect.size.height, PREVIEW_MAX_WIDTH, PREVIEW_MAX_HEIGHT);
gtk_image_set_from_pixbuf(GTK_IMAGE(campreview.cam), gdkframe);
g_object_unref(oldpixbuf);
return G_SOURCE_CONTINUE;
@@ -300,7 +287,7 @@ void camselect_open(void(*cb)(CAM*), void(*ccb)(void))
camselect.eventsource=g_timeout_add(100, camselect_frame, 0);
}
// Open the currently selected camera (usually starting with the top alternative)
- if(camselect.current){cam_close(camselect.current);}
+ if(camselect.current){cam_close(camselect.current); camselect.current=0;}
GtkComboBox* combo=GTK_COMBO_BOX(gtk_builder_get_object(gui, "camselect_combo"));
camselect_change(combo, 0);
GtkWidget* window=GTK_WIDGET(gtk_builder_get_object(gui, "camselection"));
@@ -468,3 +455,22 @@ gboolean camplaceholder_update(void* id)
g_object_unref(oldpixbuf);
return G_SOURCE_CONTINUE;
}
+
+GdkPixbuf* scaled_gdk_pixbuf_from_cam(CAM* cam, unsigned int width, unsigned int height, unsigned int maxwidth, unsigned int maxheight)
+{
+ void* buf=malloc(width*height*3);
+ cam_getframe(cam, buf);
+ GdkPixbuf* gdkframe=gdk_pixbuf_new_from_data(buf, GDK_COLORSPACE_RGB, 0, 8, width, height, width*3, 0, freebuffer);
+ if(height>maxheight || width>maxwidth) // Scale if the input is huge
+ {
+ GdkPixbuf* oldframe=gdkframe;
+ if(height*maxwidth/width>maxheight)
+ {
+ gdkframe=gdk_pixbuf_scale_simple(gdkframe, width*maxheight/height, maxheight, GDK_INTERP_BILINEAR);
+ }else{
+ gdkframe=gdk_pixbuf_scale_simple(gdkframe, maxwidth, height*maxwidth/width, GDK_INTERP_BILINEAR);
+ }
+ g_object_unref(oldframe);
+ }
+ return gdkframe;
+}
diff --git a/utilities/gtk/media.h b/utilities/gtk/media.h
index 788b36b..913022f 100644
--- a/utilities/gtk/media.h
+++ b/utilities/gtk/media.h
@@ -67,3 +67,4 @@ extern const char* camselect_file(void);
extern void camera_postproc(struct camera* cam, unsigned char* buf, unsigned int width, unsigned int height);
extern void updatescaling(unsigned int width, unsigned int height, char changedcams);
extern gboolean camplaceholder_update(void* id);
+extern GdkPixbuf* scaled_gdk_pixbuf_from_cam(CAM* cam, unsigned int width, unsigned int height, unsigned int maxwidth, unsigned int maxheight);
diff --git a/utilities/gtk/postproc.c b/utilities/gtk/postproc.c
index 625f31a..dcadafc 100644
--- a/utilities/gtk/postproc.c
+++ b/utilities/gtk/postproc.c
@@ -19,69 +19,6 @@
#include <gdk-pixbuf/gdk-pixbuf.h>
#include "postproc.h"
-// TODO: Replace this img_* API with libcamera
-struct img* img_load(const char* filename)
-{
- struct img* this=malloc(sizeof(struct img));
- this->animation=gdk_pixbuf_animation_new_from_file(filename, 0);
- if(gdk_pixbuf_animation_is_static_image(this->animation))
- {
- this->img=gdk_pixbuf_animation_get_static_image(this->animation);
- this->iter=0;
- if(gdk_pixbuf_get_n_channels(this->img)==4)
- {
- unsigned char* pixels=gdk_pixbuf_get_pixels(this->img);
- unsigned int pixelcount=gdk_pixbuf_get_width(this->img)*gdk_pixbuf_get_height(this->img);
- unsigned int i;
- for(i=0; i<pixelcount; ++i)
- {
- memmove(&pixels[i*3], &pixels[i*4], 3);
- }
- }
- }else{
- this->iter=gdk_pixbuf_animation_get_iter(this->animation, 0);
- unsigned int w=gdk_pixbuf_animation_get_width(this->animation);
- unsigned int h=gdk_pixbuf_animation_get_height(this->animation);
- this->img=gdk_pixbuf_new(GDK_COLORSPACE_RGB, 0, 8, w, h);
- }
- return this;
-}
-
-GdkPixbuf* img_get(struct img* this)
-{
- if(this->iter)
- {
- gdk_pixbuf_animation_iter_advance(this->iter, 0);
- GdkPixbuf* img=gdk_pixbuf_animation_iter_get_pixbuf(this->iter);
- if(gdk_pixbuf_get_n_channels(img)!=4){return img;}
- unsigned int w=gdk_pixbuf_get_width(img);
- unsigned int h=gdk_pixbuf_get_height(img);
- unsigned int dstw=gdk_pixbuf_get_width(this->img);
- unsigned int dsth=gdk_pixbuf_get_height(this->img);
- unsigned char* pixels=gdk_pixbuf_get_pixels(img);
- unsigned char* dstpixels=gdk_pixbuf_get_pixels(this->img);
- unsigned int x, y;
- for(y=0; y<h && y<dsth; ++y)
- for(x=0; x<w && x<dstw; ++x)
- {
- memmove(&dstpixels[(y*dstw+x)*3], &pixels[(y*w+x)*4], 3);
- }
- }
- return this->img;
-}
-
-void img_free(struct img* this)
-{
- if(this->iter)
- {
- g_object_unref(this->iter);
- g_object_unref(this->animation);
- g_object_unref(this->img);
- }else{
- g_object_unref(this->animation);
- }
-}
-
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
@@ -112,7 +49,6 @@ void postproc_init(struct postproc_ctx* pp)
pp->greenscreen_color[0]=0;
pp->greenscreen_color[1]=255;
pp->greenscreen_color[2]=0;
- pp->greenscreen_filename=0;
}
void postprocess(struct postproc_ctx* pp, unsigned char* buf, unsigned int width, unsigned int height)
@@ -169,12 +105,12 @@ void postprocess(struct postproc_ctx* pp, unsigned char* buf, unsigned int width
}
if(pp->greenscreen)
{
- GdkPixbuf* img=img_get(pp->greenscreen);
- guchar* pixels=gdk_pixbuf_get_pixels(img);
+ unsigned char pixels[pp->greenscreen_size.width*pp->greenscreen_size.height*3];
+ cam_getframe(pp->greenscreen, pixels);
unsigned int x;
unsigned int y;
- unsigned int gswidth=gdk_pixbuf_get_width(img);
- unsigned int gsheight=gdk_pixbuf_get_height(img);
+ unsigned int gswidth=pp->greenscreen_size.width;
+ unsigned int gsheight=pp->greenscreen_size.height;
unsigned int gsx, gsy;
unsigned char* gscolor=pp->greenscreen_hsv;
for(y=0; y<height; ++y)
@@ -199,6 +135,5 @@ void postprocess(struct postproc_ctx* pp, unsigned char* buf, unsigned int width
void postproc_free(struct postproc_ctx* pp)
{
- if(pp->greenscreen){img_free(pp->greenscreen);}
- free((void*)pp->greenscreen_filename);
+ if(pp->greenscreen){cam_close(pp->greenscreen);}
}
diff --git a/utilities/gtk/postproc.h b/utilities/gtk/postproc.h
index 2d52fd0..cfc9736 100644
--- a/utilities/gtk/postproc.h
+++ b/utilities/gtk/postproc.h
@@ -14,12 +14,7 @@
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-struct img
-{
- GdkPixbufAnimation* animation;
- GdkPixbufAnimationIter* iter;
- GdkPixbuf* img;
-};
+#include "../libcamera/camera.h"
struct postproc_ctx
{
@@ -28,16 +23,13 @@ struct postproc_ctx
char autoadjust;
char flip_horizontal;
char flip_vertical;
- struct img* greenscreen;
+ CAM* greenscreen;
int greenscreen_tolerance[3];
unsigned char greenscreen_color[3];
unsigned char greenscreen_hsv[3];
- const char* greenscreen_filename;
+ struct{unsigned int width;unsigned int height;} greenscreen_size;
};
-extern struct img* img_load(const char* filename);
-extern GdkPixbuf* img_get(struct img* this);
-extern void img_free(struct img* this);
extern void rgb_to_hsv(int r, int g, int b, unsigned char* h, unsigned char* s, unsigned char* v);
extern void postproc_init(struct postproc_ctx* pp);
extern void postprocess(struct postproc_ctx* pp, unsigned char* buf, unsigned int width, unsigned int height);