$ git clone http://tcclient.ion.nu/tc_client.git
commit 15cceb9ae42180524c9f5b8eab96455b75a2f2df
Author: Alicia <...>
Date:   Thu Oct 6 21:55:37 2016 +0200

    tc_client-gtk: fixed resampling of incoming audio.

diff --git a/ChangeLog b/ChangeLog
index e537164..2c3c037 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -16,6 +16,7 @@ tc_client-gtk: when keeping HTTP cookies, use separate cookies per account and o
 tc_client-gtk: stop trying to send video data if our media stream was closed.
 tc_client-gtk: added an option to pick the greenscreen "green" color from the camera.
 tc_client-gtk: added a compatibility macro for versions of gdk-pixbuf lacking gdk_pixbuf_read_pixels()
+tc_client-gtk: fixed resampling of incoming audio.
 tc_client-gtk and camviewer: updated to libavcodec's avcodec_{send,receive}_{frame,packet} API.
 libcamera(v4l2): cache the frame and if there is no data to be read, return the cache instead of blocking.
 0.39:
diff --git a/Makefile b/Makefile
index d4c5f0e..b1fa0c3 100644
--- a/Makefile
+++ b/Makefile
@@ -30,12 +30,12 @@ ifdef SWSCALE_LIBS
   INSTALLDEPS+=tc_client-gtk gtkgui.glade
   ifdef AO_LIBS
     ifdef AVRESAMPLE_LIBS
-      CONFINFO+=|  Will enable experimental mic support
-      CFLAGS+=-DHAVE_AVRESAMPLE=1 $(AVRESAMPLE_CFLAGS) $(AO_CFLAGS)
+      CONFINFO+=|  Will enable (incoming) mic support
+      CFLAGS+=-DHAVE_LIBAO=1 -DHAVE_AVRESAMPLE=1 $(AVRESAMPLE_CFLAGS) $(AO_CFLAGS)
     endif
     ifdef SWRESAMPLE_LIBS
-      CONFINFO+=|  Will enable experimental mic support
-      CFLAGS+=-DHAVE_SWRESAMPLE=1 $(SWRESAMPLE_CFLAGS) $(AO_CFLAGS)
+      CONFINFO+=|  Will enable (incoming) mic support
+      CFLAGS+=-DHAVE_LIBAO=1 -DHAVE_SWRESAMPLE=1 $(SWRESAMPLE_CFLAGS) $(AO_CFLAGS)
     endif
   endif
   ifneq ($(findstring MINGW,$(shell uname -s)),)
diff --git a/configure b/configure
index 416c1c4..2865aea 100755
--- a/configure
+++ b/configure
@@ -3,17 +3,14 @@
 
 host=''
 prefix=''
-enable_mic=false
 while [ "$#" -gt 0 ]; do
   case "$1" in
   --host=*) host="`echo "$1" | sed -e 's/--host=//'`";;
   --prefix=*) prefix="`echo "$1" | sed -e 's/--prefix=//'`";;
-  --enable-mic) enable_mic=true;;
   *)
     echo 'Available options:'
     echo '--host=...    = Specify the host triplet to build for'
     echo '--prefix=...  = Specify the prefix to install to'
-    echo '--enable-mic  = Enable experimental mic support (incoming)'
     exit;;
   esac
   shift
@@ -187,14 +184,14 @@ else
   echo no
 fi
 
-# TODO: Figure out how to mix sound sources so that this doesn't sound horrible with more than one person on mic, having it disabled by default until then
-if "$enable_mic"; then
+haveresample=false
 printf 'Checking for libavresample... '
 avresamplelibs="`pkg-config --libs libavresample 2> /dev/null`"
 if [ "x$avresamplelibs" != "x" ]; then
   echo "AVRESAMPLE_LIBS=${avresamplelibs}" >> config.mk
   echo "AVRESAMPLE_CFLAGS=`pkg-config --cflags libavresample`" >> config.mk
   echo yes
+  haveresample=true
 else
   echo no
   printf 'Checking for libswresample... '
@@ -207,20 +204,22 @@ else
     echo '#define avresample_open swresample_open' >> config.h
     echo '#define avresample_free swresample_free' >> config.h
     echo yes
+    haveresample=true
   else
     echo no
   fi
 fi
 
