$ git clone https://tcclient.ion.nu/tc_client.git
commit 8dde1f936396b82516544a6df60acd201c4b03d8
Author: Alicia <...>
Date: Sat Sep 17 18:35:07 2016 +0200
tc_client-gtk: added a placeholder animation for cameras, shown for audio-only streams and streams that haven't sent any video data yet.
diff --git a/ChangeLog b/ChangeLog
index 474f71e..d2ac7b0 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -26,6 +26,7 @@ tc_client-gtk: un-highlight selected PM tabs even if the sender has left.
tc_client-gtk: prevent new PM tabs from stopping automatic scrolling.
tc_client-gtk: if the server disconnects, print the notification and stop any outgoing broadcast.
tc_client-gtk: optimized incoming audio/video by checking for "Video:" and "Audio:" first when handling lines from the tc_client core.
+tc_client-gtk: added a placeholder animation for cameras, shown for audio-only streams and streams that haven't sent any video data yet.
tc_client-gtk and camviewer: fixed compatibility with newer libavutil.
tc_client-gtk and camviewer: added compatibility fallbacks for av_image_get_buffer_size() and av_packet_unref()
tc_client-gtk and camviewer: specify a scaling algorithm for libswscale (mandatory for older versions of libswscale)
diff --git a/Makefile b/Makefile
index da1da2e..a3efc7c 100644
--- a/Makefile
+++ b/Makefile
@@ -112,8 +112,11 @@ camviewer: $(CAMVIEWER_OBJ)
cursedchat: $(CURSEDCHAT_OBJ)
$(CC) $(LDFLAGS) $^ $(LIBS) $(READLINE_LIBS) $(CURSES_LIBS) -o $@
-tc_client-gtk: $(TC_CLIENT_GTK_OBJ)
- $(CC) $(LDFLAGS) $^ $(LIBS) $(GTK_LIBS) $(AVCODEC_LIBS) $(AVUTIL_LIBS) $(SWSCALE_LIBS) $(AVRESAMPLE_LIBS) $(SWRESAMPLE_LIBS) $(AO_LIBS) $(LIBV4L2_LIBS) -o $@
+tc_client-gtk: $(TC_CLIENT_GTK_OBJ) camplaceholder.gif
+ $(CC) $(LDFLAGS) $(TC_CLIENT_GTK_OBJ) $(LIBS) $(GTK_LIBS) $(AVCODEC_LIBS) $(AVUTIL_LIBS) $(SWSCALE_LIBS) $(AVRESAMPLE_LIBS) $(SWRESAMPLE_LIBS) $(AO_LIBS) $(LIBV4L2_LIBS) -o $@
+
+camplaceholder.gif: utilities/gtk/gencamplaceholder.sh utilities/gtk/camplaceholder.xcf utilities/gtk/spinnerdot.xcf
+ utilities/gtk/gencamplaceholder.sh
# Workaround for windows' lack of fork() and inflexibility of having or not having a terminal
utilities/gtk/camthread.gen.c: utilities/gtk/media.c
@@ -138,7 +141,7 @@ libcamera.a: $(LIBCAMERA_OBJ)
$(RANLIB) $@
clean:
- rm -f $(OBJ) $(IRCHACK_OBJ) $(MODBOT_OBJ) $(CAMVIEWER_OBJ) $(CURSEDCHAT_OBJ) $(TC_CLIENT_GTK_OBJ) $(LIBCAMERA_OBJ) tc_client irchack modbot camviewer cursedchat tc_client-gtk
+ rm -f $(OBJ) $(IRCHACK_OBJ) $(MODBOT_OBJ) $(CAMVIEWER_OBJ) $(CURSEDCHAT_OBJ) $(TC_CLIENT_GTK_OBJ) $(LIBCAMERA_OBJ) tc_client irchack modbot camviewer cursedchat tc_client-gtk camplaceholder.gif
SOURCES=Makefile client.c amfparser.c rtmp.c numlist.c amfwriter.c idlist.c colors.c endian.c media.c amfparser.h rtmp.h numlist.h amfwriter.h idlist.h colors.h endian.h media.h LICENSE README ChangeLog crossbuild.sh testbuilds.sh configure
SOURCES+=utilities/irchack/irchack.c
@@ -146,6 +149,7 @@ SOURCES+=utilities/modbot/modbot.c utilities/modbot/queue.c utilities/modbot/que
SOURCES+=utilities/camviewer/camviewer.c
SOURCES+=utilities/cursedchat/cursedchat.c utilities/cursedchat/buffer.c utilities/cursedchat/buffer.h
SOURCES+=utilities/gtk/camviewer.c utilities/gtk/userlist.c utilities/gtk/media.c utilities/gtk/compat.c utilities/gtk/config.c utilities/gtk/gui.c utilities/gtk/logging.c utilities/gtk/inputhistory.c utilities/gtk/userlist.h utilities/gtk/media.h utilities/gtk/compat.h utilities/gtk/config.h utilities/gtk/gui.h utilities/gtk/logging.h utilities/gtk/inputhistory.h gtkgui.glade
+SOURCES+=utilities/gtk/gencamplaceholder.sh utilities/gtk/camplaceholder.xcf utilities/gtk/spinnerdot.xcf
SOURCES+=utilities/compat.c utilities/compat.h utilities/list.c utilities/list.h utilities/stringutils.c utilities/stringutils.h
SOURCES+=utilities/libcamera/camera.c utilities/libcamera/camera.h utilities/libcamera/camera_v4l2.c utilities/libcamera/camera_v4l2.h utilities/libcamera/camera_img.c utilities/libcamera/camera_img.h utilities/libcamera/camera_escapi.cpp utilities/libcamera/camera_escapi.h
tarball:
diff --git a/utilities/gtk/camplaceholder.xcf b/utilities/gtk/camplaceholder.xcf
new file mode 100644
index 0000000..bfacaca
Binary files /dev/null and b/utilities/gtk/camplaceholder.xcf differ
diff --git a/utilities/gtk/camviewer.c b/utilities/gtk/camviewer.c
index e760e65..bd806ae 100644
--- a/utilities/gtk/camviewer.c
+++ b/utilities/gtk/camviewer.c
@@ -204,6 +204,11 @@ gboolean handledata(GIOChannel* iochannel, GIOCondition condition, gpointer data
avcodec_decode_video2(cam->vctx, cam->frame, &gotframe, &pkt);
if(!gotframe){return 1;}
+ if(cam->placeholder) // Remove the placeholder animation if it has it
+ {
+ g_source_remove(cam->placeholder);
+ cam->placeholder=0;
+ }
// Scale and convert to RGB24 format
unsigned int bufsize=av_image_get_buffer_size(AV_PIX_FMT_RGB24, camsize_scale.width, camsize_scale.height, 1);
unsigned char* buf=malloc(bufsize);
@@ -514,6 +519,7 @@ gboolean handledata(GIOChannel* iochannel, GIOCondition condition, gpointer data
idend[0]=0;
camera_removebynick(nick); // Remove any duplicates
struct camera* cam=camera_new(nick, id);
+ cam->placeholder=g_timeout_add(100, camplaceholder_update, cam->id);
cam->vctx=avcodec_alloc_context3(data->vdecoder);
avcodec_open2(cam->vctx, data->vdecoder, 0);
#if defined(HAVE_AVRESAMPLE) || defined(HAVE_SWRESAMPLE)
@@ -1035,6 +1041,14 @@ int main(int argc, char** argv)
g_signal_connect(gtk_builder_get_object(gui, "camcolors_flip_vertical"), "toggled", G_CALLBACK(camcolors_toggle_flip), (void*)1);
// Connect signal for hiding cameras
g_signal_connect(gtk_builder_get_object(gui, "cam_menu_hide"), "activate", G_CALLBACK(gui_hide_cam), 0);
+ // Load placeholder animation for cameras (no video data yet or mic only)
+ if(frombuild)
+ {
+ camplaceholder=gdk_pixbuf_animation_new_from_file("camplaceholder.gif", 0);
+ }else{
+ camplaceholder=gdk_pixbuf_animation_new_from_file(PREFIX "/share/tc_client/camplaceholder.gif", 0);
+ }
+ camplaceholder_iter=gdk_pixbuf_animation_get_iter(camplaceholder, 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/gencamplaceholder.sh b/utilities/gtk/gencamplaceholder.sh
new file mode 100755
index 0000000..487dd61
--- /dev/null
+++ b/utilities/gtk/gencamplaceholder.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+spinnerpos='+281+341
++270+369
++281+397
++309+408
++337+397
++348+369
++337+341
++309+330'
+{
+ for frame in `seq 1 16`; do
+ echo '( ( utilities/gtk/camplaceholder.xcf -layers Merge )'
+ for spinner in `seq 1 8`; do
+ pos="`echo "$spinnerpos" | sed -n -e "${spinner}p"`"
+ dotframe="`expr '(' "$frame" + "$spinner" '*' 2 ')' '%' 16`"
+ if [ "$dotframe" -gt 11 ]; then dotframe=11; fi # TODO: use identify | wc -l to get the max frame ID?
+ echo "( -geometry "$pos" "utilities/gtk/spinnerdot.xcf[${dotframe}]" ) -composite"
+ done
+ echo ')'
+ done
+ echo '-layers Optimize -delay 100 camplaceholder.gif'
+} | xargs convert
diff --git a/utilities/gtk/media.c b/utilities/gtk/media.c
index 794976a..4076382 100644
--- a/utilities/gtk/media.c
+++ b/utilities/gtk/media.c
@@ -53,6 +53,8 @@ struct size camsize_scale={.width=320, .height=240};
GtkWidget* cambox;
GtkWidget** camrows=0;
unsigned int camrowcount=0;
+GdkPixbufAnimation* camplaceholder=0;
+GdkPixbufAnimationIter* camplaceholder_iter=0;
#if defined(HAVE_AVRESAMPLE) || defined(HAVE_SWRESAMPLE)
// Experimental mixer, not sure if it really works
@@ -92,6 +94,10 @@ void camera_remove(const char* id)
{
if(!strcmp(cams[i].id, id))
{
+ if(cams[i].placeholder) // Remove the placeholder animation if it has it
+ {
+ g_source_remove(cams[i].placeholder);
+ }
gtk_widget_destroy(cams[i].box);
av_frame_free(&cams[i].frame);
avcodec_free_context(&cams[i].vctx);
@@ -535,3 +541,17 @@ void updatescaling(unsigned int width, unsigned int height, char changedcams)
g_object_unref(old);
}
}
+
+gboolean camplaceholder_update(void* id)
+{
+ struct camera* cam=camera_find(id);
+ GdkPixbuf* oldpixbuf=gtk_image_get_pixbuf(GTK_IMAGE(cam->cam));
+ // Get the current frame of the animation
+ gdk_pixbuf_animation_iter_advance(camplaceholder_iter, 0);
+ GdkPixbuf* frame=gdk_pixbuf_animation_iter_get_pixbuf(camplaceholder_iter);
+ // Scale and replace the current image on camera
+ GdkPixbuf* pixbuf=gdk_pixbuf_scale_simple(frame, camsize_scale.width, camsize_scale.height, GDK_INTERP_BILINEAR);
+ gtk_image_set_from_pixbuf(GTK_IMAGE(cam->cam), pixbuf);
+ g_object_unref(oldpixbuf);
+ return G_SOURCE_CONTINUE;
+}
diff --git a/utilities/gtk/media.h b/utilities/gtk/media.h
index d2bf77d..b61eaaf 100644
--- a/utilities/gtk/media.h
+++ b/utilities/gtk/media.h
@@ -36,6 +36,7 @@ struct camera
char flip_horizontal;
char flip_vertical;
} postproc;
+ unsigned int placeholder;
};
struct size
{
@@ -54,6 +55,8 @@ extern unsigned int camcount;
extern struct size camsize_out;
extern struct size camsize_scale;
extern GtkWidget* cambox;
+extern GdkPixbufAnimation* camplaceholder;
+extern GdkPixbufAnimationIter* camplaceholder_iter;
#if defined(HAVE_AVRESAMPLE) || defined(HAVE_SWRESAMPLE)
extern void camera_playsnd(int audiopipe, struct camera* cam, short* samples, unsigned int samplecount);
@@ -73,3 +76,4 @@ 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 width, unsigned int height);
extern void updatescaling(unsigned int width, unsigned int height, char changedcams);
+extern gboolean camplaceholder_update(void* id);
diff --git a/utilities/gtk/spinnerdot.xcf b/utilities/gtk/spinnerdot.xcf
new file mode 100644
index 0000000..8adf0f9
Binary files /dev/null and b/utilities/gtk/spinnerdot.xcf differ