summaryrefslogtreecommitdiff
path: root/src/audio.c
diff options
context:
space:
mode:
authorBenjamin Franzke <benjaminfranzke@googlemail.com>2013-11-21 22:08:21 +0100
committerBenjamin Franzke <benjaminfranzke@googlemail.com>2013-12-04 12:15:32 +0100
commitcd7e49ac9c02c63b43a2891f4f0178b9e4ed6694 (patch)
treebfa16435aeb3b4a84ecd6b16e9b6310f7eacb578 /src/audio.c
parentf69a59ca61e1bc8dbbd14e67b48ac0fd35b93be3 (diff)
downloadcmumble-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.c127
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;
}