-printf 'Checking for libao... '
-aolibs="`pkg-config --libs ao 2> /dev/null`"
-if [ "x$aolibs" != "x" ]; then
-  echo "AO_LIBS=${aolibs}" >> config.mk
-  echo "AO_CFLAGS=`pkg-config --cflags ao`" >> config.mk
-  echo yes
-else
-  echo no
-fi
+if "$haveresample"; then
+  printf 'Checking for libao... '
+  aolibs="`pkg-config --libs ao 2> /dev/null`"
+  if [ "x$aolibs" != "x" ]; then
+    echo "AO_LIBS=${aolibs}" >> config.mk
+    echo "AO_CFLAGS=`pkg-config --cflags ao`" >> config.mk
+    echo yes
+  else
+    echo no
+  fi
 fi
 
 printf 'Checking for ncurses... '
diff --git a/utilities/gtk/camviewer.c b/utilities/gtk/camviewer.c
index 8afac83..8bdfaf4 100644
--- a/utilities/gtk/camviewer.c
+++ b/utilities/gtk/camviewer.c
@@ -62,14 +62,7 @@ struct viddata
 {
   AVCodec* vdecoder;
   AVCodec* vencoder;
-  AVCodec* adecoder;
-#ifdef HAVE_AVRESAMPLE
-  int audiopipe;
-  AVAudioResampleContext* resamplectx;
-#elif defined(HAVE_SWRESAMPLE)
   int audiopipe;
-  SwrContext* swrctx;
-#endif
 };
 struct viddata* data;
 
@@ -240,20 +233,24 @@ gboolean handledata(GIOChannel* iochannel, GIOCondition condition, gpointer data
       g_io_channel_read_chars(iochannel, (gchar*)pkt.data+pos, size-pos, &r, 0);
       pos+=r;
     }
-#if defined(HAVE_AVRESAMPLE) || defined(HAVE_SWRESAMPLE)
+#ifdef HAVE_LIBAO
     // 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;}
+    if(!cam->actx && camera_init_audio(cam, frameinfo)){return 1;}
     int gotframe;
     avcodec_send_packet(cam->actx, &pkt);
     gotframe=avcodec_receive_frame(cam->actx, cam->frame);
     if(gotframe){return 1;}
+    unsigned int samplecount=cam->frame->nb_samples*SAMPLERATE_OUT/cam->samplerate;
+    int16_t outbuf[samplecount];
+    void* outdata[]={outbuf, 0};
   #ifdef HAVE_AVRESAMPLE
-    int outlen=avresample_convert(data->resamplectx, cam->frame->data, cam->frame->linesize[0], cam->frame->nb_samples, cam->frame->data, cam->frame->linesize[0], cam->frame->nb_samples);
+    int outlen=avresample_convert(cam->resamplectx, (void*)outdata, samplecount*sizeof(uint8_t), samplecount, cam->frame->data, cam->frame->linesize[0], cam->frame->nb_samples);
   #else
-    int outlen=swr_convert(data->swrctx, cam->frame->data, cam->frame->nb_samples, (const uint8_t**)cam->frame->data, cam->frame->nb_samples);
+    int outlen=swr_convert(cam->swrctx, (void*)outdata, samplecount, (const uint8_t**)cam->frame->data, cam->frame->nb_samples);
   #endif
-    if(outlen>0){camera_playsnd(data->audiopipe, cam, (short*)cam->frame->data[0], outlen);}
+    if(outlen>0){camera_playsnd(data->audiopipe, cam, outbuf, outlen);}
 #endif
     return 1;
   }
@@ -516,11 +513,6 @@ gboolean handledata(GIOChannel* iochannel, GIOCondition condition, gpointer data
     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)
-    cam->actx=avcodec_alloc_context3(data->adecoder);
-    avcodec_open2(cam->actx, data->adecoder, 0);
-    cam->samples=0;
-#endif
     updatescaling(0, 0, 1);
     gtk_widget_show_all(cam->box);
     return 1;
@@ -576,17 +568,18 @@ gboolean handledata(GIOChannel* iochannel, GIOCondition condition, gpointer data
   return 1;
 }
 
-#if defined(HAVE_AVRESAMPLE) || defined(HAVE_SWRESAMPLE)
-void audiothread(int fd)
+#ifdef HAVE_LIBAO
+void* audiothread(void* fdp)
 {
+  int fd=*(int*)fdp;
   ao_initialize();
   ao_sample_format samplefmt;
   samplefmt.bits=16;
-  samplefmt.rate=22050;
+  samplefmt.rate=SAMPLERATE_OUT;
   samplefmt.channels=1;
   samplefmt.byte_format=AO_FMT_NATIVE; // I'm guessing libavcodec decodes it to native
   samplefmt.matrix=0;
-  ao_option clientname={.key="client_name", .value="tc_client/camviewer", .next=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;
@@ -595,6 +588,7 @@ void audiothread(int fd)
     ao_play(dev, buf, len);
   }
   ao_close(dev);
+  return 0;
 }
 #endif
 
