From 823431e44732a0824658c82de29aaa92f8f39f79 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 12 Feb 2009 03:18:05 +0100 Subject: allow sending meta/policy events to clients --- src/map-file | 2 ++ src/pulse/context.c | 62 +++++++++++++++++++++++++++++++++++++- src/pulse/context.h | 11 +++++++ src/pulse/def.h | 10 +++++++ src/pulse/internal.h | 7 +++++ src/pulse/stream.c | 59 ++++++++++++++++++++++++++++++++++++ src/pulse/stream.h | 14 ++++++++- src/pulsecore/client.c | 29 ++++++++++++++++++ src/pulsecore/client.h | 10 +++++++ src/pulsecore/core.h | 3 ++ src/pulsecore/native-common.h | 4 +++ src/pulsecore/protocol-native.c | 66 +++++++++++++++++++++++++++++++++++++++++ src/pulsecore/sink-input.c | 29 ++++++++++++++++++ src/pulsecore/sink-input.h | 12 ++++++++ src/pulsecore/source-output.c | 28 +++++++++++++++++ src/pulsecore/source-output.h | 12 ++++++++ src/utils/pacat.c | 13 ++++++++ 17 files changed, 369 insertions(+), 2 deletions(-) diff --git a/src/map-file b/src/map-file index ca4e7a72..d9496593 100644 --- a/src/map-file +++ b/src/map-file @@ -87,6 +87,7 @@ pa_context_set_card_profile_by_index; pa_context_set_card_profile_by_name; pa_context_set_default_sink; pa_context_set_default_source; +pa_context_set_event_callback; pa_context_set_name; pa_context_set_sink_input_mute; pa_context_set_sink_input_volume; @@ -232,6 +233,7 @@ pa_stream_proplist_update; pa_stream_readable_size; pa_stream_ref; pa_stream_set_buffer_attr; +pa_stream_set_event_callback; pa_stream_set_latency_update_callback; pa_stream_set_monitor_stream; pa_stream_set_moved_callback; diff --git a/src/pulse/context.c b/src/pulse/context.c index d41e62e2..81050914 100644 --- a/src/pulse/context.c +++ b/src/pulse/context.c @@ -95,7 +95,10 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = { [PA_COMMAND_RECORD_STREAM_SUSPENDED] = pa_command_stream_suspended, [PA_COMMAND_STARTED] = pa_command_stream_started, [PA_COMMAND_SUBSCRIBE_EVENT] = pa_command_subscribe_event, - [PA_COMMAND_EXTENSION] = pa_command_extension + [PA_COMMAND_EXTENSION] = pa_command_extension, + [PA_COMMAND_PLAYBACK_STREAM_EVENT] = pa_command_stream_event, + [PA_COMMAND_RECORD_STREAM_EVENT] = pa_command_stream_event, + [PA_COMMAND_CLIENT_EVENT] = pa_command_client_event }; static void context_free(pa_context *c); @@ -112,6 +115,9 @@ static void reset_callbacks(pa_context *c) { c->subscribe_callback = NULL; c->subscribe_userdata = NULL; + c->event_callback = NULL; + c->event_userdata = NULL; + c->ext_stream_restore.callback = NULL; c->ext_stream_restore.userdata = NULL; } @@ -917,6 +923,17 @@ void pa_context_set_state_callback(pa_context *c, pa_context_notify_cb_t cb, voi c->state_userdata = userdata; } +void pa_context_set_event_callback(pa_context *c, pa_context_event_cb_t cb, void *userdata) { + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); + + if (c->state == PA_CONTEXT_TERMINATED || c->state == PA_CONTEXT_FAILED) + return; + + c->event_callback = cb; + c->event_userdata = userdata; +} + int pa_context_is_pending(pa_context *c) { pa_assert(c); pa_assert(PA_REFCNT_VALUE(c) >= 1); @@ -1245,6 +1262,11 @@ void pa_command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_t pa_context_ref(c); + if (c->version < 15) { + pa_context_fail(c, PA_ERR_PROTOCOL); + goto finish; + } + if (pa_tagstruct_getu32(t, &idx) < 0 || pa_tagstruct_gets(t, &name) < 0) { pa_context_fail(c, PA_ERR_PROTOCOL); @@ -1259,3 +1281,41 @@ void pa_command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_t finish: pa_context_unref(c); } + + +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; + const char *event; + + pa_assert(pd); + pa_assert(command == PA_COMMAND_CLIENT_EVENT); + pa_assert(t); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); + + pa_context_ref(c); + + if (c->version < 15) { + pa_context_fail(c, PA_ERR_PROTOCOL); + goto finish; + } + + pl = pa_proplist_new(); + + if (pa_tagstruct_gets(t, &event) < 0 || + pa_tagstruct_get_proplist(t, pl) < 0 || + !pa_tagstruct_eof(t) || !event) { + pa_context_fail(c, PA_ERR_PROTOCOL); + goto finish; + } + + if (c->event_callback) + c->event_callback(c, event, pl, c->event_userdata); + +finish: + pa_context_unref(c); + + if (pl) + pa_proplist_free(pl); +} diff --git a/src/pulse/context.h b/src/pulse/context.h index dfb7e4a1..2ae4c013 100644 --- a/src/pulse/context.h +++ b/src/pulse/context.h @@ -164,6 +164,13 @@ typedef void (*pa_context_notify_cb_t)(pa_context *c, void *userdata); /** A generic callback for operation completion */ typedef void (*pa_context_success_cb_t) (pa_context *c, int success, void *userdata); +/** A callback for asynchronous meta/policy event messages. The set + * of defined events can be extended at any time. Also, server modules + * may introduce additional message types so make sure that your + * callback function ignores messages it doesn't know. \since + * 0.9.15 */ +typedef void (*pa_context_event_cb_t)(pa_context *c, const char *name, pa_proplist *p, void *userdata); + /** Instantiate a new connection context with an abstract mainloop API * and an application name. It is recommended to use pa_context_new_with_proplist() * instead and specify some initial properties.*/ @@ -183,6 +190,10 @@ pa_context* pa_context_ref(pa_context *c); /** Set a callback function that is called whenever the context status changes */ void pa_context_set_state_callback(pa_context *c, pa_context_notify_cb_t cb, void *userdata); +/** Set a callback function that is called whenver a meta/policy + * control event is received. \since 0.9.15 */ +void pa_context_set_event_callback(pa_context *p, pa_context_event_cb_t cb, void *userdata); + /** Return the error number of the last failed operation */ int pa_context_errno(pa_context *c); diff --git a/src/pulse/def.h b/src/pulse/def.h index d4fa821a..7f3a0c5d 100644 --- a/src/pulse/def.h +++ b/src/pulse/def.h @@ -840,6 +840,16 @@ static inline int PA_SOURCE_IS_OPENED(pa_source_state_t x) { /** A generic free() like callback prototype */ typedef void (*pa_free_cb_t)(void *p); +/** A stream policy/meta event requesting that an application should + * cork a specific stream. See pa_stream_event_cb_t for more + * information, \since 0.9.15 */ +#define PA_STREAM_EVENT_REQUEST_CORK "request-cork" + +/** A stream policy/meta event requesting that an application should + * cork a specific stream. See pa_stream_event_cb_t for more + * information, \since 0.9.15 */ +#define PA_STREAM_EVENT_REQUEST_UNCORK "request-uncork" + PA_C_DECL_END #endif diff --git a/src/pulse/internal.h b/src/pulse/internal.h index 9a2d6457..e533625d 100644 --- a/src/pulse/internal.h +++ b/src/pulse/internal.h @@ -71,6 +71,8 @@ struct pa_context { void *state_userdata; pa_context_subscribe_cb_t subscribe_callback; void *subscribe_userdata; + pa_context_event_cb_t event_callback; + void *event_userdata; pa_mempool *mempool; @@ -181,6 +183,8 @@ struct pa_stream { void *suspended_userdata; pa_stream_notify_cb_t started_callback; void *started_userdata; + pa_stream_event_cb_t event_callback; + void *event_userdata; }; typedef void (*pa_operation_cb_t)(void); @@ -207,6 +211,9 @@ void pa_command_overflow_or_underflow(pa_pdispatch *pd, uint32_t command, uint32 void pa_command_stream_suspended(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); void pa_command_stream_moved(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); void pa_command_stream_started(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +void pa_command_stream_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +void pa_command_client_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); + pa_operation *pa_operation_new(pa_context *c, pa_stream *s, pa_operation_cb_t callback, void *userdata); void pa_operation_done(pa_operation *o); diff --git a/src/pulse/stream.c b/src/pulse/stream.c index fe2514d9..b36bf9b1 100644 --- a/src/pulse/stream.c +++ b/src/pulse/stream.c @@ -69,6 +69,8 @@ static void reset_callbacks(pa_stream *s) { s->suspended_userdata = NULL; s->started_callback = NULL; s->started_userdata = NULL; + s->event_callback = NULL; + s->event_userdata = NULL; } pa_stream *pa_stream_new_with_proplist( @@ -565,6 +567,52 @@ finish: pa_context_unref(c); } +void pa_command_stream_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { + pa_context *c = userdata; + pa_stream *s; + uint32_t channel; + pa_proplist *pl = NULL; + const char *event; + + pa_assert(pd); + pa_assert(command == PA_COMMAND_PLAYBACK_STREAM_EVENT || command == PA_COMMAND_RECORD_STREAM_EVENT); + pa_assert(t); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); + + pa_context_ref(c); + + if (c->version < 15) { + pa_context_fail(c, PA_ERR_PROTOCOL); + goto finish; + } + + pl = pa_proplist_new(); + + if (pa_tagstruct_getu32(t, &channel) < 0 || + pa_tagstruct_gets(t, &event) < 0 || + pa_tagstruct_get_proplist(t, pl) < 0 || + !pa_tagstruct_eof(t) || !event) { + pa_context_fail(c, PA_ERR_PROTOCOL); + goto finish; + } + + if (!(s = pa_dynarray_get(command == PA_COMMAND_PLAYBACK_STREAM_EVENT ? c->playback_streams : c->record_streams, channel))) + goto finish; + + if (s->state != PA_STREAM_READY) + goto finish; + + if (s->event_callback) + s->event_callback(s, event, pl, s->event_userdata); + +finish: + pa_context_unref(c); + + if (pl) + pa_proplist_free(pl); +} + void pa_command_request(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { pa_stream *s; pa_context *c = userdata; @@ -1696,6 +1744,17 @@ void pa_stream_set_started_callback(pa_stream *s, pa_stream_notify_cb_t cb, void s->started_userdata = userdata; } +void pa_stream_set_event_callback(pa_stream *s, pa_stream_event_cb_t cb, void *userdata) { + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); + + if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED) + return; + + s->event_callback = cb; + s->event_userdata = userdata; +} + void pa_stream_simple_ack_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { pa_operation *o = userdata; int success = 1; diff --git a/src/pulse/stream.h b/src/pulse/stream.h index 1bec1ebf..3965e9a2 100644 --- a/src/pulse/stream.h +++ b/src/pulse/stream.h @@ -324,6 +324,14 @@ typedef void (*pa_stream_request_cb_t)(pa_stream *p, size_t bytes, void *userdat /** A generic notification callback */ typedef void (*pa_stream_notify_cb_t)(pa_stream *p, void *userdata); +/** A callback for asynchronous meta/policy event messages. Well known + * event names are PA_STREAM_EVENT_REQUEST_CORK and + * PA_STREAM_EVENT_REQUEST_UNCORK. The set of defined events can be + * extended at any time. Also, server modules may introduce additional + * message types so make sure that your callback function ignores messages + * it doesn't know. \since 0.9.15 */ +typedef void (*pa_stream_event_cb_t)(pa_stream *p, const char *name, pa_proplist *pl, void *userdata); + /** Create a new, unconnected stream with the specified name and * sample type. It is recommended to use pa_stream_new_with_proplist() * instead and specify some initial properties. */ @@ -500,6 +508,10 @@ void pa_stream_set_moved_callback(pa_stream *p, pa_stream_notify_cb_t cb, void * * 0.9.8. \since 0.9.8 */ void pa_stream_set_suspended_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata); +/** Set the callback function that is called whenver a meta/policy + * control event is received.\since 0.9.15 */ +void pa_stream_set_event_callback(pa_stream *p, pa_stream_event_cb_t cb, void *userdata); + /** Pause (or resume) playback of this stream temporarily. Available on both playback and recording streams. */ pa_operation* pa_stream_cork(pa_stream *s, int b, pa_stream_success_cb_t cb, void *userdata); @@ -518,7 +530,7 @@ pa_operation* pa_stream_prebuf(pa_stream *s, pa_stream_success_cb_t cb, void *us * temporarily. Available for playback streams only. */ pa_operation* pa_stream_trigger(pa_stream *s, pa_stream_success_cb_t cb, void *userdata); -/** Rename the stream. */ +/** Rename the stream.*/ pa_operation* pa_stream_set_name(pa_stream *s, const char *name, pa_stream_success_cb_t cb, void *userdata); /** Return the current playback/recording time. This is based on the diff --git a/src/pulsecore/client.c b/src/pulsecore/client.c index 6f3e08e2..7ca7b97c 100644 --- a/src/pulsecore/client.c +++ b/src/pulsecore/client.c @@ -73,6 +73,7 @@ pa_client *pa_client_new(pa_core *core, pa_client_new_data *data) { c->userdata = NULL; c->kill = NULL; + c->send_event = NULL; pa_assert_se(pa_idxset_put(core->clients, c, &c->index) >= 0); @@ -144,3 +145,31 @@ void pa_client_update_proplist(pa_client *c, pa_update_mode_t mode, pa_proplist pa_hook_fire(&c->core->hooks[PA_CORE_HOOK_CLIENT_PROPLIST_CHANGED], c); pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_CHANGE, c->index); } + +void pa_client_send_event(pa_client *c, const char *event, pa_proplist *data) { + pa_proplist *pl = NULL; + pa_client_send_event_hook_data hook_data; + + pa_assert(c); + pa_assert(event); + + if (!c->send_event) + return; + + if (!data) + data = pl = pa_proplist_new(); + + hook_data.client = c; + hook_data.data = data; + hook_data.event = event; + + if (pa_hook_fire(&c->core->hooks[PA_CORE_HOOK_CLIENT_SEND_EVENT], &hook_data) < 0) + goto finish; + + c->send_event(c, event, data); + +finish: + + if (pl) + pa_proplist_free(pl); +} diff --git a/src/pulsecore/client.h b/src/pulsecore/client.h index 8ac80dd9..845a8bab 100644 --- a/src/pulsecore/client.h +++ b/src/pulsecore/client.h @@ -48,6 +48,8 @@ struct pa_client { void *userdata; void (*kill)(pa_client *c); + + void (*send_event)(pa_client *c, const char *name, pa_proplist *data); }; typedef struct pa_client_new_data { @@ -73,4 +75,12 @@ void pa_client_set_name(pa_client *c, const char *name); void pa_client_update_proplist(pa_client *c, pa_update_mode_t mode, pa_proplist *p); +void pa_client_send_event(pa_client *c, const char *event, pa_proplist *data); + +typedef struct pa_client_send_event_hook_data { + pa_client *client; + const char *event; + pa_proplist *data; +} pa_client_send_event_hook_data; + #endif diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h index 1407b1b8..aca96bba 100644 --- a/src/pulsecore/core.h +++ b/src/pulsecore/core.h @@ -73,6 +73,7 @@ typedef enum pa_core_hook { PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED, PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED, PA_CORE_HOOK_SINK_INPUT_SET_VOLUME, + PA_CORE_HOOK_SINK_INPUT_SEND_EVENT, PA_CORE_HOOK_SOURCE_OUTPUT_NEW, PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE, PA_CORE_HOOK_SOURCE_OUTPUT_PUT, @@ -82,10 +83,12 @@ typedef enum pa_core_hook { PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FINISH, PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED, PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED, + PA_CORE_HOOK_SOURCE_OUTPUT_SEND_EVENT, PA_CORE_HOOK_CLIENT_NEW, PA_CORE_HOOK_CLIENT_PUT, PA_CORE_HOOK_CLIENT_UNLINK, PA_CORE_HOOK_CLIENT_PROPLIST_CHANGED, + PA_CORE_HOOK_CLIENT_SEND_EVENT, PA_CORE_HOOK_CARD_NEW, PA_CORE_HOOK_CARD_PUT, PA_CORE_HOOK_CARD_UNLINK, diff --git a/src/pulsecore/native-common.h b/src/pulsecore/native-common.h index b31a5da1..6951e10a 100644 --- a/src/pulsecore/native-common.h +++ b/src/pulsecore/native-common.h @@ -157,6 +157,10 @@ enum { PA_COMMAND_GET_CARD_INFO_LIST, PA_COMMAND_SET_CARD_PROFILE, + PA_COMMAND_CLIENT_EVENT, + PA_COMMAND_PLAYBACK_STREAM_EVENT, + PA_COMMAND_RECORD_STREAM_EVENT, + PA_COMMAND_MAX }; diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c index 65b2bb97..c3032618 100644 --- a/src/pulsecore/protocol-native.c +++ b/src/pulsecore/protocol-native.c @@ -207,6 +207,7 @@ static void sink_input_moved_cb(pa_sink_input *i); static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes); static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes); static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes); +static void sink_input_send_event_cb(pa_sink_input *i, const char *event, pa_proplist *pl); static void native_connection_send_memblock(pa_native_connection *c); static void playback_stream_request_bytes(struct playback_stream*s); @@ -216,6 +217,7 @@ static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) static void source_output_suspend_cb(pa_source_output *o, pa_bool_t suspend); static void source_output_moved_cb(pa_source_output *o); static pa_usec_t source_output_get_latency_cb(pa_source_output *o); +static void source_output_send_event_cb(pa_source_output *o, const char *event, pa_proplist *pl); static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk); @@ -636,6 +638,7 @@ static record_stream* record_stream_new( s->source_output->get_latency = source_output_get_latency_cb; s->source_output->moved = source_output_moved_cb; s->source_output->suspend = source_output_suspend_cb; + s->source_output->send_event = source_output_send_event_cb; s->source_output->userdata = s; fix_record_buffer_attr_pre(s, adjust_latency, early_requests, maxlength, fragsize); @@ -1048,6 +1051,7 @@ static playback_stream* playback_stream_new( s->sink_input->kill = sink_input_kill_cb; s->sink_input->moved = sink_input_moved_cb; s->sink_input->suspend = sink_input_suspend_cb; + s->sink_input->send_event = sink_input_send_event_cb; s->sink_input->userdata = s; start_index = ssync ? pa_memblockq_get_read_index(ssync->memblockq) : 0; @@ -1493,6 +1497,27 @@ static void sink_input_kill_cb(pa_sink_input *i) { playback_stream_unlink(s); } +/* Called from main context */ +static void sink_input_send_event_cb(pa_sink_input *i, const char *event, pa_proplist *pl) { + playback_stream *s; + pa_tagstruct *t; + + pa_sink_input_assert_ref(i); + s = PLAYBACK_STREAM(i->userdata); + playback_stream_assert_ref(s); + + if (s->connection->version < 15) + return; + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, PA_COMMAND_PLAYBACK_STREAM_EVENT); + pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */ + pa_tagstruct_putu32(t, s->index); + pa_tagstruct_puts(t, event); + pa_tagstruct_put_proplist(t, pl); + pa_pstream_send_tagstruct(s->connection->pstream, t); +} + /* Called from main context */ static void sink_input_suspend_cb(pa_sink_input *i, pa_bool_t suspend) { playback_stream *s; @@ -1594,6 +1619,27 @@ static pa_usec_t source_output_get_latency_cb(pa_source_output *o) { return pa_bytes_to_usec(pa_memblockq_get_length(s->memblockq), &o->sample_spec); } +/* Called from main context */ +static void source_output_send_event_cb(pa_source_output *o, const char *event, pa_proplist *pl) { + record_stream *s; + pa_tagstruct *t; + + pa_source_output_assert_ref(o); + s = RECORD_STREAM(o->userdata); + record_stream_assert_ref(s); + + if (s->connection->version < 15) + return; + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, PA_COMMAND_RECORD_STREAM_EVENT); + pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */ + pa_tagstruct_putu32(t, s->index); + pa_tagstruct_puts(t, event); + pa_tagstruct_put_proplist(t, pl); + pa_pstream_send_tagstruct(s->connection->pstream, t); +} + /* Called from main context */ static void source_output_suspend_cb(pa_source_output *o, pa_bool_t suspend) { record_stream *s; @@ -4188,6 +4234,25 @@ static void client_kill_cb(pa_client *c) { pa_log_info("Connection killed."); } +static void client_send_event_cb(pa_client *client, const char*event, pa_proplist *pl) { + pa_tagstruct *t; + pa_native_connection *c; + + pa_assert(client); + c = PA_NATIVE_CONNECTION(client->userdata); + pa_native_connection_assert_ref(c); + + if (c->version < 15) + return; + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, PA_COMMAND_CLIENT_EVENT); + pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */ + pa_tagstruct_puts(t, event); + pa_tagstruct_put_proplist(t, pl); + pa_pstream_send_tagstruct(c->pstream, t); +} + /*** module entry points ***/ static void auth_timeout(pa_mainloop_api*m, pa_time_event *e, const struct timeval *tv, void *userdata) { @@ -4265,6 +4330,7 @@ void pa_native_protocol_connect(pa_native_protocol *p, pa_iochannel *io, pa_nati c->client = client; c->client->kill = client_kill_cb; + c->client->send_event = client_send_event_cb; c->client->userdata = c; c->pstream = pa_pstream_new(p->core->mainloop, io, p->core->mempool); diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index 9c0bf76a..22419ee5 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -122,6 +122,7 @@ static void reset_callbacks(pa_sink_input *i) { i->get_latency = NULL; i->state_change = NULL; i->may_move_to = NULL; + i->send_event = NULL; } /* Called from main context */ @@ -1446,3 +1447,31 @@ pa_memchunk* pa_sink_input_get_silence(pa_sink_input *i, pa_memchunk *ret) { return ret; } + +/* Called from main context */ +void pa_sink_input_send_event(pa_sink_input *i, const char *event, pa_proplist *data) { + pa_proplist *pl = NULL; + pa_sink_input_send_event_hook_data hook_data; + + pa_sink_input_assert_ref(i); + pa_assert(event); + + if (!i->send_event) + return; + + if (!data) + data = pl = pa_proplist_new(); + + hook_data.sink_input = i; + hook_data.data = data; + hook_data.event = event; + + if (pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_SEND_EVENT], &hook_data) < 0) + goto finish; + + i->send_event(i, event, data); + +finish: + if (pl) + pa_proplist_free(pl); +} diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h index 20e2cfa4..b4f05319 100644 --- a/src/pulsecore/sink-input.h +++ b/src/pulsecore/sink-input.h @@ -171,6 +171,10 @@ struct pa_sink_input { * be allowed */ pa_bool_t (*may_move_to) (pa_sink_input *i, pa_sink *s); /* may be NULL */ + /* If non-NULL this function is used to dispatch asynchronous + * control events. */ + void (*send_event)(pa_sink_input *i, const char *event, pa_proplist* data); + struct { pa_sink_input_state_t state; pa_atomic_t drained; @@ -217,6 +221,12 @@ enum { PA_SINK_INPUT_MESSAGE_MAX }; +typedef struct pa_sink_input_send_event_hook_data { + pa_sink_input *sink_input; + const char *event; + pa_proplist *data; +} pa_sink_input_send_event_hook_data; + typedef struct pa_sink_input_new_data { pa_proplist *proplist; @@ -298,6 +308,8 @@ void pa_sink_input_update_proplist(pa_sink_input *i, pa_update_mode_t mode, pa_p pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i); +void pa_sink_input_send_event(pa_sink_input *i, const char *name, pa_proplist *data); + int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, pa_bool_t save); pa_bool_t pa_sink_input_may_move(pa_sink_input *i); /* may this sink input move at all? */ pa_bool_t pa_sink_input_may_move_to(pa_sink_input *i, pa_sink *dest); /* may this sink input move to this sink? */ diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c index f2f2593c..382fb88c 100644 --- a/src/pulsecore/source-output.c +++ b/src/pulsecore/source-output.c @@ -92,6 +92,7 @@ static void reset_callbacks(pa_source_output *o) { o->get_latency = NULL; o->state_change = NULL; o->may_move_to = NULL; + o->send_event = NULL; } /* Called from main context */ @@ -867,3 +868,30 @@ int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, int return -PA_ERR_NOTIMPLEMENTED; } + +void pa_source_output_send_event(pa_source_output *o, const char *event, pa_proplist *data) { + pa_proplist *pl = NULL; + pa_source_output_send_event_hook_data hook_data; + + pa_source_output_assert_ref(o); + pa_assert(event); + + if (!o->send_event) + return; + + if (!data) + data = pl = pa_proplist_new(); + + hook_data.source_output = o; + hook_data.data = data; + hook_data.event = event; + + if (pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_SEND_EVENT], &hook_data) < 0) + goto finish; + + o->send_event(o, event, data); + +finish: + if (pl) + pa_proplist_free(pl); +} diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h index aba2510b..9369568c 100644 --- a/src/pulsecore/source-output.h +++ b/src/pulsecore/source-output.h @@ -143,6 +143,10 @@ struct pa_source_output { * will not be allowed */ pa_bool_t (*may_move_to) (pa_source_output *o, pa_source *s); /* may be NULL */ + /* If non-NULL this function is used to dispatch asynchronous + * control events. */ + void (*send_event)(pa_source_output *o, const char *event, pa_proplist* data); + struct { pa_source_output_state_t state; @@ -177,6 +181,12 @@ enum { PA_SOURCE_OUTPUT_MESSAGE_MAX }; +typedef struct pa_source_output_send_event_hook_data { + pa_source_output *source_output; + const char *event; + pa_proplist *data; +} pa_source_output_send_event_hook_data; + typedef struct pa_source_output_new_data { pa_proplist *proplist; pa_sink_input *direct_on_input; @@ -233,6 +243,8 @@ void pa_source_output_update_proplist(pa_source_output *o, pa_update_mode_t mode pa_resample_method_t pa_source_output_get_resample_method(pa_source_output *o); +void pa_source_output_send_event(pa_source_output *o, const char *name, pa_proplist *data); + pa_bool_t pa_source_output_may_move(pa_source_output *o); pa_bool_t pa_source_output_may_move_to(pa_source_output *o, pa_source *dest); int pa_source_output_move_to(pa_source_output *o, pa_source *dest, pa_bool_t save); diff --git a/src/utils/pacat.c b/src/utils/pacat.c index 2d88e7f9..10015ce4 100644 --- a/src/utils/pacat.c +++ b/src/utils/pacat.c @@ -238,6 +238,18 @@ static void stream_moved_callback(pa_stream *s, void *userdata) { fprintf(stderr, _("Stream moved to device %s (%u, %ssuspended).%s \n"), pa_stream_get_device_name(s), pa_stream_get_device_index(s), pa_stream_is_suspended(s) ? "" : _("not "), CLEAR_LINE); } +static void stream_event_callback(pa_stream *s, const char *name, pa_proplist *pl, void *userdata) { + char *t; + + assert(s); + assert(name); + assert(pl); + + t = pa_proplist_to_string_sep(pl, ", "); + fprintf(stderr, "Got event '%s', properties '%s'\n", name, t); + pa_xfree(t); +} + /* This is called whenever the context status changes */ static void context_state_callback(pa_context *c, void *userdata) { assert(c); @@ -271,6 +283,7 @@ static void context_state_callback(pa_context *c, void *userdata) { pa_stream_set_underflow_callback(stream, stream_underflow_callback, NULL); pa_stream_set_overflow_callback(stream, stream_overflow_callback, NULL); pa_stream_set_started_callback(stream, stream_started_callback, NULL); + pa_stream_set_event_callback(stream, stream_event_callback, NULL); if (latency > 0) { memset(&buffer_attr, 0, sizeof(buffer_attr)); -- cgit