$ git clone http://tcclient.ion.nu/tc_client.git
commit 38fc8d1990a2c5405f75c16f7be3e1c2b77ff9e9
Author: Alicia <...>
Date:   Wed May 24 14:21:35 2017 +0200

    tc_client-gtk: use pulseaudio for incoming audio if it's available (preferred over libao) and display the volume indicator even if no audio playback library is available.

diff --git a/ChangeLog b/ChangeLog
index e9e0343..ca0a5ce 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -20,6 +20,7 @@ tc_client-gtk: fixed some memory leaks.
 tc_client-gtk: use /quit to guarantee a clean exit.
 tc_client-gtk: fixed segfault at exit by doing camera cleanup earlier.
 tc_client-gtk: made the automatic brightness postprocessing adjustment gradual.
+tc_client-gtk: use pulseaudio for incoming audio if it's available (preferred over libao) and display the volume indicator even if no audio playback library is available.
 irchack: don't rely on connection ID.
 0.41.1:
 Use tinychat.com instead of apl.tinychat.com (works around SSL/TLS issues on windows)
diff --git a/Makefile b/Makefile
index 8e5979f..91131be 100644
--- a/Makefile
+++ b/Makefile
@@ -28,28 +28,34 @@ ifdef SWSCALE_LIBS
   UTILS+=camviewer tc_client-gtk
   CFLAGS+=$(GTK_CFLAGS) $(AVCODEC_CFLAGS) $(AVUTIL_CFLAGS) $(SWSCALE_CFLAGS)
   INSTALLDEPS+=tc_client-gtk gtkgui.glade
-  ifdef AO_LIBS
-    ifdef AVRESAMPLE_LIBS
-      CONFINFO+=|  Will enable incoming mic support
-      CFLAGS+=-DHAVE_LIBAO=1 -DHAVE_AVRESAMPLE=1 $(AVRESAMPLE_CFLAGS) $(AO_CFLAGS)
-    endif
-    ifdef SWRESAMPLE_LIBS
-      CONFINFO+=|  Will enable incoming mic support
-      CFLAGS+=-DHAVE_LIBAO=1 -DHAVE_SWRESAMPLE=1 $(SWRESAMPLE_CFLAGS) $(AO_CFLAGS)
-    endif
-    ifndef AVRESAMPLE_LIBS
-    ifndef SWRESAMPLE_LIBS
-      CONFINFO+=|  Incoming mic support will not be enabled
-    endif
+  ifdef HAVE_PULSE
+    AUDIO_CFLAGS=-DHAVE_PULSEAUDIO=1 $(PULSE_CFLAGS)
+    HAVE_AUDIOLIB=1
+  else
+    ifdef HAVE_AO
+      AUDIO_CFLAGS=-DHAVE_LIBAO=1 $(AO_CFLAGS)
+      HAVE_AUDIOLIB=1
     endif
   endif
+  ifdef AVRESAMPLE_LIBS
+    HAVE_RESAMPLELIB=1
+    CFLAGS+=-DHAVE_AVRESAMPLE=1 $(AVRESAMPLE_CFLAGS) $(AUDIO_CFLAGS)
+  endif
+  ifdef SWRESAMPLE_LIBS
+    HAVE_RESAMPLELIB=1
+    CFLAGS+=-DHAVE_SWRESAMPLE=1 $(SWRESAMPLE_CFLAGS) $(AUDIO_CFLAGS)
+  endif
+  ifeq ($(HAVE_RESAMPLELIB)$(HAVE_AUDIOLIB),11)
+    CONFINFO+=|  Will enable incoming mic support
+  else
+    CONFINFO+=|  Incoming mic support will not be enabled
+  endif
   ifdef AVFORMAT_LIBS
     CFLAGS+=-DHAVE_AVFORMAT=1
     CONFINFO+=|  Will enable integrated youtube videos
   endif
