From c38f3dacba15dc4176752ea8e13561a4d1abe41f Mon Sep 17 00:00:00 2001 From: Benjamin Franzke Date: Sat, 7 May 2016 06:20:45 +0200 Subject: WIP --- configure.ac | 10 +---- src/Makefile.am | 4 +- src/audio.c | 113 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- src/audio.h | 10 ++--- src/cmumble.h | 3 +- src/io.c | 3 ++ 6 files changed, 124 insertions(+), 19 deletions(-) diff --git a/configure.ac b/configure.ac index 864792c..4bc248d 100644 --- a/configure.ac +++ b/configure.ac @@ -48,15 +48,7 @@ if test "x$have_celt" = xno; then AC_DEFINE([HAVE_CELT071], [1], [Defined if we're using celt071]) fi - -GST_ELEMENTS="appsrc appsink celtdec celtenc capsfilter - audioconvert audioresample autoaudiosrc autoaudiosink" - -for element in $GST_ELEMENTS -do - AM_GST_ELEMENT_CHECK([$element], [], - AC_MSG_ERROR([gstreamer element $element not found])) -done +PKG_CHECK_MODULES(SPEEX, [speex speexdsp]) if test "x$GCC" = "xyes"; then GCC_CFLAGS="-Wall -g -Wstrict-prototypes -Wmissing-prototypes -fvisibility=hidden" diff --git a/src/Makefile.am b/src/Makefile.am index 18979d0..948c7bf 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -9,9 +9,9 @@ cmumble_SOURCES = cmumble.c message.c varint.c io.c \ nodist_cmumble_SOURCES = mumble.pb-c.c cmumble_LDADD = $(PROTOBUF_LIBS) $(GLIB_LIBS) $(GIO_LIBS) \ - $(GSTREAMER_LIBS) $(CELT_LIBS) + $(GSTREAMER_LIBS) $(CELT_LIBS) $(SPEEX_LIBS) AM_CPPFLAGS = $(PROTOBUF_CFLAGS) $(GLIB_CFLAGS) $(GIO_CFLAGS) \ - $(GSTREAMER_CFLAGS) $(CELT_CFLAGS) + $(GSTREAMER_CFLAGS) $(CELT_CFLAGS) $(SPEEX_LIBS) AM_CFLAGS = $(GCC_CFLAGS) diff --git a/src/audio.c b/src/audio.c index cf18c78..0b2b17e 100644 --- a/src/audio.c +++ b/src/audio.c @@ -3,6 +3,8 @@ #include "cmumble.h" #include +#include + #define SAMPLERATE 48000 #define CHANNELS 1 @@ -85,6 +87,12 @@ new_buffer(GstAppSink *sink, gpointer user_data) return GST_FLOW_OK; } +/* TODO pulseaudio with echo cancellation/webrtc audio processing? + * + * https://www.freedesktop.org/software/pulseaudio/webrtc-audio-processing/ + * + */ + static int setup_recording_gst_pipeline(struct cmumble *cm) { @@ -163,10 +171,114 @@ out: g_object_unref(G_OBJECT(elm)); } + +/* This is called whenever the context status changes */ +static void context_state_callback(pa_context *c, void *userdata) { + struct cmumble_user *user = userdata; + assert(c); + + switch (pa_context_get_state(c)) { + case PA_CONTEXT_CONNECTING: + case PA_CONTEXT_AUTHORIZING: + case PA_CONTEXT_SETTING_NAME: + break; + + case PA_CONTEXT_READY: { + int r; + pa_stream *stream = NULL; + + static const pa_sample_spec ss = { + .format = PA_SAMPLE_S16LE, + .rate = SAMPLERATE, + .channels = CHANNELS + }; + + + gchar *name; + stream_name = g_strdup_printf("cmumble [%s]", user->name); + /* TODO: use pa_stream_new_with_proplist and set filter.want=echo-cancel */ + if (!(stream = pa_stream_new(c, stream_name, &ss, NULL))) { + fprintf(stderr, "pa_stream_new() failed: %s\n", pa_strerror(pa_context_errno(c))); + g_free(name); + goto fail; + } + g_free(stream_name); + user->stream = stream; + + pa_stream_set_state_callback(stream, stream_state_callback, NULL); + pa_stream_set_write_callback(stream, stream_write_callback, NULL); + pa_stream_set_read_callback(stream, stream_read_callback, NULL); + pa_stream_set_suspended_callback(stream, stream_suspended_callback, NULL); + pa_stream_set_moved_callback(stream, stream_moved_callback, NULL); + pa_stream_set_underflow_callback(stream, stream_underflow_callback, NULL); + pa_stream_set_overflow_callback(stream, stream_overflow_callback, NULL); + pa_stream_set_started_callback(stream, stream_started_callback, NULL); + pa_stream_set_event_callback(stream, stream_event_callback, NULL); + pa_stream_set_buffer_attr_callback(stream, stream_buffer_attr_callback, NULL); + + if ((r = pa_stream_connect_playback(stream, NULL, NULL, 0, NULL, NULL)) < 0) { + g_printerr("pa_stream_connect_playback() failed: %s\n", pa_strerror(pa_context_errno(c))); + goto fail; + } + + break; + } + + case PA_CONTEXT_TERMINATED: + //quit(0); + //TODO: destroy user pipeline + break; + + case PA_CONTEXT_FAILED: + default: + fprintf(stderr, _("Connection failure: %s\n"), pa_strerror(pa_context_errno(c))); + goto fail; + } + + return; + +fail: + quit(1); + +} + int cmumble_audio_create_playback_pipeline(struct cmumble *cm, struct cmumble_user *user) { + pa_mainloop_api *mainloop_api = NULL; + pa_glib_mainloop *m = NULL; + int r; + + if (!(m = pa_glib_mainloop_new(NULL))) { + g_printerr("pa_glib_mainloop_new failed\n"); + return -1; + } + cm->mainloop_api = pa_glib_mainloop_get_api(m); + + if (!(user->context = pa_context_new(mainloop_api, "cmumble"))) { + return -1; + /* + interface_set_status(&ctx.interface, + "pa_context_new failed: %s\n", + pa_strerror(pa_context_errno(ctx.context))); + */ + } + + // define callback for connection init + pa_context_set_state_callback(user->context, + context_state_callback, &user); + if (pa_context_connect(user->context, NULL, + PA_CONTEXT_NOAUTOSPAWN, NULL)) { + return -1; + /* + interface_set_status(&ctx.interface, + "pa_context_connect failed: %s\n", + pa_strerror(pa_context_errno(ctx.context))); + */ + } + + GstElement *pipeline, *sink_bin; GError *error = NULL; char *desc = "appsrc name=src ! celtdec ! audioconvert ! autoaudiosink name=sink"; @@ -211,7 +323,6 @@ setup_playback_gst_pipeline(struct cmumble *cm) { cm->audio.celt_mode = celt_mode_create(SAMPLERATE, SAMPLERATE / 100, NULL); - #ifdef HAVE_CELT_071 celt_header_init(&cm->audio.celt_header, cm->audio.celt_mode, CHANNELS); #else diff --git a/src/audio.h b/src/audio.h index 0f7aaa2..2671647 100644 --- a/src/audio.h +++ b/src/audio.h @@ -3,10 +3,8 @@ #include -#include -#include -#include -#include +#include +#include #ifdef HAVE_CELT071 #include @@ -17,8 +15,8 @@ #endif struct cmumble_audio { - GstElement *record_pipeline; - GstAppSink *sink; + //GstElement *record_pipeline; + //GstAppSink *sink; guint8 celt_header_packet[sizeof(CELTHeader)]; CELTHeader celt_header; diff --git a/src/cmumble.h b/src/cmumble.h index b796d50..9036bdc 100644 --- a/src/cmumble.h +++ b/src/cmumble.h @@ -63,7 +63,8 @@ enum udp_message_type { udp_voice_celt_alpha, udp_ping, udp_voice_speex, - udp_voice_celt_beta + udp_voice_celt_beta, + udp_voice_opus }; enum udp_message_target { diff --git a/src/io.c b/src/io.c index d722f16..fc41893 100644 --- a/src/io.c +++ b/src/io.c @@ -201,6 +201,9 @@ cmumble_io_init(struct cmumble *cm) return -1; } + /* TODO: Maybe add comments why tcsetattr is needed? + (as in readline/examples/excallback.c) + Rename io.term to io.term_backup? */ cm->io.term = term; term.c_lflag &= ~ICANON; term.c_cc[VTIME] = 1; -- cgit