$ 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