From e810950547b4c55abdec977a002db522ccab0ad1 Mon Sep 17 00:00:00 2001 From: Benjamin Franzke Date: Fri, 24 Jan 2014 13:37:22 +0100 Subject: WIP opus --- configure.ac | 2 +- src/audio.c | 22 +++++++++++++++--- src/cmumble.c | 11 +++++++++ src/cmumble.h | 1 + src/util.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/util.h | 5 ++++ 6 files changed, 110 insertions(+), 4 deletions(-) diff --git a/configure.ac b/configure.ac index b65d62e..7b82130 100644 --- a/configure.ac +++ b/configure.ac @@ -41,7 +41,7 @@ PKG_CHECK_MODULES(GIO, [gio-2.0]) PKG_CHECK_MODULES(GSTREAMER, [gstreamer-1.0 gstreamer-app-1.0 gstreamer-tag-1.0]) PKG_CHECK_MODULES(CELT, [celt]) -GST_ELEMENTS="appsrc appsink celtdec celtenc capsfilter +GST_ELEMENTS="appsrc appsink celtdec celtenc opusdec opusenc capsfilter audioconvert audioresample autoaudiosrc autoaudiosink" for element in $GST_ELEMENTS diff --git a/src/audio.c b/src/audio.c index 37e746b..b1bba35 100644 --- a/src/audio.c +++ b/src/audio.c @@ -11,6 +11,7 @@ #define BUFFER_TIME (gst_util_uint64_scale_int(1, GST_SECOND, 100)) #define CELT_CAPS "audio/x-celt,channels=" G_STRINGIFY(CHANNELS) "," \ "rate=" G_STRINGIFY(SAMPLERATE) ",frame-size=" G_STRINGIFY(FRAMESIZE) +#define OPUS_CAPS "audio/x-opus" #define AUDIO_CAPS "audio/x-raw,format=S16LE,channels=" \ G_STRINGIFY(CHANNELS) ",rate=" G_STRINGIFY(SAMPLERATE) @@ -243,7 +244,7 @@ pull_buffer(GstAppSink *sink, gpointer user_data) GstSample *sample; GstBuffer *buf; GstClockTime *silence; - + GstCaps *caps; sample = gst_app_sink_pull_sample(cm->audio.sink); if (sample == NULL) @@ -264,6 +265,15 @@ pull_buffer(GstAppSink *sink, gpointer user_data) return GST_FLOW_OK; } + caps = gst_sample_get_caps(sample); + /* FIXME: Find a better method to match the caps */ + char *caps_s = gst_caps_to_string(caps); + if (strcmp(caps_s, "audio/x-opus") == 0) { + pull_opus_buffer(sink, cm); + } else if (strncmp(caps_s, "audio/x-celt", strlen("audio/x-celt")) == 0) { + pull_celt_buffer(sink, cm); + } + if (gst_buffer_get_size(buf) > 127) { g_printerr("error: unexpected buffer size\n"); gst_sample_unref(sample); @@ -386,8 +396,14 @@ setup_recording_gst_pipeline(struct cmumble *cm) GstBus *bus; char *desc = "autoaudiosrc name=src ! cutter name=cutter ! " "audioresample ! audioconvert ! "AUDIO_CAPS" ! " - "celtenc name=enc " /*perfect-timestamp=true hard-resync=true" */" ! " - "appsink name=sink caps="CELT_CAPS; + "output-selector name=os ! " + "os.src0 " + "celtenc name=celt-enc " /*perfect-timestamp=true hard-resync=true" */" ! " + CELT_CAPS" ! " + "os.src1 " + "opusenc name=opus-enc ! " OPUS_CAPS " ! " + "funnel ! " + "appsink name=sink" pipeline = gst_parse_launch(desc, &error); if (error) { diff --git a/src/cmumble.c b/src/cmumble.c index 8267834..0350b97 100644 --- a/src/cmumble.c +++ b/src/cmumble.c @@ -145,6 +145,12 @@ recv_codec_version(mumble_codec_version_t *codec, struct cmumble *cm) "Opus: %d\n", codec->alpha, codec->beta, codec->prefer_alpha, codec->opus); + + if (codec->has_opus && !!codec->opus != !!cm->use_opus) { + cm->use_opus = !!codec->opus; + cmumble_audio_fini(cm); + cmumble_audio_init(cm); + } } static void @@ -302,6 +308,10 @@ cmumble_protocol_init(struct cmumble *cm) authenticate.password = ""; authenticate.n_celt_versions = 1; authenticate.celt_versions = &cm->audio.celt_bitstream_version; +#if 0 + authenticate.opus = TRUE; + authenticate.has_opus = TRUE; +#endif cmumble_send_authenticate(cm, &authenticate); source = g_timeout_source_new_seconds(5); @@ -354,6 +364,7 @@ int main(int argc, char **argv) cm.user_name = user ? user : g_get_user_name(); cm.users = NULL; + cm.use_opus = FALSE; cm.verbose = verbose; g_type_init(); diff --git a/src/cmumble.h b/src/cmumble.h index 3997c8a..6dd16b3 100644 --- a/src/cmumble.h +++ b/src/cmumble.h @@ -37,6 +37,7 @@ struct cmumble { struct cmumble_user *user; gboolean verbose; + gboolean use_opus; }; struct cmumble_user { diff --git a/src/util.c b/src/util.c index 1f6fd41..186251e 100644 --- a/src/util.c +++ b/src/util.c @@ -4,6 +4,8 @@ #include #include +#include + gpointer cmumble_find_by_id(GList *list, gsize member_offset, guint id) { @@ -62,3 +64,74 @@ out: fclose(f); return os; } + +/* This code is taken from gst-plugins-bad/ext/opus/gstopusheader.c */ +GstBuffer * +_gst_opus_enc_create_id_buffer(gint nchannels, gint n_stereo_streams, + gint sample_rate, guint8 channel_mapping_family, + const guint8 *channel_mapping) +{ + GstBuffer *buffer; + GstByteWriter bw; + gboolean hdl = TRUE; + + g_return_val_if_fail (nchannels > 0 && nchannels < 256, NULL); + g_return_val_if_fail (n_stereo_streams >= 0, NULL); + g_return_val_if_fail (n_stereo_streams <= nchannels - n_stereo_streams, NULL); + + gst_byte_writer_init (&bw); + + /* See http://wiki.xiph.org/OggOpus */ + hdl &= gst_byte_writer_put_data (&bw, (const guint8 *) "OpusHead", 8); + hdl &= gst_byte_writer_put_uint8 (&bw, 0x01); /* version number */ + hdl &= gst_byte_writer_put_uint8 (&bw, nchannels); + hdl &= gst_byte_writer_put_uint16_le (&bw, 0); /* pre-skip */ + hdl &= gst_byte_writer_put_uint32_le (&bw, sample_rate); + hdl &= gst_byte_writer_put_uint16_le (&bw, 0); /* output gain */ + hdl &= gst_byte_writer_put_uint8 (&bw, channel_mapping_family); + if (channel_mapping_family > 0) { + hdl &= gst_byte_writer_put_uint8 (&bw, nchannels - n_stereo_streams); + hdl &= gst_byte_writer_put_uint8 (&bw, n_stereo_streams); + hdl &= gst_byte_writer_put_data (&bw, channel_mapping, nchannels); + } + + if (!hdl) + GST_WARNING ("Error creating header"); + + buffer = gst_byte_writer_reset_and_get_buffer (&bw); + + GST_BUFFER_OFFSET (buffer) = 0; + GST_BUFFER_OFFSET_END (buffer) = 0; + + return buffer; +} + +static int +create_gst_buffer_array (GValue *array, GstBuffer * buf, ...) +{ + va_list va; + GValue value = { 0 }; + + g_value_init (array, GST_TYPE_ARRAY); + + va_start (va, buf); + /* put buffers in a fixed list */ + while (buf) { + g_assert (gst_buffer_is_writable (buf)); + + /* mark buffer */ + GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_HEADER); + + g_value_init (&value, GST_TYPE_BUFFER); + buf = gst_buffer_copy (buf); + GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_HEADER); + gst_value_set_buffer (&value, buf); + gst_buffer_unref (buf); + gst_value_array_append_value (&array, &value); + g_value_unset (&value); + + buf = va_arg (va, GstBuffer *); + } + + return 0; + diff --git a/src/util.h b/src/util.h index e2d58c6..afbf555 100644 --- a/src/util.h +++ b/src/util.h @@ -26,4 +26,9 @@ find_channel(struct cmumble *cm, guint channel_id) channel_id); } +GstBuffer * +_gst_opus_enc_create_id_buffer(gint nchannels, gint n_stereo_streams, + gint sample_rate, guint8 channel_mapping_family, + const guint8 * channel_mapping); + #endif /* _UTIL_H_ */ -- cgit