summaryrefslogtreecommitdiffstats
path: root/src/pulsecore/source-output.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/pulsecore/source-output.c')
-rw-r--r--src/pulsecore/source-output.c175
1 files changed, 154 insertions, 21 deletions
diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c
index 963ef069..61e06954 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,