-  ifdef PULSE_LIBS
+  ifeq ($(HAVE_RESAMPLELIB)$(HAVE_PULSE),11)
     CONFINFO+=|  Will enable outgoing mic support
-    CFLAGS+=-DHAVE_PULSEAUDIO=1 $(PULSE_CFLAGS)
   else
     CONFINFO+=|  Outgoing mic support will not be enabled
   endif
diff --git a/configure b/configure
index e47dc2d..8ddf184 100755
--- a/configure
+++ b/configure
@@ -3,6 +3,7 @@
 
 host=''
 prefix=''
+args="$@"
 while [ "$#" -gt 0 ]; do
   case "$1" in
   --host=*) host="`echo "$1" | sed -e 's/--host=//'`";;
@@ -17,6 +18,7 @@ while [ "$#" -gt 0 ]; do
 done
 rm -f config.mk
 true > config.h
+echo "# Generated with: $0 $args" > config.mk
 if [ -n "$prefix" ]; then echo "PREFIX=${prefix}" >> config.mk; fi
 
 printf 'Checking for a C compiler... '
@@ -78,6 +80,7 @@ testpkgconfig()
   if [ "x$testlibs" != "x" ]; then
     echo "${varprefix}_LIBS=${testlibs}" >> config.mk
     echo "${varprefix}_CFLAGS=`pkg-config --cflags "$pkgname"`" >> config.mk
+    echo "HAVE_${varprefix}=1" >> config.mk
     echo yes
     return 0
   else
diff --git a/testbuilds.sh b/testbuilds.sh
index b6d473d..b26d118 100755
--- a/testbuilds.sh
+++ b/testbuilds.sh
@@ -16,6 +16,9 @@ while true; do
   if ! grep -q '^SWSCALE_LIBS' config.mk; then res="libswscale not found, can't test"; break; fi
   if ! grep -q '^GLIB_LIBS' config.mk; then res="glib not found, can't test"; break; fi
   sed -i -e '/^AO_LIBS/d' config.mk
+  sed -i -e '/^PULSE_LIBS/d' config.mk
+  sed -i -e '/^HAVE_AO/d' config.mk
+  sed -i -e '/^HAVE_PULSE/d' config.mk
   sed -i -e '/^AVFORMAT_LIBS/d' config.mk
   echo 'CFLAGS+=-Werror' >> config.mk
   if ! make utils > /dev/null 2> /dev/null; then
diff --git a/utilities/gtk/camviewer.c b/utilities/gtk/camviewer.c
index aa9250b..635b3c4 100644
--- a/utilities/gtk/camviewer.c
+++ b/utilities/gtk/camviewer.c
@@ -38,10 +38,15 @@
 #ifdef HAVE_AVRESAMPLE
   #include <libavutil/opt.h>
   #include <libavresample/avresample.h>
-  #include <ao/ao.h>
 #elif defined(HAVE_SWRESAMPLE)
   #include <libswresample/swresample.h>
-  #include <ao/ao.h>
+#endif
+#if defined(HAVE_AVRESAMPLE) || defined(HAVE_SWRESAMPLE)
+  #ifdef HAVE_PULSEAUDIO
+    #include <pulse/simple.h>
+  #elif defined(HAVE_LIBAO)
+    #include <ao/ao.h>
+  #endif
 #endif
 #include <gtk/gtk.h>
 #include <gdk/gdkkeysyms.h>
@@ -67,7 +72,6 @@ struct viddata
 {
   AVCodec* vdecoder;
   AVCodec* vencoder;
-  int audiopipe;
 };
 struct viddata* data;
 
@@ -209,7 +213,7 @@ gboolean handledata(GIOChannel* iochannel, GIOCondition condition, gpointer x)
       g_io_channel_read_chars(iochannel, (gchar*)pkt.data+pos, size-pos, &r, 0);
       pos+=r;
     }
-#ifdef HAVE_LIBAO
+#if defined(HAVE_AVRESAMPLE) || defined(HAVE_SWRESAMPLE)
     // Find the camera representation for the given ID (for decoder context)
     struct camera* cam=camera_find(&buf[7]);
     if(!cam){printf("No cam found with ID '%s'\n", &buf[7]); return 1;}