@@ -912,7 +906,6 @@ int main(int argc, char** argv)
   data=&datax;
   avcodec_register_all();
   data->vdecoder=avcodec_find_decoder(AV_CODEC_ID_FLV1);
-  data->adecoder=avcodec_find_decoder(AV_CODEC_ID_NELLYMOSER);
 #ifndef _WIN32
   signal(SIGCHLD, SIG_IGN);
 #else
@@ -920,32 +913,11 @@ int main(int argc, char** argv)
 #endif
   config_load();
 
-#if defined(HAVE_AVRESAMPLE) || defined(HAVE_SWRESAMPLE)
-  #ifdef HAVE_AVRESAMPLE
-  data->resamplectx=avresample_alloc_context();
-  av_opt_set_int(data->resamplectx, "in_channel_layout", AV_CH_FRONT_CENTER, 0);
-  av_opt_set_int(data->resamplectx, "in_sample_fmt", AV_SAMPLE_FMT_FLT, 0);
-  // TODO: any way to get the sample rate from the frame/decoder? cam->frame->sample_rate seems to be 0
-  av_opt_set_int(data->resamplectx, "in_sample_rate", 11025, 0);
-  av_opt_set_int(data->resamplectx, "out_channel_layout", AV_CH_FRONT_CENTER, 0);
-  av_opt_set_int(data->resamplectx, "out_sample_fmt", AV_SAMPLE_FMT_S16, 0);
-  av_opt_set_int(data->resamplectx, "out_sample_rate", 22050, 0);
-  avresample_open(data->resamplectx);
-  #else
-  data->swrctx=swr_alloc_set_opts(0, AV_CH_FRONT_CENTER, AV_SAMPLE_FMT_S16, 22050, AV_CH_FRONT_CENTER, AV_SAMPLE_FMT_FLT, 11025, 0, 0);
-  swr_init(data->swrctx);
-  #endif
+#ifdef HAVE_LIBAO
   int audiopipe[2];
   pipe(audiopipe);
   data->audiopipe=audiopipe[1];
-  if(!fork())
-  {
-    prctl(PR_SET_PDEATHSIG, SIGHUP);
-    close(audiopipe[1]);
-    audiothread(audiopipe[0]);
-    _exit(0);
-  }
-  close(audiopipe[0]);
+  g_thread_new("audio", audiothread, audiopipe);
 #endif
 
   gtk_init(&argc, &argv);
@@ -1114,10 +1086,5 @@ int main(int argc, char** argv)
   }
 #endif
   camera_cleanup();
-#ifdef HAVE_AVRESAMPLE
-  avresample_free(&data->resamplectx);
-#elif defined(HAVE_SWRESAMPLE)
-  swr_free(&data->swrctx);
-#endif
   return 0;
 }
diff --git a/utilities/gtk/gui.h b/utilities/gtk/gui.h
index 939e90a..70c82cd 100644
--- a/utilities/gtk/gui.h
+++ b/utilities/gtk/gui.h
@@ -15,6 +15,7 @@
     along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 #include <gtk/gtk.h>
+#include "media.h"
 
 struct channelopts
 {
diff --git a/utilities/gtk/media.c b/utilities/gtk/media.c
index ff95847..aaab747 100644
--- a/utilities/gtk/media.c
+++ b/utilities/gtk/media.c
@@ -87,7 +87,15 @@ void camera_free(struct camera* cam)
   av_frame_free(&cam->frame);
   avcodec_free_context(&cam->vctx);
 #if defined(HAVE_AVRESAMPLE) || defined(HAVE_SWRESAMPLE)
-  avcodec_free_context(&cam->actx);
+  if(cam->actx)
+  {
+    avcodec_free_context(&cam->actx);
+    #ifdef HAVE_AVRESAMPLE
+      avresample_free(&cam->resamplectx);
+    #else
+      swr_free(&cam->swrctx);
+    #endif
+  }
 #endif
   free(cam->id);
   free(cam->nick);
@@ -138,6 +146,7 @@ struct camera* camera_new(const char* nick, const char* id)
   cams=realloc(cams, sizeof(struct camera)*camcount);
   struct camera* cam=&cams[camcount-1];
 #if defined(HAVE_AVRESAMPLE) || defined(HAVE_SWRESAMPLE)
+  cam->actx=0;
   cam->samples=0;
   cam->samplecount=0;
 #endif
@@ -162,6 +171,46 @@ struct camera* camera_new(const char* nick, const char* id)
   return cam;
 }
 
