From 1827991548495f1b2c3f44fe45b51901109f791c Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Fri, 25 Apr 2014 17:23:21 +0200 Subject: Protocol, client: Add commands to enable srbchannel This increments protocol version to v30 and adds two new commands to enable and disable an shm ringbuffer, as well as client side implementation. Signed-off-by: David Henningsson --- PROTOCOL | 20 ++++++++ configure.ac | 2 +- src/pulse/context.c | 108 +++++++++++++++++++++++++++++++++++++++++- src/pulse/internal.h | 3 ++ src/pulsecore/native-common.h | 5 ++ 5 files changed, 136 insertions(+), 2 deletions(-) diff --git a/PROTOCOL b/PROTOCOL index 850b953ab..3c08fea10 100644 --- a/PROTOCOL +++ b/PROTOCOL @@ -351,6 +351,26 @@ New field in all commands that send/receive profile introspection data The field is added once for every profile. +## v30, implemented by >= 6.0 +# +A new protocol mechanism supported: Two ringbuffers in shared memory. +Pulseaudio fdsem (wrappers around event file descriptors) are used for +signalling new data. +The protocol has a new SHM flag telling whether a SHM memblock is writable +by both sides. + +PA_COMMAND_ENABLE_SRBCHANNEL +First sent from server to client, tells the client to start listening on +the additional SHM ringbuffer channel. +This command also has ancillary data (two eventfds attached to it). +Must be directly followed by a memblock which is the ringbuffer memory. +When memblock is received by the client, it acks by sending +PA_COMMAND_ENABLE_SRBCHANNEL back (without ancillary or memblock data). + +PA_COMMAND_DISABLE_SRBCHANNEL +Tells the client to stop listening on the additional SHM ringbuffer channel. +Acked by client by sending PA_COMMAND_DISABLE_SRBCHANNEL back. + #### If you just changed the protocol, read this ## module-tunnel depends on the sink/source/sink-input/source-input protocol ## internals, so if you changed these, you might have broken module-tunnel. diff --git a/configure.ac b/configure.ac index 39bb5c3a4..837e81e63 100644 --- a/configure.ac +++ b/configure.ac @@ -41,7 +41,7 @@ AC_SUBST(PA_MINOR, pa_minor) AC_SUBST(PA_MAJORMINOR, pa_major.pa_minor) AC_SUBST(PA_API_VERSION, 12) -AC_SUBST(PA_PROTOCOL_VERSION, 29) +AC_SUBST(PA_PROTOCOL_VERSION, 30) # The stable ABI for client applications, for the version info x:y:z # always will hold y=z diff --git a/src/pulse/context.c b/src/pulse/context.c index e1cd900d6..45ed344cf 100644 --- a/src/pulse/context.c +++ b/src/pulse/context.c @@ -69,6 +69,8 @@ #include "context.h" void pa_command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +static void pa_command_enable_srbchannel(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +static void pa_command_disable_srbchannel(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = { [PA_COMMAND_REQUEST] = pa_command_request, @@ -87,7 +89,9 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = { [PA_COMMAND_RECORD_STREAM_EVENT] = pa_command_stream_event, [PA_COMMAND_CLIENT_EVENT] = pa_command_client_event, [PA_COMMAND_PLAYBACK_BUFFER_ATTR_CHANGED] = pa_command_stream_buffer_attr, - [PA_COMMAND_RECORD_BUFFER_ATTR_CHANGED] = pa_command_stream_buffer_attr + [PA_COMMAND_RECORD_BUFFER_ATTR_CHANGED] = pa_command_stream_buffer_attr, + [PA_COMMAND_ENABLE_SRBCHANNEL] = pa_command_enable_srbchannel, + [PA_COMMAND_DISABLE_SRBCHANNEL] = pa_command_disable_srbchannel, }; static void context_free(pa_context *c); @@ -165,6 +169,9 @@ pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char * c->conf = pa_client_conf_new(); pa_client_conf_load(c->conf, true, true); + c->srb_template.readfd = -1; + c->srb_template.writefd = -1; + if (!(c->mempool = pa_mempool_new(!c->conf->disable_shm, c->conf->shm_size))) { if (!c->conf->disable_shm) @@ -206,6 +213,11 @@ static void context_unlink(pa_context *c) { c->pstream = NULL; } + if (c->srb_template.memblock) { + pa_memblock_unref(c->srb_template.memblock); + c->srb_template.memblock = NULL; + } + if (c->client) { pa_socket_client_unref(c->client); c->client = NULL; @@ -331,6 +343,35 @@ static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, const pa_a pa_context_unref(c); } +static void handle_srbchannel_memblock(pa_context *c, pa_memblock *memblock) { + pa_srbchannel *sr; + pa_tagstruct *t; + + pa_assert(c); + + /* Memblock sanity check */ + if (!memblock) + pa_context_fail(c, PA_ERR_PROTOCOL); + else if (pa_memblock_is_read_only(memblock)) + pa_context_fail(c, PA_ERR_PROTOCOL); + else if (pa_memblock_is_ours(memblock)) + pa_context_fail(c, PA_ERR_PROTOCOL); + + /* Create the srbchannel */ + c->srb_template.memblock = memblock; + pa_memblock_ref(memblock); + sr = pa_srbchannel_new_from_template(c->mainloop, &c->srb_template); + + /* Ack the enable command */ + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, PA_COMMAND_ENABLE_SRBCHANNEL); + pa_tagstruct_putu32(t, c->srb_setup_tag); + pa_pstream_send_tagstruct(c->pstream, t); + + /* ...and switch over */ + pa_pstream_set_srbchannel(c->pstream, sr); +} + static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t offset, pa_seek_mode_t seek, const pa_memchunk *chunk, void *userdata) { pa_context *c = userdata; pa_stream *s; @@ -343,6 +384,12 @@ static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t o pa_context_ref(c); + if (c->srb_template.readfd != -1 && c->srb_template.memblock == NULL) { + handle_srbchannel_memblock(c, chunk->memblock); + pa_context_unref(c); + return; + } + if ((s = pa_hashmap_get(c->record_streams, PA_UINT32_TO_PTR(channel)))) { if (chunk->memblock) { @@ -1362,6 +1409,65 @@ finish: pa_context_unref(c); } +static void pa_command_enable_srbchannel(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { + pa_context *c = userdata; + const int *fds; + int nfd; + + pa_assert(pd); + pa_assert(command == PA_COMMAND_ENABLE_SRBCHANNEL); + pa_assert(t); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); + + /* Currently only one srb channel is supported, might change in future versions */ + if (c->srb_template.readfd != -1) { + pa_context_fail(c, PA_ERR_PROTOCOL); + return; + } + + fds = pa_pdispatch_fds(pd, &nfd); + if (nfd != 2 || !fds || fds[0] == -1 || fds[1] == -1) { + pa_context_fail(c, PA_ERR_PROTOCOL); + return; + } + + pa_context_ref(c); + + c->srb_template.readfd = fds[0]; + c->srb_template.writefd = fds[1]; + c->srb_setup_tag = tag; + + pa_context_unref(c); +} + +static void pa_command_disable_srbchannel(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { + pa_context *c = userdata; + pa_tagstruct *t2; + + pa_assert(pd); + pa_assert(command == PA_COMMAND_DISABLE_SRBCHANNEL); + pa_assert(t); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); + + pa_pstream_set_srbchannel(c->pstream, NULL); + + c->srb_template.readfd = -1; + c->srb_template.writefd = -1; + if (c->srb_template.memblock) { + pa_memblock_unref(c->srb_template.memblock); + c->srb_template.memblock = NULL; + } + + /* Send disable command back again */ + t2 = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t2, PA_COMMAND_DISABLE_SRBCHANNEL); + pa_tagstruct_putu32(t2, tag); + pa_pstream_send_tagstruct(c->pstream, t2); +} + + void pa_command_client_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { pa_context *c = userdata; pa_proplist *pl = NULL; diff --git a/src/pulse/internal.h b/src/pulse/internal.h index c5084d5f3..fc2b70243 100644 --- a/src/pulse/internal.h +++ b/src/pulse/internal.h @@ -66,6 +66,9 @@ struct pa_context { pa_pstream *pstream; pa_pdispatch *pdispatch; + pa_srbchannel_template srb_template; + uint32_t srb_setup_tag; + pa_hashmap *record_streams, *playback_streams; PA_LLIST_HEAD(pa_stream, streams); PA_LLIST_HEAD(pa_operation, operations); diff --git a/src/pulsecore/native-common.h b/src/pulsecore/native-common.h index e7d297036..ca1b430cc 100644 --- a/src/pulsecore/native-common.h +++ b/src/pulsecore/native-common.h @@ -176,6 +176,11 @@ enum { /* Supported since protocol v27 (3.0) */ PA_COMMAND_SET_PORT_LATENCY_OFFSET, + /* Supported since protocol v30 (6.0) */ + /* BOTH DIRECTIONS */ + PA_COMMAND_ENABLE_SRBCHANNEL, + PA_COMMAND_DISABLE_SRBCHANNEL, + PA_COMMAND_MAX }; -- cgit v1.2.3