@@ -584,10 +588,30 @@ gboolean handledata(GIOChannel* iochannel, GIOCondition condition, gpointer x)
   return 1;
 }
 
-#ifdef HAVE_LIBAO
+#if (defined(HAVE_AVRESAMPLE) || defined(HAVE_SWRESAMPLE)) && (defined(HAVE_PULSEAUDIO) || defined(HAVE_LIBAO))
+extern void justwait(int);
 void* audiothread(void* fdp)
 {
   int fd=*(int*)fdp;
+  char buf[2048];
+  size_t len;
+  #ifdef HAVE_PULSEAUDIO
+  pa_simple* pulse;
+  pa_sample_spec pulsespec;
+  pulsespec.format=PA_SAMPLE_S16NE;
+  pulsespec.channels=1;
+  pulsespec.rate=SAMPLERATE_OUT;
+  signal(SIGCHLD, SIG_DFL); // Briefly revert to the default handler to not break pulseaudio's autospawn feature
+  pulse=pa_simple_new(0, "tc_client-gtk", PA_STREAM_PLAYBACK, 0, channel, &pulsespec, 0, 0, 0);
+  signal(SIGCHLD, justwait);
+  if(!pulse){return 0;}
+  while((len=read(fd, buf, 2048))>0)
+  {
+    if(pa_simple_write(pulse, buf, len, 0)<0){break;}
+  }
+  pa_simple_free(pulse);
+  close(fd);
+  #else
   ao_initialize();
   ao_sample_format samplefmt;
   samplefmt.bits=16;
@@ -597,13 +621,12 @@ void* audiothread(void* fdp)
   samplefmt.matrix=0;
   ao_option clientname={.key="client_name", .value="tc_client-gtk", .next=0};
   ao_device* dev=ao_open_live(ao_default_driver_id(), &samplefmt, &clientname);
-  char buf[2048];
-  size_t len;
   while((len=read(fd, buf, 2048))>0)
   {
     ao_play(dev, buf, len);
   }
   ao_close(dev);
+  #endif
   return 0;
 }
 #endif
@@ -971,6 +994,15 @@ void startsession(GtkButton* button, void* x)
   GIOChannel* tcchannel=g_io_channel_unix_new(tc_client[0]);
   g_io_channel_set_encoding(tcchannel, 0, 0);
   g_io_add_watch(tcchannel, G_IO_IN, handledata, data);
+#if (defined(HAVE_PULSEAUDIO) || defined(HAVE_LIBAO)) && (defined(HAVE_AVRESAMPLE) || defined(HAVE_SWRESAMPLE))
+  static int audiopipe[2]={-1,-1};
+  if(audiopipe[0]==-1)
+  {
+    pipe(audiopipe);
+    g_thread_new("audio", audiothread, audiopipe);
+    g_timeout_add(40, audiomixer, &audiopipe[1]);
+  }
+#endif
 }
 
 void captcha_done(GtkWidget* button, void* x)
