diff options
author | Benjamin Franzke <benjaminfranzke@googlemail.com> | 2013-11-21 22:08:21 +0100 |
---|---|---|
committer | Benjamin Franzke <benjaminfranzke@googlemail.com> | 2013-12-04 12:15:32 +0100 |
commit | cd7e49ac9c02c63b43a2891f4f0178b9e4ed6694 (patch) | |
tree | bfa16435aeb3b4a84ecd6b16e9b6310f7eacb578 /src/audio.c | |
parent | f69a59ca61e1bc8dbbd14e67b48ac0fd35b93be3 (diff) | |
download | cmumble-cd7e49ac9c02c63b43a2891f4f0178b9e4ed6694.tar.gz cmumble-cd7e49ac9c02c63b43a2891f4f0178b9e4ed6694.tar.bz2 cmumble-cd7e49ac9c02c63b43a2891f4f0178b9e4ed6694.zip |
Port to gstreamer 1.0 API
Includes fixes:
- use a callback, not a signal for appsink's new_buffer
Diffstat (limited to 'src/audio.c')
-rw-r--r-- | src/audio.c | 127 |
1 files changed, 85 insertions, 42 deletions
diff --git a/src/audio.c b/src/audio.c index 146fbf3..b1d46b3 100644 --- a/src/audio.c +++ b/src/audio.c @@ -3,8 +3,15 @@ #include "cmumble.h" #include <string.h> +#include <gst/tag/tag.h> + #define SAMPLERATE 48000 +#define FRAMESIZE 480 /* SAMPLERATE/100 */ #define CHANNELS 1 +#define CELT_CAPS "audio/x-celt,channels=" G_STRINGIFY(CHANNELS) "," \ + "rate=" G_STRINGIFY(SAMPLERATE) ",frame-size=" G_STRINGIFY(FRAMESIZE) +#define AUDIO_CAPS "audio/x-raw,format=S16LE,channels=" \ + G_STRINGIFY(CHANNELS) ",rate=" G_STRINGIFY(SAMPLERATE) void cmumble_audio_push(struct cmumble *cm, struct cmumble_user *user, @@ -32,14 +39,15 @@ cmumble_audio_push(struct cmumble *cm, struct cmumble_user *user, * - enqueue as now? */ + gstbuf = gst_buffer_new_wrapped(g_memdup(data, size), size); + /* Asume packets are in order, since we're using tcp tunnel only atm. * FIXME: This assumption is probably wrong, since the packets may have * been received out of order at the server? */ - if (sequence < 0) { - time = 0; - } else if (user->last_sequence < 0 || sequence == 0 || + if (user->last_sequence < 0 || sequence == 0 || sequence < (user->last_sequence + 1)) { + GST_BUFFER_FLAG_SET(gstbuf, GST_BUFFER_FLAG_RESYNC); time = now - base; if (cm->verbose) g_print("%s: set time to now\n", __func__); @@ -59,15 +67,15 @@ cmumble_audio_push(struct cmumble *cm, struct cmumble_user *user, } if (time < (now - base)) { + GST_BUFFER_FLAG_SET(gstbuf, GST_BUFFER_FLAG_RESYNC); time = now - base; if (cm->verbose) g_print("%s: time is in the past, setting to now\n", __func__); } - gstbuf = gst_app_buffer_new(g_memdup(data, size), size, g_free, NULL); - - GST_BUFFER_TIMESTAMP(gstbuf) = time; + GST_BUFFER_DTS(gstbuf) = now - base; + GST_BUFFER_PTS(gstbuf) = time; GST_BUFFER_DURATION(gstbuf) = gst_util_uint64_scale_int (1, GST_SECOND, 100); user->last_time_end = time + GST_BUFFER_DURATION(gstbuf); @@ -80,11 +88,11 @@ static GstFlowReturn pull_buffer(GstAppSink *sink, gpointer user_data) { struct cmumble *cm = user_data; + GstSample *sample; GstBuffer *buf; uint8_t data[1024]; uint32_t write = 0, pos = 0; mumble_udptunnel_t tunnel; - static int seq = 0; /* FIXME: Make this more generic/disable pulling * the pipeline completely if not connected? @@ -92,14 +100,20 @@ pull_buffer(GstAppSink *sink, gpointer user_data) if (cm->con.conn == NULL) return GST_FLOW_OK; - buf = gst_app_sink_pull_buffer(cm->audio.sink); + sample = gst_app_sink_pull_sample(cm->audio.sink); + if (sample == NULL) + return GST_FLOW_ERROR; + + buf = gst_sample_get_buffer(sample); - if (++seq <= 2) { - gst_buffer_unref(buf); + if (GST_BUFFER_FLAG_IS_SET(buf, GST_BUFFER_FLAG_HEADER)) { + gst_sample_unref(sample); return GST_FLOW_OK; } - if (GST_BUFFER_SIZE(buf) > 127) { - g_printerr("GOT TOO BIG BUFFER\n"); + + if (gst_buffer_get_size(buf) > 127) { + g_printerr("error: unexpected buffer size\n"); + gst_sample_unref(sample); return GST_FLOW_ERROR; } @@ -108,11 +122,11 @@ pull_buffer(GstAppSink *sink, gpointer user_data) encode_varint(&data[pos], &write, ++cm->sequence, sizeof(data)-pos); pos += write; - data[pos++] = 0x00 /*: 0x80 */ | (GST_BUFFER_SIZE(buf) & 0x7F); - memcpy(&data[pos], GST_BUFFER_DATA(buf), GST_BUFFER_SIZE(buf)); - pos += GST_BUFFER_SIZE(buf); + data[pos++] = 0x00 /*: 0x80 */ | (gst_buffer_get_size(buf) & 0x7F); + gst_buffer_extract(buf, 0, &data[pos], gst_buffer_get_size(buf)); + pos += gst_buffer_get_size(buf); - gst_buffer_unref(buf); + gst_sample_unref(sample); cmumble_init_udptunnel(&tunnel); tunnel.packet.data = data; @@ -135,7 +149,7 @@ idle(gpointer user_data) } static GstFlowReturn -new_buffer(GstAppSink *sink, gpointer user_data) +new_sample(GstAppSink *sink, gpointer user_data) { struct cmumble *cm = user_data; @@ -145,16 +159,18 @@ new_buffer(GstAppSink *sink, gpointer user_data) return GST_FLOW_OK; } +GstAppSinkCallbacks sink_cbs = { + .new_sample = new_sample +}; + static int setup_recording_gst_pipeline(struct cmumble *cm) { GstElement *pipeline, *cutter, *sink; GError *error = NULL; - GstCaps *caps; - - char *desc = "autoaudiosrc ! cutter name=cutter ! audioresample ! audioconvert ! " - "audio/x-raw-int,channels=1,depth=16,rate=48000,signed=TRUE,width=16 ! " - "celtenc ! appsink name=sink"; + char *desc = "autoaudiosrc name=src ! cutter name=cutter ! " + "audioresample ! audioconvert ! "AUDIO_CAPS" ! " + "celtenc ! appsink name=sink caps="CELT_CAPS; pipeline = gst_parse_launch(desc, &error); if (error) { @@ -170,17 +186,8 @@ setup_recording_gst_pipeline(struct cmumble *cm) g_object_set(G_OBJECT(cutter), "threshold_dB", -45.0, "leaky", TRUE, NULL); - gst_app_sink_set_emit_signals(cm->audio.sink, TRUE); + gst_app_sink_set_callbacks(cm->audio.sink, &sink_cbs, cm, NULL); gst_app_sink_set_drop(cm->audio.sink, FALSE);; - g_signal_connect(sink, "new-buffer", G_CALLBACK(new_buffer), cm); - - caps = gst_caps_new_simple("audio/x-celt", - "rate", G_TYPE_INT, SAMPLERATE, - "channels", G_TYPE_INT, 1, - "frame-size", G_TYPE_INT, SAMPLERATE/100, - NULL); - gst_app_sink_set_caps(cm->audio.sink, caps); - gst_caps_unref(caps); gst_element_set_state(pipeline, GST_STATE_PLAYING); @@ -190,9 +197,9 @@ setup_recording_gst_pipeline(struct cmumble *cm) } static void -set_pulse_states(gpointer data, gpointer user_data) +set_pulse_states(const GValue *item, gpointer user_data) { - GstElement *elm = data; + GstElement *elm = g_value_get_object(item); struct cmumble_user *user = user_data; GstStructure *props; gchar *name; @@ -224,13 +231,54 @@ out: g_object_unref(G_OBJECT(elm)); } +static void +add_celt_streamheader(struct cmumble *cm, GstAppSrc *src) +{ + GValue streamheader = { 0, }, val = { 0, }; + GstTagList *tags; + GstBuffer *buf[2]; + GstStructure *s; + GstCaps *caps; + gint i; + + buf[0] = gst_buffer_new_allocate(NULL, sizeof(CELTHeader), NULL); + gst_buffer_fill(buf[0], 0, cm->audio.celt_header_packet, + sizeof(CELTHeader)); + + tags = gst_tag_list_new_empty(); + buf[1] = gst_tag_list_to_vorbiscomment_buffer(tags, NULL, 0, "mumble"); + gst_tag_list_unref(tags); + + g_value_init(&streamheader, GST_TYPE_ARRAY); + for (i = 0; i < G_N_ELEMENTS(buf); ++i) { + GST_BUFFER_FLAG_SET(buf[i], GST_BUFFER_FLAG_HEADER); + GST_BUFFER_OFFSET(buf[i]) = 0; + GST_BUFFER_OFFSET_END(buf[i]) = 0; + g_value_init(&val, GST_TYPE_BUFFER); + gst_value_take_buffer(&val, buf[i]); + gst_value_array_append_value(&streamheader, &val); + g_value_unset(&val); + } + + caps = gst_app_src_get_caps(src); + caps = gst_caps_make_writable(caps); + s = gst_caps_get_structure(caps, 0); + gst_structure_set_value(s, "streamheader", &streamheader); + g_value_unset(&streamheader); + + gst_app_src_set_caps(src, caps); + gst_caps_unref(caps); +} + int cmumble_audio_create_playback_pipeline(struct cmumble *cm, struct cmumble_user *user) { GstElement *pipeline, *sink_bin; GError *error = NULL; - char *desc = "appsrc name=src ! celtdec ! audioconvert ! autoaudiosink name=sink"; + char *desc = "appsrc name=src format=GST_FORMAT_TIME caps="CELT_CAPS" " + "! celtdec name=dec " + "! audioresample ! audioconvert ! autoaudiosink name=sink"; GstIterator *it; pipeline = gst_parse_launch(desc, &error); @@ -241,7 +289,7 @@ cmumble_audio_create_playback_pipeline(struct cmumble *cm, user->pipeline = pipeline; user->src = GST_APP_SRC(gst_bin_get_by_name(GST_BIN(pipeline), "src")); - gst_base_src_set_format(GST_BASE_SRC(user->src), GST_FORMAT_TIME); + add_celt_streamheader(cm, user->src); gst_element_set_state(pipeline, GST_STATE_PLAYING); @@ -252,12 +300,7 @@ cmumble_audio_create_playback_pipeline(struct cmumble *cm, gst_iterator_foreach(it, set_pulse_states, user); gst_iterator_free(it); - user->last_sequence = -2; - /* Setup Celt Decoder */ - cmumble_audio_push(cm, user, - cm->audio.celt_header_packet, sizeof(CELTHeader), -2); - /* fake vorbiscomment buffer */ - cmumble_audio_push(cm, user, NULL, 0, -2); + user->last_sequence = -1; return 0; } |