Merge branch 'passthrough'

This commit is contained in:
Colin Guthrie 2011-05-15 13:14:33 +01:00
commit 26b4bd74aa
49 changed files with 1796 additions and 201 deletions

View file

@ -213,3 +213,31 @@ Two new flags at the end of sink input introspection data:
bool has_volume bool has_volume
bool volume_writable 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
New fields in reply from PA_COMMAND_GET_SINK_INFO (and thus
PA_COMMAND_GET_SINK_INFO_LIST)
uint8_t n_formats
format_info format1
...
format_info formatn
One new field in reply from PA_COMMAND_GET_SINK_INPUT_INFO (and thus
PA_COMMAND_GET_SINK_INPUT_INFO_LIST)
format_info format

View file

@ -37,7 +37,7 @@ AC_SUBST(PA_MAJORMINOR, pa_major.pa_minor)
AC_SUBST(PACKAGE_URL, [http://pulseaudio.org/]) AC_SUBST(PACKAGE_URL, [http://pulseaudio.org/])
AC_SUBST(PA_API_VERSION, 12) 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 # The stable ABI for client applications, for the version info x:y:z
# always will hold y=z # always will hold y=z
@ -603,6 +603,12 @@ fi
AC_CHECK_HEADERS_ONCE([valgrind/memcheck.h]) AC_CHECK_HEADERS_ONCE([valgrind/memcheck.h])
#### json parsing ####
PKG_CHECK_MODULES(LIBJSON, [ json >= 0.9 ])
AC_SUBST(LIBJSON_CFLAGS)
AC_SUBST(LIBJSON_LIBS)
#### Sound file #### #### Sound file ####
PKG_CHECK_MODULES(LIBSNDFILE, [ sndfile >= 1.0.20 ]) PKG_CHECK_MODULES(LIBSNDFILE, [ sndfile >= 1.0.20 ])

View file

@ -417,7 +417,7 @@ WARN_LOGFILE =
# directories like "/usr/src/myproject". Separate the files or directories # directories like "/usr/src/myproject". Separate the files or directories
# with spaces. # with spaces.
INPUT = ../src/pulse/context.h ../src/pulse/stream.h ../src/pulse/pulseaudio.h ../src/pulse/sample.h ../src/pulse/def.h ../src/pulse/subscribe.h ../src/pulse/introspect.h ../src/pulse/scache.h ../src/pulse/mainloop-api.h ../src/pulse/glib-mainloop.h ../src/pulse/mainloop.h ../src/pulse/mainloop-signal.h ../src/pulse/error.h ../src/pulse/operation.h ../src/pulse/simple.h ../src/pulse/version.h ../src/pulse/volume.h ../src/pulse/channelmap.h ../src/pulse/thread-mainloop.h ../src/pulse/xmalloc.h ../src/pulse/utf8.h ../src/pulse/util.h ../src/pulse/timeval.h ../src/pulse/proplist.h ../src/pulse/gccmacro.h ../src/pulse/ext-stream-restore.h ../src/pulse/rtclock.h INPUT = ../src/pulse/context.h ../src/pulse/stream.h ../src/pulse/pulseaudio.h ../src/pulse/sample.h ../src/pulse/def.h ../src/pulse/subscribe.h ../src/pulse/introspect.h ../src/pulse/scache.h ../src/pulse/mainloop-api.h ../src/pulse/glib-mainloop.h ../src/pulse/mainloop.h ../src/pulse/mainloop-signal.h ../src/pulse/error.h ../src/pulse/operation.h ../src/pulse/simple.h ../src/pulse/version.h ../src/pulse/volume.h ../src/pulse/channelmap.h ../src/pulse/thread-mainloop.h ../src/pulse/xmalloc.h ../src/pulse/utf8.h ../src/pulse/util.h ../src/pulse/timeval.h ../src/pulse/proplist.h ../src/pulse/gccmacro.h ../src/pulse/ext-stream-restore.h ../src/pulse/rtclock.h ../src/pulse/format.h
# If the value of the INPUT tag contains directories, you can use the # If the value of the INPUT tag contains directories, you can use the
# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp

View file

@ -177,6 +177,7 @@ src/pulse/thread-mainloop.c
src/pulse/scache.c src/pulse/scache.c
src/pulse/glib-mainloop.c src/pulse/glib-mainloop.c
src/pulse/timeval.c src/pulse/timeval.c
src/pulse/format.c
src/utils/pacat.c src/utils/pacat.c
src/utils/pasuspender.c src/utils/pasuspender.c
src/utils/pabrowse.c src/utils/pabrowse.c

View file

@ -252,6 +252,7 @@ TESTS = \
channelmap-test \ channelmap-test \
thread-mainloop-test \ thread-mainloop-test \
utf8-test \ utf8-test \
format-test \
get-binary-name-test \ get-binary-name-test \
ipacl-test \ ipacl-test \
hook-list-test \ hook-list-test \
@ -274,6 +275,7 @@ TESTS_BINARIES = \
mcalign-test \ mcalign-test \
pacat-simple \ pacat-simple \
parec-simple \ parec-simple \
extended-test \
strlist-test \ strlist-test \
close-test \ close-test \
voltest \ voltest \
@ -284,6 +286,7 @@ TESTS_BINARIES = \
channelmap-test \ channelmap-test \
thread-mainloop-test \ thread-mainloop-test \
utf8-test \ utf8-test \
format-test \
get-binary-name-test \ get-binary-name-test \
ipacl-test \ ipacl-test \
hook-list-test \ hook-list-test \
@ -369,6 +372,11 @@ utf8_test_CFLAGS = $(AM_CFLAGS)
utf8_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINOR@.la libpulse.la libpulsecommon-@PA_MAJORMINOR@.la utf8_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINOR@.la libpulse.la libpulsecommon-@PA_MAJORMINOR@.la
utf8_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) utf8_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
format_test_SOURCES = tests/format-test.c
format_test_CFLAGS = $(AM_CFLAGS)
format_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINOR@.la libpulse.la libpulsecommon-@PA_MAJORMINOR@.la
format_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
get_binary_name_test_SOURCES = tests/get-binary-name-test.c get_binary_name_test_SOURCES = tests/get-binary-name-test.c
get_binary_name_test_CFLAGS = $(AM_CFLAGS) get_binary_name_test_CFLAGS = $(AM_CFLAGS)
get_binary_name_test_LDADD = $(AM_LDADD) libpulse.la libpulsecommon-@PA_MAJORMINOR@.la get_binary_name_test_LDADD = $(AM_LDADD) libpulse.la libpulsecommon-@PA_MAJORMINOR@.la
@ -439,6 +447,11 @@ parec_simple_LDADD = $(AM_LDADD) libpulse.la libpulse-simple.la libpulsecommon-@
parec_simple_CFLAGS = $(AM_CFLAGS) parec_simple_CFLAGS = $(AM_CFLAGS)
parec_simple_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) parec_simple_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
extended_test_SOURCES = tests/extended-test.c
extended_test_LDADD = $(AM_LDADD) libpulse.la libpulsecommon-@PA_MAJORMINOR@.la
extended_test_CFLAGS = $(AM_CFLAGS)
extended_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
strlist_test_SOURCES = tests/strlist-test.c strlist_test_SOURCES = tests/strlist-test.c
strlist_test_CFLAGS = $(AM_CFLAGS) strlist_test_CFLAGS = $(AM_CFLAGS)
strlist_test_LDADD = $(AM_LDADD) $(WINSOCK_LIBS) libpulsecore-@PA_MAJORMINOR@.la libpulse.la libpulsecommon-@PA_MAJORMINOR@.la strlist_test_LDADD = $(AM_LDADD) $(WINSOCK_LIBS) libpulsecore-@PA_MAJORMINOR@.la libpulse.la libpulsecommon-@PA_MAJORMINOR@.la
@ -589,6 +602,7 @@ libpulsecommon_@PA_MAJORMINOR@_la_SOURCES = \
pulse/util.c pulse/util.h \ pulse/util.c pulse/util.h \
pulse/timeval.c pulse/timeval.h \ pulse/timeval.c pulse/timeval.h \
pulse/rtclock.c pulse/rtclock.h \ pulse/rtclock.c pulse/rtclock.h \
pulse/format.c pulse/format.h \
pulsecore/atomic.h \ pulsecore/atomic.h \
pulsecore/authkey.c pulsecore/authkey.h \ pulsecore/authkey.c pulsecore/authkey.h \
pulsecore/conf-parser.c pulsecore/conf-parser.h \ pulsecore/conf-parser.c pulsecore/conf-parser.h \
@ -645,9 +659,9 @@ libpulsecommon_@PA_MAJORMINOR@_la_SOURCES = \
pulsecore/sndfile-util.c pulsecore/sndfile-util.h \ pulsecore/sndfile-util.c pulsecore/sndfile-util.h \
pulsecore/socket.h pulsecore/socket.h
libpulsecommon_@PA_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS) $(LIBSAMPLERATE_CFLAGS) $(LIBSNDFILE_CFLAGS) libpulsecommon_@PA_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS) $(LIBSAMPLERATE_CFLAGS) $(LIBSNDFILE_CFLAGS) $(LIBJSON_CFLAGS)
libpulsecommon_@PA_MAJORMINOR@_la_LDFLAGS = $(AM_LDFLAGS) -avoid-version libpulsecommon_@PA_MAJORMINOR@_la_LDFLAGS = $(AM_LDFLAGS) -avoid-version
libpulsecommon_@PA_MAJORMINOR@_la_LIBADD = $(AM_LIBADD) $(LIBWRAP_LIBS) $(WINSOCK_LIBS) $(LTLIBICONV) $(LIBSAMPLERATE_LIBS) $(LIBSNDFILE_LIBS) libpulsecommon_@PA_MAJORMINOR@_la_LIBADD = $(AM_LIBADD) $(LIBWRAP_LIBS) $(WINSOCK_LIBS) $(LTLIBICONV) $(LIBSAMPLERATE_LIBS) $(LIBSNDFILE_LIBS) $(LIBJSON_LIBS)
if HAVE_X11 if HAVE_X11
libpulsecommon_@PA_MAJORMINOR@_la_SOURCES += pulse/client-conf-x11.c pulse/client-conf-x11.h libpulsecommon_@PA_MAJORMINOR@_la_SOURCES += pulse/client-conf-x11.c pulse/client-conf-x11.h
@ -719,6 +733,7 @@ pulseinclude_HEADERS = \
pulse/error.h \ pulse/error.h \
pulse/ext-device-manager.h \ pulse/ext-device-manager.h \
pulse/ext-stream-restore.h \ pulse/ext-stream-restore.h \
pulse/format.h \
pulse/gccmacro.h \ pulse/gccmacro.h \
pulse/introspect.h \ pulse/introspect.h \
pulse/mainloop-api.h \ pulse/mainloop-api.h \
@ -1158,8 +1173,7 @@ dist_alsapaths_DATA = \
modules/alsa/mixer/paths/analog-output-headphones-2.conf \ modules/alsa/mixer/paths/analog-output-headphones-2.conf \
modules/alsa/mixer/paths/analog-output-lfe-on-mono.conf \ modules/alsa/mixer/paths/analog-output-lfe-on-mono.conf \
modules/alsa/mixer/paths/analog-output-mono.conf \ modules/alsa/mixer/paths/analog-output-mono.conf \
modules/alsa/mixer/paths/iec958-stereo-output.conf \ modules/alsa/mixer/paths/iec958-stereo-output.conf
modules/alsa/mixer/paths/iec958-passthrough-output.conf
endif endif

View file

@ -146,6 +146,7 @@ pa_cvolume_set_fade;
pa_cvolume_set_position; pa_cvolume_set_position;
pa_cvolume_snprint; pa_cvolume_snprint;
pa_cvolume_valid; pa_cvolume_valid;
pa_encoding_to_string;
pa_ext_device_manager_delete; pa_ext_device_manager_delete;
pa_ext_device_manager_enable_role_device_priority_routing; pa_ext_device_manager_enable_role_device_priority_routing;
pa_ext_device_manager_read; pa_ext_device_manager_read;
@ -160,6 +161,22 @@ pa_ext_stream_restore_set_subscribe_cb;
pa_ext_stream_restore_subscribe; pa_ext_stream_restore_subscribe;
pa_ext_stream_restore_test; pa_ext_stream_restore_test;
pa_ext_stream_restore_write; pa_ext_stream_restore_write;
pa_format_info_copy;
pa_format_info_free;
pa_format_info_is_compatible;
pa_format_info_is_pcm;
pa_format_info_new;
pa_format_info_set_channel_map;
pa_format_info_set_channels;
pa_format_info_set_prop_int;
pa_format_info_set_prop_int_array;
pa_format_info_set_prop_int_range;
pa_format_info_set_prop_string;
pa_format_info_set_prop_string_array;
pa_format_info_set_rate;
pa_format_info_set_sample_format;
pa_format_info_snprint;
pa_format_info_valid;
pa_frame_size; pa_frame_size;
pa_get_binary_name; pa_get_binary_name;
pa_get_fqdn; pa_get_fqdn;
@ -251,6 +268,7 @@ pa_stream_get_channel_map;
pa_stream_get_context; pa_stream_get_context;
pa_stream_get_device_index; pa_stream_get_device_index;
pa_stream_get_device_name; pa_stream_get_device_name;
pa_stream_get_format_info;
pa_stream_get_index; pa_stream_get_index;
pa_stream_get_latency; pa_stream_get_latency;
pa_stream_get_monitor_stream; pa_stream_get_monitor_stream;
@ -261,6 +279,7 @@ pa_stream_get_timing_info;
pa_stream_is_corked; pa_stream_is_corked;
pa_stream_is_suspended; pa_stream_is_suspended;
pa_stream_new; pa_stream_new;
pa_stream_new_extended;
pa_stream_new_with_proplist; pa_stream_new_with_proplist;
pa_stream_peek; pa_stream_peek;
pa_stream_prebuf; pa_stream_prebuf;

View file

@ -108,6 +108,8 @@ struct userdata {
pa_cvolume hardware_volume; pa_cvolume hardware_volume;
uint32_t old_rate;
size_t size_t
frame_size, frame_size,
fragment_size, fragment_size,
@ -1051,6 +1053,56 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
switch (code) { switch (code) {
case PA_SINK_MESSAGE_FINISH_MOVE:
case PA_SINK_MESSAGE_ADD_INPUT: {
pa_sink_input *i = PA_SINK_INPUT(data);
int r = 0;
if (PA_LIKELY(!pa_sink_input_is_passthrough(i)))
break;
u->old_rate = u->sink->sample_spec.rate;
/* Passthrough format, see if we need to reset sink sample rate */
if (u->sink->sample_spec.rate == i->thread_info.sample_spec.rate)
break;
/* .. we do */
if ((r = suspend(u)) < 0)
return r;
u->sink->sample_spec.rate = i->thread_info.sample_spec.rate;
if ((r = unsuspend(u)) < 0)
return r;
break;
}
case PA_SINK_MESSAGE_START_MOVE:
case PA_SINK_MESSAGE_REMOVE_INPUT: {
pa_sink_input *i = PA_SINK_INPUT(data);
int r = 0;
if (PA_LIKELY(!pa_sink_input_is_passthrough(i)))
break;
/* Passthrough format, see if we need to reset sink sample rate */
if (u->sink->sample_spec.rate == u->old_rate)
break;
/* .. we do */
if ((r = suspend(u)) < 0)
return r;
u->sink->sample_spec.rate = u->old_rate;
if ((r = unsuspend(u)) < 0)
return r;
break;
}
case PA_SINK_MESSAGE_GET_LATENCY: { case PA_SINK_MESSAGE_GET_LATENCY: {
pa_usec_t r = 0; pa_usec_t r = 0;
@ -1707,13 +1759,6 @@ static int setup_mixer(struct userdata *u, pa_bool_t ignore_dB, pa_bool_t sync_v
return 0; return 0;
} }
/* FIXME: need automatic detection rather than hard-coded path */
if (!strcmp(u->mixer_path->name, "iec958-passthrough-output")) {
u->sink->flags |= PA_SINK_PASSTHROUGH;
} else {
u->sink->flags &= ~PA_SINK_PASSTHROUGH;
}
if (!u->mixer_path->has_volume) if (!u->mixer_path->has_volume)
pa_log_info("Driver does not support hardware volume control, falling back to software volume control."); pa_log_info("Driver does not support hardware volume control, falling back to software volume control.");
else { else {

View file

@ -1,19 +0,0 @@
# This file is part of PulseAudio.
#
# PulseAudio is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as
# published by the Free Software Foundation; either version 2.1 of the
# License, or (at your option) any later version.
#
# PulseAudio is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with PulseAudio; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
[Element IEC958]
switch = mute

View file

@ -155,13 +155,6 @@ paths-input = iec958-stereo-input
paths-output = iec958-stereo-output paths-output = iec958-stereo-output
priority = 5 priority = 5
[Mapping iec958-passthrough]
device-strings = iec958:%f
channel-map = left,right
direction = output
paths-output = iec958-passthrough-output
priority = 5
[Mapping iec958-ac3-surround-40] [Mapping iec958-ac3-surround-40]
device-strings = a52:%f device-strings = a52:%f
channel-map = front-left,front-right,rear-left,rear-right channel-map = front-left,front-right,rear-left,rear-right

View file

@ -1549,7 +1549,7 @@ int pa__init(pa_module*m) {
pa_sink_input_new_data_init(&sink_input_data); pa_sink_input_new_data_init(&sink_input_data);
sink_input_data.driver = __FILE__; sink_input_data.driver = __FILE__;
sink_input_data.module = m; 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; 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_NAME, "Echo-Cancel Sink Stream");
pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter"); pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter");

View file

@ -845,7 +845,7 @@ static int output_create_sink_input(struct output *o) {
return 0; return 0;
pa_sink_input_new_data_init(&data); 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__; 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_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"); pa_proplist_sets(data.proplist, PA_PROP_MEDIA_ROLE, "filter");

View file

@ -832,8 +832,8 @@ static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_n
pa_sink *sink; pa_sink *sink;
if ((sink = pa_idxset_get_by_index(u->core->sinks, device_index))) { if ((sink = pa_idxset_get_by_index(u->core->sinks, device_index))) {
new_data->sink = sink; if (!pa_sink_input_new_data_set_sink(new_data, sink, FALSE))
new_data->save_sink = FALSE; pa_log_debug("Not restoring device for stream because no supported format was found");
} }
} }
} }

View file

@ -1212,7 +1212,7 @@ int pa__init(pa_module*m) {
pa_sink_input_new_data_init(&sink_input_data); pa_sink_input_new_data_init(&sink_input_data);
sink_input_data.driver = __FILE__; sink_input_data.driver = __FILE__;
sink_input_data.module = m; 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; 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_NAME, "Equalized Stream");
pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter"); pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter");

View file

@ -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... */ /* Prefer the default sink over any other sink, just in case... */
if ((def = pa_namereg_get_default_sink(c))) if ((def = pa_namereg_get_default_sink(c)))
if (role_match(def->proplist, role)) { if (role_match(def->proplist, role) && pa_sink_input_new_data_set_sink(new_data, def, FALSE))
new_data->sink = def;
new_data->save_sink = FALSE;
return PA_HOOK_OK; return PA_HOOK_OK;
}
/* @todo: favour the highest priority device, not the first one we find? */ /* @todo: favour the highest priority device, not the first one we find? */
PA_IDXSET_FOREACH(s, c->sinks, idx) { PA_IDXSET_FOREACH(s, c->sinks, idx) {
@ -131,11 +128,8 @@ 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))) if (!PA_SINK_IS_LINKED(pa_sink_get_state(s)))
continue; continue;
if (role_match(s->proplist, role)) { if (role_match(s->proplist, role) && pa_sink_input_new_data_set_sink(new_data, s, FALSE))
new_data->sink = s;
new_data->save_sink = FALSE;
return PA_HOOK_OK; return PA_HOOK_OK;
}
} }
return PA_HOOK_OK; return PA_HOOK_OK;

View file

@ -907,7 +907,7 @@ int pa__init(pa_module*m) {
pa_sink_input_new_data_init(&sink_input_data); pa_sink_input_new_data_init(&sink_input_data);
sink_input_data.driver = __FILE__; sink_input_data.driver = __FILE__;
sink_input_data.module = m; 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; 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_NAME, "LADSPA Stream");
pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter"); pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter");

View file

@ -695,7 +695,7 @@ int pa__init(pa_module *m) {
pa_sink_input_new_data_init(&sink_input_data); pa_sink_input_new_data_init(&sink_input_data);
sink_input_data.driver = __FILE__; sink_input_data.driver = __FILE__;
sink_input_data.module = m; 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))) if ((n = pa_modargs_get_value(ma, "sink_input_name", NULL)))
pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, n); pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, n);

View file

@ -419,7 +419,7 @@ int pa__init(pa_module*m) {
pa_sink_input_new_data_init(&sink_input_data); pa_sink_input_new_data_init(&sink_input_data);
sink_input_data.driver = __FILE__; sink_input_data.driver = __FILE__;
sink_input_data.module = m; 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; 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_NAME, "Remapped Stream");
pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter"); pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter");

View file

@ -157,7 +157,7 @@ int pa__init(pa_module*m) {
pa_sink_input_new_data_init(&data); pa_sink_input_new_data_init(&data);
data.driver = __FILE__; data.driver = __FILE__;
data.module = m; 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_setf(data.proplist, PA_PROP_MEDIA_NAME, "%u Hz Sine", frequency);
pa_proplist_sets(data.proplist, PA_PROP_MEDIA_ROLE, "abstract"); pa_proplist_sets(data.proplist, PA_PROP_MEDIA_ROLE, "abstract");
pa_proplist_setf(data.proplist, "sine.hz", "%u", frequency); pa_proplist_setf(data.proplist, "sine.hz", "%u", frequency);

View file

@ -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 /* 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 same time, in which case we want to make sure we don't
interfere with that */ 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)))
pa_log_info("Restoring device for stream %s.", name); if (pa_sink_input_new_data_set_sink(new_data, s, TRUE))
new_data->sink = s; pa_log_info("Restoring device for stream %s.", name);
new_data->save_sink = TRUE;
}
pa_xfree(e); pa_xfree(e);
} }

View file

@ -1095,6 +1095,23 @@ static void sink_info_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_t
} }
} }
if (u->version >= 21) {
uint8_t n_formats;
pa_format_info format;
if (pa_tagstruct_getu8(t, &n_formats) < 0) { /* no. of formats */
pa_log("Parse failure");
goto fail;
}
for (uint8_t j = 0; j < n_formats; j++) {
if (pa_tagstruct_get_format_info(t, &format)) { /* format info */
pa_log("Parse failure");
goto fail;
}
}
}
if (!pa_tagstruct_eof(t)) { if (!pa_tagstruct_eof(t)) {
pa_log("Packet too long"); pa_log("Packet too long");
goto fail; goto fail;
@ -1128,6 +1145,7 @@ static void sink_input_info_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag
pa_channel_map channel_map; pa_channel_map channel_map;
pa_cvolume volume; pa_cvolume volume;
pa_proplist *pl; pa_proplist *pl;
pa_bool_t b;
pa_assert(pd); pa_assert(pd);
pa_assert(u); pa_assert(u);
@ -1175,6 +1193,33 @@ static void sink_input_info_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag
} }
} }
if (u->version >= 19) {
if (pa_tagstruct_get_boolean(t, &b) < 0) {
pa_log("Parse failure");
goto fail;
}
}
if (u->version >= 20) {
if (pa_tagstruct_get_boolean(t, &b) < 0 ||
pa_tagstruct_get_boolean(t, &b) < 0) {
pa_log("Parse failure");
goto fail;
}
}
if (u->version >= 21) {
pa_format_info format;
if (pa_tagstruct_get_format_info(t, &format) < 0) {
pa_log("Parse failure");
goto fail;
}
}
if (!pa_tagstruct_eof(t)) { if (!pa_tagstruct_eof(t)) {
pa_log("Packet too long"); pa_log("Packet too long");
goto fail; goto fail;
@ -1491,6 +1536,13 @@ static void create_stream_callback(pa_pdispatch *pd, uint32_t command, uint32_t
/* #endif */ /* #endif */
} }
if (u->version >= 21) {
pa_format_info format;
if (pa_tagstruct_get_format_info(t, &format) < 0)
goto parse_error;
}
if (!pa_tagstruct_eof(t)) if (!pa_tagstruct_eof(t))
goto parse_error; goto parse_error;
@ -1693,6 +1745,13 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t
pa_tagstruct_put_boolean(reply, FALSE); /* passthrough stream */ pa_tagstruct_put_boolean(reply, FALSE); /* passthrough stream */
#endif #endif
#ifdef TUNNEL_SINK
if (u->version >= 21) {
/* We're not using the extended API, so n_formats = 0 and that's that */
pa_tagstruct_putu8(t, 0);
}
#endif
pa_pstream_send_tagstruct(u->pstream, reply); pa_pstream_send_tagstruct(u->pstream, reply);
pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, create_stream_callback, u, NULL); pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, create_stream_callback, u, NULL);

View file

@ -579,7 +579,7 @@ int pa__init(pa_module*m) {
pa_sink_input_new_data_init(&sink_input_data); pa_sink_input_new_data_init(&sink_input_data);
sink_input_data.driver = __FILE__; sink_input_data.driver = __FILE__;
sink_input_data.module = m; 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; 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_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"); pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter");

View file

@ -512,7 +512,7 @@ static struct session *session_new(struct userdata *u, const pa_sdp_info *sdp_in
goto fail; goto fail;
pa_sink_input_new_data_init(&data); pa_sink_input_new_data_init(&data);
data.sink = sink; pa_sink_input_new_data_set_sink(&data, sink, FALSE);
data.driver = __FILE__; data.driver = __FILE__;
pa_proplist_sets(data.proplist, PA_PROP_MEDIA_ROLE, "stream"); pa_proplist_sets(data.proplist, PA_PROP_MEDIA_ROLE, "stream");
pa_proplist_setf(data.proplist, PA_PROP_MEDIA_NAME, pa_proplist_setf(data.proplist, PA_PROP_MEDIA_NAME,

View file

@ -739,12 +739,7 @@ typedef enum pa_sink_flags {
/**< The latency can be adjusted dynamically depending on the /**< The latency can be adjusted dynamically depending on the
* needs of the connected streams. \since 0.9.15 */ * needs of the connected streams. \since 0.9.15 */
PA_SINK_PASSTHROUGH = 0x0100U, PA_SINK_SYNC_VOLUME = 0x0100U,
/**< This sink has support for passthrough mode. The data will be left
* as is and not reformatted, resampled, mixed.
* \since 1.0 */
PA_SINK_SYNC_VOLUME = 0x0200U,
/**< The HW volume changes are syncronized with SW volume. /**< The HW volume changes are syncronized with SW volume.
* \since 1.0 */ * \since 1.0 */
@ -753,7 +748,7 @@ typedef enum pa_sink_flags {
* The server will filter out these flags anyway, so you should never see * The server will filter out these flags anyway, so you should never see
* these flags in sinks. */ * these flags in sinks. */
PA_SINK_SHARE_VOLUME_WITH_MASTER = 0x0400U, PA_SINK_SHARE_VOLUME_WITH_MASTER = 0x0200U,
/**< This sink shares the volume with the master sink (used by some filter /**< This sink shares the volume with the master sink (used by some filter
* sinks). */ * sinks). */
/** \endcond */ /** \endcond */
@ -769,7 +764,6 @@ typedef enum pa_sink_flags {
#define PA_SINK_DECIBEL_VOLUME PA_SINK_DECIBEL_VOLUME #define PA_SINK_DECIBEL_VOLUME PA_SINK_DECIBEL_VOLUME
#define PA_SINK_FLAT_VOLUME PA_SINK_FLAT_VOLUME #define PA_SINK_FLAT_VOLUME PA_SINK_FLAT_VOLUME
#define PA_SINK_DYNAMIC_LATENCY PA_SINK_DYNAMIC_LATENCY #define PA_SINK_DYNAMIC_LATENCY PA_SINK_DYNAMIC_LATENCY
#define PA_SINK_PASSTHROUGH PA_SINK_PASSTHROUGH
#define PA_SINK_SYNC_VOLUME PA_SINK_SYNC_VOLUME #define PA_SINK_SYNC_VOLUME PA_SINK_SYNC_VOLUME
#define PA_SINK_SHARE_VOLUME_WITH_MASTER PA_SINK_SHARE_VOLUME_WITH_MASTER #define PA_SINK_SHARE_VOLUME_WITH_MASTER PA_SINK_SHARE_VOLUME_WITH_MASTER
@ -918,6 +912,13 @@ typedef void (*pa_free_cb_t)(void *p);
* information, \since 0.9.15 */ * information, \since 0.9.15 */
#define PA_STREAM_EVENT_REQUEST_UNCORK "request-uncork" #define PA_STREAM_EVENT_REQUEST_UNCORK "request-uncork"
/** A stream event notifying that the stream is going to be
* disconnected because the underlying sink changed and no longer
* supports the format that was originally negotiated. Clients need
* to connect a new stream to renegotiate a format and continue
* playback, \since 1.0 */
#define PA_STREAM_EVENT_FORMAT_LOST "format-lost"
PA_C_DECL_END PA_C_DECL_END
#endif #endif

460
src/pulse/format.c Normal file
View file

@ -0,0 +1,460 @@
/***
This file is part of PulseAudio.
Copyright 2011 Intel Corporation
Copyright 2011 Collabora Multimedia
Copyright 2011 Arun Raghavan <arun.raghavan@collabora.co.uk>
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <json.h>
#include <pulse/internal.h>
#include <pulse/xmalloc.h>
#include <pulse/i18n.h>
#include <pulsecore/core-util.h>
#include <pulsecore/macro.h>
#include "format.h"
#define PA_JSON_MIN_KEY "min"
#define PA_JSON_MAX_KEY "max"
static int pa_format_info_prop_compatible(const char *one, const char *two);
const char *pa_encoding_to_string(pa_encoding_t e) {
static const char* const table[]= {
[PA_ENCODING_PCM] = "pcm",
[PA_ENCODING_AC3_IEC61937] = "ac3-iec61937",
[PA_ENCODING_EAC3_IEC61937] = "eac3-iec61937",
[PA_ENCODING_MPEG_IEC61937] = "mpeg-iec61937",
[PA_ENCODING_DTS_IEC61937] = "dts-iec61937",
[PA_ENCODING_ANY] = "any",
};
if (e < 0 || e >= PA_ENCODING_MAX)
return NULL;
return table[e];
}
pa_format_info* pa_format_info_new(void) {
pa_format_info *f = pa_xnew(pa_format_info, 1);
f->encoding = PA_ENCODING_INVALID;
f->plist = pa_proplist_new();
return f;
}
pa_format_info* pa_format_info_copy(const pa_format_info *src) {
pa_format_info *dest;
pa_assert(src);
dest = pa_xnew(pa_format_info, 1);
dest->encoding = src->encoding;
if (src->plist)
dest->plist = pa_proplist_copy(src->plist);
else
dest->plist = NULL;
return dest;
}
void pa_format_info_free(pa_format_info *f) {
pa_assert(f);
pa_proplist_free(f->plist);
pa_xfree(f);
}
void pa_format_info_free2(pa_format_info *f, void *userdata) {
pa_format_info_free(f);
}
int pa_format_info_valid(const pa_format_info *f) {
return (f->encoding >= 0 && f->encoding < PA_ENCODING_MAX && f->plist != NULL);
}
int pa_format_info_is_pcm(const pa_format_info *f) {
return f->encoding == PA_ENCODING_PCM;
}
char *pa_format_info_snprint(char *s, size_t l, const pa_format_info *f) {
char *tmp;
pa_assert(s);
pa_assert(l > 0);
pa_assert(f);
pa_init_i18n();
if (!pa_format_info_valid(f))
pa_snprintf(s, l, _("(invalid)"));
else {
tmp = pa_proplist_to_string_sep(f->plist, ", ");
pa_snprintf(s, l, _("%s, %s"), pa_encoding_to_string(f->encoding), tmp[0] ? tmp : _("(no properties)"));
pa_xfree(tmp);
}
return s;
}
int pa_format_info_is_compatible(pa_format_info *first, pa_format_info *second) {
const char *key;
void *state = NULL;
pa_assert(first);
pa_assert(second);
if (first->encoding != second->encoding)
return FALSE;
while ((key = pa_proplist_iterate(first->plist, &state))) {
const char *value_one, *value_two;
value_one = pa_proplist_gets(first->plist, key);
value_two = pa_proplist_gets(second->plist, key);
if (!value_two || !pa_format_info_prop_compatible(value_one, value_two))
return FALSE;
}
return TRUE;
}
pa_format_info* pa_format_info_from_sample_spec(pa_sample_spec *ss, pa_channel_map *map) {
char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
pa_format_info *f;
pa_assert(ss && pa_sample_spec_valid(ss));
pa_assert(!map || pa_channel_map_valid(map));
f = pa_format_info_new();
f->encoding = PA_ENCODING_PCM;
pa_format_info_set_sample_format(f, ss->format);
pa_format_info_set_rate(f, ss->rate);
pa_format_info_set_channels(f, ss->channels);
if (map) {
pa_channel_map_snprint(cm, sizeof(cm), map);
pa_format_info_set_prop_string(f, PA_PROP_FORMAT_CHANNEL_MAP, cm);
}
return f;
}
/* For PCM streams */
pa_bool_t pa_format_info_to_sample_spec(pa_format_info *f, pa_sample_spec *ss, pa_channel_map *map) {
char *sf = NULL, *m = NULL;
int rate, channels;
pa_bool_t ret = FALSE;
pa_assert(f);
pa_assert(ss);
pa_return_val_if_fail(f->encoding == PA_ENCODING_PCM, FALSE);
if (!pa_format_info_get_prop_string(f, PA_PROP_FORMAT_SAMPLE_FORMAT, &sf))
goto out;
if (!pa_format_info_get_prop_int(f, PA_PROP_FORMAT_RATE, &rate))
goto out;
if (!pa_format_info_get_prop_int(f, PA_PROP_FORMAT_CHANNELS, &channels))
goto out;
if ((ss->format = pa_parse_sample_format(sf)) == PA_SAMPLE_INVALID)
goto out;
ss->rate = (uint32_t) rate;
ss->channels = (uint8_t) channels;
if (map) {
pa_channel_map_init(map);
if (!pa_format_info_get_prop_string(f, PA_PROP_FORMAT_CHANNEL_MAP, &m))
goto out;
if (m && pa_channel_map_parse(map, m) == NULL)
goto out;
}
ret = TRUE;
out:
if (sf)
pa_xfree(sf);
if (m)
pa_xfree(m);
return ret;
}
/* For compressed streams */
pa_bool_t pa_format_info_to_sample_spec_fake(pa_format_info *f, pa_sample_spec *ss) {
int rate;
pa_assert(f);
pa_assert(ss);
pa_return_val_if_fail(f->encoding != PA_ENCODING_PCM, FALSE);
ss->format = PA_SAMPLE_S16LE;
ss->channels = 2;
pa_return_val_if_fail(pa_format_info_get_prop_int(f, PA_PROP_FORMAT_RATE, &rate), FALSE);
ss->rate = (uint32_t) rate;
if (f->encoding == PA_ENCODING_EAC3_IEC61937)
ss->rate *= 4;
return TRUE;
}
void pa_format_info_set_sample_format(pa_format_info *f, pa_sample_format_t sf) {
pa_format_info_set_prop_string(f, PA_PROP_FORMAT_SAMPLE_FORMAT, pa_sample_format_to_string(sf));
}
void pa_format_info_set_rate(pa_format_info *f, int rate) {
pa_format_info_set_prop_int(f, PA_PROP_FORMAT_RATE, rate);
}
void pa_format_info_set_channels(pa_format_info *f, int channels) {
pa_format_info_set_prop_int(f, PA_PROP_FORMAT_CHANNELS, channels);
}
void pa_format_info_set_channel_map(pa_format_info *f, const pa_channel_map *map) {
char map_str[PA_CHANNEL_MAP_SNPRINT_MAX];
pa_channel_map_snprint(map_str, sizeof(map_str), map);
pa_format_info_set_prop_string(f, PA_PROP_FORMAT_CHANNEL_MAP, map_str);
}
pa_bool_t pa_format_info_get_prop_int(pa_format_info *f, const char *key, int *v) {
const char *str;
json_object *o;
pa_assert(f);
pa_assert(key);
pa_assert(v);
pa_return_val_if_fail(str = pa_proplist_gets(f->plist, key), FALSE);
o = json_tokener_parse(str);
pa_return_val_if_fail(!is_error(o), FALSE);
if (json_object_get_type(o) != json_type_int) {
json_object_put(o);
return FALSE;
}
*v = json_object_get_int(o);
json_object_put(o);
return TRUE;
}
pa_bool_t pa_format_info_get_prop_string(pa_format_info *f, const char *key, char **v) {
const char *str = NULL;
json_object *o;
pa_assert(f);
pa_assert(key);
pa_assert(v);
pa_return_val_if_fail(str = pa_proplist_gets(f->plist, key), FALSE);
o = json_tokener_parse(str);
pa_return_val_if_fail(!is_error(o), FALSE);
if (json_object_get_type(o) != json_type_string) {
json_object_put(o);
return FALSE;
}
*v = pa_xstrdup(json_object_get_string(o));
json_object_put(o);
return TRUE;
}
void pa_format_info_set_prop_int(pa_format_info *f, const char *key, int value) {
json_object *o;
pa_assert(f);
pa_assert(key);
o = json_object_new_int(value);
pa_proplist_sets(f->plist, key, json_object_to_json_string(o));
json_object_put(o);
}
void pa_format_info_set_prop_int_array(pa_format_info *f, const char *key, const int *values, int n_values) {
json_object *o;
int i;
pa_assert(f);
pa_assert(key);
o = json_object_new_array();
for (i = 0; i < n_values; i++)
json_object_array_add(o, json_object_new_int(values[i]));
pa_proplist_sets(f->plist, key, json_object_to_json_string(o));
json_object_put(o);
}
void pa_format_info_set_prop_int_range(pa_format_info *f, const char *key, int min, int max) {
json_object *o;
pa_assert(f);
pa_assert(key);
o = json_object_new_object();
json_object_object_add(o, PA_JSON_MIN_KEY, json_object_new_int(min));
json_object_object_add(o, PA_JSON_MAX_KEY, json_object_new_int(max));
pa_proplist_sets(f->plist, key, json_object_to_json_string(o));
json_object_put(o);
}
void pa_format_info_set_prop_string(pa_format_info *f, const char *key, const char *value) {
json_object *o;
pa_assert(f);
pa_assert(key);
o = json_object_new_string(value);
pa_proplist_sets(f->plist, key, json_object_to_json_string(o));
json_object_put(o);
}
void pa_format_info_set_prop_string_array(pa_format_info *f, const char *key, const char **values, int n_values) {
json_object *o;
int i;
pa_assert(f);
pa_assert(key);
o = json_object_new_array();
for (i = 0; i < n_values; i++)
json_object_array_add(o, json_object_new_string(values[i]));
pa_proplist_sets(f->plist, key, json_object_to_json_string(o));
json_object_put(o);
}
static pa_bool_t pa_json_is_fixed_type(json_object *o)
{
switch(json_object_get_type(o)) {
case json_type_object:
case json_type_array:
return FALSE;
default:
return TRUE;
}
}
static int pa_json_value_equal(json_object *o1, json_object *o2) {
return (json_object_get_type(o1) == json_object_get_type(o2)) &&
pa_streq(json_object_to_json_string(o1), json_object_to_json_string(o2));
}
static int pa_format_info_prop_compatible(const char *one, const char *two) {
json_object *o1 = NULL, *o2 = NULL;
int i, ret = 0;
o1 = json_tokener_parse(one);
if (is_error(o1))
goto out;
o2 = json_tokener_parse(two);
if (is_error(o2))
goto out;
/* We don't deal with both values being non-fixed - just because there is no immediate need (FIXME) */
pa_return_val_if_fail(pa_json_is_fixed_type(o1) || pa_json_is_fixed_type(o2), FALSE);
if (pa_json_is_fixed_type(o1) && pa_json_is_fixed_type(o2)) {
ret = pa_json_value_equal(o1, o2);
goto out;
}
if (pa_json_is_fixed_type(o1)) {
json_object *tmp = o2;
o2 = o1;
o1 = tmp;
}
/* o2 is now a fixed type, and o1 is not */
if (json_object_get_type(o1) == json_type_array) {
for (i = 0; i < json_object_array_length(o1); i++) {
if (pa_json_value_equal(json_object_array_get_idx(o1, i), o2)) {
ret = 1;
break;
}
}
} else if (json_object_get_type(o1) == json_type_object) {
/* o1 should be a range type */
int min, max, v;
json_object *o_min = NULL, *o_max = NULL;
if (json_object_get_type(o2) != json_type_int) {
/* We don't support non-integer ranges */
goto out;
}
o_min = json_object_object_get(o1, PA_JSON_MIN_KEY);
if (!o_min || json_object_get_type(o_min) != json_type_int)
goto out;
o_max = json_object_object_get(o1, PA_JSON_MAX_KEY);
if (!o_max || json_object_get_type(o_max) != json_type_int)
goto out;
v = json_object_get_int(o2);
min = json_object_get_int(o_min);
max = json_object_get_int(o_max);
ret = v >= min && v <= max;
} else {
pa_log_warn("Got a format type that we don't support");
}
out:
if (o1)
json_object_put(o1);
if (o2)
json_object_put(o2);
return ret;
}

129
src/pulse/format.h Normal file
View file

@ -0,0 +1,129 @@
#ifndef fooformathfoo
#define fooformathfoo
/***
This file is part of PulseAudio.
Copyright 2011 Intel Corporation
Copyright 2011 Collabora Multimedia
Copyright 2011 Arun Raghavan <arun.raghavan@collabora.co.uk>
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <pulse/cdecl.h>
#include <pulse/proplist.h>
#include <pulse/sample.h>
#include <pulse/channelmap.h>
PA_C_DECL_BEGIN
/**< Represents the type of encoding used in a stream or accepted by a sink. \since 1.0 */
typedef enum pa_encoding {
PA_ENCODING_ANY,
/**< Any encoding format, PCM or compressed */
PA_ENCODING_PCM,
/**< Any PCM format */
PA_ENCODING_AC3_IEC61937,
/**< AC3 data encapsulated in IEC 61937 header/padding */
PA_ENCODING_EAC3_IEC61937,
/**< EAC3 data encapsulated in IEC 61937 header/padding */
PA_ENCODING_MPEG_IEC61937,
/**< MPEG-1 or MPEG-2 (Part 3, not AAC) data encapsulated in IEC 61937 header/padding */
PA_ENCODING_DTS_IEC61937,
/**< DTS data encapsulated in IEC 61937 header/padding */
PA_ENCODING_MAX,
/**< Valid encoding types must be less than this value */
PA_ENCODING_INVALID = -1,
/**< Represents an invalid encoding */
} pa_encoding_t;
/** Returns a printable string representing the given encoding type. \since 1.0 */
const char *pa_encoding_to_string(pa_encoding_t e) PA_GCC_CONST;
/**< Represents the format of data provided in a stream or processed by a sink. \since 1.0 */
typedef struct pa_format_info {
pa_encoding_t encoding;
/**< The encoding used for the format */
pa_proplist *plist;
/**< Additional encoding-specific properties such as sample rate, bitrate, etc. */
} pa_format_info;
/**< Allocates a new \a pa_format_info structure. Clients must initialise at least the encoding field themselves. */
pa_format_info* pa_format_info_new(void);
/**< Returns a new \a pa_format_info struct and representing the same format as \a src */
pa_format_info* pa_format_info_copy(const pa_format_info *src);
/**< Frees a \a pa_format_info structure */
void pa_format_info_free(pa_format_info *f);
/** Returns non-zero when the format info structure is valid */
int pa_format_info_valid(const pa_format_info *f);
/** Returns non-zero when the format info structure represents a PCM (i.e. uncompressed data) format */
int pa_format_info_is_pcm(const pa_format_info *f);
/** Returns non-zero if the format represented \a first is a subset of
* the format represented by \second. This means that \a second must
* have all the fields that \a first does, but the reverse need not
* be true. This is typically expected to be used to check if a
* stream's format is compatible with a given sink. In such a case,
* \a first would be the sink's format and \a second would be the
* stream's.*/
int pa_format_info_is_compatible(pa_format_info *first, pa_format_info *second);
/** Maximum required string length for
* pa_format_info_snprint(). Please note that this value can change
* with any release without warning and without being considered API
* or ABI breakage. You should not use this definition anywhere where
* it might become part of an ABI. \since 1.0 */
#define PA_FORMAT_INFO_SNPRINT_MAX 256
/** Return a human-readable string representing the given format. \since 1.0 */
char *pa_format_info_snprint(char *s, size_t l, const pa_format_info *f);
/** Sets an integer property on the given format info */
void pa_format_info_set_prop_int(pa_format_info *f, const char *key, int value);
/** Sets a property with a list of integer values on the given format info */
void pa_format_info_set_prop_int_array(pa_format_info *f, const char *key, const int *values, int n_values);
/** Sets a property which can have any value in a given integer range on the given format info */
void pa_format_info_set_prop_int_range(pa_format_info *f, const char *key, int min, int max);
/** Sets a string property on the given format info */
void pa_format_info_set_prop_string(pa_format_info *f, const char *key, const char *value);
/** Sets a property with a list of string values on the given format info */
void pa_format_info_set_prop_string_array(pa_format_info *f, const char *key, const char **values, int n_values);
/** Convenience method to set the sample format as a property on the given format */
void pa_format_info_set_sample_format(pa_format_info *f, pa_sample_format_t sf);
/** Convenience method to set the sampling rate as a property on the given format */
void pa_format_info_set_rate(pa_format_info *f, int rate);
/** Convenience method to set the number of channels as a property on the given format */
void pa_format_info_set_channels(pa_format_info *f, int channels);
/** Convenience method to set the channel map as a property on the given format */
void pa_format_info_set_channel_map(pa_format_info *f, const pa_channel_map *map);
PA_C_DECL_END
#endif

View file

@ -122,6 +122,8 @@ typedef struct pa_index_correction {
pa_bool_t corrupt:1; pa_bool_t corrupt:1;
} pa_index_correction; } pa_index_correction;
#define PA_MAX_FORMATS (PA_ENCODING_MAX)
struct pa_stream { struct pa_stream {
PA_REFCNT_DECLARE; PA_REFCNT_DECLARE;
PA_LLIST_FIELDS(pa_stream); PA_LLIST_FIELDS(pa_stream);
@ -137,6 +139,9 @@ struct pa_stream {
pa_sample_spec sample_spec; pa_sample_spec sample_spec;
pa_channel_map channel_map; pa_channel_map channel_map;
uint8_t n_formats;
pa_format_info *req_formats[PA_MAX_FORMATS];
pa_format_info *format;
pa_proplist *proplist; pa_proplist *proplist;
@ -291,6 +296,13 @@ pa_tagstruct *pa_tagstruct_command(pa_context *c, uint32_t command, uint32_t *ta
void pa_ext_device_manager_command(pa_context *c, uint32_t tag, pa_tagstruct *t); void pa_ext_device_manager_command(pa_context *c, uint32_t tag, pa_tagstruct *t);
void pa_ext_stream_restore_command(pa_context *c, uint32_t tag, pa_tagstruct *t); void pa_ext_stream_restore_command(pa_context *c, uint32_t tag, pa_tagstruct *t);
void pa_format_info_free2(pa_format_info *f, void *userdata);
pa_format_info* pa_format_info_from_sample_spec(pa_sample_spec *ss, pa_channel_map *map);
pa_bool_t pa_format_info_to_sample_spec(pa_format_info *f, pa_sample_spec *ss, pa_channel_map *map);
pa_bool_t pa_format_info_to_sample_spec_fake(pa_format_info *f, pa_sample_spec *ss);
pa_bool_t pa_format_info_get_prop_int(pa_format_info *f, const char *key, int *v);
pa_bool_t pa_format_info_get_prop_string(pa_format_info *f, const char *key, char **v);
pa_bool_t pa_mainloop_is_our_api(pa_mainloop_api*m); pa_bool_t pa_mainloop_is_our_api(pa_mainloop_api*m);
#endif #endif

View file

@ -240,6 +240,33 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, u
} }
} }
if (o->context->version >= 21) {
i.formats = NULL;
if (pa_tagstruct_getu8(t, &i.n_formats)) {
pa_context_fail(o->context, PA_ERR_PROTOCOL);
pa_proplist_free(i.proplist);
goto finish;
}
pa_assert(i.n_formats > 0);
i.formats = pa_xnew0(pa_format_info*, i.n_formats);
for (j = 0; j < i.n_formats; j++) {
i.formats[j] = pa_format_info_new();
if (pa_tagstruct_get_format_info(t, i.formats[j]) < 0) {
do {
pa_format_info_free(i.formats[j]);
} while (j--);
pa_xfree(i.formats);
pa_context_fail(o->context, PA_ERR_PROTOCOL);
pa_proplist_free(i.proplist);
goto finish;
}
}
}
i.mute = (int) mute; i.mute = (int) mute;
i.flags = (pa_sink_flags_t) flags; i.flags = (pa_sink_flags_t) flags;
i.state = (pa_sink_state_t) state; i.state = (pa_sink_state_t) state;
@ -253,6 +280,13 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, u
pa_xfree(i.ports[0]); pa_xfree(i.ports[0]);
pa_xfree(i.ports); pa_xfree(i.ports);
} }
if (i.formats) {
for (j = 0; j < i.n_formats; j++)
pa_format_info_free(i.formats[j]);
pa_xfree(i.formats);
}
pa_proplist_free(i.proplist); pa_proplist_free(i.proplist);
} }
} }
@ -999,6 +1033,7 @@ static void context_get_sink_input_info_callback(pa_pdispatch *pd, uint32_t comm
pa_zero(i); pa_zero(i);
i.proplist = pa_proplist_new(); i.proplist = pa_proplist_new();
i.format = pa_format_info_new();
if (pa_tagstruct_getu32(t, &i.index) < 0 || if (pa_tagstruct_getu32(t, &i.index) < 0 ||
pa_tagstruct_gets(t, &i.name) < 0 || pa_tagstruct_gets(t, &i.name) < 0 ||
@ -1016,10 +1051,12 @@ static void context_get_sink_input_info_callback(pa_pdispatch *pd, uint32_t comm
(o->context->version >= 13 && pa_tagstruct_get_proplist(t, i.proplist) < 0) || (o->context->version >= 13 && pa_tagstruct_get_proplist(t, i.proplist) < 0) ||
(o->context->version >= 19 && pa_tagstruct_get_boolean(t, &corked) < 0) || (o->context->version >= 19 && pa_tagstruct_get_boolean(t, &corked) < 0) ||
(o->context->version >= 20 && (pa_tagstruct_get_boolean(t, &has_volume) < 0 || (o->context->version >= 20 && (pa_tagstruct_get_boolean(t, &has_volume) < 0 ||
pa_tagstruct_get_boolean(t, &volume_writable) < 0))) { pa_tagstruct_get_boolean(t, &volume_writable) < 0)) ||
(o->context->version >= 21 && pa_tagstruct_get_format_info(t, i.format) < 0)) {
pa_context_fail(o->context, PA_ERR_PROTOCOL); pa_context_fail(o->context, PA_ERR_PROTOCOL);
pa_proplist_free(i.proplist); pa_proplist_free(i.proplist);
pa_format_info_free(i.format);
goto finish; goto finish;
} }
@ -1034,6 +1071,7 @@ static void context_get_sink_input_info_callback(pa_pdispatch *pd, uint32_t comm
} }
pa_proplist_free(i.proplist); pa_proplist_free(i.proplist);
pa_format_info_free(i.format);
} }
} }

View file

@ -32,6 +32,7 @@
#include <pulse/channelmap.h> #include <pulse/channelmap.h>
#include <pulse/volume.h> #include <pulse/volume.h>
#include <pulse/proplist.h> #include <pulse/proplist.h>
#include <pulse/format.h>
#include <pulse/version.h> #include <pulse/version.h>
/** \page introspect Server Query and Control /** \page introspect Server Query and Control
@ -230,6 +231,8 @@ typedef struct pa_sink_info {
uint32_t n_ports; /**< Number of entries in port array \since 0.9.16 */ uint32_t n_ports; /**< Number of entries in port array \since 0.9.16 */
pa_sink_port_info** ports; /**< Array of available ports, or NULL. Array is terminated by an entry set to NULL. The number of entries is stored in n_ports \since 0.9.16 */ pa_sink_port_info** ports; /**< Array of available ports, or NULL. Array is terminated by an entry set to NULL. The number of entries is stored in n_ports \since 0.9.16 */
pa_sink_port_info* active_port; /**< Pointer to active port in the array, or NULL \since 0.9.16 */ pa_sink_port_info* active_port; /**< Pointer to active port in the array, or NULL \since 0.9.16 */
uint8_t n_formats; /**< Number of formats supported by the sink. \since 1.0 */
pa_format_info **formats; /**< Array of formats supported by the sink. \since 1.0 */
} pa_sink_info; } pa_sink_info;
/** Callback prototype for pa_context_get_sink_info_by_name() and friends */ /** Callback prototype for pa_context_get_sink_info_by_name() and friends */
@ -505,6 +508,7 @@ typedef struct pa_sink_input_info {
int corked; /**< Stream corked \since 1.0 */ int corked; /**< Stream corked \since 1.0 */
int has_volume; /**< Stream has volume. If not set, then the meaning of this struct's volume member is unspecified. \since 1.0 */ int has_volume; /**< Stream has volume. If not set, then the meaning of this struct's volume member is unspecified. \since 1.0 */
int volume_writable; /**< The volume can be set. If not set, the volume can still change even though clients can't control the volume. \since 1.0 */ int volume_writable; /**< The volume can be set. If not set, the volume can still change even though clients can't control the volume. \since 1.0 */
pa_format_info *format; /**< Stream format information. \since 1.0 */
} pa_sink_input_info; } pa_sink_input_info;
/** Callback prototype for pa_context_get_sink_input_info() and friends*/ /** Callback prototype for pa_context_get_sink_input_info() and friends*/

View file

@ -254,6 +254,18 @@ PA_C_DECL_BEGIN
/** For modules: a version string for the module. e.g. "0.9.15" */ /** For modules: a version string for the module. e.g. "0.9.15" */
#define PA_PROP_MODULE_VERSION "module.version" #define PA_PROP_MODULE_VERSION "module.version"
/** For PCM formats: the sample format used as returned by pa_sample_format_to_string() \since 1.0 */
#define PA_PROP_FORMAT_SAMPLE_FORMAT "format.sample_format"
/** For all formats: the sample rate (unsigned integer) \since 1.0 */
#define PA_PROP_FORMAT_RATE "format.rate"
/** For all formats: the number of channels (unsigned integer) \since 1.0 */
#define PA_PROP_FORMAT_CHANNELS "format.channels"
/** For PCM formats: the channel map of the stream as returned by pa_channel_map_snprint() \since 1.0 */
#define PA_PROP_FORMAT_CHANNEL_MAP "format.channel_map"
/** A property list object. Basically a dictionary with ASCII strings /** A property list object. Basically a dictionary with ASCII strings
* as keys and arbitrary data as values. \since 0.9.11 */ * as keys and arbitrary data as values. \since 0.9.11 */
typedef struct pa_proplist pa_proplist; typedef struct pa_proplist pa_proplist;

View file

@ -25,6 +25,7 @@
#include <pulse/mainloop-api.h> #include <pulse/mainloop-api.h>
#include <pulse/sample.h> #include <pulse/sample.h>
#include <pulse/format.h>
#include <pulse/def.h> #include <pulse/def.h>
#include <pulse/context.h> #include <pulse/context.h>
#include <pulse/stream.h> #include <pulse/stream.h>

View file

@ -242,7 +242,7 @@ pa_sample_format_t pa_parse_sample_format(const char *format) {
else if (strcasecmp(format, "s24-32re") == 0) else if (strcasecmp(format, "s24-32re") == 0)
return PA_SAMPLE_S24_32RE; return PA_SAMPLE_S24_32RE;
return -1; return PA_SAMPLE_INVALID;
} }
int pa_sample_format_is_le(pa_sample_format_t f) { int pa_sample_format_is_le(pa_sample_format_t f) {

View file

@ -80,31 +80,24 @@ static void reset_callbacks(pa_stream *s) {
s->buffer_attr_userdata = NULL; s->buffer_attr_userdata = NULL;
} }
pa_stream *pa_stream_new_with_proplist( static pa_stream *pa_stream_new_with_proplist_internal(
pa_context *c, pa_context *c,
const char *name, const char *name,
const pa_sample_spec *ss, const pa_sample_spec *ss,
const pa_channel_map *map, const pa_channel_map *map,
pa_format_info * const *formats,
pa_proplist *p) { pa_proplist *p) {
pa_stream *s; pa_stream *s;
int i; int i;
pa_channel_map tmap;
pa_assert(c); pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1); 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, !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); 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); s = pa_xnew(pa_stream, 1);
PA_REFCNT_INIT(s); PA_REFCNT_INIT(s);
s->context = c; s->context = c;
@ -114,8 +107,28 @@ pa_stream *pa_stream_new_with_proplist(
s->state = PA_STREAM_UNCONNECTED; s->state = PA_STREAM_UNCONNECTED;
s->flags = 0; s->flags = 0;
s->sample_spec = *ss; if (ss)
s->channel_map = *map; 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; s->direct_on_input = PA_INVALID_INDEX;
@ -136,7 +149,18 @@ pa_stream *pa_stream_new_with_proplist(
* what older PA versions provided. */ * what older PA versions provided. */
s->buffer_attr.maxlength = (uint32_t) -1; s->buffer_attr.maxlength = (uint32_t) -1;
s->buffer_attr.tlength = (uint32_t) pa_usec_to_bytes(250*PA_USEC_PER_MSEC, ss); /* 250ms of buffering */ if (ss)
s->buffer_attr.tlength = (uint32_t) pa_usec_to_bytes(250*PA_USEC_PER_MSEC, ss); /* 250ms of buffering */
else {
/* FIXME: We assume a worst-case compressed format corresponding to
* 48000 Hz, 2 ch, S16 PCM, but this can very well be incorrect */
pa_sample_spec tmp_ss = {
.format = PA_SAMPLE_S16NE,
.rate = 48000,
.channels = 2,
};
s->buffer_attr.tlength = (uint32_t) pa_usec_to_bytes(250*PA_USEC_PER_MSEC, &tmp_ss); /* 250ms of buffering */
}
s->buffer_attr.minreq = (uint32_t) -1; s->buffer_attr.minreq = (uint32_t) -1;
s->buffer_attr.prebuf = (uint32_t) -1; s->buffer_attr.prebuf = (uint32_t) -1;
s->buffer_attr.fragsize = (uint32_t) -1; s->buffer_attr.fragsize = (uint32_t) -1;
@ -179,6 +203,38 @@ pa_stream *pa_stream_new_with_proplist(
return s; 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);
return pa_stream_new_with_proplist_internal(c, name, NULL, NULL, formats, p);
}
static void stream_unlink(pa_stream *s) { static void stream_unlink(pa_stream *s) {
pa_operation *o, *n; pa_operation *o, *n;
pa_assert(s); pa_assert(s);
@ -220,6 +276,8 @@ static void stream_unlink(pa_stream *s) {
} }
static void stream_free(pa_stream *s) { static void stream_free(pa_stream *s) {
unsigned int i;
pa_assert(s); pa_assert(s);
stream_unlink(s); stream_unlink(s);
@ -244,6 +302,9 @@ static void stream_free(pa_stream *s) {
if (s->smoother) if (s->smoother)
pa_smoother_free(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->device_name);
pa_xfree(s); pa_xfree(s);
} }
@ -705,6 +766,14 @@ void pa_command_stream_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, p
if (s->state != PA_STREAM_READY) if (s->state != PA_STREAM_READY)
goto finish; goto finish;
if (pa_streq(event, PA_STREAM_EVENT_FORMAT_LOST)) {
/* Let client know what the running time was when the stream had to be
* killed */
pa_usec_t time;
if (pa_stream_get_time(s, &time) == 0)
pa_proplist_setf(pl, "stream-time", "%llu", (unsigned long long) time);
}
if (s->event_callback) if (s->event_callback)
s->event_callback(s, event, pl, s->event_userdata); s->event_callback(s, event, pl, s->event_userdata);
@ -970,9 +1039,10 @@ void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag,
ss.channels != cm.channels || ss.channels != cm.channels ||
!pa_channel_map_valid(&cm) || !pa_channel_map_valid(&cm) ||
!pa_sample_spec_valid(&ss) || !pa_sample_spec_valid(&ss) ||
(!(s->flags & PA_STREAM_FIX_FORMAT) && ss.format != s->sample_spec.format) || (s->n_formats == 0 && (
(!(s->flags & PA_STREAM_FIX_RATE) && ss.rate != s->sample_spec.rate) || (!(s->flags & PA_STREAM_FIX_FORMAT) && ss.format != s->sample_spec.format) ||
(!(s->flags & PA_STREAM_FIX_CHANNELS) && !pa_channel_map_equal(&cm, &s->channel_map))) { (!(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))))) {
pa_context_fail(s->context, PA_ERR_PROTOCOL); pa_context_fail(s->context, PA_ERR_PROTOCOL);
goto finish; goto finish;
} }
@ -999,6 +1069,22 @@ void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag,
s->timing_info.configured_sink_usec = usec; 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 (s->n_formats > 0) {
/* We used the extended API, so we should have got back a proper format */
pa_context_fail(s->context, PA_ERR_PROTOCOL);
goto finish;
}
}
}
if (!pa_tagstruct_eof(t)) { if (!pa_tagstruct_eof(t)) {
pa_context_fail(s->context, PA_ERR_PROTOCOL); pa_context_fail(s->context, PA_ERR_PROTOCOL);
goto finish; goto finish;
@ -1039,6 +1125,7 @@ static int create_stream(
pa_tagstruct *t; pa_tagstruct *t;
uint32_t tag; uint32_t tag;
pa_bool_t volume_set = FALSE; pa_bool_t volume_set = FALSE;
uint32_t i;
pa_assert(s); pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1); pa_assert(PA_REFCNT_VALUE(s) >= 1);
@ -1079,7 +1166,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_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, 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, !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); 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 +1234,16 @@ static int create_stream(
volume_set = !!volume; volume_set = !!volume;
if (!volume) if (!volume) {
volume = pa_cvolume_reset(&cv, s->sample_spec.channels); 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); pa_tagstruct_put_cvolume(t, volume);
} else } else
@ -1214,6 +1309,15 @@ static int create_stream(
pa_tagstruct_put_boolean(t, flags & (PA_STREAM_PASSTHROUGH)); 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_pstream_send_tagstruct(s->context->pstream, t);
pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_create_stream_callback, s, NULL); pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_create_stream_callback, s, NULL);
@ -2374,6 +2478,16 @@ const pa_channel_map* pa_stream_get_channel_map(pa_stream *s) {
return &s->channel_map; return &s->channel_map;
} }
const pa_format_info* pa_stream_get_format_info(pa_stream *s) {
pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1);
/* We don't have the format till routing is done */
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, !pa_detect_fork(), PA_ERR_FORKED);
return s->format;
}
const pa_buffer_attr* pa_stream_get_buffer_attr(pa_stream *s) { const pa_buffer_attr* pa_stream_get_buffer_attr(pa_stream *s) {
pa_assert(s); pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1); pa_assert(PA_REFCNT_VALUE(s) >= 1);

View file

@ -26,6 +26,7 @@
#include <sys/types.h> #include <sys/types.h>
#include <pulse/sample.h> #include <pulse/sample.h>
#include <pulse/format.h>
#include <pulse/channelmap.h> #include <pulse/channelmap.h>
#include <pulse/volume.h> #include <pulse/volume.h>
#include <pulse/def.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 */, const pa_channel_map *map /**< The desired channel map, or NULL for default */,
pa_proplist *p /**< The initial property list */); 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 */ /** Decrease the reference counter by one */
void pa_stream_unref(pa_stream *s); void pa_stream_unref(pa_stream *s);
@ -698,6 +709,9 @@ const pa_sample_spec* pa_stream_get_sample_spec(pa_stream *s);
/** Return a pointer to the stream's channel map. */ /** Return a pointer to the stream's channel map. */
const pa_channel_map* pa_stream_get_channel_map(pa_stream *s); const pa_channel_map* pa_stream_get_channel_map(pa_stream *s);
/** Return a pointer to the stream's format \since 1.0 */
const pa_format_info* pa_stream_get_format_info(pa_stream *s);
/** Return the per-stream server-side buffer metrics of the /** Return the per-stream server-side buffer metrics of the
* stream. Only valid after the stream has been connected successfuly * stream. Only valid after the stream has been connected successfuly
* and if the server is at least PulseAudio 0.9. This will return the * and if the server is at least PulseAudio 0.9. This will return the

View file

@ -35,6 +35,7 @@ typedef enum pa_suspend_cause {
PA_SUSPEND_APPLICATION = 2, /* Used by the device reservation logic */ PA_SUSPEND_APPLICATION = 2, /* Used by the device reservation logic */
PA_SUSPEND_IDLE = 4, /* Used by module-suspend-on-idle */ PA_SUSPEND_IDLE = 4, /* Used by module-suspend-on-idle */
PA_SUSPEND_SESSION = 8, /* Used by module-hal for mark inactive sessions */ PA_SUSPEND_SESSION = 8, /* Used by module-hal for mark inactive sessions */
PA_SUSPEND_PASSTHROUGH = 16, /* Used to suspend monitor sources when the sink is in passthrough mode */
PA_SUSPEND_ALL = 0xFFFF /* Magic cause that can be used to resume forcibly */ PA_SUSPEND_ALL = 0xFFFF /* Magic cause that can be used to resume forcibly */
} pa_suspend_cause_t; } pa_suspend_cause_t;

View file

@ -202,7 +202,7 @@ pa_sink_input* pa_memblockq_sink_input_new(
u->memblockq = NULL; u->memblockq = NULL;
pa_sink_input_new_data_init(&data); pa_sink_input_new_data_init(&data);
data.sink = sink; pa_sink_input_new_data_set_sink(&data, sink, FALSE);
data.driver = __FILE__; data.driver = __FILE__;
pa_sink_input_new_data_set_sample_spec(&data, ss); pa_sink_input_new_data_set_sample_spec(&data, ss);
pa_sink_input_new_data_set_channel_map(&data, map); pa_sink_input_new_data_set_channel_map(&data, map);

View file

@ -426,7 +426,7 @@ static int esd_proto_stream_play(connection *c, esd_proto_t request, const void
sdata.driver = __FILE__; sdata.driver = __FILE__;
sdata.module = c->options->module; sdata.module = c->options->module;
sdata.client = c->client; 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_data_set_sample_spec(&sdata, &ss);
pa_sink_input_new(&c->sink_input, c->protocol->core, &sdata); pa_sink_input_new(&c->sink_input, c->protocol->core, &sdata);

View file

@ -35,6 +35,7 @@
#include <pulse/utf8.h> #include <pulse/utf8.h>
#include <pulse/util.h> #include <pulse/util.h>
#include <pulse/xmalloc.h> #include <pulse/xmalloc.h>
#include <pulse/internal.h>
#include <pulsecore/native-common.h> #include <pulsecore/native-common.h>
#include <pulsecore/packet.h> #include <pulsecore/packet.h>
@ -1014,6 +1015,7 @@ static playback_stream* playback_stream_new(
pa_sink *sink, pa_sink *sink,
pa_sample_spec *ss, pa_sample_spec *ss,
pa_channel_map *map, pa_channel_map *map,
pa_idxset *formats,
pa_buffer_attr *a, pa_buffer_attr *a,
pa_cvolume *volume, pa_cvolume *volume,
pa_bool_t muted, pa_bool_t muted,
@ -1067,12 +1069,14 @@ static playback_stream* playback_stream_new(
data.driver = __FILE__; data.driver = __FILE__;
data.module = c->options->module; data.module = c->options->module;
data.client = c->client; data.client = c->client;
if (sink) { if (sink)
data.sink = sink; pa_sink_input_new_data_set_sink(&data, sink, TRUE);
data.save_sink = TRUE; if (pa_sample_spec_valid(ss))
} pa_sink_input_new_data_set_sample_spec(&data, 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); pa_sink_input_new_data_set_channel_map(&data, map);
if (formats)
pa_sink_input_new_data_set_formats(&data, formats);
if (volume) { if (volume) {
pa_sink_input_new_data_set_volume(&data, volume); pa_sink_input_new_data_set_volume(&data, volume);
data.volume_is_absolute = !relative_volume; data.volume_is_absolute = !relative_volume;
@ -1876,9 +1880,13 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
passthrough = FALSE; passthrough = FALSE;
pa_sink_input_flags_t flags = 0; pa_sink_input_flags_t flags = 0;
pa_proplist *p; pa_proplist *p = NULL;
pa_bool_t volume_set = TRUE; pa_bool_t volume_set = TRUE;
int ret = PA_ERR_INVALID; 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_native_connection_assert_ref(c);
pa_assert(t); pa_assert(t);
@ -1901,17 +1909,14 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
PA_TAG_INVALID) < 0) { PA_TAG_INVALID) < 0) {
protocol_error(c); protocol_error(c);
return; goto error;
} }
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS); 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_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_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, !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, 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(); p = pa_proplist_new();
@ -1930,8 +1935,7 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
pa_tagstruct_get_boolean(t, &variable_rate) < 0) { pa_tagstruct_get_boolean(t, &variable_rate) < 0) {
protocol_error(c); protocol_error(c);
pa_proplist_free(p); goto error;
return;
} }
} }
@ -1940,9 +1944,9 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
if (pa_tagstruct_get_boolean(t, &muted) < 0 || if (pa_tagstruct_get_boolean(t, &muted) < 0 ||
pa_tagstruct_get_boolean(t, &adjust_latency) < 0 || pa_tagstruct_get_boolean(t, &adjust_latency) < 0 ||
pa_tagstruct_get_proplist(t, p) < 0) { pa_tagstruct_get_proplist(t, p) < 0) {
protocol_error(c); protocol_error(c);
pa_proplist_free(p); goto error;
return;
} }
} }
@ -1950,9 +1954,9 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
if (pa_tagstruct_get_boolean(t, &volume_set) < 0 || if (pa_tagstruct_get_boolean(t, &volume_set) < 0 ||
pa_tagstruct_get_boolean(t, &early_requests) < 0) { pa_tagstruct_get_boolean(t, &early_requests) < 0) {
protocol_error(c); protocol_error(c);
pa_proplist_free(p); goto error;
return;
} }
} }
@ -1961,18 +1965,18 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
if (pa_tagstruct_get_boolean(t, &muted_set) < 0 || if (pa_tagstruct_get_boolean(t, &muted_set) < 0 ||
pa_tagstruct_get_boolean(t, &dont_inhibit_auto_suspend) < 0 || pa_tagstruct_get_boolean(t, &dont_inhibit_auto_suspend) < 0 ||
pa_tagstruct_get_boolean(t, &fail_on_suspend) < 0) { pa_tagstruct_get_boolean(t, &fail_on_suspend) < 0) {
protocol_error(c); protocol_error(c);
pa_proplist_free(p); goto error;
return;
} }
} }
if (c->version >= 17) { if (c->version >= 17) {
if (pa_tagstruct_get_boolean(t, &relative_volume) < 0) { if (pa_tagstruct_get_boolean(t, &relative_volume) < 0) {
protocol_error(c); protocol_error(c);
pa_proplist_free(p); goto error;
return;
} }
} }
@ -1980,31 +1984,57 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
if (pa_tagstruct_get_boolean(t, &passthrough) < 0 ) { if (pa_tagstruct_get_boolean(t, &passthrough) < 0 ) {
protocol_error(c); protocol_error(c);
pa_proplist_free(p); goto error;
return; }
}
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);
}
}
if (n_formats == 0) {
CHECK_VALIDITY(c->pstream, pa_sample_spec_valid(&ss), tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, map.channels == ss.channels && volume.channels == ss.channels, tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, pa_channel_map_valid(&map), tag, PA_ERR_INVALID);
} else {
PA_IDXSET_FOREACH(format, formats, i) {
CHECK_VALIDITY(c->pstream, pa_format_info_valid(format), tag, PA_ERR_INVALID);
} }
} }
if (!pa_tagstruct_eof(t)) { if (!pa_tagstruct_eof(t)) {
protocol_error(c); protocol_error(c);
pa_proplist_free(p); goto error;
return;
} }
if (sink_index != PA_INVALID_INDEX) { if (sink_index != PA_INVALID_INDEX) {
if (!(sink = pa_idxset_get_by_index(c->protocol->core->sinks, sink_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_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
pa_proplist_free(p); goto error;
return;
} }
} else if (sink_name) { } else if (sink_name) {
if (!(sink = pa_namereg_get(c->protocol->core, sink_name, PA_NAMEREG_SINK))) { if (!(sink = pa_namereg_get(c->protocol->core, sink_name, PA_NAMEREG_SINK))) {
pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY); pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
pa_proplist_free(p); goto error;
return;
} }
} }
@ -2025,7 +2055,7 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
* flag. For older versions we synthesize it here */ * flag. For older versions we synthesize it here */
muted_set = muted_set || muted; 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); pa_proplist_free(p);
CHECK_VALIDITY(c->pstream, s, tag, ret); CHECK_VALIDITY(c->pstream, s, tag, ret);
@ -2064,7 +2094,26 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
if (c->version >= 13) if (c->version >= 13)
pa_tagstruct_put_usec(reply, s->configured_sink_latency); 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); 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) pa_format_info_free2, NULL);
return;
} }
static void command_delete_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { static void command_delete_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
@ -2935,6 +2984,19 @@ static void sink_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_sin
pa_tagstruct_puts(t, sink->active_port ? sink->active_port->name : NULL); pa_tagstruct_puts(t, sink->active_port ? sink->active_port->name : NULL);
} }
if (c->version >= 21) {
uint32_t i;
pa_format_info *f;
pa_idxset *formats = pa_sink_get_formats(sink);
pa_tagstruct_putu8(t, (uint8_t) pa_idxset_size(formats));
PA_IDXSET_FOREACH(f, formats, i) {
pa_tagstruct_put_format_info(t, f);
}
pa_idxset_free(formats, (pa_free2_cb_t) pa_format_info_free2, NULL);
}
} }
static void source_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_source *source) { static void source_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_source *source) {
@ -3091,6 +3153,8 @@ static void sink_input_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t,
pa_tagstruct_put_boolean(t, has_volume); pa_tagstruct_put_boolean(t, has_volume);
pa_tagstruct_put_boolean(t, s->volume_writable); pa_tagstruct_put_boolean(t, s->volume_writable);
} }
if (c->version >= 21)
pa_tagstruct_put_format_info(t, s->format);
} }
static void source_output_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_source_output *s) { static void source_output_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_source_output *s) {

View file

@ -538,7 +538,7 @@ void pa_simple_protocol_connect(pa_simple_protocol *p, pa_iochannel *io, pa_simp
data.driver = __FILE__; data.driver = __FILE__;
data.module = o->module; data.module = o->module;
data.client = c->client; 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_proplist_update(data.proplist, PA_UPDATE_MERGE, c->client->proplist);
pa_sink_input_new_data_set_sample_spec(&data, &o->sample_spec); pa_sink_input_new_data_set_sample_spec(&data, &o->sample_spec);

View file

@ -31,6 +31,7 @@
#include <pulse/utf8.h> #include <pulse/utf8.h>
#include <pulse/xmalloc.h> #include <pulse/xmalloc.h>
#include <pulse/util.h> #include <pulse/util.h>
#include <pulse/internal.h>
#include <pulsecore/sample-util.h> #include <pulsecore/sample-util.h>
#include <pulsecore/core-subscribe.h> #include <pulsecore/core-subscribe.h>
@ -49,36 +50,18 @@ PA_DEFINE_PUBLIC_CLASS(pa_sink_input, pa_msgobject);
static void sink_input_free(pa_object *o); static void sink_input_free(pa_object *o);
static void set_real_ratio(pa_sink_input *i, const pa_cvolume *v); static void set_real_ratio(pa_sink_input *i, const pa_cvolume *v);
static int check_passthrough_connection(pa_sink_input_flags_t flags, pa_sink *dest) { static int check_passthrough_connection(pa_bool_t passthrough, pa_sink *dest) {
if (pa_sink_is_passthrough(dest)) {
if (dest->flags & PA_SINK_PASSTHROUGH) { pa_log_warn("Sink is already connected to PASSTHROUGH input");
return -PA_ERR_BUSY;
if (pa_idxset_size(dest->inputs) > 0) {
pa_sink_input *alt_i;
uint32_t idx;
alt_i = pa_idxset_first(dest->inputs, &idx);
/* only need to check the first input is not PASSTHROUGH */
if (alt_i->flags & PA_SINK_INPUT_PASSTHROUGH) {
pa_log_warn("Sink is already connected to PASSTHROUGH input");
return -PA_ERR_BUSY;
}
/* Current inputs are PCM, check new input is not PASSTHROUGH */
if (flags & PA_SINK_INPUT_PASSTHROUGH) {
pa_log_warn("Sink is already connected, cannot accept new PASSTHROUGH INPUT");
return -PA_ERR_BUSY;
}
}
} else {
if (flags & PA_SINK_INPUT_PASSTHROUGH) {
pa_log_warn("Cannot connect PASSTHROUGH sink input to sink without PASSTHROUGH capabilities");
return -PA_ERR_INVALID;
}
} }
/* If current input(s) exist, check new input is not PASSTHROUGH */
if (pa_idxset_size(dest->inputs) > 0 && passthrough) {
pa_log_warn("Sink is already connected, cannot accept new PASSTHROUGH INPUT");
return -PA_ERR_BUSY;
}
return PA_OK; return PA_OK;
} }
@ -107,6 +90,18 @@ void pa_sink_input_new_data_set_channel_map(pa_sink_input_new_data *data, const
data->channel_map = *map; data->channel_map = *map;
} }
pa_bool_t pa_sink_input_new_data_is_passthrough(pa_sink_input_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_SINK_INPUT_PASSTHROUGH))
return TRUE;
return FALSE;
}
void pa_sink_input_new_data_set_volume(pa_sink_input_new_data *data, const pa_cvolume *volume) { void pa_sink_input_new_data_set_volume(pa_sink_input_new_data *data, const pa_cvolume *volume) {
pa_assert(data); pa_assert(data);
pa_assert(data->volume_writable); pa_assert(data->volume_writable);
@ -146,9 +141,68 @@ void pa_sink_input_new_data_set_muted(pa_sink_input_new_data *data, pa_bool_t mu
data->muted = !!mute; data->muted = !!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 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) pa_format_info_free2, 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) pa_format_info_free2, 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) pa_format_info_free2, 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) { void pa_sink_input_new_data_done(pa_sink_input_new_data *data) {
pa_assert(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); pa_proplist_free(data->proplist);
} }
@ -189,6 +243,8 @@ int pa_sink_input_new(
pa_channel_map original_cm; pa_channel_map original_cm;
int r; int r;
char *pt; char *pt;
pa_sample_spec ss;
pa_channel_map map;
pa_assert(_i); pa_assert(_i);
pa_assert(core); pa_assert(core);
@ -201,22 +257,53 @@ int pa_sink_input_new(
if (data->origin_sink && (data->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)) if (data->origin_sink && (data->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER))
data->volume_writable = FALSE; 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) if ((r = pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], data)) < 0)
return r; return r;
pa_return_val_if_fail(!data->driver || pa_utf8_valid(data->driver), -PA_ERR_INVALID); pa_return_val_if_fail(!data->driver || pa_utf8_valid(data->driver), -PA_ERR_INVALID);
if (!data->sink) { if (!data->sink)
data->sink = pa_namereg_get(core, NULL, PA_NAMEREG_SINK); pa_sink_input_new_data_set_sink(data, pa_namereg_get(core, NULL, PA_NAMEREG_SINK), FALSE);
data->save_sink = FALSE;
/* Routing's done, we have a sink. 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_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_return_val_if_fail(pa_format_info_to_sample_spec_fake(data->format, &ss), -PA_ERR_INVALID);
pa_sink_input_new_data_set_sample_spec(data, &ss);
} }
pa_return_val_if_fail(data->sink, -PA_ERR_NOENTITY); pa_return_val_if_fail(data->sink, -PA_ERR_NOENTITY);
pa_return_val_if_fail(PA_SINK_IS_LINKED(pa_sink_get_state(data->sink)), -PA_ERR_BADSTATE); pa_return_val_if_fail(PA_SINK_IS_LINKED(pa_sink_get_state(data->sink)), -PA_ERR_BADSTATE);
pa_return_val_if_fail(!data->sync_base || (data->sync_base->sink == data->sink && pa_sink_input_get_state(data->sync_base) == PA_SINK_INPUT_CORKED), -PA_ERR_INVALID); pa_return_val_if_fail(!data->sync_base || (data->sync_base->sink == data->sink && pa_sink_input_get_state(data->sync_base) == PA_SINK_INPUT_CORKED), -PA_ERR_INVALID);
r = check_passthrough_connection(data->flags, data->sink); r = check_passthrough_connection(pa_sink_input_new_data_is_passthrough(data), data->sink);
pa_return_val_if_fail(r == PA_OK, r); if (r != PA_OK)
return r;
if (!data->sample_spec_is_set) if (!data->sample_spec_is_set)
data->sample_spec = data->sink->sample_spec; data->sample_spec = data->sink->sample_spec;
@ -232,6 +319,12 @@ int pa_sink_input_new(
pa_return_val_if_fail(pa_channel_map_compatible(&data->channel_map, &data->sample_spec), -PA_ERR_INVALID); pa_return_val_if_fail(pa_channel_map_compatible(&data->channel_map, &data->sample_spec), -PA_ERR_INVALID);
/* Don't restore (or save) stream volume for passthrough streams */
if (!pa_format_info_is_pcm(data->format)) {
data->volume_is_set = FALSE;
data->volume_factor_is_set = FALSE;
}
if (!data->volume_is_set) { if (!data->volume_is_set) {
pa_cvolume_reset(&data->volume, data->sample_spec.channels); pa_cvolume_reset(&data->volume, data->sample_spec.channels);
data->volume_is_absolute = FALSE; data->volume_is_absolute = FALSE;
@ -295,18 +388,20 @@ int pa_sink_input_new(
!pa_sample_spec_equal(&data->sample_spec, &data->sink->sample_spec) || !pa_sample_spec_equal(&data->sample_spec, &data->sink->sample_spec) ||
!pa_channel_map_equal(&data->channel_map, &data->sink->channel_map)) { !pa_channel_map_equal(&data->channel_map, &data->sink->channel_map)) {
if (!(resampler = pa_resampler_new( /* Note: for passthrough content we need to adjust the output rate to that of the current sink-input */
core->mempool, if (!pa_sink_input_new_data_is_passthrough(data)) /* no resampler for passthrough content */
&data->sample_spec, &data->channel_map, if (!(resampler = pa_resampler_new(
&data->sink->sample_spec, &data->sink->channel_map, core->mempool,
data->resample_method, &data->sample_spec, &data->channel_map,
((data->flags & PA_SINK_INPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) | &data->sink->sample_spec, &data->sink->channel_map,
((data->flags & PA_SINK_INPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) | data->resample_method,
(core->disable_remixing || (data->flags & PA_SINK_INPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0) | ((data->flags & PA_SINK_INPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) |
(core->disable_lfe_remixing ? PA_RESAMPLER_NO_LFE : 0)))) { ((data->flags & PA_SINK_INPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) |
pa_log_warn("Unsupported resampling operation."); (core->disable_remixing || (data->flags & PA_SINK_INPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0) |
return -PA_ERR_NOTSUPPORTED; (core->disable_lfe_remixing ? PA_RESAMPLER_NO_LFE : 0)))) {
} pa_log_warn("Unsupported resampling operation.");
return -PA_ERR_NOTSUPPORTED;
}
} }
i = pa_msgobject_new(pa_sink_input); i = pa_msgobject_new(pa_sink_input);
@ -327,6 +422,7 @@ int pa_sink_input_new(
i->actual_resample_method = resampler ? pa_resampler_get_method(resampler) : PA_RESAMPLER_INVALID; i->actual_resample_method = resampler ? pa_resampler_get_method(resampler) : PA_RESAMPLER_INVALID;
i->sample_spec = data->sample_spec; i->sample_spec = data->sample_spec;
i->channel_map = data->channel_map; 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)) { if (!data->volume_is_absolute && pa_sink_flat_volume_enabled(i->sink)) {
pa_cvolume remapped; pa_cvolume remapped;
@ -519,6 +615,10 @@ void pa_sink_input_unlink(pa_sink_input *i) {
if (i->sink->asyncmsgq) if (i->sink->asyncmsgq)
pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_REMOVE_INPUT, i, 0, NULL) == 0); pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_REMOVE_INPUT, i, 0, NULL) == 0);
/* We suspend the monitor if there was a passthrough sink, unsuspend now if required */
if (pa_sink_input_is_passthrough(i) && i->sink->monitor_source)
pa_source_suspend(i->sink->monitor_source, FALSE, PA_SUSPEND_PASSTHROUGH);
} }
reset_callbacks(i); reset_callbacks(i);
@ -562,6 +662,9 @@ static void sink_input_free(pa_object *o) {
if (i->thread_info.resampler) if (i->thread_info.resampler)
pa_resampler_free(i->thread_info.resampler); pa_resampler_free(i->thread_info.resampler);
if (i->format)
pa_format_info_free(i->format);
if (i->proplist) if (i->proplist)
pa_proplist_free(i->proplist); pa_proplist_free(i->proplist);
@ -606,6 +709,10 @@ void pa_sink_input_put(pa_sink_input *i) {
set_real_ratio(i, &i->volume); set_real_ratio(i, &i->volume);
} }
/* If we're entering passthrough mode, disable the monitor */
if (pa_sink_input_is_passthrough(i) && i->sink->monitor_source)
pa_source_suspend(i->sink->monitor_source, TRUE, PA_SUSPEND_PASSTHROUGH);
i->thread_info.soft_volume = i->soft_volume; i->thread_info.soft_volume = i->soft_volume;
i->thread_info.muted = i->muted; i->thread_info.muted = i->muted;
@ -1087,12 +1194,25 @@ static void set_real_ratio(pa_sink_input *i, const pa_cvolume *v) {
/* We don't copy the data to the thread_info data. That's left for someone else to do */ /* We don't copy the data to the thread_info data. That's left for someone else to do */
} }
/* Called from main or I/O context */
pa_bool_t pa_sink_input_is_passthrough(pa_sink_input *i) {
pa_sink_input_assert_ref(i);
if (PA_UNLIKELY(!pa_format_info_is_pcm(i->format)))
return TRUE;
if (PA_UNLIKELY(i->flags & PA_SINK_INPUT_PASSTHROUGH))
return TRUE;
return FALSE;
}
/* Called from main context */ /* Called from main context */
pa_bool_t pa_sink_input_is_volume_readable(pa_sink_input *i) { pa_bool_t pa_sink_input_is_volume_readable(pa_sink_input *i) {
pa_sink_input_assert_ref(i); pa_sink_input_assert_ref(i);
pa_assert_ctl_context(); pa_assert_ctl_context();
return !(i->flags & PA_SINK_INPUT_PASSTHROUGH); return !pa_sink_input_is_passthrough(i);
} }
/* Called from main context */ /* Called from main context */
@ -1251,7 +1371,7 @@ pa_bool_t pa_sink_input_may_move_to(pa_sink_input *i, pa_sink *dest) {
return FALSE; return FALSE;
} }
if (check_passthrough_connection(i->flags, dest) < 0) if (check_passthrough_connection(pa_sink_input_is_passthrough(i), dest) < 0)
return FALSE; return FALSE;
if (i->may_move_to) if (i->may_move_to)
@ -1297,6 +1417,10 @@ int pa_sink_input_start_move(pa_sink_input *i) {
pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_START_MOVE, i, 0, NULL) == 0); pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_START_MOVE, i, 0, NULL) == 0);
/* We suspend the monitor if there was a passthrough sink, unsuspend now if required */
if (pa_sink_input_is_passthrough(i) && i->sink->monitor_source)
pa_source_suspend(i->sink->monitor_source, FALSE, PA_SUSPEND_PASSTHROUGH);
pa_sink_update_status(i->sink); pa_sink_update_status(i->sink);
pa_cvolume_remap(&i->volume_factor_sink, &i->sink->channel_map, &i->channel_map); pa_cvolume_remap(&i->volume_factor_sink, &i->sink->channel_map, &i->channel_map);
i->sink = NULL; i->sink = NULL;
@ -1469,6 +1593,16 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
if (!pa_sink_input_may_move_to(i, dest)) if (!pa_sink_input_may_move_to(i, dest))
return -PA_ERR_NOTSUPPORTED; return -PA_ERR_NOTSUPPORTED;
if (pa_sink_input_is_passthrough(i) && !pa_sink_check_format(dest, i->format)) {
pa_proplist *p = pa_proplist_new();
pa_log_debug("New sink 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_sink_input_send_event(i, PA_STREAM_EVENT_FORMAT_LOST, p);
return -PA_ERR_NOTSUPPORTED;
}
if (i->thread_info.resampler && if (i->thread_info.resampler &&
pa_sample_spec_equal(pa_resampler_output_sample_spec(i->thread_info.resampler), &dest->sample_spec) && pa_sample_spec_equal(pa_resampler_output_sample_spec(i->thread_info.resampler), &dest->sample_spec) &&
pa_channel_map_equal(pa_resampler_output_channel_map(i->thread_info.resampler), &dest->channel_map)) pa_channel_map_equal(pa_resampler_output_channel_map(i->thread_info.resampler), &dest->channel_map))
@ -1533,6 +1667,10 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_FINISH_MOVE, i, 0, NULL) == 0); pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_FINISH_MOVE, i, 0, NULL) == 0);
/* If we're entering passthrough mode, disable the monitor */
if (pa_sink_input_is_passthrough(i) && i->sink->monitor_source)
pa_source_suspend(i->sink->monitor_source, TRUE, PA_SUSPEND_PASSTHROUGH);
pa_log_debug("Successfully moved sink input %i to %s.", i->index, dest->name); pa_log_debug("Successfully moved sink input %i to %s.", i->index, dest->name);
/* Notify everyone */ /* Notify everyone */

View file

@ -28,6 +28,7 @@
typedef struct pa_sink_input pa_sink_input; typedef struct pa_sink_input pa_sink_input;
#include <pulse/sample.h> #include <pulse/sample.h>
#include <pulse/format.h>
#include <pulsecore/hook-list.h> #include <pulsecore/hook-list.h>
#include <pulsecore/memblockq.h> #include <pulsecore/memblockq.h>
#include <pulsecore/resampler.h> #include <pulsecore/resampler.h>
@ -92,6 +93,7 @@ struct pa_sink_input {
pa_sample_spec sample_spec; pa_sample_spec sample_spec;
pa_channel_map channel_map; pa_channel_map channel_map;
pa_format_info *format;
pa_sink_input *sync_prev, *sync_next; pa_sink_input *sync_prev, *sync_next;
@ -279,6 +281,9 @@ typedef struct pa_sink_input_new_data {
pa_sample_spec sample_spec; pa_sample_spec sample_spec;
pa_channel_map channel_map; 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_cvolume volume, volume_factor, volume_factor_sink;
pa_bool_t muted:1; pa_bool_t muted:1;
@ -299,10 +304,13 @@ typedef struct pa_sink_input_new_data {
pa_sink_input_new_data* pa_sink_input_new_data_init(pa_sink_input_new_data *data); pa_sink_input_new_data* pa_sink_input_new_data_init(pa_sink_input_new_data *data);
void pa_sink_input_new_data_set_sample_spec(pa_sink_input_new_data *data, const pa_sample_spec *spec); void pa_sink_input_new_data_set_sample_spec(pa_sink_input_new_data *data, const pa_sample_spec *spec);
void pa_sink_input_new_data_set_channel_map(pa_sink_input_new_data *data, const pa_channel_map *map); void pa_sink_input_new_data_set_channel_map(pa_sink_input_new_data *data, const pa_channel_map *map);
pa_bool_t pa_sink_input_new_data_is_passthrough(pa_sink_input_new_data *data);
void pa_sink_input_new_data_set_volume(pa_sink_input_new_data *data, const pa_cvolume *volume); void pa_sink_input_new_data_set_volume(pa_sink_input_new_data *data, const pa_cvolume *volume);
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(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_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); 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); void pa_sink_input_new_data_done(pa_sink_input_new_data *data);
/* To be called by the implementing module only */ /* To be called by the implementing module only */
@ -343,6 +351,7 @@ void pa_sink_input_kill(pa_sink_input*i);
pa_usec_t pa_sink_input_get_latency(pa_sink_input *i, pa_usec_t *sink_latency); pa_usec_t pa_sink_input_get_latency(pa_sink_input *i, pa_usec_t *sink_latency);
pa_bool_t pa_sink_input_is_passthrough(pa_sink_input *i);
pa_bool_t pa_sink_input_is_volume_readable(pa_sink_input *i); pa_bool_t pa_sink_input_is_volume_readable(pa_sink_input *i);
void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, pa_bool_t save, pa_bool_t absolute); void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, pa_bool_t save, pa_bool_t absolute);
pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i, pa_cvolume *volume, pa_bool_t absolute); pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i, pa_cvolume *volume, pa_bool_t absolute);

View file

@ -35,6 +35,7 @@
#include <pulse/util.h> #include <pulse/util.h>
#include <pulse/i18n.h> #include <pulse/i18n.h>
#include <pulse/rtclock.h> #include <pulse/rtclock.h>
#include <pulse/internal.h>
#include <pulsecore/sink-input.h> #include <pulsecore/sink-input.h>
#include <pulsecore/namereg.h> #include <pulsecore/namereg.h>
@ -177,6 +178,7 @@ static void reset_callbacks(pa_sink *s) {
s->request_rewind = NULL; s->request_rewind = NULL;
s->update_requested_latency = NULL; s->update_requested_latency = NULL;
s->set_port = NULL; s->set_port = NULL;
s->get_formats = NULL;
} }
/* Called from main context */ /* Called from main context */
@ -1239,6 +1241,24 @@ pa_bool_t pa_sink_flat_volume_enabled(pa_sink *s) {
return (s->flags & PA_SINK_FLAT_VOLUME); return (s->flags & PA_SINK_FLAT_VOLUME);
} }
/* Called from main context */
pa_bool_t pa_sink_is_passthrough(pa_sink *s) {
pa_sink_input *alt_i;
uint32_t idx;
pa_sink_assert_ref(s);
/* one and only one PASSTHROUGH input can possibly be connected */
if (pa_idxset_size(s->inputs) == 1) {
alt_i = pa_idxset_first(s->inputs, &idx);
if (pa_sink_input_is_passthrough(alt_i))
return TRUE;
}
return FALSE;
}
/* Called from main context. */ /* Called from main context. */
static void compute_reference_ratio(pa_sink_input *i) { static void compute_reference_ratio(pa_sink_input *i) {
unsigned c = 0; unsigned c = 0;
@ -1630,21 +1650,10 @@ void pa_sink_set_volume(
pa_assert(!volume || volume->channels == 1 || pa_cvolume_compatible(volume, &s->sample_spec)); pa_assert(!volume || volume->channels == 1 || pa_cvolume_compatible(volume, &s->sample_spec));
/* make sure we don't change the volume when a PASSTHROUGH input is connected */ /* make sure we don't change the volume when a PASSTHROUGH input is connected */
if (s->flags & PA_SINK_PASSTHROUGH) { if (pa_sink_is_passthrough(s)) {
pa_sink_input *alt_i; /* FIXME: Need to notify client that volume control is disabled */
uint32_t idx; pa_log_warn("Cannot change volume, Sink is connected to PASSTHROUGH input");
return;
/* one and only one PASSTHROUGH input can possibly be connected */
if (pa_idxset_size(s->inputs) == 1) {
alt_i = pa_idxset_first(s->inputs, &idx);
if (alt_i->flags & PA_SINK_INPUT_PASSTHROUGH) {
/* FIXME: Need to notify client that volume control is disabled */
pa_log_warn("Cannot change volume, Sink is connected to PASSTHROUGH input");
return;
}
}
} }
/* In case of volume sharing, the volume is set for the root sink first, /* In case of volume sharing, the volume is set for the root sink first,
@ -2144,7 +2153,7 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
/* If you change anything here, make sure to change the /* If you change anything here, make sure to change the
* sink input handling a few lines down at * sink input handling a few lines down at
* PA_SINK_MESSAGE_PREPAPRE_MOVE, too. */ * PA_SINK_MESSAGE_START_MOVE, too. */
if (i->detach) if (i->detach)
i->detach(i); i->detach(i);
@ -3258,3 +3267,82 @@ static void pa_sink_volume_change_rewind(pa_sink *s, size_t nbytes) {
} }
pa_sink_volume_change_apply(s, NULL); pa_sink_volume_change_apply(s, NULL);
} }
/* Called from the main thread */
/* Gets the list of formats supported by the sink. The members and idxset must
* be freed by the caller. */
pa_idxset* pa_sink_get_formats(pa_sink *s) {
pa_idxset *ret;
pa_assert(s);
if (s->get_formats) {
/* Sink supports format query, all is good */
ret = s->get_formats(s);
} else {
/* Sink 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 sink can accept this format */
pa_bool_t pa_sink_check_format(pa_sink *s, pa_format_info *f)
{
pa_idxset *sink_formats = NULL;
pa_format_info *f_sink;
uint32_t i;
pa_bool_t ret = FALSE;
pa_assert(s);
pa_assert(f);
sink_formats = pa_sink_get_formats(s);
PA_IDXSET_FOREACH(f_sink, sink_formats, i) {
if (pa_format_info_is_compatible(f_sink, f)) {
ret = TRUE;
break;
}
}
if (sink_formats)
pa_idxset_free(sink_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 sink and
* in_formats, and returns these, in the order of the sink's formats. */
pa_idxset* pa_sink_check_formats(pa_sink *s, pa_idxset *in_formats) {
pa_idxset *out_formats = pa_idxset_new(NULL, NULL), *sink_formats = NULL;
pa_format_info *f_sink, *f_in;
uint32_t i, j;
pa_assert(s);
if (!in_formats || pa_idxset_isempty(in_formats))
goto done;
sink_formats = pa_sink_get_formats(s);
PA_IDXSET_FOREACH(f_sink, sink_formats, i) {
PA_IDXSET_FOREACH(f_in, in_formats, j) {
if (pa_format_info_is_compatible(f_sink, f_in))
pa_idxset_put(out_formats, pa_format_info_copy(f_in), NULL);
}
}
done:
if (sink_formats)
pa_idxset_free(sink_formats, (pa_free2_cb_t) pa_format_info_free2, NULL);
return out_formats;
}

View file

@ -161,7 +161,7 @@ struct pa_sink {
* s->real_volume and/or s->soft_volume so that they together * s->real_volume and/or s->soft_volume so that they together
* match the actual hardware volume that will be set later in the * match the actual hardware volume that will be set later in the
* write_volume callback. */ * write_volume callback. */
void (*set_volume)(pa_sink *s); /* dito */ void (*set_volume)(pa_sink *s); /* ditto */
/* Sink drivers that set PA_SINK_SYNC_VOLUME must provide this /* Sink drivers that set PA_SINK_SYNC_VOLUME must provide this
* callback. This callback is not used with sinks that do not set * callback. This callback is not used with sinks that do not set
@ -174,30 +174,34 @@ struct pa_sink {
* not called automatically - it is the driver's responsibility to * not called automatically - it is the driver's responsibility to
* schedule that function to be called at the right times in the * schedule that function to be called at the right times in the
* IO thread. */ * IO thread. */
void (*write_volume)(pa_sink *s); /* dito */ void (*write_volume)(pa_sink *s); /* ditto */
/* Called when the mute setting is queried. A PA_SINK_MESSAGE_GET_MUTE /* Called when the mute setting is queried. A PA_SINK_MESSAGE_GET_MUTE
* message will also be sent. Called from IO thread if PA_SINK_SYNC_VOLUME * message will also be sent. Called from IO thread if PA_SINK_SYNC_VOLUME
* flag is set otherwise from main loop context. If refresh_mute is FALSE * flag is set otherwise from main loop context. If refresh_mute is FALSE
* neither this function is called nor a message is sent.*/ * neither this function is called nor a message is sent.*/
void (*get_mute)(pa_sink *s); /* dito */ void (*get_mute)(pa_sink *s); /* ditto */
/* Called when the mute setting shall be changed. A PA_SINK_MESSAGE_SET_MUTE /* Called when the mute setting shall be changed. A PA_SINK_MESSAGE_SET_MUTE
* message will also be sent. Called from IO thread if PA_SINK_SYNC_VOLUME * message will also be sent. Called from IO thread if PA_SINK_SYNC_VOLUME
* flag is set otherwise from main loop context. */ * flag is set otherwise from main loop context. */
void (*set_mute)(pa_sink *s); /* dito */ void (*set_mute)(pa_sink *s); /* ditto */
/* Called when a rewind request is issued. Called from IO thread /* Called when a rewind request is issued. Called from IO thread
* context. */ * context. */
void (*request_rewind)(pa_sink *s); /* dito */ void (*request_rewind)(pa_sink *s); /* ditto */
/* Called when a the requested latency is changed. Called from IO /* Called when a the requested latency is changed. Called from IO
* thread context. */ * thread context. */
void (*update_requested_latency)(pa_sink *s); /* dito */ void (*update_requested_latency)(pa_sink *s); /* ditto */
/* Called whenever the port shall be changed. Called from main /* Called whenever the port shall be changed. Called from main
* thread. */ * thread. */
int (*set_port)(pa_sink *s, pa_device_port *port); /* dito */ int (*set_port)(pa_sink *s, pa_device_port *port); /* ditto */
/* Called to get the list of formats supported by the sink, sorted
* in descending order of preference. */
pa_idxset* (*get_formats)(pa_sink *s); /* ditto */
/* Contains copies of the above data so that the real-time worker /* Contains copies of the above data so that the real-time worker
* thread can work without access locking */ * thread can work without access locking */
@ -378,6 +382,10 @@ int pa_sink_suspend_all(pa_core *c, pa_bool_t suspend, pa_suspend_cause_t cause)
/* Use this instead of checking s->flags & PA_SINK_FLAT_VOLUME directly. */ /* Use this instead of checking s->flags & PA_SINK_FLAT_VOLUME directly. */
pa_bool_t pa_sink_flat_volume_enabled(pa_sink *s); pa_bool_t pa_sink_flat_volume_enabled(pa_sink *s);
/* Is the sink in passthrough mode? (that is, is there a passthrough sink input
* connected to this sink? */
pa_bool_t pa_sink_is_passthrough(pa_sink *s);
void pa_sink_set_volume(pa_sink *sink, const pa_cvolume *volume, pa_bool_t sendmsg, pa_bool_t save); void pa_sink_set_volume(pa_sink *sink, const pa_cvolume *volume, pa_bool_t sendmsg, pa_bool_t save);
const pa_cvolume *pa_sink_get_volume(pa_sink *sink, pa_bool_t force_refresh); const pa_cvolume *pa_sink_get_volume(pa_sink *sink, pa_bool_t force_refresh);
@ -398,6 +406,10 @@ pa_queue *pa_sink_move_all_start(pa_sink *s, pa_queue *q);
void pa_sink_move_all_finish(pa_sink *s, pa_queue *q, pa_bool_t save); void pa_sink_move_all_finish(pa_sink *s, pa_queue *q, pa_bool_t save);
void pa_sink_move_all_fail(pa_queue *q); void pa_sink_move_all_fail(pa_queue *q);
pa_idxset* pa_sink_get_formats(pa_sink *s);
pa_bool_t pa_sink_check_format(pa_sink *s, pa_format_info *f);
pa_idxset* pa_sink_check_formats(pa_sink *s, pa_idxset *in_formats);
/*** To be called exclusively by the sink driver, from IO context */ /*** To be called exclusively by the sink driver, from IO context */
void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result); void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result);

View file

@ -299,7 +299,7 @@ int pa_play_file(
u->readf_function = pa_sndfile_readf_function(&ss); u->readf_function = pa_sndfile_readf_function(&ss);
pa_sink_input_new_data_init(&data); pa_sink_input_new_data_init(&data);
data.sink = sink; pa_sink_input_new_data_set_sink(&data, sink, FALSE);
data.driver = __FILE__; data.driver = __FILE__;
pa_sink_input_new_data_set_sample_spec(&data, &ss); pa_sink_input_new_data_set_sample_spec(&data, &ss);
pa_sink_input_new_data_set_channel_map(&data, &cm); pa_sink_input_new_data_set_channel_map(&data, &cm);

View file

@ -514,7 +514,7 @@ int pa_source_suspend(pa_source *s, pa_bool_t suspend, pa_suspend_cause_t cause)
pa_assert(PA_SOURCE_IS_LINKED(s->state)); pa_assert(PA_SOURCE_IS_LINKED(s->state));
pa_assert(cause != 0); pa_assert(cause != 0);
if (s->monitor_of) if (s->monitor_of && cause != PA_SUSPEND_PASSTHROUGH)
return -PA_ERR_NOTSUPPORTED; return -PA_ERR_NOTSUPPORTED;
if (suspend) if (suspend)

View file

@ -291,6 +291,17 @@ void pa_tagstruct_put_proplist(pa_tagstruct *t, pa_proplist *p) {
pa_tagstruct_puts(t, NULL); pa_tagstruct_puts(t, NULL);
} }
void pa_tagstruct_put_format_info(pa_tagstruct *t, pa_format_info *f) {
pa_assert(t);
pa_assert(f);
extend(t, 1);
t->data[t->length++] = PA_TAG_FORMAT_INFO;
pa_tagstruct_putu8(t, (uint8_t) f->encoding);
pa_tagstruct_put_proplist(t, f->plist);
}
int pa_tagstruct_gets(pa_tagstruct*t, const char **s) { int pa_tagstruct_gets(pa_tagstruct*t, const char **s) {
int error = 0; int error = 0;
size_t n; size_t n;
@ -631,6 +642,37 @@ fail:
return -1; return -1;
} }
int pa_tagstruct_get_format_info(pa_tagstruct *t, pa_format_info *f) {
size_t saved_rindex;
uint8_t encoding;
pa_assert(t);
pa_assert(f);
if (t->rindex+1 > t->length)
return -1;
if (t->data[t->rindex] != PA_TAG_FORMAT_INFO)
return -1;
saved_rindex = t->rindex;
t->rindex++;
if (pa_tagstruct_getu8(t, &encoding) < 0)
goto fail;
f->encoding = encoding;
if (pa_tagstruct_get_proplist(t, f->plist) < 0)
goto fail;
return 0;
fail:
t->rindex = saved_rindex;
return -1;
}
void pa_tagstruct_put(pa_tagstruct *t, ...) { void pa_tagstruct_put(pa_tagstruct *t, ...) {
va_list va; va_list va;
pa_assert(t); pa_assert(t);

View file

@ -27,6 +27,7 @@
#include <sys/time.h> #include <sys/time.h>
#include <pulse/sample.h> #include <pulse/sample.h>
#include <pulse/format.h>
#include <pulse/channelmap.h> #include <pulse/channelmap.h>
#include <pulse/volume.h> #include <pulse/volume.h>
#include <pulse/proplist.h> #include <pulse/proplist.h>
@ -58,7 +59,8 @@ enum {
PA_TAG_CHANNEL_MAP = 'm', PA_TAG_CHANNEL_MAP = 'm',
PA_TAG_CVOLUME = 'v', PA_TAG_CVOLUME = 'v',
PA_TAG_PROPLIST = 'P', PA_TAG_PROPLIST = 'P',
PA_TAG_VOLUME = 'V' PA_TAG_VOLUME = 'V',
PA_TAG_FORMAT_INFO = 'f',
}; };
pa_tagstruct *pa_tagstruct_new(const uint8_t* data, size_t length); pa_tagstruct *pa_tagstruct_new(const uint8_t* data, size_t length);
@ -84,6 +86,7 @@ void pa_tagstruct_put_channel_map(pa_tagstruct *t, const pa_channel_map *map);
void pa_tagstruct_put_cvolume(pa_tagstruct *t, const pa_cvolume *cvolume); void pa_tagstruct_put_cvolume(pa_tagstruct *t, const pa_cvolume *cvolume);
void pa_tagstruct_put_proplist(pa_tagstruct *t, pa_proplist *p); void pa_tagstruct_put_proplist(pa_tagstruct *t, pa_proplist *p);
void pa_tagstruct_put_volume(pa_tagstruct *t, pa_volume_t volume); void pa_tagstruct_put_volume(pa_tagstruct *t, pa_volume_t volume);
void pa_tagstruct_put_format_info(pa_tagstruct *t, pa_format_info *f);
int pa_tagstruct_get(pa_tagstruct *t, ...); int pa_tagstruct_get(pa_tagstruct *t, ...);
@ -101,5 +104,6 @@ int pa_tagstruct_get_channel_map(pa_tagstruct *t, pa_channel_map *map);
int pa_tagstruct_get_cvolume(pa_tagstruct *t, pa_cvolume *v); int pa_tagstruct_get_cvolume(pa_tagstruct *t, pa_cvolume *v);
int pa_tagstruct_get_proplist(pa_tagstruct *t, pa_proplist *p); int pa_tagstruct_get_proplist(pa_tagstruct *t, pa_proplist *p);
int pa_tagstruct_get_volume(pa_tagstruct *t, pa_volume_t *v); int pa_tagstruct_get_volume(pa_tagstruct *t, pa_volume_t *v);
int pa_tagstruct_get_format_info(pa_tagstruct *t, pa_format_info *f);
#endif #endif

197
src/tests/extended-test.c Normal file
View file

@ -0,0 +1,197 @@
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <signal.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <math.h>
#include <pulse/pulseaudio.h>
#include <pulse/mainloop.h>
#define NSTREAMS 4
#define SINE_HZ 440
#define SAMPLE_HZ 8000
static pa_context *context = NULL;
static pa_stream *streams[NSTREAMS];
static pa_mainloop_api *mainloop_api = NULL;
static float data[SAMPLE_HZ]; /* one second space */
static int n_streams_ready = 0;
static const pa_buffer_attr buffer_attr = {
.maxlength = SAMPLE_HZ*sizeof(float)*NSTREAMS, /* exactly space for the entire play time */
.tlength = (uint32_t) -1,
.prebuf = 0, /* Setting prebuf to 0 guarantees us the the streams will run synchronously, no matter what */
.minreq = (uint32_t) -1,
.fragsize = 0
};
static void nop_free_cb(void *p) {}
static void underflow_cb(struct pa_stream *s, void *userdata) {
int i = (int) (long) userdata;
fprintf(stderr, "Stream %i finished\n", i);
if (++n_streams_ready >= 2*NSTREAMS) {
fprintf(stderr, "We're done\n");
mainloop_api->quit(mainloop_api, 0);
}
}
/* This routine is called whenever the stream state changes */
static void stream_state_callback(pa_stream *s, void *userdata) {
assert(s);
switch (pa_stream_get_state(s)) {
case PA_STREAM_UNCONNECTED:
case PA_STREAM_CREATING:
case PA_STREAM_TERMINATED:
break;
case PA_STREAM_READY: {
int r, i = (int) (long) userdata;
fprintf(stderr, "Writing data to stream %i.\n", i);
r = pa_stream_write(s, data, sizeof(data), nop_free_cb, (int64_t) sizeof(data) * (int64_t) i, PA_SEEK_ABSOLUTE);
assert(r == 0);
/* Be notified when this stream is drained */
pa_stream_set_underflow_callback(s, underflow_cb, userdata);
/* All streams have been set up, let's go! */
if (++n_streams_ready >= NSTREAMS) {
fprintf(stderr, "Uncorking\n");
pa_operation_unref(pa_stream_cork(s, 0, NULL, NULL));
}
break;
}
default:
case PA_STREAM_FAILED:
fprintf(stderr, "Stream error: %s\n", pa_strerror(pa_context_errno(pa_stream_get_context(s))));
abort();
}
}
/* This is called whenever the context status changes */
static void context_state_callback(pa_context *c, void *userdata) {
assert(c);
switch (pa_context_get_state(c)) {
case PA_CONTEXT_CONNECTING:
case PA_CONTEXT_AUTHORIZING:
case PA_CONTEXT_SETTING_NAME:
break;
case PA_CONTEXT_READY: {
int i;
fprintf(stderr, "Connection established.\n");
for (i = 0; i < NSTREAMS; i++) {
char name[64];
pa_format_info *formats[2] = { NULL };
formats[0] = pa_format_info_new();
formats[0]->encoding = PA_ENCODING_PCM;
pa_proplist_sets(formats[0]->plist, PA_PROP_FORMAT_SAMPLE_FORMAT, pa_sample_format_to_string(PA_SAMPLE_FLOAT32));
pa_proplist_setf(formats[0]->plist, PA_PROP_FORMAT_RATE, "%u", SAMPLE_HZ);
pa_proplist_setf(formats[0]->plist, PA_PROP_FORMAT_CHANNELS, "%u", 1);
fprintf(stderr, "Creating stream %i\n", i);
snprintf(name, sizeof(name), "stream #%i", i);
streams[i] = pa_stream_new_extended(c, name, formats, NULL);
assert(streams[i]);
pa_stream_set_state_callback(streams[i], stream_state_callback, (void*) (long) i);
pa_stream_connect_playback(streams[i], NULL, &buffer_attr, PA_STREAM_START_CORKED, NULL, i == 0 ? NULL : streams[0]);
}
break;
}
case PA_CONTEXT_TERMINATED:
mainloop_api->quit(mainloop_api, 0);
break;
case PA_CONTEXT_FAILED:
default:
fprintf(stderr, "Context error: %s\n", pa_strerror(pa_context_errno(c)));
abort();
}
}
int main(int argc, char *argv[]) {
pa_mainloop* m = NULL;
int i, ret = 0;
for (i = 0; i < SAMPLE_HZ; i++)
data[i] = (float) sin(((double) i/SAMPLE_HZ)*2*M_PI*SINE_HZ)/2;
for (i = 0; i < NSTREAMS; i++)
streams[i] = NULL;
/* Set up a new main loop */
m = pa_mainloop_new();
assert(m);
mainloop_api = pa_mainloop_get_api(m);
context = pa_context_new(mainloop_api, argv[0]);
assert(context);
pa_context_set_state_callback(context, context_state_callback, NULL);
/* Connect the context */
if (pa_context_connect(context, NULL, 0, NULL) < 0) {
fprintf(stderr, "pa_context_connect() failed.\n");
goto quit;
}
if (pa_mainloop_run(m, &ret) < 0)
fprintf(stderr, "pa_mainloop_run() failed.\n");
quit:
pa_context_unref(context);
for (i = 0; i < NSTREAMS; i++)
if (streams[i])
pa_stream_unref(streams[i]);
pa_mainloop_free(m);
return ret;
}

106
src/tests/format-test.c Normal file
View file

@ -0,0 +1,106 @@
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdlib.h>
#include <pulsecore/macro.h>
#include <pulse/format.h>
#define INIT(f) f = pa_format_info_new()
#define DEINIT(f) pa_format_info_free(f);
#define REINIT(f) { DEINIT(f); INIT(f); }
int main(int argc, char *argv[]) {
pa_format_info *f1 = NULL, *f2 = NULL;
int rates1[] = { 32000, 44100, 48000 };
const char *strings[] = { "thing1", "thing2", "thing3" };
/* 1. Simple fixed format int check */
INIT(f1); INIT(f2);
f1->encoding = PA_ENCODING_AC3_IEC61937;
pa_format_info_set_prop_int(f1, PA_PROP_FORMAT_RATE, 32000);
f2->encoding = PA_ENCODING_AC3_IEC61937;
pa_format_info_set_prop_int(f2, PA_PROP_FORMAT_RATE, 44100);
pa_assert(!pa_format_info_is_compatible(f1, f2));
/* 2. Check int array membership - positive */
REINIT(f1); REINIT(f2);
f1->encoding = PA_ENCODING_AC3_IEC61937;
pa_format_info_set_prop_int_array(f1, PA_PROP_FORMAT_RATE, rates1, PA_ELEMENTSOF(rates1));
f2->encoding = PA_ENCODING_AC3_IEC61937;
pa_format_info_set_prop_int(f2, PA_PROP_FORMAT_RATE, 44100);
pa_assert(pa_format_info_is_compatible(f1, f2));
pa_assert(pa_format_info_is_compatible(f2, f1));
/* 3. Check int array memebership - negative */
REINIT(f2);
f2->encoding = PA_ENCODING_AC3_IEC61937;
pa_format_info_set_prop_int(f2, PA_PROP_FORMAT_RATE, 96000);
pa_assert(!pa_format_info_is_compatible(f1, f2));
pa_assert(!pa_format_info_is_compatible(f2, f1));
/* 4. Check int range - positive */
REINIT(f1); REINIT(f2);
f1->encoding = PA_ENCODING_AC3_IEC61937;
pa_format_info_set_prop_int_range(f1, PA_PROP_FORMAT_RATE, 32000, 48000);
f2->encoding = PA_ENCODING_AC3_IEC61937;
pa_format_info_set_prop_int(f2, PA_PROP_FORMAT_RATE, 44100);
pa_assert(pa_format_info_is_compatible(f1, f2));
pa_assert(pa_format_info_is_compatible(f2, f1));
/* 5. Check int range - negative */
REINIT(f2);
f2->encoding = PA_ENCODING_AC3_IEC61937;
pa_format_info_set_prop_int(f2, PA_PROP_FORMAT_RATE, 96000);
pa_assert(!pa_format_info_is_compatible(f1, f2));
pa_assert(!pa_format_info_is_compatible(f2, f1));
/* 6. Simple fixed format string check */
REINIT(f1); REINIT(f2);
f1->encoding = PA_ENCODING_AC3_IEC61937;
pa_format_info_set_prop_string(f1, "format.test_string", "thing1");
f2->encoding = PA_ENCODING_AC3_IEC61937;
pa_format_info_set_prop_string(f2, "format.test_string", "notthing1");
pa_assert(!pa_format_info_is_compatible(f1, f2));
/* 7. Check string array membership - positive */
REINIT(f1); REINIT(f2);
f1->encoding = PA_ENCODING_AC3_IEC61937;
pa_format_info_set_prop_string_array(f1, "format.test_string", strings, PA_ELEMENTSOF(strings));
f2->encoding = PA_ENCODING_AC3_IEC61937;
pa_format_info_set_prop_string(f2, "format.test_string", "thing3");
pa_assert(pa_format_info_is_compatible(f1, f2));
pa_assert(pa_format_info_is_compatible(f2, f1));
/* 8. Check string array memebership - negative */
REINIT(f2);
f2->encoding = PA_ENCODING_AC3_IEC61937;
pa_format_info_set_prop_string(f2, "format.test_string", "thing5");
pa_assert(!pa_format_info_is_compatible(f1, f2));
pa_assert(!pa_format_info_is_compatible(f2, f1));
DEINIT(f1);
DEINIT(f2);
return 0;
}

View file

@ -222,7 +222,8 @@ static void get_sink_info_callback(pa_context *c, const pa_sink_info *i, int is_
cvdb[PA_SW_CVOLUME_SNPRINT_DB_MAX], cvdb[PA_SW_CVOLUME_SNPRINT_DB_MAX],
v[PA_VOLUME_SNPRINT_MAX], v[PA_VOLUME_SNPRINT_MAX],
vdb[PA_SW_VOLUME_SNPRINT_DB_MAX], vdb[PA_SW_VOLUME_SNPRINT_DB_MAX],
cm[PA_CHANNEL_MAP_SNPRINT_MAX]; cm[PA_CHANNEL_MAP_SNPRINT_MAX],
f[PA_FORMAT_INFO_SNPRINT_MAX];
char *pl; char *pl;
if (is_last < 0) { if (is_last < 0) {
@ -307,6 +308,14 @@ static void get_sink_info_callback(pa_context *c, const pa_sink_info *i, int is_
if (i->active_port) if (i->active_port)
printf(_("\tActive Port: %s\n"), printf(_("\tActive Port: %s\n"),
i->active_port->name); i->active_port->name);
if (i->formats) {
uint8_t j;
printf(_("\tFormats:\n"));
for (j = 0; j < i->n_formats; j++)
printf("\t\t%s\n", pa_format_info_snprint(f, sizeof(f), i->formats[j]));
}
} }
static void get_source_info_callback(pa_context *c, const pa_source_info *i, int is_last, void *userdata) { static void get_source_info_callback(pa_context *c, const pa_source_info *i, int is_last, void *userdata) {
@ -551,7 +560,7 @@ static void get_card_info_callback(pa_context *c, const pa_card_info *i, int is_
} }
static void get_sink_input_info_callback(pa_context *c, const pa_sink_input_info *i, int is_last, void *userdata) { static void get_sink_input_info_callback(pa_context *c, const pa_sink_input_info *i, int is_last, void *userdata) {
char t[32], k[32], s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cvdb[PA_SW_CVOLUME_SNPRINT_DB_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX]; char t[32], k[32], s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cvdb[PA_SW_CVOLUME_SNPRINT_DB_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], f[PA_FORMAT_INFO_SNPRINT_MAX];
char *pl; char *pl;
if (is_last < 0) { if (is_last < 0) {
@ -591,6 +600,7 @@ static void get_sink_input_info_callback(pa_context *c, const pa_sink_input_info
"\tSink: %u\n" "\tSink: %u\n"
"\tSample Specification: %s\n" "\tSample Specification: %s\n"
"\tChannel Map: %s\n" "\tChannel Map: %s\n"
"\tFormat: %s\n"
"\tMute: %s\n" "\tMute: %s\n"
"\tVolume: %s\n" "\tVolume: %s\n"
"\t %s\n" "\t %s\n"
@ -606,6 +616,7 @@ static void get_sink_input_info_callback(pa_context *c, const pa_sink_input_info
i->sink, i->sink,
pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec), pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec),
pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map), pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),
pa_format_info_snprint(f, sizeof(f), i->format),
pa_yes_no(i->mute), pa_yes_no(i->mute),
pa_cvolume_snprint(cv, sizeof(cv), &i->volume), pa_cvolume_snprint(cv, sizeof(cv), &i->volume),
pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), &i->volume), pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), &i->volume),