@@ -988,7 +1020,7 @@ void justwait(int x){waitpid(-1, 0, WNOHANG);}
 int main(int argc, char** argv)
 {
   if(!strncmp(argv[0], "./", 2)){frombuild=1;}
-  struct viddata datax={0,0,0};
+  struct viddata datax={0,0};
   data=&datax;
   avcodec_register_all();
   data->vdecoder=avcodec_find_decoder(AV_CODEC_ID_FLV1);
@@ -1000,20 +1032,10 @@ int main(int argc, char** argv)
 #endif
   config_load();
 
-#ifdef HAVE_LIBAO
-  int audiopipe[2];
-  pipe(audiopipe);
-  data->audiopipe=audiopipe[1];
-  g_thread_new("audio", audiothread, audiopipe);
-#endif
-
   gtk_init(&argc, &argv);
   gui_init(frombuild);
   campreview.frame=av_frame_alloc();
   campreview.frame->data[0]=0;
-#ifdef HAVE_LIBAO
-  g_timeout_add(40, audiomixer, &audiopipe[1]);
-#endif
   gtk_main();
   camera_cleanup();
   write(tc_client_in[1], "/quit\n", 6);
diff --git a/utilities/gtk/media.c b/utilities/gtk/media.c
index eb9a7fe..d87932b 100644
--- a/utilities/gtk/media.c
+++ b/utilities/gtk/media.c
@@ -64,11 +64,14 @@ unsigned int camout_delay;
 #if defined(HAVE_AVRESAMPLE) || defined(HAVE_SWRESAMPLE)
 void camera_playsnd(struct camera* cam, int16_t* samples, unsigned int samplecount)
 {
+#if defined(HAVE_PULSEAUDIO) || defined(HAVE_LIBAO)
   cam->samples=realloc(cam->samples, sizeof(int16_t)*(cam->samplecount+samplecount));
   memcpy(&cam->samples[cam->samplecount], samples, samplecount*sizeof(short));
   cam->samplecount+=samplecount;
+#endif
 }
 
+#if defined(HAVE_PULSEAUDIO) || defined(HAVE_LIBAO)
 gboolean audiomixer(void* p)
 {
   int audiopipe=*(int*)p;
@@ -103,6 +106,7 @@ gboolean audiomixer(void* p)
   return G_SOURCE_CONTINUE;
 }
 #endif
+#endif
 
 void camera_free(struct camera* cam)
 {
@@ -213,7 +217,7 @@ struct camera* camera_new(const char* nick, const char* id, unsigned char flags)
   return cam;
 }
 
-#ifdef HAVE_LIBAO
+#if defined(HAVE_AVRESAMPLE) || defined(HAVE_SWRESAMPLE)
 char camera_init_audio(struct camera* cam, uint8_t frameinfo)
 {
   switch((frameinfo&0xc)/0x4)
@@ -797,7 +801,7 @@ void camera_decode(struct camera* cam, AVPacket* pkt, unsigned int width, unsign
   if(oldpixbuf){g_object_unref(oldpixbuf);}
 }
 
-#ifdef HAVE_LIBAO
+#if defined(HAVE_AVRESAMPLE) || defined(HAVE_SWRESAMPLE)
 void mic_decode(struct camera* cam, AVPacket* pkt)
 {
   int gotframe;
diff --git a/utilities/gtk/media.h b/utilities/gtk/media.h
index b7e45a7..cea12d7 100644
--- a/utilities/gtk/media.h
+++ b/utilities/gtk/media.h
@@ -78,7 +78,9 @@ extern void camera_remove(const char* id, char isnick);
 extern struct camera* camera_find(const char* id);
 extern struct camera* camera_findbynick(const char* nick);
 extern struct camera* camera_new(const char* nick, const char* id, unsigned char flags);
+#if defined(HAVE_AVRESAMPLE) || defined(HAVE_SWRESAMPLE)
 extern char camera_init_audio(struct camera* cam, uint8_t frameinfo);
+#endif
 extern void camera_cleanup(void);
 extern void freebuffer(guchar* pixels, gpointer data);
 extern void startcamout(CAM* cam);
@@ -97,7 +99,7 @@ extern gboolean mic_encode(GIOChannel* iochannel, GIOCondition condition, gpoint
 extern void camera_calcvolume(struct camera* cam, float* samples, unsigned int samplecount);
 extern void volume_indicator(GdkPixbuf* frame, struct camera* cam);
 extern void camera_decode(struct camera* cam, AVPacket* pkt, unsigned int width, unsigned int height);
-#ifdef HAVE_LIBAO
+#if defined(HAVE_AVRESAMPLE) || defined(HAVE_SWRESAMPLE)
 extern void mic_decode(struct camera* cam, AVPacket* pkt);
 #endif
 #endif