$ git clone http://tcclient.ion.nu/tc_client.git
commit 75ac6fb98ecbb9f0c7aeedac287869e41ab12ada
Author: Alicia <...>
Date: Sat Apr 30 11:45:30 2016 +0200
tc_client-gtk: added postprocessing options for cams upon right-click, starting with brightness adjustment.
diff --git a/ChangeLog b/ChangeLog
index dcc870a..29a2096 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,6 +1,7 @@
0.39:
modbot: only use youtube-dl's 'ytsearch:' for --get-id, and only if it doesn't appear to be an ID already.
tc_client-gtk: handle only one sendmessage event at a time, and don't send empty lines.
+tc_client-gtk: added postprocessing options for cams upon right-click, starting with brightness adjustment.
tc_client-gtk and camviewer: fixed compatibility with newer libavutil.
0.38:
Handle multi-line messages.
diff --git a/gtkgui.glade b/gtkgui.glade
index 88dbdf1..11054af 100644
--- a/gtkgui.glade
+++ b/gtkgui.glade
@@ -490,6 +490,29 @@
</object>
</child>
</object>
+ <object class="GtkMenu" id="cam_menu">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="cam_menu_colors">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Adjust color levels</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ </object>
+ <object class="GtkAdjustment" id="camcolors_max_brightness">
+ <property name="upper">255</property>
+ <property name="value">255</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
+ <object class="GtkAdjustment" id="camcolors_min_brightness">
+ <property name="upper">255</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
<object class="GtkWindow" id="camselection">
<property name="can_focus">False</property>
<property name="modal">True</property>
@@ -1126,4 +1149,86 @@
</object>
</child>
</object>
+ <object class="GtkWindow" id="cam_colors">
+ <property name="can_focus">False</property>
+ <property name="title" translatable="yes">Color adjustment</property>
+ <property name="transient_for">main</property>
+ <signal name="delete-event" handler="gtk_widget_hide_on_delete" swapped="no"/>
+ <child>
+ <object class="GtkBox" id="box12">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkLabel" id="label23">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Lower bound</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScale" id="scale1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="adjustment">camcolors_min_brightness</property>
+ <property name="round_digits">1</property>
+ <property name="digits">0</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label24">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Upper bound</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScale" id="scale2">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="adjustment">camcolors_max_brightness</property>
+ <property name="round_digits">1</property>
+ <property name="digits">0</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="camcolors_auto">
+ <property name="label" translatable="yes">Automatically adjust</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="xalign">0</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
</interface>
diff --git a/utilities/gtk/camviewer.c b/utilities/gtk/camviewer.c
index 2d1b096..173827d 100644
--- a/utilities/gtk/camviewer.c
+++ b/utilities/gtk/camviewer.c
@@ -464,11 +464,7 @@ gboolean handledata(GIOChannel* iochannel, GIOCondition condition, gpointer data
if(!idend){return 1;}
idend[0]=0;
camera_removebynick(nick); // Remove any duplicates
- struct camera* cam=camera_new();
- cam->frame=av_frame_alloc();
- cam->dstframe=av_frame_alloc();
- cam->nick=strdup(nick);
- cam->id=strdup(id);
+ struct camera* cam=camera_new(nick, id);
cam->vctx=avcodec_alloc_context3(data->vdecoder);
avcodec_open2(cam->vctx, data->vdecoder, 0);
#if defined(HAVE_AVRESAMPLE) || defined(HAVE_SWRESAMPLE)
@@ -476,12 +472,6 @@ gboolean handledata(GIOChannel* iochannel, GIOCondition condition, gpointer data
avcodec_open2(cam->actx, data->adecoder, 0);
cam->samples=0;
#endif
- cam->cam=gtk_image_new();
- cam->box=gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
- gtk_box_set_homogeneous(GTK_BOX(cam->box), 0);
- gtk_box_pack_start(GTK_BOX(cam->box), cam->cam, 0, 0, 0);
- cam->label=gtk_label_new(cam->nick);
- gtk_box_pack_start(GTK_BOX(cam->box), cam->label, 0, 0, 0);
gtk_box_pack_start(GTK_BOX(data->box), cam->box, 0, 0, 0);
gtk_widget_show_all(cam->box);
updatescaling(data, 0, 0);
@@ -491,19 +481,10 @@ gboolean handledata(GIOChannel* iochannel, GIOCondition condition, gpointer data
}
if(!strcmp(buf, "Starting outgoing media stream"))
{
- struct camera* cam=camera_new();
- cam->frame=av_frame_alloc();
- cam->dstframe=av_frame_alloc();
- cam->nick=strdup(nickname);
- cam->id=strdup("out");
+ struct camera* cam=camera_new(nickname, "out");
cam->vctx=avcodec_alloc_context3(data->vdecoder);
avcodec_open2(cam->vctx, data->vdecoder, 0);
cam->actx=0;
- cam->cam=gtk_image_new();
- cam->box=gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
- gtk_box_pack_start(GTK_BOX(cam->box), cam->cam, 0, 0, 0);
- cam->label=gtk_label_new(cam->nick);
- gtk_box_pack_start(GTK_BOX(cam->box), cam->label, 0, 0, 0);
gtk_box_pack_start(GTK_BOX(data->box), cam->box, 0, 0, 0);
gtk_widget_show_all(cam->box);
updatescaling(data, 0, 0);
@@ -608,6 +589,7 @@ gboolean handledata(GIOChannel* iochannel, GIOCondition condition, gpointer data
struct SwsContext* swsctx=sws_getContext(cam->frame->width, cam->frame->height, cam->frame->format, scalewidth, scaleheight, AV_PIX_FMT_RGB24, 0, 0, 0, 0);
sws_scale(swsctx, (const uint8_t*const*)cam->frame->data, cam->frame->linesize, 0, cam->frame->height, cam->dstframe->data, cam->dstframe->linesize);
sws_freeContext(swsctx);
+ camera_postproc(cam, buf, scalewidth*scaleheight);
GdkPixbuf* oldpixbuf=gtk_image_get_pixbuf(GTK_IMAGE(cam->cam));
GdkPixbuf* gdkframe=gdk_pixbuf_new_from_data(cam->dstframe->data[0], GDK_COLORSPACE_RGB, 0, 8, scalewidth, scaleheight, cam->dstframe->linesize[0], freebuffer, 0);
@@ -1061,6 +1043,11 @@ int main(int argc, char** argv)
g_signal_connect(item, "switch-page", G_CALLBACK(pm_select), 0);
// Connect signal for captcha
g_signal_connect(gtk_builder_get_object(gui, "captcha_done"), "clicked", G_CALLBACK(captcha_done), 0);
+ // Connect signals for camera postprocessing
+ g_signal_connect(gtk_builder_get_object(gui, "cam_menu_colors"), "activate", G_CALLBACK(gui_show_camcolors), 0);
+ g_signal_connect(gtk_builder_get_object(gui, "camcolors_min_brightness"), "value-changed", G_CALLBACK(camcolors_adjust_min), 0);
+ g_signal_connect(gtk_builder_get_object(gui, "camcolors_max_brightness"), "value-changed", G_CALLBACK(camcolors_adjust_max), 0);
+ g_signal_connect(gtk_builder_get_object(gui, "camcolors_auto"), "toggled", G_CALLBACK(camcolors_toggle_auto), 0);
// Populate saved channels
GtkWidget* startbox=GTK_WIDGET(gtk_builder_get_object(gui, "startbox"));
int channelcount=config_get_int("channelcount");
diff --git a/utilities/gtk/gui.c b/utilities/gtk/gui.c
index 9831076..08f674b 100644
--- a/utilities/gtk/gui.c
+++ b/utilities/gtk/gui.c
@@ -18,11 +18,12 @@
#include <string.h>
#include <stdint.h>
#include <gtk/gtk.h>
-#include "gui.h"
#include "config.h"
#include "logging.h"
#include "compat.h"
#include "userlist.h"
+#include "media.h"
+#include "gui.h"
extern void startsession(GtkButton* button, void* x);
GtkBuilder* gui;
@@ -452,3 +453,63 @@ void fontsize_set(double size)
buffer_updatesize(userlist[i].pm_buffer);
}
}
+
+const char* menu_context_cam=0;
+gboolean gui_show_cam_menu(GtkWidget* widget, GdkEventButton* event, const char* id)
+{
+ if(event->button!=3){return 0;} // Only act on right-click
+ struct camera* cam=camera_find(id);
+ free((void*)menu_context_cam);
+ menu_context_cam=strdup(cam->id);
+ GtkMenu* menu=GTK_MENU(gtk_builder_get_object(gui, "cam_menu"));
+ gtk_menu_popup(menu, 0, 0, 0, 0, event->button, event->time);
+ return 1;
+}
+
+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")));
+ GtkAdjustment* adjustment=GTK_ADJUSTMENT(gtk_builder_get_object(gui, "camcolors_min_brightness"));
+ gtk_adjustment_set_value(adjustment, cam->postproc.min_brightness);
+ adjustment=GTK_ADJUSTMENT(gtk_builder_get_object(gui, "camcolors_max_brightness"));
+ gtk_adjustment_set_value(adjustment, cam->postproc.max_brightness);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gtk_builder_get_object(gui, "camcolors_auto")), cam->postproc.autoadjust);
+}
+
+void camcolors_adjust_min(GtkAdjustment* adjustment, void* x)
+{
+ GtkAdjustment* other=GTK_ADJUSTMENT(gtk_builder_get_object(gui, "camcolors_max_brightness"));
+ double value=gtk_adjustment_get_value(adjustment);
+ if(value>gtk_adjustment_get_value(other))
+ {
+ gtk_adjustment_set_value(other, value);
+ }
+ if(!menu_context_cam){return;}
+ struct camera* cam=camera_find(menu_context_cam);
+ if(!cam){return;}
+ cam->postproc.min_brightness=value;
+}
+
+void camcolors_adjust_max(GtkAdjustment* adjustment, void* x)
+{
+ GtkAdjustment* other=GTK_ADJUSTMENT(gtk_builder_get_object(gui, "camcolors_min_brightness"));
+ double value=gtk_adjustment_get_value(adjustment);
+ if(value<gtk_adjustment_get_value(other))
+ {
+ gtk_adjustment_set_value(other, value);
+ }
+ if(!menu_context_cam){return;}
+ struct camera* cam=camera_find(menu_context_cam);
+ if(!cam){return;}
+ cam->postproc.max_brightness=value;
+}
+
+void camcolors_toggle_auto(GtkToggleButton* button, void* x)
+{
+ if(!menu_context_cam){return;}
+ struct camera* cam=camera_find(menu_context_cam);
+ if(!cam){return;}
+ cam->postproc.autoadjust=gtk_toggle_button_get_active(button);
+}
diff --git a/utilities/gtk/gui.h b/utilities/gtk/gui.h
index 91a1699..a2b46ae 100644
--- a/utilities/gtk/gui.h
+++ b/utilities/gtk/gui.h
@@ -38,5 +38,10 @@ extern char pm_select(GtkNotebook* tabs, GtkWidget* tab, int num, void* x);
extern void buffer_setup_colors(GtkTextBuffer* buffer);
extern void buffer_updatesize(GtkTextBuffer* buffer);
extern void fontsize_set(double size);
+extern gboolean gui_show_cam_menu(GtkWidget* widget, GdkEventButton* event, const char* id);
+extern void gui_show_camcolors(GtkMenuItem* menuitem, void* x);
+extern void camcolors_adjust_min(GtkAdjustment* adjustment, void* x);
+extern void camcolors_adjust_max(GtkAdjustment* adjustment, void* x);
+extern void camcolors_toggle_auto(GtkToggleButton* button, void* x);
extern GtkBuilder* gui;
diff --git a/utilities/gtk/media.c b/utilities/gtk/media.c
index 948c622..59db50a 100644
--- a/utilities/gtk/media.c
+++ b/utilities/gtk/media.c
@@ -32,7 +32,11 @@
#include "../compat.h"
#include "gui.h"
#include "media.h"
-struct camera campreview;
+struct camera campreview={
+ .postproc.min_brightness=0,
+ .postproc.max_brightness=255,
+ .postproc.autoadjust=0
+};
struct camera* cams=0;
unsigned int camcount=0;
#ifdef _WIN32
@@ -136,15 +140,35 @@ struct camera* camera_findbynick(const char* nick)
return 0;
}
-struct camera* camera_new(void)
+struct camera* camera_new(const char* nick, const char* id)
{
++camcount;
cams=realloc(cams, sizeof(struct camera)*camcount);
+ struct camera* cam=&cams[camcount-1];
#if defined(HAVE_AVRESAMPLE) || defined(HAVE_SWRESAMPLE)
- cams[camcount-1].samples=0;
- cams[camcount-1].samplecount=0;
+ cam->samples=0;
+ cam->samplecount=0;
#endif
- return &cams[camcount-1];
+ cam->nick=strdup(nick);
+ cam->id=strdup(id);
+ cam->frame=av_frame_alloc();
+ cam->dstframe=av_frame_alloc();
+ cam->cam=gtk_image_new();
+ cam->box=gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
+ gtk_box_set_homogeneous(GTK_BOX(cam->box), 0);
+ // Wrap cam image in an event box to catch (right) clicks
+ GtkWidget* eventbox=gtk_event_box_new();
+ gtk_container_add(GTK_CONTAINER(eventbox), cam->cam);
+ gtk_event_box_set_above_child(GTK_EVENT_BOX(eventbox), 1);
+ cam->label=gtk_label_new(cam->nick);
+ gtk_box_pack_start(GTK_BOX(cam->box), eventbox, 0, 0, 0);
+ gtk_box_pack_start(GTK_BOX(cam->box), cam->label, 0, 0, 0);
+ g_signal_connect(eventbox, "button-release-event", G_CALLBACK(gui_show_cam_menu), cam->id);
+ // Initialize postprocessing values
+ cam->postproc.min_brightness=0;
+ cam->postproc.max_brightness=255;
+ cam->postproc.autoadjust=0;
+ return cam;
}
void camera_cleanup(void)
@@ -325,3 +349,28 @@ const char* camselect_file(void)
gtk_widget_destroy(dialog);
return file;
}
+
+void camera_postproc(struct camera* cam, unsigned char* buf, unsigned int count)
+{
+ if(cam->postproc.min_brightness==0 && cam->postproc.max_brightness==255 && !cam->postproc.autoadjust){return;}
+ unsigned char min=255;
+ unsigned char max=0;
+ unsigned int i;
+ for(i=0; i<count*3; ++i)
+ {
+ if(cam->postproc.autoadjust)
+ {
+ if(buf[i]<min){min=buf[i];}
+ if(buf[i]>max){max=buf[i];}
+ }
+ double v=((double)buf[i]-cam->postproc.min_brightness)*255/(cam->postproc.max_brightness-cam->postproc.min_brightness);
+ if(v<0){v=0;}
+ if(v>255){v=255;}
+ buf[i]=v;
+ }
+ if(cam->postproc.autoadjust)
+ {
+ cam->postproc.min_brightness=min;
+ cam->postproc.max_brightness=max;
+ }
+}
diff --git a/utilities/gtk/media.h b/utilities/gtk/media.h
index e94bb30..21ab1dd 100644
--- a/utilities/gtk/media.h
+++ b/utilities/gtk/media.h
@@ -14,6 +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/>.
*/
+#include <libavcodec/avcodec.h>
struct camera
{
AVFrame* frame;
@@ -27,6 +28,12 @@ struct camera
char* nick;
GtkWidget* box; // holds label and cam
GtkWidget* label;
+ struct
+ {
+ double min_brightness;
+ double max_brightness;
+ char autoadjust;
+ } postproc;
};
extern struct camera campreview;
extern struct camera* cams;
@@ -45,10 +52,11 @@ extern void camera_remove(const char* nick);
extern void camera_removebynick(const char* nick);
extern struct camera* camera_find(const char* id);
extern struct camera* camera_findbynick(const char* nick);
-extern struct camera* camera_new(void);
+extern struct camera* camera_new(const char* nick, const char* id);
extern void camera_cleanup(void);
extern GIOChannel* camthread(const char* name, AVCodec* vencoder, unsigned int delay);
extern void camselect_change(GtkComboBox* combo, AVCodec* vencoder);
extern gboolean camselect_cancel(GtkWidget* widget, void* x1, void* x2);
extern void camselect_accept(GtkWidget* widget, AVCodec* vencoder);
extern const char* camselect_file(void);
+extern void camera_postproc(struct camera* cam, unsigned char* buf, unsigned int count);