+#ifdef HAVE_LIBAO
+char camera_init_audio(struct camera* cam, uint8_t frameinfo)
+{
+  switch((frameinfo&0xc)/0x4)
+  {
+    case 0: cam->samplerate=5500; break;
+    case 1: cam->samplerate=11025; break;
+    case 2: cam->samplerate=22050; break;
+    case 3: cam->samplerate=44100; break;
+  }
+  AVCodec* decoder=0;
+  switch(frameinfo/0x10)
+  {
+    case 0x4: cam->samplerate=16000; decoder=avcodec_find_decoder(AV_CODEC_ID_NELLYMOSER); break;
+    case 0x5: cam->samplerate=8000;
+    case 0x6: decoder=avcodec_find_decoder(AV_CODEC_ID_NELLYMOSER); break;
+    case 0xb: decoder=avcodec_find_decoder(AV_CODEC_ID_SPEEX); break;
+    default:
+      printf("Unknown audio codec ID 0x%hhx\n", frameinfo/0x10);
+      return 1;
+  }
+  cam->actx=avcodec_alloc_context3(decoder);
+  avcodec_open2(cam->actx, decoder, 0);
+  #ifdef HAVE_AVRESAMPLE
+    cam->resamplectx=avresample_alloc_context();
+    av_opt_set_int(cam->resamplectx, "in_channel_layout", AV_CH_FRONT_CENTER, 0);
+    av_opt_set_int(cam->resamplectx, "in_sample_fmt", AV_SAMPLE_FMT_FLT, 0);
+    av_opt_set_int(cam->resamplectx, "in_sample_rate", cam->samplerate, 0);
+    av_opt_set_int(cam->resamplectx, "out_channel_layout", AV_CH_FRONT_CENTER, 0);
+    av_opt_set_int(cam->resamplectx, "out_sample_fmt", AV_SAMPLE_FMT_S16, 0);
+    av_opt_set_int(cam->resamplectx, "out_sample_rate", SAMPLERATE_OUT, 0);
+    avresample_open(cam->resamplectx);
+  #else
+    cam->swrctx=swr_alloc_set_opts(0, AV_CH_FRONT_CENTER, AV_SAMPLE_FMT_S16, SAMPLERATE_OUT, AV_CH_FRONT_CENTER, AV_SAMPLE_FMT_FLT, cam->samplerate, 0, 0);
+    swr_init(cam->swrctx);
+  #endif
+  return 0;
+}
+#endif
+
 void camera_cleanup(void)
 {
   unsigned int i;
diff --git a/utilities/gtk/media.h b/utilities/gtk/media.h
index 913022f..44d15e3 100644
--- a/utilities/gtk/media.h
+++ b/utilities/gtk/media.h
@@ -14,9 +14,17 @@
     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/>.
 */
+#ifndef MEDIA_H
+#define MEDIA_H
 #include <libavcodec/avcodec.h>
+#ifdef HAVE_AVRESAMPLE
+  #include <libavresample/avresample.h>
+#elif defined(HAVE_SWRESAMPLE)
+  #include <libswresample/swresample.h>
+#endif
 #include "../libcamera/camera.h"
 #include "postproc.h"
+#define SAMPLERATE_OUT 11025 // 11025 is the most common input sample rate, and it is more CPU-efficient to keep it that way than upsampling or downsampling it when converting from flt to s16
 struct camera
 {
   AVFrame* frame;
@@ -32,6 +40,12 @@ struct camera
   GtkWidget* label;
   struct postproc_ctx postproc;
   unsigned int placeholder;
+#ifdef HAVE_AVRESAMPLE
+  AVAudioResampleContext* resamplectx;
+#elif defined(HAVE_SWRESAMPLE)
+  SwrContext* swrctx;
+#endif
+  unsigned int samplerate;
 };
 struct size
 {
@@ -55,6 +69,7 @@ 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);
+extern char camera_init_audio(struct camera* cam, uint8_t frameinfo);
 extern void camera_cleanup(void);
 extern void freebuffer(guchar* pixels, gpointer data);
 extern void startcamout(CAM* cam);
@@ -68,3 +83,4 @@ extern void camera_postproc(struct camera* cam, unsigned char* buf, unsigned int
 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);
+#endif