summaryrefslogtreecommitdiffstats
path: root/src/pulsecore
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2007-11-21 01:30:40 +0000
committerLennart Poettering <lennart@poettering.net>2007-11-21 01:30:40 +0000
commit14a9b80afbb0bddc216462b72156f14e032e1b5e (patch)
tree4b76ca3fe8eb313a3d6e1eb866fdc1589383cfc6 /src/pulsecore
parent4ac6b53478be63ae5d0163a330e67e4134f17f89 (diff)
- Check process name when dealing with PID files
- Add new PA_STREAM_FIX_CHANNELS, FIX_RATE, FIX_FORMAT, DONT_MOVE, VARIABLE_RATES to pa_sream_flags_t adn implement it - Expose those flags in pacat - Add notifications about device suspend/resume to the protocol and expose them in libpulse - Allow changing of buffer_attr during playback - allow disabling for remixing globally - hookup polkit support git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@2067 fefdeb5f-60dc-0310-8127-8f9354f1896f
Diffstat (limited to 'src/pulsecore')
-rw-r--r--src/pulsecore/cli-text.c14
-rw-r--r--src/pulsecore/core.c1
-rw-r--r--src/pulsecore/core.h3
-rw-r--r--src/pulsecore/pid.c34
-rw-r--r--src/pulsecore/pid.h4
-rw-r--r--src/pulsecore/protocol-native.c365
-rw-r--r--src/pulsecore/sink-input.c46
-rw-r--r--src/pulsecore/sink-input.h18
-rw-r--r--src/pulsecore/sink.c27
-rw-r--r--src/pulsecore/source-output.c42
-rw-r--r--src/pulsecore/source-output.h18
-rw-r--r--src/pulsecore/source.c27
12 files changed, 528 insertions, 71 deletions
diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c
index a77bcc2c..b64cafe2 100644
--- a/src/pulsecore/cli-text.c
+++ b/src/pulsecore/cli-text.c
@@ -236,7 +236,7 @@ char *pa_source_output_list_to_string(pa_core *c) {
" index: %u\n"
"\tname: '%s'\n"
"\tdriver: <%s>\n"
- "\tflags: %s%s\n"
+ "\tflags: %s%s%s%s%s%s%s\n"
"\tstate: %s\n"
"\tsource: <%u> '%s'\n"
"\tlatency: <%0.0f usec>\n"
@@ -248,6 +248,11 @@ char *pa_source_output_list_to_string(pa_core *c) {
o->driver,
o->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE ? "VARIABLE_RATE " : "",
o->flags & PA_SOURCE_OUTPUT_DONT_MOVE ? "DONT_MOVE " : "",
+ o->flags & PA_SOURCE_OUTPUT_NO_REMAP ? "NO_REMAP " : "",
+ o->flags & PA_SOURCE_OUTPUT_NO_REMIX ? "NO_REMIX " : "",
+ o->flags & PA_SOURCE_OUTPUT_FIX_FORMAT ? "FIX_FORMAT " : "",
+ o->flags & PA_SOURCE_OUTPUT_FIX_RATE ? "FIX_RATE " : "",
+ o->flags & PA_SOURCE_OUTPUT_FIX_CHANNELS ? "FIX_CHANNELS " : "",
state_table[pa_source_output_get_state(o)],
o->source->index, o->source->name,
(double) pa_source_output_get_latency(o),
@@ -289,7 +294,7 @@ char *pa_sink_input_list_to_string(pa_core *c) {
" index: %u\n"
"\tname: <%s>\n"
"\tdriver: <%s>\n"
- "\tflags: %s%s\n"
+ "\tflags: %s%s%s%s%s%s%s\n"
"\tstate: %s\n"
"\tsink: <%u> '%s'\n"
"\tvolume: <%s>\n"
@@ -303,6 +308,11 @@ char *pa_sink_input_list_to_string(pa_core *c) {
i->driver,
i->flags & PA_SINK_INPUT_VARIABLE_RATE ? "VARIABLE_RATE " : "",
i->flags & PA_SINK_INPUT_DONT_MOVE ? "DONT_MOVE " : "",
+ i->flags & PA_SINK_INPUT_NO_REMAP ? "NO_REMAP " : "",
+ i->flags & PA_SINK_INPUT_NO_REMIX ? "NO_REMIX " : "",
+ i->flags & PA_SINK_INPUT_FIX_FORMAT ? "FIX_FORMAT " : "",
+ i->flags & PA_SINK_INPUT_FIX_RATE ? "FIX_RATE " : "",
+ i->flags & PA_SINK_INPUT_FIX_CHANNELS ? "FIX_CHANNELS " : "",
state_table[pa_sink_input_get_state(i)],
i->sink->index, i->sink->name,
pa_cvolume_snprint(cv, sizeof(cv), pa_sink_input_get_volume(i)),
diff --git a/src/pulsecore/core.c b/src/pulsecore/core.c
index 9b420c94..cf018509 100644
--- a/src/pulsecore/core.c
+++ b/src/pulsecore/core.c
@@ -138,6 +138,7 @@ pa_core* pa_core_new(pa_mainloop_api *m, int shared) {
c->disallow_module_loading = FALSE;
c->realtime_scheduling = FALSE;
c->realtime_priority = 5;
+ c->disable_remixing = FALSE;
for (j = 0; j < PA_CORE_HOOK_MAX; j++)
pa_hook_init(&c->hooks[j], c);
diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h
index 9aeb7888..ce45e300 100644
--- a/src/pulsecore/core.h
+++ b/src/pulsecore/core.h
@@ -54,6 +54,7 @@ typedef enum pa_core_hook {
PA_CORE_HOOK_SOURCE_STATE_CHANGED,
PA_CORE_HOOK_SOURCE_DESCRIPTION_CHANGED,
PA_CORE_HOOK_SINK_INPUT_NEW,
+ PA_CORE_HOOK_SINK_INPUT_FIXATE,
PA_CORE_HOOK_SINK_INPUT_PUT,
PA_CORE_HOOK_SINK_INPUT_UNLINK,
PA_CORE_HOOK_SINK_INPUT_UNLINK_POST,
@@ -62,6 +63,7 @@ typedef enum pa_core_hook {
PA_CORE_HOOK_SINK_INPUT_NAME_CHANGED,
PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED,
PA_CORE_HOOK_SOURCE_OUTPUT_NEW,
+ PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE,
PA_CORE_HOOK_SOURCE_OUTPUT_PUT,
PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK,
PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK_POST,
@@ -118,6 +120,7 @@ struct pa_core {
pa_bool_t is_system_instance;
pa_bool_t realtime_scheduling;
int realtime_priority;
+ pa_bool_t disable_remixing;
/* hooks */
pa_hook hooks[PA_CORE_HOOK_MAX];
diff --git a/src/pulsecore/pid.c b/src/pulsecore/pid.c
index 55ff2088..f3c9faaa 100644
--- a/src/pulsecore/pid.c
+++ b/src/pulsecore/pid.c
@@ -42,6 +42,7 @@
#endif
#include <pulse/xmalloc.h>
+#include <pulse/util.h>
#include <pulsecore/core-error.h>
#include <pulsecore/core-util.h>
@@ -260,8 +261,8 @@ fail:
* exists and the PID therein too. Returns 0 on succcess, -1
* otherwise. If pid is non-NULL and a running daemon was found,
* return its PID therein */
-int pa_pid_file_check_running(pid_t *pid) {
- return pa_pid_file_kill(0, pid);
+int pa_pid_file_check_running(pid_t *pid, const char *binary_name) {
+ return pa_pid_file_kill(0, pid, binary_name);
}
#ifndef OS_IS_WIN32
@@ -269,12 +270,14 @@ int pa_pid_file_check_running(pid_t *pid) {
/* Kill a current running daemon. Return non-zero on success, -1
* otherwise. If successful *pid contains the PID of the daemon
* process. */
-int pa_pid_file_kill(int sig, pid_t *pid) {
+int pa_pid_file_kill(int sig, pid_t *pid, const char *binary_name) {
int fd = -1;
char fn[PATH_MAX];
int ret = -1;
pid_t _pid;
-
+#ifdef __linux__
+ char *e = NULL;
+#endif
if (!pid)
pid = &_pid;
@@ -286,6 +289,23 @@ int pa_pid_file_kill(int sig, pid_t *pid) {
if ((*pid = read_pid(fn, fd)) == (pid_t) -1)
goto fail;
+#ifdef __linux__
+ if (binary_name) {
+ pa_snprintf(fn, sizeof(fn), "/proc/%lu/exe", (unsigned long) pid);
+
+ if ((e = pa_readlink(fn))) {
+ char *f = pa_path_get_filename(e);
+ if (strcmp(f, binary_name)
+#if defined(__OPTIMIZE__)
+ /* libtool likes to rename our binary names ... */
+ && !(pa_startswith(f, "lt-") && strcmp(f+3, binary_name) == 0)
+#endif
+ )
+ goto fail;
+ }
+ }
+#endif
+
ret = kill(*pid, sig);
fail:
@@ -295,13 +315,17 @@ fail:
pa_close(fd);
}
+#ifdef __linux__
+ pa_xfree(e);
+#endif
+
return ret;
}
#else /* OS_IS_WIN32 */
-int pa_pid_file_kill(int sig, pid_t *pid) {
+int pa_pid_file_kill(int sig, pid_t *pid, const char *exe_name) {
return -1;
}
diff --git a/src/pulsecore/pid.h b/src/pulsecore/pid.h
index 0f25d1c8..1d6de7b5 100644
--- a/src/pulsecore/pid.h
+++ b/src/pulsecore/pid.h
@@ -26,7 +26,7 @@
int pa_pid_file_create(void);
int pa_pid_file_remove(void);
-int pa_pid_file_check_running(pid_t *pid);
-int pa_pid_file_kill(int sig, pid_t *pid);
+int pa_pid_file_check_running(pid_t *pid, const char *binary_name);
+int pa_pid_file_kill(int sig, pid_t *pid, const char *binary_name);
#endif
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index 1d294746..48d5cd70 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -202,12 +202,16 @@ enum {
static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk);
static void sink_input_drop_cb(pa_sink_input *i, size_t length);
static void sink_input_kill_cb(pa_sink_input *i);
+static void sink_input_suspend_cb(pa_sink_input *i, pa_bool_t suspend);
+static void sink_input_moved_cb(pa_sink_input *i);
static void send_memblock(connection *c);
static void request_bytes(struct playback_stream*s);
static void source_output_kill_cb(pa_source_output *o);
static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk);
+static void source_output_suspend_cb(pa_source_output *o, pa_bool_t suspend);
+static void source_output_moved_cb(pa_source_output *o);
static pa_usec_t source_output_get_latency_cb(pa_source_output *o);
static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
@@ -248,6 +252,8 @@ static void command_cork_record_stream(pa_pdispatch *pd, uint32_t command, uint3
static void command_flush_record_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
static void command_move_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
static void command_suspend(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_update_stream_sample_rate(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
[PA_COMMAND_ERROR] = NULL,
@@ -323,7 +329,13 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
[PA_COMMAND_REMOVE_AUTOLOAD] = command_remove_autoload,
[PA_COMMAND_MOVE_SINK_INPUT] = command_move_stream,
- [PA_COMMAND_MOVE_SOURCE_OUTPUT] = command_move_stream
+ [PA_COMMAND_MOVE_SOURCE_OUTPUT] = command_move_stream,
+
+ [PA_COMMAND_SET_PLAYBACK_STREAM_BUFFER_ATTR] = command_set_stream_buffer_attr,
+ [PA_COMMAND_SET_RECORD_STREAM_BUFFER_ATTR] = command_set_stream_buffer_attr,
+
+ [PA_COMMAND_UPDATE_PLAYBACK_STREAM_SAMPLE_RATE] = command_update_stream_sample_rate,
+ [PA_COMMAND_UPDATE_RECORD_STREAM_SAMPLE_RATE] = command_update_stream_sample_rate
};
/* structure management */
@@ -435,12 +447,12 @@ static int record_stream_process_msg(pa_msgobject *o, int code, void*userdata, i
static record_stream* record_stream_new(
connection *c,
pa_source *source,
- const pa_sample_spec *ss,
- const pa_channel_map *map,
+ pa_sample_spec *ss,
+ pa_channel_map *map,
const char *name,
uint32_t *maxlength,
uint32_t fragment_size,
- int corked) {
+ pa_source_output_flags_t flags) {
record_stream *s;
pa_source_output *source_output;
@@ -462,7 +474,7 @@ static record_stream* record_stream_new(
pa_source_output_new_data_set_sample_spec(&data, ss);
pa_source_output_new_data_set_channel_map(&data, map);
- if (!(source_output = pa_source_output_new(c->protocol->core, &data, corked ? PA_SOURCE_OUTPUT_START_CORKED : 0)))
+ if (!(source_output = pa_source_output_new(c->protocol->core, &data, flags)))
return NULL;
s = pa_msgobject_new(record_stream);
@@ -473,21 +485,30 @@ static record_stream* record_stream_new(
s->source_output->push = source_output_push_cb;
s->source_output->kill = source_output_kill_cb;
s->source_output->get_latency = source_output_get_latency_cb;
+ s->source_output->moved = source_output_moved_cb;
+ s->source_output->suspend = source_output_suspend_cb;
s->source_output->userdata = s;
s->memblockq = pa_memblockq_new(
0,
*maxlength,
0,
- base = pa_frame_size(ss),
+ base = pa_frame_size(&s->source_output->sample_spec),
1,
0,
NULL);
+ *maxlength = pa_memblockq_get_maxlength(s->memblockq);
+
s->fragment_size = (fragment_size/base)*base;
if (s->fragment_size <= 0)
s->fragment_size = base;
- *maxlength = pa_memblockq_get_maxlength(s->memblockq);
+
+ if (s->fragment_size > *maxlength)
+ s->fragment_size = *maxlength;
+
+ *ss = s->source_output->sample_spec;
+ *map = s->source_output->channel_map;
pa_idxset_put(c->record_streams, s, &s->index);
@@ -602,8 +623,8 @@ static int playback_stream_process_msg(pa_msgobject *o, int code, void*userdata,
static playback_stream* playback_stream_new(
connection *c,
pa_sink *sink,
- const pa_sample_spec *ss,
- const pa_channel_map *map,
+ pa_sample_spec *ss,
+ pa_channel_map *map,
const char *name,
uint32_t *maxlength,
uint32_t *tlength,
@@ -611,8 +632,8 @@ static playback_stream* playback_stream_new(
uint32_t *minreq,
pa_cvolume *volume,
uint32_t syncid,
- int corked,
- uint32_t *missing) {
+ uint32_t *missing,
+ pa_sink_input_flags_t flags) {
playback_stream *s, *ssync;
pa_sink_input *sink_input;
@@ -656,7 +677,7 @@ static playback_stream* playback_stream_new(
data.client = c->client;
data.sync_base = ssync ? ssync->sink_input : NULL;
- if (!(sink_input = pa_sink_input_new(c->protocol->core, &data, corked ? PA_SINK_INPUT_START_CORKED : 0)))
+ if (!(sink_input = pa_sink_input_new(c->protocol->core, &data, flags)))
return NULL;
s = pa_msgobject_new(playback_stream);
@@ -671,17 +692,19 @@ static playback_stream* playback_stream_new(
s->sink_input->peek = sink_input_peek_cb;
s->sink_input->drop = sink_input_drop_cb;
s->sink_input->kill = sink_input_kill_cb;
+ s->sink_input->moved = sink_input_moved_cb;
+ s->sink_input->suspend = sink_input_suspend_cb;
s->sink_input->userdata = s;
start_index = ssync ? pa_memblockq_get_read_index(ssync->memblockq) : 0;
- silence = pa_silence_memblock_new(c->protocol->core->mempool, ss, 0);
+ silence = pa_silence_memblock_new(c->protocol->core->mempool, &s->sink_input->sample_spec, 0);
s->memblockq = pa_memblockq_new(
start_index,
*maxlength,
*tlength,
- pa_frame_size(ss),
+ pa_frame_size(&s->sink_input->sample_spec),
*prebuf,
*minreq,
silence);
@@ -694,6 +717,9 @@ static playback_stream* playback_stream_new(
*minreq = (uint32_t) pa_memblockq_get_minreq(s->memblockq);
*missing = (uint32_t) pa_memblockq_pop_missing(s->memblockq);
+ *ss = s->sink_input->sample_spec;
+ *map = s->sink_input->channel_map;
+
s->minreq = pa_memblockq_get_minreq(s->memblockq);
pa_atomic_store(&s->missing, 0);
s->drain_request = 0;
@@ -1022,6 +1048,7 @@ static void sink_input_drop_cb(pa_sink_input *i, size_t length) {
/* pa_log("after_drop: %u %u", pa_memblockq_get_length(s->memblockq), pa_memblockq_is_readable(s->memblockq)); */
}
+/* Called from main context */
static void sink_input_kill_cb(pa_sink_input *i) {
playback_stream *s;
@@ -1033,6 +1060,42 @@ static void sink_input_kill_cb(pa_sink_input *i) {
playback_stream_unlink(s);
}
+/* Called from main context */
+static void sink_input_suspend_cb(pa_sink_input *i, pa_bool_t suspend) {
+ playback_stream *s;
+ pa_tagstruct *t;
+
+ pa_sink_input_assert_ref(i);
+ s = PLAYBACK_STREAM(i->userdata);
+ playback_stream_assert_ref(s);
+
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_PLAYBACK_STREAM_SUSPENDED);
+ pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+ pa_tagstruct_putu32(t, s->index);
+ pa_tagstruct_put_boolean(t, suspend);
+ pa_pstream_send_tagstruct(s->connection->pstream, t);
+}
+
+/* Called from main context */
+static void sink_input_moved_cb(pa_sink_input *i) {
+ playback_stream *s;
+ pa_tagstruct *t;
+
+ pa_sink_input_assert_ref(i);
+ s = PLAYBACK_STREAM(i->userdata);
+ playback_stream_assert_ref(s);
+
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_PLAYBACK_STREAM_MOVED);
+ pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+ pa_tagstruct_putu32(t, s->index);
+ pa_tagstruct_putu32(t, i->sink->index);
+ pa_tagstruct_puts(t, i->sink->name);
+ pa_tagstruct_put_boolean(t, pa_sink_get_state(i->sink) == PA_SINK_SUSPENDED);
+ pa_pstream_send_tagstruct(s->connection->pstream, t);
+}
+
/*** source_output callbacks ***/
/* Called from thread context */
@@ -1070,6 +1133,41 @@ static pa_usec_t source_output_get_latency_cb(pa_source_output *o) {
return pa_bytes_to_usec(pa_memblockq_get_length(s->memblockq), &o->sample_spec);
}
+/* Called from main context */
+static void source_output_suspend_cb(pa_source_output *o, pa_bool_t suspend) {
+ record_stream *s;
+ pa_tagstruct *t;
+
+ pa_source_output_assert_ref(o);
+ s = RECORD_STREAM(o->userdata);
+ record_stream_assert_ref(s);
+
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_RECORD_STREAM_SUSPENDED);
+ pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+ pa_tagstruct_putu32(t, s->index);
+ pa_tagstruct_put_boolean(t, suspend);
+ pa_pstream_send_tagstruct(s->connection->pstream, t);
+}
+
+/* Called from main context */
+static void source_output_moved_cb(pa_source_output *o) {
+ record_stream *s;
+ pa_tagstruct *t;
+
+ pa_source_output_assert_ref(o);
+ s = RECORD_STREAM(o->userdata);
+ record_stream_assert_ref(s);
+
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_RECORD_STREAM_MOVED);
+ pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+ pa_tagstruct_putu32(t, s->index);
+ pa_tagstruct_putu32(t, o->source->index);
+ pa_tagstruct_puts(t, o->source->name);
+ pa_pstream_send_tagstruct(s->connection->pstream, t);
+}
+
/*** pdispatch callbacks ***/
static void protocol_error(connection *c) {
@@ -1104,6 +1202,8 @@ static void command_create_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GC
pa_sink *sink = NULL;
pa_cvolume volume;
int corked;
+ int no_remap = 0, no_remix = 0, fix_format = 0, fix_rate = 0, fix_channels = 0, no_move = 0, variable_rate = 0;
+ pa_sink_input_flags_t flags = 0;
connection_assert_ref(c);
pa_assert(t);
@@ -1122,9 +1222,27 @@ static void command_create_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GC
PA_TAG_U32, &minreq,
PA_TAG_U32, &syncid,
PA_TAG_CVOLUME, &volume,
- PA_TAG_INVALID) < 0 ||
- !pa_tagstruct_eof(t) ||
- !name) {
+ PA_TAG_INVALID) < 0 || !name) {
+ protocol_error(c);
+ return;
+ }
+
+ if (c->version >= 12) {
+ /* Since 0.9.8 the user can ask for a couple of additional flags */
+
+ if (pa_tagstruct_get_boolean(t, &no_remap) < 0 ||
+ pa_tagstruct_get_boolean(t, &no_remix) < 0 ||
+ pa_tagstruct_get_boolean(t, &fix_format) < 0 ||
+ pa_tagstruct_get_boolean(t, &fix_rate) < 0 ||
+ pa_tagstruct_get_boolean(t, &fix_channels) < 0 ||
+ pa_tagstruct_get_boolean(t, &no_move) < 0 ||
+ pa_tagstruct_get_boolean(t, &variable_rate) < 0) {
+ protocol_error(c);
+ return;
+ }
+ }
+
+ if (!pa_tagstruct_eof(t)) {
protocol_error(c);
return;
}
@@ -1136,8 +1254,8 @@ static void command_create_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GC
CHECK_VALIDITY(c->pstream, pa_sample_spec_valid(&ss), tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, pa_cvolume_valid(&volume), tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, map.channels == ss.channels && volume.channels == ss.channels, tag, PA_ERR_INVALID);
- CHECK_VALIDITY(c->pstream, maxlength > 0 && maxlength <= MAX_MEMBLOCKQ_LENGTH, tag, PA_ERR_INVALID);
- CHECK_VALIDITY(c->pstream, maxlength >= pa_frame_size(&ss), tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, maxlength > 0, tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, maxlength <= MAX_MEMBLOCKQ_LENGTH, tag, PA_ERR_INVALID);
if (sink_index != PA_INVALID_INDEX) {
sink = pa_idxset_get_by_index(c->protocol->core->sinks, sink_index);
@@ -1147,7 +1265,17 @@ static void command_create_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GC
CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY);
}
- s = playback_stream_new(c, sink, &ss, &map, name, &maxlength, &tlength, &prebuf, &minreq, &volume, syncid, corked, &missing);
+ flags =
+ (corked ? PA_SINK_INPUT_START_CORKED : 0) |
+ (no_remap ? PA_SINK_INPUT_NO_REMAP : 0) |
+ (no_remix ? PA_SINK_INPUT_NO_REMIX : 0) |
+ (fix_format ? PA_SINK_INPUT_FIX_FORMAT : 0) |
+ (fix_rate ? PA_SINK_INPUT_FIX_RATE : 0) |
+ (fix_channels ? PA_SINK_INPUT_FIX_CHANNELS : 0) |
+ (no_move ? PA_SINK_INPUT_DONT_MOVE : 0) |
+ (variable_rate ? PA_SINK_INPUT_VARIABLE_RATE : 0);
+
+ s = playback_stream_new(c, sink, &ss, &map, name, &maxlength, &tlength, &prebuf, &minreq, &volume, syncid, &missing, flags);
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_INVALID);
reply = reply_new(tag);
@@ -1159,7 +1287,7 @@ static void command_create_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GC
/* pa_log("initial request is %u", missing); */
if (c->version >= 9) {
- /* Since 0.9 we support sending the buffer metrics back to the client */
+ /* Since 0.9.0 we support sending the buffer metrics back to the client */
pa_tagstruct_putu32(reply, (uint32_t) maxlength);
pa_tagstruct_putu32(reply, (uint32_t) tlength);
@@ -1167,6 +1295,20 @@ static void command_create_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GC
pa_tagstruct_putu32(reply, (uint32_t) minreq);
}
+ if (c->version >= 12) {
+ /* Since 0.9.8 we support sending the chosen sample
+ * spec/channel map/device/suspend status back to the
+ * client */
+
+ pa_tagstruct_put_sample_spec(reply, &ss);
+ pa_tagstruct_put_channel_map(reply, &map);
+
+ pa_tagstruct_putu32(reply, s->sink_input->sink->index);
+ pa_tagstruct_puts(reply, s->sink_input->sink->name);
+
+ pa_tagstruct_put_boolean(reply, pa_sink_get_state(s->sink_input->sink) == PA_SINK_SUSPENDED);
+ }
+
pa_pstream_send_tagstruct(c->pstream, reply);
}
@@ -1239,6 +1381,8 @@ static void command_create_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_
pa_tagstruct *reply;
pa_source *source = NULL;
int corked;
+ int no_remap = 0, no_remix = 0, fix_format = 0, fix_rate = 0, fix_channels = 0, no_move = 0, variable_rate = 0;
+ pa_source_output_flags_t flags = 0;
connection_assert_ref(c);
pa_assert(t);
@@ -1250,18 +1394,48 @@ static void command_create_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_
pa_tagstruct_gets(t, &source_name) < 0 ||
pa_tagstruct_getu32(t, &maxlength) < 0 ||
pa_tagstruct_get_boolean(t, &corked) < 0 ||
- pa_tagstruct_getu32(t, &fragment_size) < 0 ||
- !pa_tagstruct_eof(t)) {
+ pa_tagstruct_getu32(t, &fragment_size) < 0) {
+ protocol_error(c);
+ return;
+ }
+
+ if (c->version >= 12) {
+ /* Since 0.9.8 the user can ask for a couple of additional flags */
+
+ if (pa_tagstruct_get_boolean(t, &no_remap) < 0 ||
+ pa_tagstruct_get_boolean(t, &no_remix) < 0 ||
+ pa_tagstruct_get_boolean(t, &fix_format) < 0 ||
+ pa_tagstruct_get_boolean(t, &fix_rate) < 0 ||
+ pa_tagstruct_get_boolean(t, &fix_channels) < 0 ||
+ pa_tagstruct_get_boolean(t, &no_move) < 0 ||
+ pa_tagstruct_get_boolean(t, &variable_rate) < 0) {
+ protocol_error(c);
+ return;
+ }
+ }
+
+ if (!pa_tagstruct_eof(t)) {
protocol_error(c);
return;
}
+ flags =
+ (corked ? PA_SOURCE_OUTPUT_START_CORKED : 0) |
+ (no_remap ? PA_SOURCE_OUTPUT_NO_REMAP : 0) |
+ (no_remix ? PA_SOURCE_OUTPUT_NO_REMIX : 0) |
+ (fix_format ? PA_SOURCE_OUTPUT_FIX_FORMAT : 0) |
+ (fix_rate ? PA_SOURCE_OUTPUT_FIX_RATE : 0) |
+ (fix_channels ? PA_SOURCE_OUTPUT_FIX_CHANNELS : 0) |
+ (no_move ? PA_SOURCE_OUTPUT_DONT_MOVE : 0) |
+ (variable_rate ? PA_SOURCE_OUTPUT_VARIABLE_RATE : 0);
+
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
CHECK_VALIDITY(c->pstream, name && pa_utf8_valid(name), tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, pa_sample_spec_valid(&ss), tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, pa_channel_map_valid(&map), tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, source_index != PA_INVALID_INDEX || !source_name || (*source_name && pa_utf8_valid(source_name)), tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, map.channels == ss.channels, tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, maxlength > 0, tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, maxlength <= MAX_MEMBLOCKQ_LENGTH, tag, PA_ERR_INVALID);
if (source_index != PA_INVALID_INDEX) {
@@ -1272,7 +1446,7 @@ static void command_create_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_
CHECK_VALIDITY(c->pstream, source, tag, PA_ERR_NOENTITY);
}
- s = record_stream_new(c, source, &ss, &map, name, &maxlength, fragment_size, corked);
+ s = record_stream_new(c, source, &ss, &map, name, &maxlength, fragment_size, flags);
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_INVALID);
reply = reply_new(tag);
@@ -1287,6 +1461,20 @@ static void command_create_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_
pa_tagstruct_putu32(reply, (uint32_t) s->fragment_size);
}
+ if (c->version >= 12) {
+ /* Since 0.9.8 we support sending the chosen sample
+ * spec/channel map/device/suspend status back to the
+ * client */
+
+ pa_tagstruct_put_sample_spec(reply, &ss);
+ pa_tagstruct_put_channel_map(reply, &map);
+
+ pa_tagstruct_putu32(reply, s->source_output->source->index);
+ pa_tagstruct_puts(reply, s->source_output->source->name);
+
+ pa_tagstruct_put_boolean(reply, pa_source_get_state(s->source_output->source) == PA_SOURCE_SUSPENDED);
+ }
+
pa_pstream_send_tagstruct(c->pstream, reply);
}
@@ -2291,6 +2479,134 @@ static void command_flush_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_U
pa_pstream_send_simple_ack(c->pstream, tag);
}
+static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ connection *c = CONNECTION(userdata);
+ uint32_t idx;
+ uint32_t maxlength, tlength, prebuf, minreq, fragsize;
+ pa_tagstruct *reply;
+
+ connection_assert_ref(c);
+ pa_assert(t);
+
+ if (pa_tagstruct_getu32(t, &idx) < 0) {
+ protocol_error(c);
+ return;
+ }
+
+ CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+
+ if (command == PA_COMMAND_SET_PLAYBACK_STREAM_BUFFER_ATTR) {
+ playback_stream *s;
+
+ s = pa_idxset_get_by_index(c->output_streams, idx);
+ CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+ CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
+
+ if (pa_tagstruct_get(
+ t,
+ PA_TAG_U32, &maxlength,
+ PA_TAG_U32, &tlength,
+ PA_TAG_U32, &prebuf,
+ PA_TAG_U32, &minreq,
+ PA_TAG_INVALID) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ return;
+ }
+
+ CHECK_VALIDITY(c->pstream, maxlength > 0, tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, maxlength <= MAX_MEMBLOCKQ_LENGTH, tag, PA_ERR_INVALID);
+
+ pa_memblockq_set_maxlength(s->memblockq, maxlength);
+ pa_memblockq_set_tlength(s->memblockq, tlength);
+ pa_memblockq_set_prebuf(s->memblockq, prebuf);
+ pa_memblockq_set_minreq(s->memblockq, minreq);
+
+ reply = reply_new(tag);
+ pa_tagstruct_putu32(reply, (uint32_t) pa_memblockq_get_maxlength(s->memblockq));
+ pa_tagstruct_putu32(reply, (uint32_t) pa_memblockq_get_tlength(s->memblockq));
+ pa_tagstruct_putu32(reply, (uint32_t) pa_memblockq_get_prebuf(s->memblockq));
+ pa_tagstruct_putu32(reply, (uint32_t) pa_memblockq_get_minreq(s->memblockq));
+
+ } else {
+ record_stream *s;
+ size_t base;
+ pa_assert(command == PA_COMMAND_SET_RECORD_STREAM_BUFFER_ATTR);
+
+ s = pa_idxset_get_by_index(c->record_streams, idx);
+ CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+
+ if (pa_tagstruct_get(
+ t,
+ PA_TAG_U32, &maxlength,
+ PA_TAG_U32, &fragsize,
+ PA_TAG_INVALID) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ return;
+ }
+
+ CHECK_VALIDITY(c->pstream, maxlength > 0, tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, maxlength <= MAX_MEMBLOCKQ_LENGTH, tag, PA_ERR_INVALID);
+
+ pa_memblockq_set_maxlength(s->memblockq, maxlength);
+
+ base = pa_frame_size(&s->source_output->sample_spec);
+ s->fragment_size = (fragsize/base)*base;
+ if (s->fragment_size <= 0)
+ s->fragment_size = base;
+
+ if (s->fragment_size > pa_memblockq_get_maxlength(s->memblockq))
+ s->fragment_size = pa_memblockq_get_maxlength(s->memblockq);
+
+ reply = reply_new(tag);
+ pa_tagstruct_putu32(reply, (uint32_t) pa_memblockq_get_maxlength(s->memblockq));
+ pa_tagstruct_putu32(reply, s->fragment_size);
+ }
+
+ pa_pstream_send_tagstruct(c->pstream, reply);
+}
+
+static void command_update_stream_sample_rate(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ connection *c = CONNECTION(userdata);
+ uint32_t idx;
+ uint32_t rate;
+
+ connection_assert_ref(c);
+ pa_assert(t);
+
+ if (pa_tagstruct_getu32(t, &idx) < 0 ||
+ pa_tagstruct_getu32(t, &rate) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ protocol_error(c);
+ return;
+ }
+
+ CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+ CHECK_VALIDITY(c->pstream, rate > 0 && rate <= PA_RATE_MAX, tag, PA_ERR_INVALID);
+
+ if (command == PA_COMMAND_UPDATE_PLAYBACK_STREAM_SAMPLE_RATE) {
+ playback_stream *s;
+
+ s = pa_idxset_get_by_index(c->output_streams, idx);
+ CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+ CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
+
+ pa_sink_input_set_rate(s->sink_input, rate);
+
+ } else {
+ record_stream *s;
+ pa_assert(command == PA_COMMAND_UPDATE_RECORD_STREAM_SAMPLE_RATE);
+
+ s = pa_idxset_get_by_index(c->record_streams, idx);
+ CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+
+ pa_source_output_set_rate(s->source_output, rate);
+ }
+
+ pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
static void command_set_default_sink_or_source(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
connection *c = CONNECTION(userdata);
const char *s;
@@ -2340,6 +2656,7 @@ static void command_set_stream_name(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t com
} else {
record_stream *s;
+ pa_assert(command == PA_COMMAND_SET_RECORD_STREAM_NAME);
s = pa_idxset_get_by_index(c->record_streams, idx);
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index 730f3e5c..ec0914ec 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -93,7 +93,7 @@ pa_sink_input* pa_sink_input_new(
pa_sink_input *i;
pa_resampler *resampler = NULL;
- char st[PA_SAMPLE_SPEC_SNPRINT_MAX];
+ char st[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
pa_assert(core);
pa_assert(data);
@@ -132,6 +132,24 @@ pa_sink_input* pa_sink_input_new(
pa_return_null_if_fail(pa_cvolume_valid(&data->volume));
pa_return_null_if_fail(data->volume.channels == data->sample_spec.channels);
+ if (flags & PA_SINK_INPUT_FIX_FORMAT)
+ data->sample_spec.format = data->sink->sample_spec.format;
+
+ if (flags & PA_SINK_INPUT_FIX_RATE)
+ data->sample_spec.rate = data->sink->sample_spec.rate;
+
+ if (flags & PA_SINK_INPUT_FIX_CHANNELS) {
+ data->sample_spec.channels = data->sink->sample_spec.channels;
+ data->channel_map = data->sink->channel_map;
+ }
+
+ pa_assert(pa_sample_spec_valid(&data->sample_spec));
+ pa_assert(pa_channel_map_valid(&data->channel_map));
+
+ /* Due to the fixing of the sample spec the volume might not match anymore */
+ if (data->volume.channels != data->sample_spec.channels)
+ pa_cvolume_set(&data->volume, data->sample_spec.channels, pa_cvolume_avg(&data->volume));
+
if (!data->muted_is_set)
data->muted = 0;
@@ -140,6 +158,9 @@ pa_sink_input* pa_sink_input_new(
pa_return_null_if_fail(data->resample_method < PA_RESAMPLER_MAX);
+ if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], data) < 0)
+ return NULL;
+
if (pa_idxset_size(data->sink->inputs) >= PA_MAX_INPUTS_PER_SINK) {
pa_log_warn("Failed to create sink input: too many inputs per sink.");
return NULL;
@@ -154,7 +175,9 @@ pa_sink_input* pa_sink_input_new(
&data->sample_spec, &data->channel_map,
&data->sink->sample_spec, &data->sink->channel_map,
data->resample_method,
- (flags & PA_SINK_INPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0))) {
+ ((flags & PA_SINK_INPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) |
+ ((flags & PA_SINK_INPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) |
+ (core->disable_remixing || (flags & PA_SINK_INPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0)))) {
pa_log_warn("Unsupported resampling operation.");
return NULL;
}
@@ -199,6 +222,7 @@ pa_sink_input* pa_sink_input_new(
i->attach = NULL;
i->detach = NULL;
i->suspend = NULL;
+ i->moved = NULL;
i->userdata = NULL;
i->thread_info.state = i->state;
@@ -215,11 +239,12 @@ pa_sink_input* pa_sink_input_new(
pa_assert_se(pa_idxset_put(core->sink_inputs, pa_sink_input_ref(i), &i->index) == 0);
pa_assert_se(pa_idxset_put(i->sink->inputs, i, NULL) == 0);
- pa_log_info("Created input %u \"%s\" on %s with sample spec %s",
+ pa_log_info("Created input %u \"%s\" on %s with sample spec %s and channel map %s",
i->index,
i->name,
i->sink->name,
- pa_sample_spec_snprint(st, sizeof(st), &i->sample_spec));
+ pa_sample_spec_snprint(st, sizeof(st), &i->sample_spec),
+ pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map));
/* Don't forget to call pa_sink_input_put! */
@@ -307,6 +332,7 @@ void pa_sink_input_unlink(pa_sink_input *i) {
i->attach = NULL;
i->detach = NULL;
i->suspend = NULL;
+ i->moved = NULL;
if (linked) {
pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_REMOVE, i->index);
@@ -709,6 +735,7 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, int immediately) {
pa_sink *origin;
pa_usec_t silence_usec = 0;
pa_sink_input_move_info info;
+ pa_sink_input_move_hook_data hook_data;
pa_sink_input_assert_ref(i);
pa_assert(PA_SINK_INPUT_LINKED(i->state));
@@ -750,14 +777,18 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, int immediately) {
&i->sample_spec, &i->channel_map,
&dest->sample_spec, &dest->channel_map,
i->resample_method,
- (i->flags & PA_SINK_INPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0))) {
+ ((i->flags & PA_SINK_INPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) |
+ ((i->flags & PA_SINK_INPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) |
+ (i->core->disable_remixing || (i->flags & PA_SINK_INPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0)))) {
pa_log_warn("Unsupported resampling operation.");
return -1;
}
} else
new_resampler = NULL;
- pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE], i);
+ hook_data.sink_input = i;
+ hook_data.destination = dest;
+ pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE], &hook_data);
memset(&info, 0, sizeof(info));
info.sink_input = i;
@@ -870,6 +901,9 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, int immediately) {
pa_sink_update_status(origin);
pa_sink_update_status(dest);
+ if (i->moved)
+ i->moved(i);
+
pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_POST], i);
pa_log_debug("Successfully moved sink input %i from %s to %s.", i->index, origin->name, dest->name);
diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h
index 3f8e2039..8975db9e 100644
--- a/src/pulsecore/sink-input.h
+++ b/src/pulsecore/sink-input.h
@@ -53,7 +53,12 @@ static inline pa_bool_t PA_SINK_INPUT_LINKED(pa_sink_input_state_t x) {
typedef enum pa_sink_input_flags {
PA_SINK_INPUT_VARIABLE_RATE = 1,
PA_SINK_INPUT_DONT_MOVE = 2,
- PA_SINK_INPUT_START_CORKED = 4
+ PA_SINK_INPUT_START_CORKED = 4,
+ PA_SINK_INPUT_NO_REMAP = 8,
+ PA_SINK_INPUT_NO_REMIX = 16,
+ PA_SINK_INPUT_FIX_FORMAT = 32,
+ PA_SINK_INPUT_FIX_RATE = 64,
+ PA_SINK_INPUT_FIX_CHANNELS = 128
} pa_sink_input_flags_t;
struct pa_sink_input {
@@ -107,7 +112,11 @@ struct pa_sink_input {
/* If non-NULL called whenever the the sink this input is attached
* to suspends or resumes. Called from main context */
- void (*suspend) (pa_sink_input *i, int b); /* may be NULL */
+ void (*suspend) (pa_sink_input *i, pa_bool_t b); /* may be NULL */
+
+ /* If non-NULL called whenever the the sink this input is attached
+ * to changes. Called from main context */
+ void (*moved) (pa_sink_input *i); /* may be NULL */
/* Supposed to unlink and destroy this stream. Called from main
* context. */
@@ -181,6 +190,11 @@ typedef struct pa_sink_input_new_data {
pa_sink_input *sync_base;
} pa_sink_input_new_data;
+typedef struct pa_sink_input_move_hook_data {
+ pa_sink_input *sink_input;
+ pa_sink *destination;
+} pa_sink_input_move_hook_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_channel_map(pa_sink_input_new_data *data, const pa_channel_map *map);
diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index dccb34cc..fcc91cb1 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -149,23 +149,16 @@ pa_sink* pa_sink_new(
static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
int ret;
+ pa_bool_t suspend_change;
pa_assert(s);
if (s->state == state)
return 0;
- if ((s->state == PA_SINK_SUSPENDED && PA_SINK_OPENED(state)) ||
- (PA_SINK_OPENED(s->state) && state == PA_SINK_SUSPENDED)) {
- pa_sink_input *i;
- uint32_t idx;
-
- /* We're suspending or resuming, tell everyone about it */
-
- for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx)))
- if (i->suspend)
- i->suspend(i, state == PA_SINK_SUSPENDED);
- }
+ suspend_change =
+ (s->state == PA_SINK_SUSPENDED && PA_SINK_OPENED(state)) ||
+ (PA_SINK_OPENED(s->state) && state == PA_SINK_SUSPENDED);
if (s->set_state)
if ((ret = s->set_state(s, state)) < 0)
@@ -176,8 +169,20 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
s->state = state;
+ if (suspend_change) {
+ pa_sink_input *i;
+ uint32_t idx;
+
+ /* We're suspending or resuming, tell everyone about it */
+
+ for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx)))
+ if (i->suspend)
+ i->suspend(i, state == PA_SINK_SUSPENDED);
+ }
+
if (state != PA_SINK_UNLINKED) /* if we enter UNLINKED state pa_sink_unlink() will fire the apropriate events */
pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_STATE_CHANGED], s);
+
return 0;
}
diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c
index 5c52419e..576ddcf2 100644
--- a/src/pulsecore/source-output.c
+++ b/src/pulsecore/source-output.c
@@ -71,7 +71,7 @@ pa_source_output* pa_source_output_new(
pa_source_output *o;
pa_resampler *resampler = NULL;
- char st[PA_SAMPLE_SPEC_SNPRINT_MAX];
+ char st[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
pa_assert(core);
pa_assert(data);
@@ -103,11 +103,28 @@ pa_source_output* pa_source_output_new(
pa_return_null_if_fail(pa_channel_map_valid(&data->channel_map));
pa_return_null_if_fail(data->channel_map.channels == data->sample_spec.channels);
+ if (flags & PA_SOURCE_OUTPUT_FIX_FORMAT)
+ data->sample_spec.format = data->source->sample_spec.format;
+
+ if (flags & PA_SOURCE_OUTPUT_FIX_RATE)
+ data->sample_spec.rate = data->source->sample_spec.rate;
+
+ if (flags & PA_SOURCE_OUTPUT_FIX_CHANNELS) {
+ data->sample_spec.channels = data->source->sample_spec.channels;
+ data->channel_map = data->source->channel_map;
+ }
+
+ pa_assert(pa_sample_spec_valid(&data->sample_spec));
+ pa_assert(pa_channel_map_valid(&data->channel_map));
+
if (data->resample_method == PA_RESAMPLER_INVALID)
data->resample_method = core->resample_method;
pa_return_null_if_fail(data->resample_method < PA_RESAMPLER_MAX);
+ if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE], data) < 0)
+ return NULL;
+
if (pa_idxset_size(data->source->outputs) >= PA_MAX_OUTPUTS_PER_SOURCE) {
pa_log("Failed to create source output: too many outputs per source.");
return NULL;
@@ -122,7 +139,9 @@ pa_source_output* pa_source_output_new(
&data->source->sample_spec, &data->source->channel_map,
&data->sample_spec, &data->channel_map,
data->resample_method,
- (flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0))) {
+ ((flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) |
+ ((flags & PA_SOURCE_OUTPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) |
+ (core->disable_remixing || (flags & PA_SOURCE_OUTPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0)))) {
pa_log_warn("Unsupported resampling operation.");
return NULL;
}
@@ -153,6 +172,7 @@ pa_source_output* pa_source_output_new(
o->detach = NULL;
o->attach = NULL;
o->suspend = NULL;
+ o->moved = NULL;
o->userdata = NULL;
o->thread_info.state = o->state;
@@ -163,11 +183,12 @@ pa_source_output* pa_source_output_new(
pa_assert_se(pa_idxset_put(core->source_outputs, o, &o->index) == 0);
pa_assert_se(pa_idxset_put(o->source->outputs, pa_source_output_ref(o), NULL) == 0);
- pa_log_info("Created output %u \"%s\" on %s with sample spec %s",
+ pa_log_info("Created output %u \"%s\" on %s with sample spec %s and channel map %s",
o->index,
o->name,
o->source->name,
- pa_sample_spec_snprint(st, sizeof(st), &o->sample_spec));
+ pa_sample_spec_snprint(st, sizeof(st), &o->sample_spec),
+ pa_channel_map_snprint(cm, sizeof(cm), &o->channel_map));
/* Don't forget to call pa_source_output_put! */
@@ -229,6 +250,7 @@ void pa_source_output_unlink(pa_source_output*o) {
o->attach = NULL;
o->detach = NULL;
o->suspend = NULL;
+ o->moved = NULL;
if (linked) {
pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_REMOVE, o->index);
@@ -379,6 +401,7 @@ pa_resample_method_t pa_source_output_get_resample_method(pa_source_output *o) {
int pa_source_output_move_to(pa_source_output *o, pa_source *dest) {
pa_source *origin;
pa_resampler *new_resampler = NULL;
+ pa_source_output_move_hook_data hook_data;
pa_source_output_assert_ref(o);
pa_assert(PA_SOURCE_OUTPUT_LINKED(o->state));
@@ -415,13 +438,17 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest) {
&dest->sample_spec, &dest->channel_map,
&o->sample_spec, &o->channel_map,
o->resample_method,
- (o->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0))) {
+ ((o->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) |
+ ((o->flags & PA_SOURCE_OUTPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) |
+ (o->core->disable_remixing || (o->flags & PA_SOURCE_OUTPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0)))) {
pa_log_warn("Unsupported resampling operation.");
return -1;
}
}
- pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE], o);
+ hook_data.source_output = o;
+ hook_data.destination = dest;
+ pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE], &hook_data);
/* Okey, let's move it */
pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_REMOVE_OUTPUT, o, 0, NULL);
@@ -447,6 +474,9 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest) {
pa_source_update_status(origin);
pa_source_update_status(dest);
+ if (o->moved)
+ o->moved(o);
+
pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_POST], o);
pa_log_debug("Successfully moved source output %i from %s to %s.", o->index, origin->name, dest->name);
diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h
index e38a1e5a..d6da8d00 100644
--- a/src/pulsecore/source-output.h
+++ b/src/pulsecore/source-output.h
@@ -49,7 +49,12 @@ static inline pa_bool_t PA_SOURCE_OUTPUT_LINKED(pa_source_output_state_t x) {
typedef enum pa_source_output_flags {
PA_SOURCE_OUTPUT_VARIABLE_RATE = 1,
PA_SOURCE_OUTPUT_DONT_MOVE = 2,
- PA_SOURCE_OUTPUT_START_CORKED = 4
+ PA_SOURCE_OUTPUT_START_CORKED = 4,
+ PA_SOURCE_OUTPUT_NO_REMAP = 8,
+ PA_SOURCE_OUTPUT_NO_REMIX = 16,
+ PA_SOURCE_OUTPUT_FIX_FORMAT = 32,
+ PA_SOURCE_OUTPUT_FIX_RATE = 64,
+ PA_SOURCE_OUTPUT_FIX_CHANNELS = 128
} pa_source_output_flags_t;
struct pa_source_output {
@@ -82,8 +87,12 @@ struct pa_source_output {
void (*detach) (pa_source_output *o); /* may be NULL */
/* If non-NULL called whenever the the source this output is attached
+ * to changes. Called from main context */
+ void (*moved) (pa_source_output *o); /* may be NULL */
+
+ /* If non-NULL called whenever the the source this output is attached
* to suspends or resumes. Called from main context */
- void (*suspend) (pa_source_output *o, int b); /* may be NULL */
+ void (*suspend) (pa_source_output *o, pa_bool_t b); /* may be NULL */
/* Supposed to unlink and destroy this stream. Called from main
* context. */
@@ -135,6 +144,11 @@ typedef struct pa_source_output_new_data {
pa_resample_method_t resample_method;
} pa_source_output_new_data;
+typedef struct pa_source_output_move_hook_data {
+ pa_source_output *source_output;
+ pa_source *destination;
+} pa_source_output_move_hook_data;
+
pa_source_output_new_data* pa_source_output_new_data_init(pa_source_output_new_data *data);
void pa_source_output_new_data_set_sample_spec(pa_source_output_new_data *data, const pa_sample_spec *spec);
void pa_source_output_new_data_set_channel_map(pa_source_output_new_data *data, const pa_channel_map *map);
diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
index 9a6902ae..5fd65cef 100644
--- a/src/pulsecore/source.c
+++ b/src/pulsecore/source.c
@@ -126,23 +126,16 @@ pa_source* pa_source_new(
static int source_set_state(pa_source *s, pa_source_state_t state) {
int ret;
+ pa_bool_t suspend_change;
pa_assert(s);
if (s->state == state)
return 0;
- if ((s->state == PA_SOURCE_SUSPENDED && PA_SOURCE_OPENED(state)) ||
- (PA_SOURCE_OPENED(s->state) && state == PA_SOURCE_SUSPENDED)) {
- pa_source_output *o;
- uint32_t idx;
-
- /* We're suspending or resuming, tell everyone about it */
-
- for (o = PA_SOURCE_OUTPUT(pa_idxset_first(s->outputs, &idx)); o; o = PA_SOURCE_OUTPUT(pa_idxset_next(s->outputs, &idx)))
- if (o->suspend)
- o->suspend(o, state == PA_SINK_SUSPENDED);
- }
+ suspend_change =
+ (s->state == PA_SOURCE_SUSPENDED && PA_SOURCE_OPENED(state)) ||
+ (PA_SOURCE_OPENED(s->state) && state == PA_SOURCE_SUSPENDED);
if (s->set_state)
if ((ret = s->set_state(s, state)) < 0)
@@ -153,8 +146,20 @@ static int source_set_state(pa_source *s, pa_source_state_t state) {
s->state = state;
+ if (suspend_change) {
+ pa_source_output *o;
+ uint32_t idx;
+
+ /* We're suspending or resuming, tell everyone about it */
+
+ for (o = PA_SOURCE_OUTPUT(pa_idxset_first(s->outputs, &idx)); o; o = PA_SOURCE_OUTPUT(pa_idxset_next(s->outputs, &idx)))
+ if (o->suspend)
+ o->suspend(o, state == PA_SINK_SUSPENDED);
+ }
+
if (state != PA_SOURCE_UNLINKED) /* if we enter UNLINKED state pa_source_unlink() will fire the apropriate events */
pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_STATE_CHANGED], s);
+
return 0;
}