diff options
-rw-r--r-- | src/cmumble.c | 148 |
1 files changed, 120 insertions, 28 deletions
diff --git a/src/cmumble.c b/src/cmumble.c index 342c63c..92c399f 100644 --- a/src/cmumble.c +++ b/src/cmumble.c @@ -38,15 +38,25 @@ struct context { GSocket *sock; GIOStream *iostream; + uint8_t celt_header_packet[sizeof(CELTHeader)]; CELTHeader celt_header; CELTMode *celt_mode; - GstElement *playback_pipeline; GstElement *record_pipeline; - GstAppSrc *src; GstAppSink *sink; int64_t sequence; + + GList *users; +}; + +struct user { + uint32_t session; + char *name; + uint32_t user_id; + + GstElement *pipeline; + GstAppSrc *src; }; enum udp_message_type { @@ -56,6 +66,23 @@ enum udp_message_type { udp_voice_celt_beta }; +static struct user * +find_user(struct context *ctx, uint32_t session) +{ + struct user *user = NULL, *tmp; + GList *l; + + for (l = ctx->users; l; l = l->next) { + tmp = l->data; + if (tmp->session == session) { + user = tmp; + break; + } + } + + return user; +} + static void appsrc_push(GstAppSrc *src, const void *mem, size_t size) { @@ -147,12 +174,19 @@ handle_udp(struct context *ctx, uint8_t *data, uint32_t len) uint32_t pos = 1; uint32_t read = 0; uint8_t frame_len, terminator; + struct user *user = NULL; session = decode_varint(&data[pos], &read, len-pos); pos += read; sequence = decode_varint(&data[pos], &read, len-pos); pos += read; + user = find_user(ctx, session); + if (user == NULL) { + g_printerr("received audio packet from unknown user, dropping.\n"); + return; + } + do { frame_len = data[pos] & 0x7F; terminator = data[pos] & 0x80; @@ -161,7 +195,7 @@ handle_udp(struct context *ctx, uint8_t *data, uint32_t len) if (frame_len == 0 || frame_len > len-pos) break; - appsrc_push(ctx->src, &data[pos], frame_len); + appsrc_push(user->src, &data[pos], frame_len); pos += frame_len; sequence++; @@ -226,12 +260,56 @@ recv_codec_version(MumbleProto__CodecVersion *codec, struct context *ctx) codec->alpha, codec->beta, codec->prefer_alpha); } + +static void +recv_user_remove(MumbleProto__UserRemove *remove, struct context *ctx) +{ + struct user *user = NULL; + + if ((user = find_user(ctx, remove->session))) { + ctx->users = g_list_remove(ctx->users, user); + g_free(user->name); + /* FIXME: destroy playback pipeline */ + g_free(user); + } +} + +static int +user_create_playback_pipeline(struct context *ctx, struct user *user); + +static void +recv_user_state(MumbleProto__UserState *state, struct context *ctx) +{ + struct user *user = NULL; + + if ((user = find_user(ctx, state->session))) { + /* update */ + return; + } + + user = g_new0(struct user, 1); + if (user == NULL) { + g_printerr("Out of memory.\n"); + exit(1); + } + + user->session = state->session; + user->name = g_strdup(state->name); + user->user_id = state->user_id; + + user_create_playback_pipeline(ctx, user); + g_print("receive user: %s\n", user->name); + ctx->users = g_list_prepend(ctx->users, user); +} + typedef void (*callback_t)(void *, void *); static const callback_t callbacks[] = { /* VERSION */ (callback_t) recv_version, [5] = (callback_t) recv_server_sync, [7] = (callback_t) recv_channel_state, + [8] = (callback_t) recv_user_remove, + [9] = (callback_t) recv_user_state, [15] = (callback_t) recv_crypt_setup, [21] = (callback_t) recv_codec_version, [127] = NULL, @@ -415,16 +493,18 @@ static GstAppSrcCallbacks app_callbacks = { }; static int -setup_playback_gst_pipeline(struct context *ctx) +user_create_playback_pipeline(struct context *ctx, struct user *user) { GstElement *pipeline, *src, *decoder, *conv, *sink; GstBus *bus; + uint32_t s = user->session; - pipeline = gst_pipeline_new("cmumble-output"); - src = gst_element_factory_make("appsrc", "input"); - decoder = gst_element_factory_make("celtdec", "celt-decoder"); - conv = gst_element_factory_make("audioconvert", "converter"); - sink = gst_element_factory_make("autoaudiosink", "audio-output"); + /* FIXME: free strings buffers */ + pipeline = gst_pipeline_new(g_strdup_printf("cmumble-output-%d", s)); + src = gst_element_factory_make("appsrc", g_strdup_printf("input-%d", s)); + decoder = gst_element_factory_make("celtdec", g_strdup_printf("celt-decoder-%d", s)); + conv = gst_element_factory_make("audioconvert", g_strdup_printf("converter-%d", s)); + sink = gst_element_factory_make("autoaudiosink", g_strdup_printf("audio-output-%d", s)); if (!pipeline || !src || !decoder || !conv || !sink) { g_printerr("failed to initialize pipeline\n"); @@ -439,34 +519,39 @@ setup_playback_gst_pipeline(struct context *ctx) src, decoder, conv, sink, NULL); gst_element_link_many(src, decoder, conv, sink, NULL); - ctx->src = GST_APP_SRC(src); - ctx->playback_pipeline = pipeline; + user->src = GST_APP_SRC(src); + user->pipeline = pipeline; /* Important! */ - gst_base_src_set_live(GST_BASE_SRC(ctx->src), TRUE); - gst_base_src_set_do_timestamp(GST_BASE_SRC(ctx->src), TRUE); - gst_base_src_set_format(GST_BASE_SRC(ctx->src), GST_FORMAT_TIME); + gst_base_src_set_live(GST_BASE_SRC(user->src), TRUE); + gst_base_src_set_do_timestamp(GST_BASE_SRC(user->src), TRUE); + gst_base_src_set_format(GST_BASE_SRC(user->src), GST_FORMAT_TIME); - gst_app_src_set_stream_type(ctx->src, GST_APP_STREAM_TYPE_STREAM); - gst_app_src_set_callbacks(ctx->src, &app_callbacks, ctx, NULL); + gst_app_src_set_stream_type(user->src, GST_APP_STREAM_TYPE_STREAM); + gst_app_src_set_callbacks(user->src, &app_callbacks, ctx, NULL); gst_element_set_state(pipeline, GST_STATE_PLAYING); - { /* Setup Celt Decoder */ + /* Setup Celt Decoder */ + appsrc_push(user->src, ctx->celt_header_packet, sizeof(CELTHeader)); + /* fake vorbiscomment buffer */ + appsrc_push(user->src, NULL, 0); + + + return 0; +} + +static int +setup_playback_gst_pipeline(struct context *ctx) +{ #define SAMPLERATE 48000 #define CHANNELS 1 - uint8_t celt_header_packet[sizeof(CELTHeader)]; - - ctx->celt_mode = celt_mode_create(SAMPLERATE, - SAMPLERATE / 100, NULL); - celt_header_init(&ctx->celt_header, ctx->celt_mode, CHANNELS); - celt_header_to_packet(&ctx->celt_header, - celt_header_packet, sizeof(CELTHeader)); - appsrc_push(ctx->src, celt_header_packet, sizeof(CELTHeader)); - /* fake vorbiscomment buffer */ - appsrc_push(ctx->src, NULL, 0); - } + ctx->celt_mode = celt_mode_create(SAMPLERATE, + SAMPLERATE / 100, NULL); + celt_header_init(&ctx->celt_header, ctx->celt_mode, CHANNELS); + celt_header_to_packet(&ctx->celt_header, + ctx->celt_header_packet, sizeof(CELTHeader)); return 0; } @@ -552,8 +637,15 @@ int main(int argc, char **argv) GError *error = NULL; GSource *source; + if (argc >= 3) + host = argv[2]; + if (argc >= 4) + port = atoi(argv[3]); + memset(&ctx, 0, sizeof(ctx)); + ctx.users = NULL; + g_type_init(); ctx.sock_client = g_socket_client_new(); g_socket_client_set_tls(ctx.sock_client, TRUE); |