mirror of
https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
synced 2025-10-28 05:40:21 -04:00
core: Add extended stream API to support compressed formats
This is the beginning of work to support compressed formats natively in PulseAudio. This adds a pa_stream_new_extended() that takes a format structure, sends it to the server (=> protocol extension) and has the server negotiate with the appropropriate sink to figure out what format it should use. This is work in progress, and works only with PCM streams. Actual compressed format support in some sink needs to be implemented, and extensive testing is required. More details on how this is supposed to work is available at: http://pulseaudio.org/wiki/PassthroughSupport
This commit is contained in:
parent
47e0f91aa2
commit
0ac2cfce6d
25 changed files with 347 additions and 82 deletions
15
PROTOCOL
15
PROTOCOL
|
|
@ -213,3 +213,18 @@ Two new flags at the end of sink input introspection data:
|
|||
|
||||
bool has_volume
|
||||
bool volume_writable
|
||||
|
||||
## v21, implemented by >= 1.0
|
||||
|
||||
Changes for format negotiation in the extended API.
|
||||
|
||||
New fields PA_COMMAND_CREATE_PLAYBACK_STREAM:
|
||||
|
||||
uint8_t n_formats
|
||||
format_info format1
|
||||
...
|
||||
format_info formatn
|
||||
|
||||
One new field in reply from PA_COMMAND_CREATE_PLAYBACK_STREAM:
|
||||
|
||||
format_info format
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ AC_SUBST(PA_MAJORMINOR, pa_major.pa_minor)
|
|||
AC_SUBST(PACKAGE_URL, [http://pulseaudio.org/])
|
||||
|
||||
AC_SUBST(PA_API_VERSION, 12)
|
||||
AC_SUBST(PA_PROTOCOL_VERSION, 20)
|
||||
AC_SUBST(PA_PROTOCOL_VERSION, 21)
|
||||
|
||||
# The stable ABI for client applications, for the version info x:y:z
|
||||
# always will hold y=z
|
||||
|
|
|
|||
|
|
@ -265,6 +265,7 @@ pa_stream_get_timing_info;
|
|||
pa_stream_is_corked;
|
||||
pa_stream_is_suspended;
|
||||
pa_stream_new;
|
||||
pa_stream_new_extended;
|
||||
pa_stream_new_with_proplist;
|
||||
pa_stream_peek;
|
||||
pa_stream_prebuf;
|
||||
|
|
|
|||
|
|
@ -1549,7 +1549,7 @@ int pa__init(pa_module*m) {
|
|||
pa_sink_input_new_data_init(&sink_input_data);
|
||||
sink_input_data.driver = __FILE__;
|
||||
sink_input_data.module = m;
|
||||
sink_input_data.sink = sink_master;
|
||||
pa_sink_input_new_data_set_sink(&sink_input_data, sink_master, FALSE);
|
||||
sink_input_data.origin_sink = u->sink;
|
||||
pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "Echo-Cancel Sink Stream");
|
||||
pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter");
|
||||
|
|
|
|||
|
|
@ -845,7 +845,7 @@ static int output_create_sink_input(struct output *o) {
|
|||
return 0;
|
||||
|
||||
pa_sink_input_new_data_init(&data);
|
||||
data.sink = o->sink;
|
||||
pa_sink_input_new_data_set_sink(&data, o->sink, FALSE);
|
||||
data.driver = __FILE__;
|
||||
pa_proplist_setf(data.proplist, PA_PROP_MEDIA_NAME, "Simultaneous output on %s", pa_strnull(pa_proplist_gets(o->sink->proplist, PA_PROP_DEVICE_DESCRIPTION)));
|
||||
pa_proplist_sets(data.proplist, PA_PROP_MEDIA_ROLE, "filter");
|
||||
|
|
|
|||
|
|
@ -832,8 +832,8 @@ static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_n
|
|||
pa_sink *sink;
|
||||
|
||||
if ((sink = pa_idxset_get_by_index(u->core->sinks, device_index))) {
|
||||
new_data->sink = sink;
|
||||
new_data->save_sink = FALSE;
|
||||
if (!pa_sink_input_new_data_set_sink(new_data, sink, FALSE))
|
||||
pa_log_debug("Not restoring device for stream because no supported format was found");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1212,7 +1212,7 @@ int pa__init(pa_module*m) {
|
|||
pa_sink_input_new_data_init(&sink_input_data);
|
||||
sink_input_data.driver = __FILE__;
|
||||
sink_input_data.module = m;
|
||||
sink_input_data.sink = master;
|
||||
pa_sink_input_new_data_set_sink(&sink_input_data, master, FALSE);
|
||||
sink_input_data.origin_sink = u->sink;
|
||||
pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "Equalized Stream");
|
||||
pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter");
|
||||
|
|
|
|||
|
|
@ -117,11 +117,8 @@ static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_n
|
|||
|
||||
/* Prefer the default sink over any other sink, just in case... */
|
||||
if ((def = pa_namereg_get_default_sink(c)))
|
||||
if (role_match(def->proplist, role)) {
|
||||
new_data->sink = def;
|
||||
new_data->save_sink = FALSE;
|
||||
if (role_match(def->proplist, role) && pa_sink_input_new_data_set_sink(new_data, def, FALSE))
|
||||
return PA_HOOK_OK;
|
||||
}
|
||||
|
||||
/* @todo: favour the highest priority device, not the first one we find? */
|
||||
PA_IDXSET_FOREACH(s, c->sinks, idx) {
|
||||
|
|
@ -131,12 +128,9 @@ static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_n
|
|||
if (!PA_SINK_IS_LINKED(pa_sink_get_state(s)))
|
||||
continue;
|
||||
|
||||
if (role_match(s->proplist, role)) {
|
||||
new_data->sink = s;
|
||||
new_data->save_sink = FALSE;
|
||||
if (role_match(s->proplist, role) && pa_sink_input_new_data_set_sink(new_data, s, FALSE))
|
||||
return PA_HOOK_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return PA_HOOK_OK;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -907,7 +907,7 @@ int pa__init(pa_module*m) {
|
|||
pa_sink_input_new_data_init(&sink_input_data);
|
||||
sink_input_data.driver = __FILE__;
|
||||
sink_input_data.module = m;
|
||||
sink_input_data.sink = master;
|
||||
pa_sink_input_new_data_set_sink(&sink_input_data, master, FALSE);
|
||||
sink_input_data.origin_sink = u->sink;
|
||||
pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "LADSPA Stream");
|
||||
pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter");
|
||||
|
|
|
|||
|
|
@ -695,7 +695,7 @@ int pa__init(pa_module *m) {
|
|||
pa_sink_input_new_data_init(&sink_input_data);
|
||||
sink_input_data.driver = __FILE__;
|
||||
sink_input_data.module = m;
|
||||
sink_input_data.sink = sink;
|
||||
pa_sink_input_new_data_set_sink(&sink_input_data, sink, FALSE);
|
||||
|
||||
if ((n = pa_modargs_get_value(ma, "sink_input_name", NULL)))
|
||||
pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, n);
|
||||
|
|
|
|||
|
|
@ -419,7 +419,7 @@ int pa__init(pa_module*m) {
|
|||
pa_sink_input_new_data_init(&sink_input_data);
|
||||
sink_input_data.driver = __FILE__;
|
||||
sink_input_data.module = m;
|
||||
sink_input_data.sink = master;
|
||||
pa_sink_input_new_data_set_sink(&sink_input_data, master, FALSE);
|
||||
sink_input_data.origin_sink = u->sink;
|
||||
pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "Remapped Stream");
|
||||
pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter");
|
||||
|
|
|
|||
|
|
@ -157,7 +157,7 @@ int pa__init(pa_module*m) {
|
|||
pa_sink_input_new_data_init(&data);
|
||||
data.driver = __FILE__;
|
||||
data.module = m;
|
||||
data.sink = sink;
|
||||
pa_sink_input_new_data_set_sink(&data, sink, FALSE);
|
||||
pa_proplist_setf(data.proplist, PA_PROP_MEDIA_NAME, "%u Hz Sine", frequency);
|
||||
pa_proplist_sets(data.proplist, PA_PROP_MEDIA_ROLE, "abstract");
|
||||
pa_proplist_setf(data.proplist, "sine.hz", "%u", frequency);
|
||||
|
|
|
|||
|
|
@ -1301,11 +1301,9 @@ static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_n
|
|||
/* It might happen that a stream and a sink are set up at the
|
||||
same time, in which case we want to make sure we don't
|
||||
interfere with that */
|
||||
if (s && PA_SINK_IS_LINKED(pa_sink_get_state(s))) {
|
||||
if (s && PA_SINK_IS_LINKED(pa_sink_get_state(s)))
|
||||
if (pa_sink_input_new_data_set_sink(new_data, s, TRUE))
|
||||
pa_log_info("Restoring device for stream %s.", name);
|
||||
new_data->sink = s;
|
||||
new_data->save_sink = TRUE;
|
||||
}
|
||||
|
||||
pa_xfree(e);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -579,7 +579,7 @@ int pa__init(pa_module*m) {
|
|||
pa_sink_input_new_data_init(&sink_input_data);
|
||||
sink_input_data.driver = __FILE__;
|
||||
sink_input_data.module = m;
|
||||
sink_input_data.sink = master;
|
||||
pa_sink_input_new_data_set_sink(&sink_input_data, master, FALSE);
|
||||
sink_input_data.origin_sink = u->sink;
|
||||
pa_proplist_setf(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "Virtual Sink Stream from %s", pa_proplist_gets(u->sink->proplist, PA_PROP_DEVICE_DESCRIPTION));
|
||||
pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter");
|
||||
|
|
|
|||
|
|
@ -512,7 +512,7 @@ static struct session *session_new(struct userdata *u, const pa_sdp_info *sdp_in
|
|||
goto fail;
|
||||
|
||||
pa_sink_input_new_data_init(&data);
|
||||
data.sink = sink;
|
||||
pa_sink_input_new_data_set_sink(&data, sink, FALSE);
|
||||
data.driver = __FILE__;
|
||||
pa_proplist_sets(data.proplist, PA_PROP_MEDIA_ROLE, "stream");
|
||||
pa_proplist_setf(data.proplist, PA_PROP_MEDIA_NAME,
|
||||
|
|
|
|||
|
|
@ -122,6 +122,8 @@ typedef struct pa_index_correction {
|
|||
pa_bool_t corrupt:1;
|
||||
} pa_index_correction;
|
||||
|
||||
#define PA_MAX_FORMATS (PA_ENCODING_MAX)
|
||||
|
||||
struct pa_stream {
|
||||
PA_REFCNT_DECLARE;
|
||||
PA_LLIST_FIELDS(pa_stream);
|
||||
|
|
@ -137,6 +139,9 @@ struct pa_stream {
|
|||
|
||||
pa_sample_spec sample_spec;
|
||||
pa_channel_map channel_map;
|
||||
uint8_t n_formats;
|
||||
pa_format_info *req_formats[PA_MAX_FORMATS];
|
||||
pa_format_info *format;
|
||||
|
||||
pa_proplist *proplist;
|
||||
|
||||
|
|
|
|||
|
|
@ -80,31 +80,24 @@ static void reset_callbacks(pa_stream *s) {
|
|||
s->buffer_attr_userdata = NULL;
|
||||
}
|
||||
|
||||
pa_stream *pa_stream_new_with_proplist(
|
||||
static pa_stream *pa_stream_new_with_proplist_internal(
|
||||
pa_context *c,
|
||||
const char *name,
|
||||
const pa_sample_spec *ss,
|
||||
const pa_channel_map *map,
|
||||
pa_format_info * const *formats,
|
||||
pa_proplist *p) {
|
||||
|
||||
pa_stream *s;
|
||||
int i;
|
||||
pa_channel_map tmap;
|
||||
|
||||
pa_assert(c);
|
||||
pa_assert(PA_REFCNT_VALUE(c) >= 1);
|
||||
pa_assert((ss == NULL && map == NULL) || formats == NULL);
|
||||
|
||||
PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
|
||||
PA_CHECK_VALIDITY_RETURN_NULL(c, ss && pa_sample_spec_valid(ss), PA_ERR_INVALID);
|
||||
PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 12 || (ss->format != PA_SAMPLE_S32LE && ss->format != PA_SAMPLE_S32BE), PA_ERR_NOTSUPPORTED);
|
||||
PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 15 || (ss->format != PA_SAMPLE_S24LE && ss->format != PA_SAMPLE_S24BE), PA_ERR_NOTSUPPORTED);
|
||||
PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 15 || (ss->format != PA_SAMPLE_S24_32LE && ss->format != PA_SAMPLE_S24_32BE), PA_ERR_NOTSUPPORTED);
|
||||
PA_CHECK_VALIDITY_RETURN_NULL(c, !map || (pa_channel_map_valid(map) && map->channels == ss->channels), PA_ERR_INVALID);
|
||||
PA_CHECK_VALIDITY_RETURN_NULL(c, name || (p && pa_proplist_contains(p, PA_PROP_MEDIA_NAME)), PA_ERR_INVALID);
|
||||
|
||||
if (!map)
|
||||
PA_CHECK_VALIDITY_RETURN_NULL(c, map = pa_channel_map_init_auto(&tmap, ss->channels, PA_CHANNEL_MAP_DEFAULT), PA_ERR_INVALID);
|
||||
|
||||
s = pa_xnew(pa_stream, 1);
|
||||
PA_REFCNT_INIT(s);
|
||||
s->context = c;
|
||||
|
|
@ -114,8 +107,28 @@ pa_stream *pa_stream_new_with_proplist(
|
|||
s->state = PA_STREAM_UNCONNECTED;
|
||||
s->flags = 0;
|
||||
|
||||
if (ss)
|
||||
s->sample_spec = *ss;
|
||||
else
|
||||
s->sample_spec.format = PA_SAMPLE_INVALID;
|
||||
|
||||
if (map)
|
||||
s->channel_map = *map;
|
||||
else
|
||||
pa_channel_map_init(&s->channel_map);
|
||||
|
||||
s->n_formats = 0;
|
||||
if (formats) {
|
||||
for (i = 0; formats[i] && i < PA_MAX_FORMATS; i++) {
|
||||
s->n_formats++;
|
||||
s->req_formats[i] = pa_format_info_copy(formats[i]);
|
||||
}
|
||||
/* Make sure the input array was NULL-terminated */
|
||||
pa_assert(formats[i] == NULL);
|
||||
}
|
||||
|
||||
/* We'll get the final negotiated format after connecting */
|
||||
s->format = NULL;
|
||||
|
||||
s->direct_on_input = PA_INVALID_INDEX;
|
||||
|
||||
|
|
@ -136,7 +149,10 @@ pa_stream *pa_stream_new_with_proplist(
|
|||
* what older PA versions provided. */
|
||||
|
||||
s->buffer_attr.maxlength = (uint32_t) -1;
|
||||
if (ss)
|
||||
s->buffer_attr.tlength = (uint32_t) pa_usec_to_bytes(250*PA_USEC_PER_MSEC, ss); /* 250ms of buffering */
|
||||
else
|
||||
/* XXX: How do we apply worst case conversion here? */
|
||||
s->buffer_attr.minreq = (uint32_t) -1;
|
||||
s->buffer_attr.prebuf = (uint32_t) -1;
|
||||
s->buffer_attr.fragsize = (uint32_t) -1;
|
||||
|
|
@ -179,6 +195,40 @@ pa_stream *pa_stream_new_with_proplist(
|
|||
return s;
|
||||
}
|
||||
|
||||
pa_stream *pa_stream_new_with_proplist(
|
||||
pa_context *c,
|
||||
const char *name,
|
||||
const pa_sample_spec *ss,
|
||||
const pa_channel_map *map,
|
||||
pa_proplist *p) {
|
||||
|
||||
pa_channel_map tmap;
|
||||
|
||||
PA_CHECK_VALIDITY_RETURN_NULL(c, ss && pa_sample_spec_valid(ss), PA_ERR_INVALID);
|
||||
PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 12 || (ss->format != PA_SAMPLE_S32LE && ss->format != PA_SAMPLE_S32BE), PA_ERR_NOTSUPPORTED);
|
||||
PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 15 || (ss->format != PA_SAMPLE_S24LE && ss->format != PA_SAMPLE_S24BE), PA_ERR_NOTSUPPORTED);
|
||||
PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 15 || (ss->format != PA_SAMPLE_S24_32LE && ss->format != PA_SAMPLE_S24_32BE), PA_ERR_NOTSUPPORTED);
|
||||
PA_CHECK_VALIDITY_RETURN_NULL(c, !map || (pa_channel_map_valid(map) && map->channels == ss->channels), PA_ERR_INVALID);
|
||||
|
||||
if (!map)
|
||||
PA_CHECK_VALIDITY_RETURN_NULL(c, map = pa_channel_map_init_auto(&tmap, ss->channels, PA_CHANNEL_MAP_DEFAULT), PA_ERR_INVALID);
|
||||
|
||||
return pa_stream_new_with_proplist_internal(c, name, ss, map, NULL, p);
|
||||
}
|
||||
|
||||
pa_stream *pa_stream_new_extended(
|
||||
pa_context *c,
|
||||
const char *name,
|
||||
pa_format_info * const *formats,
|
||||
pa_proplist *p) {
|
||||
|
||||
PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 21, PA_ERR_NOTSUPPORTED);
|
||||
|
||||
/* XXX: For the single-format PCM case, pass ss/map instead of formats */
|
||||
|
||||
return pa_stream_new_with_proplist_internal(c, name, NULL, NULL, formats, p);
|
||||
}
|
||||
|
||||
static void stream_unlink(pa_stream *s) {
|
||||
pa_operation *o, *n;
|
||||
pa_assert(s);
|
||||
|
|
@ -220,6 +270,8 @@ static void stream_unlink(pa_stream *s) {
|
|||
}
|
||||
|
||||
static void stream_free(pa_stream *s) {
|
||||
unsigned int i;
|
||||
|
||||
pa_assert(s);
|
||||
|
||||
stream_unlink(s);
|
||||
|
|
@ -244,6 +296,9 @@ static void stream_free(pa_stream *s) {
|
|||
if (s->smoother)
|
||||
pa_smoother_free(s->smoother);
|
||||
|
||||
for (i = 0; i < s->n_formats; i++)
|
||||
pa_xfree(s->req_formats[i]);
|
||||
|
||||
pa_xfree(s->device_name);
|
||||
pa_xfree(s);
|
||||
}
|
||||
|
|
@ -970,9 +1025,11 @@ void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag,
|
|||
ss.channels != cm.channels ||
|
||||
!pa_channel_map_valid(&cm) ||
|
||||
!pa_sample_spec_valid(&ss) ||
|
||||
(s->n_formats == 0 && (
|
||||
(!(s->flags & PA_STREAM_FIX_FORMAT) && ss.format != s->sample_spec.format) ||
|
||||
(!(s->flags & PA_STREAM_FIX_RATE) && ss.rate != s->sample_spec.rate) ||
|
||||
(!(s->flags & PA_STREAM_FIX_CHANNELS) && !pa_channel_map_equal(&cm, &s->channel_map))) {
|
||||
(!(s->flags & PA_STREAM_FIX_CHANNELS) && !pa_channel_map_equal(&cm, &s->channel_map))))) {
|
||||
/* XXX: checks for the n_formats > 0 case? */
|
||||
pa_context_fail(s->context, PA_ERR_PROTOCOL);
|
||||
goto finish;
|
||||
}
|
||||
|
|
@ -999,6 +1056,16 @@ 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) {
|
||||
pa_format_info *f = pa_format_info_new();
|
||||
pa_tagstruct_get_format_info(t, f);
|
||||
|
||||
if (pa_format_info_valid(f))
|
||||
s->format = f;
|
||||
else
|
||||
pa_format_info_free(f);
|
||||
}
|
||||
|
||||
if (!pa_tagstruct_eof(t)) {
|
||||
pa_context_fail(s->context, PA_ERR_PROTOCOL);
|
||||
goto finish;
|
||||
|
|
@ -1039,6 +1106,7 @@ static int create_stream(
|
|||
pa_tagstruct *t;
|
||||
uint32_t tag;
|
||||
pa_bool_t volume_set = FALSE;
|
||||
uint32_t i;
|
||||
|
||||
pa_assert(s);
|
||||
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
||||
|
|
@ -1079,7 +1147,7 @@ static int create_stream(
|
|||
|
||||
PA_CHECK_VALIDITY(s->context, direction == PA_STREAM_PLAYBACK || !(flags & (PA_STREAM_START_MUTED)), PA_ERR_INVALID);
|
||||
PA_CHECK_VALIDITY(s->context, direction == PA_STREAM_RECORD || !(flags & (PA_STREAM_PEAK_DETECT)), PA_ERR_INVALID);
|
||||
PA_CHECK_VALIDITY(s->context, !volume || volume->channels == s->sample_spec.channels, PA_ERR_INVALID);
|
||||
PA_CHECK_VALIDITY(s->context, !volume || (pa_sample_spec_valid(&s->sample_spec) && volume->channels == s->sample_spec.channels), PA_ERR_INVALID);
|
||||
PA_CHECK_VALIDITY(s->context, !sync_stream || (direction == PA_STREAM_PLAYBACK && sync_stream->direction == PA_STREAM_PLAYBACK), PA_ERR_INVALID);
|
||||
PA_CHECK_VALIDITY(s->context, (flags & (PA_STREAM_ADJUST_LATENCY|PA_STREAM_EARLY_REQUESTS)) != (PA_STREAM_ADJUST_LATENCY|PA_STREAM_EARLY_REQUESTS), PA_ERR_INVALID);
|
||||
|
||||
|
|
@ -1147,8 +1215,16 @@ static int create_stream(
|
|||
|
||||
volume_set = !!volume;
|
||||
|
||||
if (!volume)
|
||||
if (!volume) {
|
||||
if (pa_sample_spec_valid(&s->sample_spec))
|
||||
volume = pa_cvolume_reset(&cv, s->sample_spec.channels);
|
||||
else {
|
||||
/* This is not really relevant, since no volume was set, and
|
||||
* the real number of channels is embedded in the format_info
|
||||
* structure */
|
||||
volume = pa_cvolume_reset(&cv, PA_CHANNELS_MAX);
|
||||
}
|
||||
}
|
||||
|
||||
pa_tagstruct_put_cvolume(t, volume);
|
||||
} else
|
||||
|
|
@ -1214,6 +1290,15 @@ static int create_stream(
|
|||
pa_tagstruct_put_boolean(t, flags & (PA_STREAM_PASSTHROUGH));
|
||||
}
|
||||
|
||||
if (s->context->version >= 21) {
|
||||
|
||||
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_pstream_send_tagstruct(s->context->pstream, t);
|
||||
pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_create_stream_callback, s, NULL);
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
#include <sys/types.h>
|
||||
|
||||
#include <pulse/sample.h>
|
||||
#include <pulse/format.h>
|
||||
#include <pulse/channelmap.h>
|
||||
#include <pulse/volume.h>
|
||||
#include <pulse/def.h>
|
||||
|
|
@ -356,6 +357,16 @@ pa_stream* pa_stream_new_with_proplist(
|
|||
const pa_channel_map *map /**< The desired channel map, or NULL for default */,
|
||||
pa_proplist *p /**< The initial property list */);
|
||||
|
||||
/* Create a new, unconnected stream with the specified name, the set of formats
|
||||
* this client can provide, and an initial list of properties. While
|
||||
* connecting, the server will select the most appropriate format which the
|
||||
* client must then provide. \since 1.0 */
|
||||
pa_stream *pa_stream_new_extended(
|
||||
pa_context *c /**< The context to create this stream in */,
|
||||
const char *name /**< A name for this stream */,
|
||||
pa_format_info * const * formats /**< The list of formats that can be provided */,
|
||||
pa_proplist *p /**< The initial property list */);
|
||||
|
||||
/** Decrease the reference counter by one */
|
||||
void pa_stream_unref(pa_stream *s);
|
||||
|
||||
|
|
|
|||
|
|
@ -202,7 +202,7 @@ pa_sink_input* pa_memblockq_sink_input_new(
|
|||
u->memblockq = NULL;
|
||||
|
||||
pa_sink_input_new_data_init(&data);
|
||||
data.sink = sink;
|
||||
pa_sink_input_new_data_set_sink(&data, sink, FALSE);
|
||||
data.driver = __FILE__;
|
||||
pa_sink_input_new_data_set_sample_spec(&data, ss);
|
||||
pa_sink_input_new_data_set_channel_map(&data, map);
|
||||
|
|
|
|||
|
|
@ -426,7 +426,7 @@ static int esd_proto_stream_play(connection *c, esd_proto_t request, const void
|
|||
sdata.driver = __FILE__;
|
||||
sdata.module = c->options->module;
|
||||
sdata.client = c->client;
|
||||
sdata.sink = sink;
|
||||
pa_sink_input_new_data_set_sink(&sdata, sink, FALSE);
|
||||
pa_sink_input_new_data_set_sample_spec(&sdata, &ss);
|
||||
|
||||
pa_sink_input_new(&c->sink_input, c->protocol->core, &sdata);
|
||||
|
|
|
|||
|
|
@ -1014,6 +1014,7 @@ static playback_stream* playback_stream_new(
|
|||
pa_sink *sink,
|
||||
pa_sample_spec *ss,
|
||||
pa_channel_map *map,
|
||||
pa_idxset *formats,
|
||||
pa_buffer_attr *a,
|
||||
pa_cvolume *volume,
|
||||
pa_bool_t muted,
|
||||
|
|
@ -1067,12 +1068,14 @@ static playback_stream* playback_stream_new(
|
|||
data.driver = __FILE__;
|
||||
data.module = c->options->module;
|
||||
data.client = c->client;
|
||||
if (sink) {
|
||||
data.sink = sink;
|
||||
data.save_sink = TRUE;
|
||||
}
|
||||
if (sink)
|
||||
pa_sink_input_new_data_set_sink(&data, sink, TRUE);
|
||||
if (pa_sample_spec_valid(ss))
|
||||
pa_sink_input_new_data_set_sample_spec(&data, ss);
|
||||
if (pa_channel_map_valid(map))
|
||||
pa_sink_input_new_data_set_channel_map(&data, map);
|
||||
if (formats)
|
||||
pa_sink_input_new_data_set_formats(&data, formats);
|
||||
if (volume) {
|
||||
pa_sink_input_new_data_set_volume(&data, volume);
|
||||
data.volume_is_absolute = !relative_volume;
|
||||
|
|
@ -1846,6 +1849,10 @@ static pa_tagstruct *reply_new(uint32_t tag) {
|
|||
return reply;
|
||||
}
|
||||
|
||||
static void free_format_info(pa_format_info *f, void *userdata) {
|
||||
pa_format_info_free(f);
|
||||
}
|
||||
|
||||
static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
||||
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
|
||||
playback_stream *s;
|
||||
|
|
@ -1876,9 +1883,13 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
|
|||
passthrough = FALSE;
|
||||
|
||||
pa_sink_input_flags_t flags = 0;
|
||||
pa_proplist *p;
|
||||
pa_proplist *p = NULL;
|
||||
pa_bool_t volume_set = TRUE;
|
||||
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);
|
||||
|
|
@ -1901,17 +1912,14 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
|
|||
PA_TAG_INVALID) < 0) {
|
||||
|
||||
protocol_error(c);
|
||||
return;
|
||||
goto error;
|
||||
}
|
||||
|
||||
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
|
||||
CHECK_VALIDITY(c->pstream, !sink_name || pa_namereg_is_valid_name_or_wildcard(sink_name, PA_NAMEREG_SINK), tag, PA_ERR_INVALID);
|
||||
CHECK_VALIDITY(c->pstream, sink_index == PA_INVALID_INDEX || !sink_name, tag, PA_ERR_INVALID);
|
||||
CHECK_VALIDITY(c->pstream, !sink_name || sink_index == PA_INVALID_INDEX, tag, PA_ERR_INVALID);
|
||||
CHECK_VALIDITY(c->pstream, pa_channel_map_valid(&map), tag, PA_ERR_INVALID);
|
||||
CHECK_VALIDITY(c->pstream, pa_sample_spec_valid(&ss), tag, PA_ERR_INVALID);
|
||||
CHECK_VALIDITY(c->pstream, pa_cvolume_valid(&volume), tag, PA_ERR_INVALID);
|
||||
CHECK_VALIDITY(c->pstream, map.channels == ss.channels && volume.channels == ss.channels, tag, PA_ERR_INVALID);
|
||||
|
||||
p = pa_proplist_new();
|
||||
|
||||
|
|
@ -1930,8 +1938,7 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
|
|||
pa_tagstruct_get_boolean(t, &variable_rate) < 0) {
|
||||
|
||||
protocol_error(c);
|
||||
pa_proplist_free(p);
|
||||
return;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1940,9 +1947,9 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
|
|||
if (pa_tagstruct_get_boolean(t, &muted) < 0 ||
|
||||
pa_tagstruct_get_boolean(t, &adjust_latency) < 0 ||
|
||||
pa_tagstruct_get_proplist(t, p) < 0) {
|
||||
|
||||
protocol_error(c);
|
||||
pa_proplist_free(p);
|
||||
return;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1950,9 +1957,9 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
|
|||
|
||||
if (pa_tagstruct_get_boolean(t, &volume_set) < 0 ||
|
||||
pa_tagstruct_get_boolean(t, &early_requests) < 0) {
|
||||
|
||||
protocol_error(c);
|
||||
pa_proplist_free(p);
|
||||
return;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1961,18 +1968,18 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
|
|||
if (pa_tagstruct_get_boolean(t, &muted_set) < 0 ||
|
||||
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 error;
|
||||
}
|
||||
}
|
||||
|
||||
if (c->version >= 17) {
|
||||
|
||||
if (pa_tagstruct_get_boolean(t, &relative_volume) < 0) {
|
||||
|
||||
protocol_error(c);
|
||||
pa_proplist_free(p);
|
||||
return;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1980,31 +1987,52 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
|
|||
|
||||
if (pa_tagstruct_get_boolean(t, &passthrough) < 0 ) {
|
||||
protocol_error(c);
|
||||
pa_proplist_free(p);
|
||||
return;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if (c->version >= 21) {
|
||||
|
||||
if (pa_tagstruct_getu8(t, &n_formats) < 0) {
|
||||
protocol_error(c);
|
||||
goto error;
|
||||
}
|
||||
|
||||
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 error;
|
||||
}
|
||||
pa_idxset_put(formats, format, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
CHECK_VALIDITY(c->pstream, n_formats > 0 || pa_sample_spec_valid(&ss), tag, PA_ERR_INVALID);
|
||||
CHECK_VALIDITY(c->pstream, n_formats > 0 || (map.channels == ss.channels && volume.channels == ss.channels), tag, PA_ERR_INVALID);
|
||||
CHECK_VALIDITY(c->pstream, n_formats > 0 || pa_channel_map_valid(&map), tag, PA_ERR_INVALID);
|
||||
/* XXX: add checks on formats. At least inverse checks of the 3 above */
|
||||
|
||||
if (!pa_tagstruct_eof(t)) {
|
||||
protocol_error(c);
|
||||
pa_proplist_free(p);
|
||||
return;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (sink_index != PA_INVALID_INDEX) {
|
||||
|
||||
if (!(sink = pa_idxset_get_by_index(c->protocol->core->sinks, sink_index))) {
|
||||
pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
|
||||
pa_proplist_free(p);
|
||||
return;
|
||||
goto error;
|
||||
}
|
||||
|
||||
} else if (sink_name) {
|
||||
|
||||
if (!(sink = pa_namereg_get(c->protocol->core, sink_name, PA_NAMEREG_SINK))) {
|
||||
pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
|
||||
pa_proplist_free(p);
|
||||
return;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2025,7 +2053,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, &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, syncid, &missing, flags, p, adjust_latency, early_requests, relative_volume, &ret);
|
||||
pa_proplist_free(p);
|
||||
|
||||
CHECK_VALIDITY(c->pstream, s, tag, ret);
|
||||
|
|
@ -2064,7 +2092,26 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
|
|||
if (c->version >= 13)
|
||||
pa_tagstruct_put_usec(reply, s->configured_sink_latency);
|
||||
|
||||
if (c->version >= 21) {
|
||||
/* Send back the format we negotiated */
|
||||
if (s->sink_input->format)
|
||||
pa_tagstruct_put_format_info(reply, s->sink_input->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);
|
||||
return;
|
||||
|
||||
error:
|
||||
if (p)
|
||||
pa_proplist_free(p);
|
||||
if (formats)
|
||||
pa_idxset_free(formats, (pa_free2_cb_t) free_format_info, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
static void command_delete_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
||||
|
|
|
|||
|
|
@ -538,7 +538,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.sink = sink;
|
||||
pa_sink_input_new_data_set_sink(&data, sink, FALSE);
|
||||
pa_proplist_update(data.proplist, PA_UPDATE_MERGE, c->client->proplist);
|
||||
pa_sink_input_new_data_set_sample_spec(&data, &o->sample_spec);
|
||||
|
||||
|
|
|
|||
|
|
@ -31,6 +31,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>
|
||||
|
|
@ -146,8 +147,74 @@ void pa_sink_input_new_data_set_muted(pa_sink_input_new_data *data, pa_bool_t mu
|
|||
data->muted = !!mute;
|
||||
}
|
||||
|
||||
void pa_sink_input_new_data_done(pa_sink_input_new_data *data) {
|
||||
static void free_format_info(pa_format_info *f, void *userdata) {
|
||||
pa_format_info_free(f);
|
||||
}
|
||||
|
||||
pa_bool_t pa_sink_input_new_data_set_sink(pa_sink_input_new_data *data, pa_sink *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->sink = s;
|
||||
data->save_sink = save;
|
||||
} else {
|
||||
/* Extended API: let's see if this sink supports the formats the client can provide */
|
||||
formats = pa_sink_check_formats(s, data->req_formats);
|
||||
|
||||
if (formats && !pa_idxset_isempty(formats)) {
|
||||
/* Sink supports at least one of the requested formats */
|
||||
data->sink = s;
|
||||
data->save_sink = save;
|
||||
if (data->nego_formats)
|
||||
pa_idxset_free(data->nego_formats, (pa_free2_cb_t) free_format_info, NULL);
|
||||
data->nego_formats = formats;
|
||||
} else {
|
||||
/* Sink doesn't support any of the formats requested by the client */
|
||||
if (formats)
|
||||
pa_idxset_free(formats, (pa_free2_cb_t) free_format_info, NULL);
|
||||
ret = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
pa_bool_t pa_sink_input_new_data_set_formats(pa_sink_input_new_data *data, pa_idxset *formats) {
|
||||
pa_assert(data);
|
||||
pa_assert(formats);
|
||||
|
||||
if (data->req_formats)
|
||||
pa_idxset_free(formats, (pa_free2_cb_t) free_format_info, NULL);
|
||||
|
||||
data->req_formats = formats;
|
||||
|
||||
if (data->sink) {
|
||||
/* Trigger format negotiation */
|
||||
return pa_sink_input_new_data_set_sink(data, data->sink, data->save_sink);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void pa_sink_input_new_data_done(pa_sink_input_new_data *data) {
|
||||
pa_format_info *f;
|
||||
int i;
|
||||
|
||||
pa_assert(data);
|
||||
|
||||
if (data->req_formats)
|
||||
pa_idxset_free(data->req_formats, (pa_free2_cb_t) free_format_info, NULL);
|
||||
|
||||
if (data->nego_formats)
|
||||
pa_idxset_free(data->nego_formats, (pa_free2_cb_t) free_format_info, NULL);
|
||||
|
||||
if (data->format)
|
||||
pa_format_info_free(data->format);
|
||||
|
||||
pa_proplist_free(data->proplist);
|
||||
}
|
||||
|
|
@ -189,6 +256,8 @@ int pa_sink_input_new(
|
|||
pa_channel_map original_cm;
|
||||
int r;
|
||||
char *pt;
|
||||
pa_sample_spec ss;
|
||||
pa_channel_map map;
|
||||
|
||||
pa_assert(_i);
|
||||
pa_assert(core);
|
||||
|
|
@ -201,14 +270,43 @@ int pa_sink_input_new(
|
|||
if (data->origin_sink && (data->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER))
|
||||
data->volume_writable = FALSE;
|
||||
|
||||
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_sink_input_new_data_set_formats(data, tmp);
|
||||
}
|
||||
|
||||
if ((r = pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], data)) < 0)
|
||||
return r;
|
||||
|
||||
pa_return_val_if_fail(!data->driver || pa_utf8_valid(data->driver), -PA_ERR_INVALID);
|
||||
|
||||
if (!data->sink) {
|
||||
data->sink = pa_namereg_get(core, NULL, PA_NAMEREG_SINK);
|
||||
data->save_sink = FALSE;
|
||||
if (!data->sink)
|
||||
pa_sink_input_new_data_set_sink(data, pa_namereg_get(core, NULL, PA_NAMEREG_SINK), FALSE);
|
||||
|
||||
/* Routing's done, we have a sink. Now let's fix the format and set up the
|
||||
* sample spec */
|
||||
pa_return_val_if_fail(data->format || (data->nego_formats && !pa_idxset_isempty(data->nego_formats)), -PA_ERR_INVALID);
|
||||
/* 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->format = pa_format_info_copy(pa_idxset_first(data->nego_formats, NULL));
|
||||
/* 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_format_info_to_sample_spec(data->format, &ss, &map);
|
||||
pa_sink_input_new_data_set_sample_spec(data, &ss);
|
||||
if (pa_channel_map_valid(&map))
|
||||
pa_sink_input_new_data_set_channel_map(data, &map);
|
||||
} else {
|
||||
pa_format_info_to_sample_spec_fake(data->format, &ss);
|
||||
pa_sink_input_new_data_set_sample_spec(data, &ss);
|
||||
/* XXX: this is redundant - we can just check the encoding */
|
||||
data->flags |= PA_SINK_INPUT_PASSTHROUGH;
|
||||
}
|
||||
|
||||
pa_return_val_if_fail(data->sink, -PA_ERR_NOENTITY);
|
||||
|
|
@ -329,6 +427,7 @@ int pa_sink_input_new(
|
|||
i->actual_resample_method = resampler ? pa_resampler_get_method(resampler) : PA_RESAMPLER_INVALID;
|
||||
i->sample_spec = data->sample_spec;
|
||||
i->channel_map = data->channel_map;
|
||||
i->format = pa_format_info_copy(data->format);
|
||||
|
||||
if (!data->volume_is_absolute && pa_sink_flat_volume_enabled(i->sink)) {
|
||||
pa_cvolume remapped;
|
||||
|
|
@ -564,6 +663,9 @@ static void sink_input_free(pa_object *o) {
|
|||
if (i->thread_info.resampler)
|
||||
pa_resampler_free(i->thread_info.resampler);
|
||||
|
||||
if (i->format)
|
||||
pa_format_info_free(i->format);
|
||||
|
||||
if (i->proplist)
|
||||
pa_proplist_free(i->proplist);
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
typedef struct pa_sink_input pa_sink_input;
|
||||
|
||||
#include <pulse/sample.h>
|
||||
#include <pulse/format.h>
|
||||
#include <pulsecore/hook-list.h>
|
||||
#include <pulsecore/memblockq.h>
|
||||
#include <pulsecore/resampler.h>
|
||||
|
|
@ -92,6 +93,7 @@ struct pa_sink_input {
|
|||
|
||||
pa_sample_spec sample_spec;
|
||||
pa_channel_map channel_map;
|
||||
pa_format_info *format;
|
||||
|
||||
pa_sink_input *sync_prev, *sync_next;
|
||||
|
||||
|
|
@ -279,6 +281,9 @@ typedef struct pa_sink_input_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_cvolume volume, volume_factor, volume_factor_sink;
|
||||
pa_bool_t muted:1;
|
||||
|
|
@ -303,6 +308,8 @@ void pa_sink_input_new_data_set_volume(pa_sink_input_new_data *data, const pa_cv
|
|||
void pa_sink_input_new_data_apply_volume_factor(pa_sink_input_new_data *data, const pa_cvolume *volume_factor);
|
||||
void pa_sink_input_new_data_apply_volume_factor_sink(pa_sink_input_new_data *data, const pa_cvolume *volume_factor);
|
||||
void pa_sink_input_new_data_set_muted(pa_sink_input_new_data *data, pa_bool_t mute);
|
||||
pa_bool_t pa_sink_input_new_data_set_sink(pa_sink_input_new_data *data, pa_sink *s, pa_bool_t save);
|
||||
pa_bool_t pa_sink_input_new_data_set_formats(pa_sink_input_new_data *data, pa_idxset *formats);
|
||||
void pa_sink_input_new_data_done(pa_sink_input_new_data *data);
|
||||
|
||||
/* To be called by the implementing module only */
|
||||
|
|
|
|||
|
|
@ -299,7 +299,7 @@ int pa_play_file(
|
|||
u->readf_function = pa_sndfile_readf_function(&ss);
|
||||
|
||||
pa_sink_input_new_data_init(&data);
|
||||
data.sink = sink;
|
||||
pa_sink_input_new_data_set_sink(&data, sink, FALSE);
|
||||
data.driver = __FILE__;
|
||||
pa_sink_input_new_data_set_sample_spec(&data, &ss);
|
||||
pa_sink_input_new_data_set_channel_map(&data, &cm);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue