summaryrefslogtreecommitdiffstats
path: root/src/pulsecore/source-output.c
diff options
context:
space:
mode:
authorColin Guthrie <colin@mageia.org>2011-05-17 21:56:10 +0100
committerColin Guthrie <colin@mageia.org>2011-06-22 21:55:27 +0100
commit5d35375aa758fde7d9f3d6467e2506aca9784597 (patch)
tree13a5eef2e1a06d0d5a9a76a83f0c90d085819e06 /src/pulsecore/source-output.c
parent30597b7c2747a52b1025e1172d73825e148fdec9 (diff)
capture: Add the passthrough format negotiation to capture streams.
This helps to keep the API more symmetrical and also potentially allows support for passthrough monitor sources at some point in the future.
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,