diff options
-rw-r--r-- | PROTOCOL | 13 | ||||
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r-- | src/modules/echo-cancel/module-echo-cancel.c | 2 | ||||
-rw-r--r-- | src/modules/module-device-manager.c | 7 | ||||
-rw-r--r-- | src/modules/module-intended-roles.c | 6 | ||||
-rw-r--r-- | src/modules/module-loopback.c | 2 | ||||
-rw-r--r-- | src/modules/module-stream-restore.c | 5 | ||||
-rw-r--r-- | src/modules/module-virtual-source.c | 2 | ||||
-rw-r--r-- | src/modules/rtp/module-rtp-send.c | 2 | ||||
-rw-r--r-- | src/pulse/stream.c | 30 | ||||
-rw-r--r-- | src/pulsecore/protocol-esound.c | 2 | ||||
-rw-r--r-- | src/pulsecore/protocol-http.c | 2 | ||||
-rw-r--r-- | src/pulsecore/protocol-native.c | 129 | ||||
-rw-r--r-- | src/pulsecore/protocol-simple.c | 2 | ||||
-rw-r--r-- | src/pulsecore/source-output.c | 175 | ||||
-rw-r--r-- | src/pulsecore/source-output.h | 13 | ||||
-rw-r--r-- | src/pulsecore/source.c | 92 | ||||
-rw-r--r-- | src/pulsecore/source.h | 24 |
18 files changed, 406 insertions, 104 deletions
@@ -241,3 +241,16 @@ One new field in reply from PA_COMMAND_GET_SINK_INPUT_INFO (and thus PA_COMMAND_GET_SINK_INPUT_INFO_LIST) format_info format + +## v22, implemented by >= 1.0 + +New fields PA_COMMAND_CREATE_RECORD_STREAM: + + uint8_t n_formats + format_info format1 + ... + format_info formatn + +One new field in reply from PA_COMMAND_CREATE_RECORD_STREAM: + + format_info format diff --git a/configure.ac b/configure.ac index 88ad875d8..f95cbee9f 100644 --- a/configure.ac +++ b/configure.ac @@ -36,7 +36,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, 21) +AC_SUBST(PA_PROTOCOL_VERSION, 22) # The stable ABI for client applications, for the version info x:y:z # always will hold y=z diff --git a/src/modules/echo-cancel/module-echo-cancel.c b/src/modules/echo-cancel/module-echo-cancel.c index 96eb9ef13..3d22ef845 100644 --- a/src/modules/echo-cancel/module-echo-cancel.c +++ b/src/modules/echo-cancel/module-echo-cancel.c @@ -1599,7 +1599,7 @@ int pa__init(pa_module*m) { pa_source_output_new_data_init(&source_output_data); source_output_data.driver = __FILE__; source_output_data.module = m; - source_output_data.source = source_master; + pa_source_output_new_data_set_source(&source_output_data, source_master, FALSE); source_output_data.destination_source = u->source; /* FIXME source_output_data.flags = PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND; */ diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c index 4138794c4..272fad2ba 100644 --- a/src/modules/module-device-manager.c +++ b/src/modules/module-device-manager.c @@ -871,10 +871,9 @@ static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_ou if (PA_INVALID_INDEX != device_index) { pa_source *source; - if ((source = pa_idxset_get_by_index(u->core->sources, device_index))) { - new_data->source = source; - new_data->save_source = FALSE; - } + if ((source = pa_idxset_get_by_index(u->core->sources, device_index))) + if (!pa_source_output_new_data_set_source(new_data, source, FALSE)) + pa_log_debug("Not restoring device for stream because no supported format was found"); } } } diff --git a/src/modules/module-intended-roles.c b/src/modules/module-intended-roles.c index 90385622b..2f9bba4be 100644 --- a/src/modules/module-intended-roles.c +++ b/src/modules/module-intended-roles.c @@ -162,8 +162,7 @@ static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_ou /* Prefer the default source over any other source, just in case... */ if ((def = pa_namereg_get_default_source(c))) if (role_match(def->proplist, role)) { - new_data->source = def; - new_data->save_source = FALSE; + pa_source_output_new_data_set_source(new_data, def, FALSE); return PA_HOOK_OK; } @@ -179,8 +178,7 @@ static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_ou /* @todo: favour the highest priority device, not the first one we find? */ if (role_match(s->proplist, role)) { - new_data->source = s; - new_data->save_source = FALSE; + pa_source_output_new_data_set_source(new_data, s, FALSE); return PA_HOOK_OK; } } diff --git a/src/modules/module-loopback.c b/src/modules/module-loopback.c index a5b08bd5c..936133fc7 100644 --- a/src/modules/module-loopback.c +++ b/src/modules/module-loopback.c @@ -755,7 +755,7 @@ int pa__init(pa_module *m) { pa_source_output_new_data_init(&source_output_data); source_output_data.driver = __FILE__; source_output_data.module = m; - source_output_data.source = source; + pa_source_output_new_data_set_source(&source_output_data, source, FALSE); if ((n = pa_modargs_get_value(ma, "source_output_name", NULL))) pa_proplist_sets(source_output_data.proplist, PA_PROP_MEDIA_NAME, n); diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c index fa2c0210c..617de5bb9 100644 --- a/src/modules/module-stream-restore.c +++ b/src/modules/module-stream-restore.c @@ -1217,7 +1217,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3 if (source_output->save_source) { pa_strlcpy(entry.device, source_output->source->name, sizeof(entry.device)); - entry.device_valid = source_output->save_source; + entry.device_valid = TRUE; device_updated = !created_new_entry && (!old->device_valid || !pa_streq(entry.device, old->device)); @@ -1399,8 +1399,7 @@ static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_ou interfere with that */ if (s && PA_SOURCE_IS_LINKED(pa_source_get_state(s))) { pa_log_info("Restoring device for stream %s.", name); - new_data->source = s; - new_data->save_source = TRUE; + pa_source_output_new_data_set_source(new_data, s, TRUE); } pa_xfree(e); diff --git a/src/modules/module-virtual-source.c b/src/modules/module-virtual-source.c index 835cf3ce1..170fa4e06 100644 --- a/src/modules/module-virtual-source.c +++ b/src/modules/module-virtual-source.c @@ -630,7 +630,7 @@ int pa__init(pa_module*m) { pa_source_output_new_data_init(&source_output_data); source_output_data.driver = __FILE__; source_output_data.module = m; - source_output_data.source = master; + pa_source_output_new_data_set_source(&source_output_data, master, FALSE); source_output_data.destination_source = u->source; /* FIXME source_output_data.flags = PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND; */ diff --git a/src/modules/rtp/module-rtp-send.c b/src/modules/rtp/module-rtp-send.c index f53020d1a..e0fed9974 100644 --- a/src/modules/rtp/module-rtp-send.c +++ b/src/modules/rtp/module-rtp-send.c @@ -325,7 +325,7 @@ int pa__init(pa_module*m) { pa_proplist_setf(data.proplist, "rtp.ttl", "%lu", (unsigned long) ttl); data.driver = __FILE__; data.module = m; - data.source = s; + pa_source_output_new_data_set_source(&data, s, FALSE); pa_source_output_new_data_set_sample_spec(&data, &ss); pa_source_output_new_data_set_channel_map(&data, &cm); data.flags = PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND; diff --git a/src/pulse/stream.c b/src/pulse/stream.c index aeb897c23..329368486 100644 --- a/src/pulse/stream.c +++ b/src/pulse/stream.c @@ -1092,7 +1092,9 @@ void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, s->timing_info.configured_sink_usec = usec; } - if (s->context->version >= 21 && s->direction == PA_STREAM_PLAYBACK) { + if ((s->context->version >= 21 && s->direction == PA_STREAM_PLAYBACK) + || s->context->version >= 22) { + pa_format_info *f = pa_format_info_new(); pa_tagstruct_get_format_info(t, f); @@ -1319,26 +1321,18 @@ static int create_stream( pa_tagstruct_put_boolean(t, flags & PA_STREAM_FAIL_ON_SUSPEND); } - if (s->context->version >= 17) { - - if (s->direction == PA_STREAM_PLAYBACK) - pa_tagstruct_put_boolean(t, flags & PA_STREAM_RELATIVE_VOLUME); - - } - - if (s->context->version >= 18) { + if (s->context->version >= 17 && s->direction == PA_STREAM_PLAYBACK) + pa_tagstruct_put_boolean(t, flags & PA_STREAM_RELATIVE_VOLUME); - if (s->direction == PA_STREAM_PLAYBACK) - pa_tagstruct_put_boolean(t, flags & (PA_STREAM_PASSTHROUGH)); - } + if (s->context->version >= 18 && s->direction == PA_STREAM_PLAYBACK) + pa_tagstruct_put_boolean(t, flags & (PA_STREAM_PASSTHROUGH)); - if (s->context->version >= 21) { + if ((s->context->version >= 21 && s->direction == PA_STREAM_PLAYBACK) + || s->context->version >= 22) { - if (s->direction == PA_STREAM_PLAYBACK) { - pa_tagstruct_putu8(t, s->n_formats); - for (i = 0; i < s->n_formats; i++) - pa_tagstruct_put_format_info(t, s->req_formats[i]); - } + pa_tagstruct_putu8(t, s->n_formats); + for (i = 0; i < s->n_formats; i++) + pa_tagstruct_put_format_info(t, s->req_formats[i]); } pa_pstream_send_tagstruct(s->context->pstream, t); diff --git a/src/pulsecore/protocol-esound.c b/src/pulsecore/protocol-esound.c index c2af77c2c..edca96a19 100644 --- a/src/pulsecore/protocol-esound.c +++ b/src/pulsecore/protocol-esound.c @@ -524,7 +524,7 @@ static int esd_proto_stream_record(connection *c, esd_proto_t request, const voi sdata.driver = __FILE__; sdata.module = c->options->module; sdata.client = c->client; - sdata.source = source; + pa_source_output_new_data_set_source(&sdata, source, FALSE); pa_source_output_new_data_set_sample_spec(&sdata, &ss); pa_source_output_new(&c->source_output, c->protocol->core, &sdata); diff --git a/src/pulsecore/protocol-http.c b/src/pulsecore/protocol-http.c index 83067f854..1de04345a 100644 --- a/src/pulsecore/protocol-http.c +++ b/src/pulsecore/protocol-http.c @@ -560,7 +560,7 @@ static void handle_listen_prefix(struct connection *c, const char *source_name) data.driver = __FILE__; data.module = c->module; data.client = c->client; - data.source = source; + pa_source_output_new_data_set_source(&data, source, FALSE); pa_proplist_update(data.proplist, PA_UPDATE_MERGE, c->client->proplist); pa_source_output_new_data_set_sample_spec(&data, &ss); pa_source_output_new_data_set_channel_map(&data, &cm); diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c index b16793d9d..501ff3788 100644 --- a/src/pulsecore/protocol-native.c +++ b/src/pulsecore/protocol-native.c @@ -629,13 +629,14 @@ static record_stream* record_stream_new( pa_source *source, pa_sample_spec *ss, pa_channel_map *map, - pa_bool_t peak_detect, + pa_idxset *formats, pa_buffer_attr *attr, pa_source_output_flags_t flags, pa_proplist *p, pa_bool_t adjust_latency, - pa_sink_input *direct_on_input, pa_bool_t early_requests, + pa_bool_t peak_detect, + pa_sink_input *direct_on_input, int *ret) { record_stream *s; @@ -653,10 +654,15 @@ static record_stream* record_stream_new( data.driver = __FILE__; data.module = c->options->module; data.client = c->client; - data.source = source; + if (source) + pa_source_output_new_data_set_source(&data, source, TRUE); + if (pa_sample_spec_valid(ss)) + pa_source_output_new_data_set_sample_spec(&data, ss); + if (pa_channel_map_valid(map)) + pa_source_output_new_data_set_channel_map(&data, map); + if (formats) + pa_source_output_new_data_set_formats(&data, formats); data.direct_on_input = direct_on_input; - pa_source_output_new_data_set_sample_spec(&data, ss); - pa_source_output_new_data_set_channel_map(&data, map); if (peak_detect) data.resample_method = PA_RESAMPLER_PEAKS; data.flags = flags; @@ -1028,13 +1034,13 @@ static playback_stream* playback_stream_new( pa_cvolume *volume, pa_bool_t muted, pa_bool_t muted_set, - uint32_t syncid, - uint32_t *missing, pa_sink_input_flags_t flags, pa_proplist *p, pa_bool_t adjust_latency, pa_bool_t early_requests, pa_bool_t relative_volume, + uint32_t syncid, + uint32_t *missing, int *ret) { /* Note: This function takes ownership of the 'formats' param, so we need @@ -1900,6 +1906,7 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u adjust_latency = FALSE, early_requests = FALSE, dont_inhibit_auto_suspend = FALSE, + volume_set = TRUE, muted_set = FALSE, fail_on_suspend = FALSE, relative_volume = FALSE, @@ -1907,7 +1914,6 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u pa_sink_input_flags_t flags = 0; pa_proplist *p = NULL; - pa_bool_t volume_set = TRUE; int ret = PA_ERR_INVALID; uint8_t n_formats = 0; pa_format_info *format; @@ -2081,7 +2087,7 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u * flag. For older versions we synthesize it here */ muted_set = muted_set || muted; - s = playback_stream_new(c, sink, &ss, &map, formats, &attr, volume_set ? &volume : NULL, muted, muted_set, syncid, &missing, flags, p, adjust_latency, early_requests, relative_volume, &ret); + s = playback_stream_new(c, sink, &ss, &map, formats, &attr, volume_set ? &volume : NULL, muted, muted_set, flags, p, adjust_latency, early_requests, relative_volume, syncid, &missing, &ret); /* We no longer own the formats idxset */ formats = NULL; @@ -2222,12 +2228,18 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin peak_detect = FALSE, early_requests = FALSE, dont_inhibit_auto_suspend = FALSE, - fail_on_suspend = FALSE; + fail_on_suspend = FALSE, + passthrough = FALSE; + pa_source_output_flags_t flags = 0; - pa_proplist *p; + pa_proplist *p = NULL; uint32_t direct_on_input_idx = PA_INVALID_INDEX; pa_sink_input *direct_on_input = NULL; int ret = PA_ERR_INVALID; + uint8_t n_formats = 0; + pa_format_info *format; + pa_idxset *formats = NULL; + uint32_t i; pa_native_connection_assert_ref(c); pa_assert(t); @@ -2242,17 +2254,15 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin pa_tagstruct_getu32(t, &attr.maxlength) < 0 || pa_tagstruct_get_boolean(t, &corked) < 0 || pa_tagstruct_getu32(t, &attr.fragsize) < 0) { + protocol_error(c); - return; + goto finish; } - CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS); - CHECK_VALIDITY(c->pstream, !source_name || pa_namereg_is_valid_name_or_wildcard(source_name, PA_NAMEREG_SOURCE), tag, PA_ERR_INVALID); - CHECK_VALIDITY(c->pstream, source_index == PA_INVALID_INDEX || !source_name, tag, PA_ERR_INVALID); - CHECK_VALIDITY(c->pstream, !source_name || source_index == PA_INVALID_INDEX, tag, PA_ERR_INVALID); - CHECK_VALIDITY(c->pstream, pa_sample_spec_valid(&ss), tag, PA_ERR_INVALID); - CHECK_VALIDITY(c->pstream, pa_channel_map_valid(&map), tag, PA_ERR_INVALID); - CHECK_VALIDITY(c->pstream, map.channels == ss.channels, tag, PA_ERR_INVALID); + CHECK_VALIDITY_GOTO(c->pstream, c->authorized, tag, PA_ERR_ACCESS, finish); + CHECK_VALIDITY_GOTO(c->pstream, !source_name || pa_namereg_is_valid_name_or_wildcard(source_name, PA_NAMEREG_SOURCE), tag, PA_ERR_INVALID, finish); + CHECK_VALIDITY_GOTO(c->pstream, source_index == PA_INVALID_INDEX || !source_name, tag, PA_ERR_INVALID, finish); + CHECK_VALIDITY_GOTO(c->pstream, !source_name || source_index == PA_INVALID_INDEX, tag, PA_ERR_INVALID, finish); p = pa_proplist_new(); @@ -2271,8 +2281,7 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin pa_tagstruct_get_boolean(t, &variable_rate) < 0) { protocol_error(c); - pa_proplist_free(p); - return; + goto finish; } } @@ -2282,9 +2291,9 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin pa_tagstruct_get_boolean(t, &adjust_latency) < 0 || pa_tagstruct_get_proplist(t, p) < 0 || pa_tagstruct_getu32(t, &direct_on_input_idx) < 0) { + protocol_error(c); - pa_proplist_free(p); - return; + goto finish; } } @@ -2292,8 +2301,7 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin if (pa_tagstruct_get_boolean(t, &early_requests) < 0) { protocol_error(c); - pa_proplist_free(p); - return; + goto finish; } } @@ -2301,32 +2309,59 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin if (pa_tagstruct_get_boolean(t, &dont_inhibit_auto_suspend) < 0 || pa_tagstruct_get_boolean(t, &fail_on_suspend) < 0) { + protocol_error(c); - pa_proplist_free(p); - return; + goto finish; + } + } + + if (c->version >= 22) { + + if (pa_tagstruct_getu8(t, &n_formats) < 0) { + protocol_error(c); + goto finish; + } + + if (n_formats) + formats = pa_idxset_new(NULL, NULL); + + for (i = 0; i < n_formats; i++) { + format = pa_format_info_new(); + if (pa_tagstruct_get_format_info(t, format) < 0) { + protocol_error(c); + goto finish; + } + pa_idxset_put(formats, format, NULL); } } + if (n_formats == 0) { + CHECK_VALIDITY_GOTO(c->pstream, pa_sample_spec_valid(&ss), tag, PA_ERR_INVALID, finish); + CHECK_VALIDITY_GOTO(c->pstream, pa_channel_map_valid(&map), tag, PA_ERR_INVALID, finish); + } else { + PA_IDXSET_FOREACH(format, formats, i) { + CHECK_VALIDITY_GOTO(c->pstream, pa_format_info_valid(format), tag, PA_ERR_INVALID, finish); + } + } + + if (!pa_tagstruct_eof(t)) { protocol_error(c); - pa_proplist_free(p); - return; + goto finish; } if (source_index != PA_INVALID_INDEX) { if (!(source = pa_idxset_get_by_index(c->protocol->core->sources, source_index))) { pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY); - pa_proplist_free(p); - return; + goto finish; } } else if (source_name) { if (!(source = pa_namereg_get(c->protocol->core, source_name, PA_NAMEREG_SOURCE))) { pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY); - pa_proplist_free(p); - return; + goto finish; } } @@ -2334,8 +2369,7 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin if (!(direct_on_input = pa_idxset_get_by_index(c->protocol->core->sink_inputs, direct_on_input_idx))) { pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY); - pa_proplist_free(p); - return; + goto finish; } } @@ -2349,12 +2383,12 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin (no_move ? PA_SOURCE_OUTPUT_DONT_MOVE : 0) | (variable_rate ? PA_SOURCE_OUTPUT_VARIABLE_RATE : 0) | (dont_inhibit_auto_suspend ? PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND : 0) | - (fail_on_suspend ? PA_SOURCE_OUTPUT_NO_CREATE_ON_SUSPEND|PA_SOURCE_OUTPUT_KILL_ON_SUSPEND : 0); + (fail_on_suspend ? PA_SOURCE_OUTPUT_NO_CREATE_ON_SUSPEND|PA_SOURCE_OUTPUT_KILL_ON_SUSPEND : 0) | + (passthrough ? PA_SOURCE_OUTPUT_PASSTHROUGH : 0); - s = record_stream_new(c, source, &ss, &map, peak_detect, &attr, flags, p, adjust_latency, direct_on_input, early_requests, &ret); - pa_proplist_free(p); + s = record_stream_new(c, source, &ss, &map, formats, &attr, flags, p, adjust_latency, early_requests, peak_detect, direct_on_input, &ret); - CHECK_VALIDITY(c->pstream, s, tag, ret); + CHECK_VALIDITY_GOTO(c->pstream, s, tag, ret, finish); reply = reply_new(tag); pa_tagstruct_putu32(reply, s->index); @@ -2385,7 +2419,24 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin if (c->version >= 13) pa_tagstruct_put_usec(reply, s->configured_source_latency); + if (c->version >= 22) { + /* Send back the format we negotiated */ + if (s->source_output->format) + pa_tagstruct_put_format_info(reply, s->source_output->format); + else { + pa_format_info *f = pa_format_info_new(); + pa_tagstruct_put_format_info(reply, f); + pa_format_info_free(f); + } + } + pa_pstream_send_tagstruct(c->pstream, reply); + +finish: + if (p) + pa_proplist_free(p); + if (formats) + pa_idxset_free(formats, (pa_free2_cb_t) pa_format_info_free2, NULL); } static void command_exit(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { diff --git a/src/pulsecore/protocol-simple.c b/src/pulsecore/protocol-simple.c index c1aaa8133..41a3cc5c1 100644 --- a/src/pulsecore/protocol-simple.c +++ b/src/pulsecore/protocol-simple.c @@ -593,7 +593,7 @@ void pa_simple_protocol_connect(pa_simple_protocol *p, pa_iochannel *io, pa_simp data.driver = __FILE__; data.module = o->module; data.client = c->client; - data.source = source; + pa_source_output_new_data_set_source(&data, source, FALSE); pa_proplist_update(data.proplist, PA_UPDATE_MERGE, c->client->proplist); pa_source_output_new_data_set_sample_spec(&data, &o->sample_spec); diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c index 963ef0694..61e069548 100644 --- a/src/pulsecore/source-output.c +++ b/src/pulsecore/source-output.c @@ -30,6 +30,7 @@ #include <pulse/utf8.h> #include <pulse/xmalloc.h> #include <pulse/util.h> +#include <pulse/internal.h> #include <pulsecore/sample-util.h> #include <pulsecore/core-subscribe.h> @@ -69,9 +70,80 @@ void pa_source_output_new_data_set_channel_map(pa_source_output_new_data *data, data->channel_map = *map; } +pa_bool_t pa_source_output_new_data_is_passthrough(pa_source_output_new_data *data) { + pa_assert(data); + + if (PA_LIKELY(data->format) && PA_UNLIKELY(!pa_format_info_is_pcm(data->format))) + return TRUE; + + if (PA_UNLIKELY(data->flags & PA_SOURCE_OUTPUT_PASSTHROUGH)) + return TRUE; + + return FALSE; +} + +pa_bool_t pa_source_output_new_data_set_source(pa_source_output_new_data *data, pa_source *s, pa_bool_t save) { + pa_bool_t ret = TRUE; + pa_idxset *formats = NULL; + + pa_assert(data); + pa_assert(s); + + if (!data->req_formats) { + /* We're not working with the extended API */ + data->source = s; + data->save_source = save; + } else { + /* Extended API: let's see if this source supports the formats the client would like */ + formats = pa_source_check_formats(s, data->req_formats); + + if (formats && !pa_idxset_isempty(formats)) { + /* Source supports at least one of the requested formats */ + data->source = s; + data->save_source = save; + if (data->nego_formats) + pa_idxset_free(data->nego_formats, (pa_free2_cb_t) pa_format_info_free2, NULL); + data->nego_formats = formats; + } else { + /* Source doesn't support any of the formats requested by the client */ + if (formats) + pa_idxset_free(formats, (pa_free2_cb_t) pa_format_info_free2, NULL); + ret = FALSE; + } + } + + return ret; +} + +pa_bool_t pa_source_output_new_data_set_formats(pa_source_output_new_data *data, pa_idxset *formats) { + pa_assert(data); + pa_assert(formats); + + if (data->req_formats) + pa_idxset_free(formats, (pa_free2_cb_t) pa_format_info_free2, NULL); + + data->req_formats = formats; + + if (data->source) { + /* Trigger format negotiation */ + return pa_source_output_new_data_set_source(data, data->source, data->save_source); + } + + return TRUE; +} + void pa_source_output_new_data_done(pa_source_output_new_data *data) { pa_assert(data); + if (data->req_formats) + pa_idxset_free(data->req_formats, (pa_free2_cb_t) pa_format_info_free2, NULL); + + if (data->nego_formats) + pa_idxset_free(data->nego_formats, (pa_free2_cb_t) pa_format_info_free2, NULL); + + if (data->format) + pa_format_info_free(data->format); + pa_proplist_free(data->proplist); } @@ -106,8 +178,11 @@ int pa_source_output_new( pa_source_output *o; pa_resampler *resampler = NULL; char st[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX]; + pa_channel_map original_cm; int r; char *pt; + pa_sample_spec ss; + pa_channel_map map; pa_assert(_o); pa_assert(core); @@ -117,14 +192,44 @@ int pa_source_output_new( if (data->client) pa_proplist_update(data->proplist, PA_UPDATE_MERGE, data->client->proplist); + if (!data->req_formats) { + /* From this point on, we want to work only with formats, and get back + * to using the sample spec and channel map after all decisions w.r.t. + * routing are complete. */ + pa_idxset *tmp = pa_idxset_new(NULL, NULL); + pa_format_info *f = pa_format_info_from_sample_spec(&data->sample_spec, &data->channel_map); + pa_idxset_put(tmp, f, NULL); + pa_source_output_new_data_set_formats(data, tmp); + } + if ((r = pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], data)) < 0) return r; pa_return_val_if_fail(!data->driver || pa_utf8_valid(data->driver), -PA_ERR_INVALID); - if (!data->source) { - data->source = pa_namereg_get(core, NULL, PA_NAMEREG_SOURCE); - data->save_source = FALSE; + if (!data->source) + pa_source_output_new_data_set_source(data, pa_namereg_get(core, NULL, PA_NAMEREG_SOURCE), FALSE); + + /* Routing's done, we have a source. Now let's fix the format and set up the + * sample spec */ + + /* If something didn't pick a format for us, pick the top-most format since + * we assume this is sorted in priority order */ + if (!data->format && data->nego_formats && !pa_idxset_isempty(data->nego_formats)) + data->format = pa_format_info_copy(pa_idxset_first(data->nego_formats, NULL)); + + pa_return_val_if_fail(data->format, -PA_ERR_NOTSUPPORTED); + + /* Now populate the sample spec and format according to the final + * format that we've negotiated */ + if (PA_LIKELY(data->format->encoding == PA_ENCODING_PCM)) { + pa_return_val_if_fail(pa_format_info_to_sample_spec(data->format, &ss, &map), -PA_ERR_INVALID); + pa_source_output_new_data_set_sample_spec(data, &ss); + if (pa_channel_map_valid(&map)) + pa_source_output_new_data_set_channel_map(data, &map); + } else { + pa_return_val_if_fail(pa_format_info_to_sample_spec_fake(data->format, &ss), -PA_ERR_INVALID); + pa_source_output_new_data_set_sample_spec(data, &ss); } pa_return_val_if_fail(data->source, -PA_ERR_NOENTITY); @@ -143,7 +248,6 @@ int pa_source_output_new( pa_channel_map_init_extend(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT); } - pa_return_val_if_fail(pa_channel_map_valid(&data->channel_map), -PA_ERR_INVALID); pa_return_val_if_fail(pa_channel_map_compatible(&data->channel_map, &data->sample_spec), -PA_ERR_INVALID); if (data->flags & PA_SOURCE_OUTPUT_FIX_FORMAT) @@ -152,6 +256,8 @@ int pa_source_output_new( if (data->flags & PA_SOURCE_OUTPUT_FIX_RATE) data->sample_spec.rate = data->source->sample_spec.rate; + original_cm = data->channel_map; + if (data->flags & PA_SOURCE_OUTPUT_FIX_CHANNELS) { data->sample_spec.channels = data->source->sample_spec.channels; data->channel_map = data->source->channel_map; @@ -183,18 +289,19 @@ int pa_source_output_new( !pa_sample_spec_equal(&data->sample_spec, &data->source->sample_spec) || !pa_channel_map_equal(&data->channel_map, &data->source->channel_map)) { - if (!(resampler = pa_resampler_new( - core->mempool, - &data->source->sample_spec, &data->source->channel_map, - &data->sample_spec, &data->channel_map, - data->resample_method, - ((data->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) | - ((data->flags & PA_SOURCE_OUTPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) | - (core->disable_remixing || (data->flags & PA_SOURCE_OUTPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0) | - (core->disable_lfe_remixing ? PA_RESAMPLER_NO_LFE : 0)))) { - pa_log_warn("Unsupported resampling operation."); - return -PA_ERR_NOTSUPPORTED; - } + if (!pa_source_output_new_data_is_passthrough(data)) /* no resampler for passthrough content */ + if (!(resampler = pa_resampler_new( + core->mempool, + &data->source->sample_spec, &data->source->channel_map, + &data->sample_spec, &data->channel_map, + data->resample_method, + ((data->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) | + ((data->flags & PA_SOURCE_OUTPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) | + (core->disable_remixing || (data->flags & PA_SOURCE_OUTPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0) | + (core->disable_lfe_remixing ? PA_RESAMPLER_NO_LFE : 0)))) { + pa_log_warn("Unsupported resampling operation."); + return -PA_ERR_NOTSUPPORTED; + } } o = pa_msgobject_new(pa_source_output); @@ -211,15 +318,14 @@ int pa_source_output_new( o->destination_source = data->destination_source; o->client = data->client; - o->actual_resample_method = resampler ? pa_resampler_get_method(resampler) : PA_RESAMPLER_INVALID; o->requested_resample_method = data->resample_method; + o->actual_resample_method = resampler ? pa_resampler_get_method(resampler) : PA_RESAMPLER_INVALID; o->sample_spec = data->sample_spec; o->channel_map = data->channel_map; + o->format = pa_format_info_copy(data->format); o->direct_on_input = data->direct_on_input; - o->save_source = data->save_source; - reset_callbacks(o); o->userdata = NULL; @@ -373,6 +479,9 @@ static void source_output_free(pa_object* mo) { if (o->thread_info.resampler) pa_resampler_free(o->thread_info.resampler); + if (o->format) + pa_format_info_free(o->format); + if (o->proplist) pa_proplist_free(o->proplist); @@ -677,6 +786,19 @@ void pa_source_output_set_name(pa_source_output *o, const char *name) { } } +/* Called from main or I/O context */ +pa_bool_t pa_source_output_is_passthrough(pa_source_output *o) { + pa_source_output_assert_ref(o); + + if (PA_UNLIKELY(!pa_format_info_is_pcm(o->format))) + return TRUE; + + if (PA_UNLIKELY(o->flags & PA_SOURCE_OUTPUT_PASSTHROUGH)) + return TRUE; + + return FALSE; +} + /* Called from main thread */ void pa_source_output_update_proplist(pa_source_output *o, pa_update_mode_t mode, pa_proplist *p) { pa_source_output_assert_ref(o); @@ -782,7 +904,18 @@ int pa_source_output_finish_move(pa_source_output *o, pa_source *dest, pa_bool_t pa_source_assert_ref(dest); if (!pa_source_output_may_move_to(o, dest)) - return -1; + return -PA_ERR_NOTSUPPORTED; + + if (pa_source_output_is_passthrough(o) && !pa_source_check_format(dest, o->format)) { + pa_proplist *p = pa_proplist_new(); + pa_log_debug("New source doesn't support stream format, sending format-changed and killing"); + /* Tell the client what device we want to be on if it is going to + * reconnect */ + pa_proplist_sets(p, "device", dest->name); + pa_source_output_send_event(o, PA_STREAM_EVENT_FORMAT_LOST, p); + pa_proplist_free(p); + return -PA_ERR_NOTSUPPORTED; + } if (o->thread_info.resampler && pa_sample_spec_equal(pa_resampler_input_sample_spec(o->thread_info.resampler), &dest->sample_spec) && @@ -795,7 +928,7 @@ int pa_source_output_finish_move(pa_source_output *o, pa_source *dest, pa_bool_t !pa_sample_spec_equal(&o->sample_spec, &dest->sample_spec) || !pa_channel_map_equal(&o->channel_map, &dest->channel_map)) { - /* Okey, we need a new resampler for the new source */ + /* Okay, we need a new resampler for the new source */ if (!(new_resampler = pa_resampler_new( o->core->mempool, diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h index f16f9520b..88de4ae60 100644 --- a/src/pulsecore/source-output.h +++ b/src/pulsecore/source-output.h @@ -28,6 +28,7 @@ typedef struct pa_source_output pa_source_output; #include <pulse/sample.h> #include <pulsecore/source.h> +#include <pulse/format.h> #include <pulsecore/memblockq.h> #include <pulsecore/resampler.h> #include <pulsecore/module.h> @@ -56,7 +57,8 @@ typedef enum pa_source_output_flags { PA_SOURCE_OUTPUT_FIX_CHANNELS = 128, PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND = 256, PA_SOURCE_OUTPUT_NO_CREATE_ON_SUSPEND = 512, - PA_SOURCE_OUTPUT_KILL_ON_SUSPEND = 1024 + PA_SOURCE_OUTPUT_KILL_ON_SUSPEND = 1024, + PA_SOURCE_OUTPUT_PASSTHROUGH = 2048 } pa_source_output_flags_t; struct pa_source_output { @@ -82,6 +84,7 @@ struct pa_source_output { pa_sample_spec sample_spec; pa_channel_map channel_map; + pa_format_info *format; /* if TRUE then the source we are connected to is worth * remembering, i.e. was explicitly chosen by the user and not @@ -218,6 +221,9 @@ typedef struct pa_source_output_new_data { pa_sample_spec sample_spec; pa_channel_map channel_map; + pa_format_info *format; + pa_idxset *req_formats; + pa_idxset *nego_formats; pa_bool_t sample_spec_is_set:1; pa_bool_t channel_map_is_set:1; @@ -228,6 +234,9 @@ typedef struct pa_source_output_new_data { pa_source_output_new_data* pa_source_output_new_data_init(pa_source_output_new_data *data); void pa_source_output_new_data_set_sample_spec(pa_source_output_new_data *data, const pa_sample_spec *spec); void pa_source_output_new_data_set_channel_map(pa_source_output_new_data *data, const pa_channel_map *map); +pa_bool_t pa_source_output_new_data_is_passthrough(pa_source_output_new_data *data); +pa_bool_t pa_source_output_new_data_set_source(pa_source_output_new_data *data, pa_source *s, pa_bool_t save); +pa_bool_t pa_source_output_new_data_set_formats(pa_source_output_new_data *data, pa_idxset *formats); void pa_source_output_new_data_done(pa_source_output_new_data *data); /* To be called by the implementing module only */ @@ -257,6 +266,8 @@ void pa_source_output_kill(pa_source_output*o); pa_usec_t pa_source_output_get_latency(pa_source_output *o, pa_usec_t *source_latency); +pa_bool_t pa_source_output_is_passthrough(pa_source_output *o); + void pa_source_output_update_proplist(pa_source_output *o, pa_update_mode_t mode, pa_proplist *p); pa_resample_method_t pa_source_output_get_resample_method(pa_source_output *o); diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c index 15a5b8d9c..587291adc 100644 --- a/src/pulsecore/source.c +++ b/src/pulsecore/source.c @@ -32,6 +32,7 @@ #include <pulse/xmalloc.h> #include <pulse/timeval.h> #include <pulse/util.h> +#include <pulse/internal.h> #include <pulsecore/core-util.h> #include <pulsecore/source-output.h> @@ -130,6 +131,7 @@ static void reset_callbacks(pa_source *s) { s->set_mute = NULL; s->update_requested_latency = NULL; s->set_port = NULL; + s->get_formats = NULL; } /* Called from main context */ @@ -754,6 +756,15 @@ pa_usec_t pa_source_get_latency_within_thread(pa_source *s) { return usec; } +/* Called from main context */ +pa_bool_t pa_source_is_passthrough(pa_source *s) { + + pa_source_assert_ref(s); + + /* NB Currently only monitor sources support passthrough mode */ + return (s->monitor_of && pa_sink_is_passthrough(s->monitor_of)); +} + /* Called from main thread */ void pa_source_set_volume( pa_source *s, @@ -1575,3 +1586,84 @@ int pa_source_set_port(pa_source *s, const char *name, pa_bool_t save) { return 0; } + +/* Called from the main thread */ +/* Gets the list of formats supported by the source. The members and idxset must + * be freed by the caller. */ +pa_idxset* pa_source_get_formats(pa_source *s) { + pa_idxset *ret; + + pa_assert(s); + + if (s->get_formats) { + /* Source supports format query, all is good */ + ret = s->get_formats(s); + } else { + /* Source doesn't support format query, so assume it does PCM */ + pa_format_info *f = pa_format_info_new(); + f->encoding = PA_ENCODING_PCM; + + ret = pa_idxset_new(NULL, NULL); + pa_idxset_put(ret, f, NULL); + } + + return ret; +} + +/* Called from the main thread */ +/* Checks if the source can accept this format */ +pa_bool_t pa_source_check_format(pa_source *s, pa_format_info *f) +{ + pa_idxset *formats = NULL; + pa_bool_t ret = FALSE; + + pa_assert(s); + pa_assert(f); + + formats = pa_source_get_formats(s); + + if (formats) { + pa_format_info *finfo_device; + uint32_t i; + + PA_IDXSET_FOREACH(finfo_device, formats, i) { + if (pa_format_info_is_compatible(finfo_device, f)) { + ret = TRUE; + break; + } + } + + pa_idxset_free(formats, (pa_free2_cb_t) pa_format_info_free2, NULL); + } + + return ret; +} + +/* Called from the main thread */ +/* Calculates the intersection between formats supported by the source and + * in_formats, and returns these, in the order of the source's formats. */ +pa_idxset* pa_source_check_formats(pa_source *s, pa_idxset *in_formats) { + pa_idxset *out_formats = pa_idxset_new(NULL, NULL), *source_formats = NULL; + pa_format_info *f_source, *f_in; + uint32_t i, j; + + pa_assert(s); + + if (!in_formats || pa_idxset_isempty(in_formats)) + goto done; + + source_formats = pa_source_get_formats(s); + + PA_IDXSET_FOREACH(f_source, source_formats, i) { + PA_IDXSET_FOREACH(f_in, in_formats, j) { + if (pa_format_info_is_compatible(f_source, f_in)) + pa_idxset_put(out_formats, pa_format_info_copy(f_in), NULL); + } + } + +done: + if (source_formats) + pa_idxset_free(source_formats, (pa_free2_cb_t) pa_format_info_free2, NULL); + + return out_formats; +} diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h index 9e8e2ada2..3cdc77aef 100644 --- a/src/pulsecore/source.h +++ b/src/pulsecore/source.h @@ -108,31 +108,35 @@ struct pa_source { * context. If this is NULL a PA_SOURCE_MESSAGE_GET_VOLUME message * will be sent to the IO thread instead. If refresh_volume is * FALSE neither this function is called nor a message is sent. */ - void (*get_volume)(pa_source *s); /* dito */ + void (*get_volume)(pa_source *s); /* ditto */ /* Called when the volume shall be changed. Called from main loop * context. If this is NULL a PA_SOURCE_MESSAGE_SET_VOLUME message * will be sent to the IO thread instead. */ - void (*set_volume)(pa_source *s); /* dito */ + void (*set_volume)(pa_source *s); /* ditto */ /* Called when the mute setting is queried. Called from main loop * context. If this is NULL a PA_SOURCE_MESSAGE_GET_MUTE message * will be sent to the IO thread instead. If refresh_mute is * FALSE neither this function is called nor a message is sent.*/ - void (*get_mute)(pa_source *s); /* dito */ + void (*get_mute)(pa_source *s); /* ditto */ /* Called when the mute setting shall be changed. Called from main * loop context. If this is NULL a PA_SOURCE_MESSAGE_SET_MUTE * message will be sent to the IO thread instead. */ - void (*set_mute)(pa_source *s); /* dito */ + void (*set_mute)(pa_source *s); /* ditto */ /* Called when a the requested latency is changed. Called from IO * thread context. */ - void (*update_requested_latency)(pa_source *s); /* dito */ + void (*update_requested_latency)(pa_source *s); /* ditto */ /* Called whenever the port shall be changed. Called from main * thread. */ - int (*set_port)(pa_source *s, pa_device_port *port); /*dito */ + int (*set_port)(pa_source *s, pa_device_port *port); /*ditto */ + + /* Called to get the list of formats supported by the source, sorted + * in descending order of preference. */ + pa_idxset* (*get_formats)(pa_source *s); /* ditto */ /* Contains copies of the above data so that the real-time worker * thread can work without access locking */ @@ -265,6 +269,10 @@ int pa_source_update_status(pa_source*s); int pa_source_suspend(pa_source *s, pa_bool_t suspend, pa_suspend_cause_t cause); int pa_source_suspend_all(pa_core *c, pa_bool_t suspend, pa_suspend_cause_t cause); +/* Is the source in passthrough mode? (that is, is this a monitor source for a sink + * that has a passthrough sink input connected to it. */ +pa_bool_t pa_source_is_passthrough(pa_source *s); + void pa_source_set_volume(pa_source *source, const pa_cvolume *volume, pa_bool_t save); const pa_cvolume *pa_source_get_volume(pa_source *source, pa_bool_t force_refresh); @@ -285,6 +293,10 @@ pa_queue *pa_source_move_all_start(pa_source *s, pa_queue *q); void pa_source_move_all_finish(pa_source *s, pa_queue *q, pa_bool_t save); void pa_source_move_all_fail(pa_queue *q); +pa_idxset* pa_source_get_formats(pa_source *s); +pa_bool_t pa_source_check_format(pa_source *s, pa_format_info *f); +pa_idxset* pa_source_check_formats(pa_source *s, pa_idxset *in_formats); + /*** To be called exclusively by the source driver, from IO context */ void pa_source_post(pa_source*s, const pa_memchunk *chunk); |