summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/daemon/main.c36
-rw-r--r--src/modules/alsa/alsa-sink.c2
-rw-r--r--src/modules/alsa/alsa-source.c2
-rw-r--r--src/modules/alsa/alsa-util.c9
-rw-r--r--src/modules/alsa/alsa-util.h3
-rw-r--r--src/modules/bluetooth/bluetooth-util.c2
-rw-r--r--src/modules/bluetooth/module-bluetooth-device.c225
-rw-r--r--src/modules/module-null-sink.c7
-rw-r--r--src/modules/reserve-wrap.c11
-rw-r--r--src/modules/rtp/module-rtp-recv.c73
-rw-r--r--src/modules/rtp/module-rtp-send.c6
-rw-r--r--src/modules/rtp/rtp.c21
-rw-r--r--src/modules/rtp/rtp.h2
-rw-r--r--src/pulse/stream.h81
-rw-r--r--src/pulse/volume.c8
-rw-r--r--src/pulsecore/cli-text.c12
-rw-r--r--src/pulsecore/dbus-shared.c12
-rw-r--r--src/pulsecore/dbus-util.c21
-rw-r--r--src/pulsecore/log.h1
-rw-r--r--src/pulsecore/memblockq.c52
-rw-r--r--src/pulsecore/protocol-native.c14
-rw-r--r--src/pulsecore/sink-input.c90
-rw-r--r--src/pulsecore/sink-input.h12
-rw-r--r--src/pulsecore/sink.c158
-rw-r--r--src/pulsecore/sink.h8
-rw-r--r--src/pulsecore/socket-server.c1
-rw-r--r--src/pulsecore/source-output.c30
-rw-r--r--src/pulsecore/source-output.h4
-rw-r--r--src/pulsecore/source.c55
-rw-r--r--src/pulsecore/source.h2
-rw-r--r--src/pulsecore/time-smoother.c42
-rw-r--r--src/tests/interpol-test.c15
-rw-r--r--src/tests/voltest.c3
33 files changed, 759 insertions, 261 deletions
diff --git a/src/daemon/main.c b/src/daemon/main.c
index d25647cb..4c4a9052 100644
--- a/src/daemon/main.c
+++ b/src/daemon/main.c
@@ -334,27 +334,38 @@ static void set_all_rlimits(const pa_daemon_conf *conf) {
#endif
#ifdef HAVE_DBUS
-static void register_org_pulseaudio(pa_core *c)
-{
+static pa_dbus_connection *register_dbus(pa_core *c) {
DBusError error;
pa_dbus_connection *conn;
dbus_error_init(&error);
+
if (!(conn = pa_dbus_bus_get(c, pa_in_system_mode() ? DBUS_BUS_SYSTEM : DBUS_BUS_SESSION, &error)) || dbus_error_is_set(&error)) {
- pa_log_warn("Unable to contact DBUS: %s: %s", error.name, error.message);
- goto finish_dbus;
+ pa_log_warn("Unable to contact D-Bus: %s: %s", error.name, error.message);
+ goto fail;
}
- if (dbus_bus_request_name (pa_dbus_connection_get(conn), "org.pulseaudio.Server", DBUS_NAME_FLAG_DO_NOT_QUEUE, &error) == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)
+ if (dbus_bus_request_name(pa_dbus_connection_get(conn), "org.pulseaudio.Server", DBUS_NAME_FLAG_DO_NOT_QUEUE, &error) == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
pa_log_debug("Got org.pulseaudio.Server!");
- else if (dbus_error_is_set(&error))
- pa_log_warn("Unable to get org.pulseaudio.Server: %s: %s", error.name, error.message);
+ return conn;
+ }
+
+ if (dbus_error_is_set(&error))
+ pa_log_warn("Failed to acquire org.pulseaudio.Server: %s: %s", error.name, error.message);
+ else
+ pa_log_warn("D-Bus name org.pulseaudio.Server already taken. Weird shit!");
+
+ /* PA cannot be started twice by the same user and hence we can
+ * ignore mostly the case that org.pulseaudio.Server is already
+ * taken. */
+
+fail:
-finish_dbus:
if (conn)
pa_dbus_connection_unref(conn);
dbus_error_free(&error);
+ return NULL;
}
#endif
@@ -380,6 +391,9 @@ int main(int argc, char *argv[]) {
#endif
int autospawn_fd = -1;
pa_bool_t autospawn_locked = FALSE;
+#ifdef HAVE_DBUS
+ pa_dbus_connection *dbus = NULL;
+#endif
pa_log_set_ident("pulseaudio");
pa_log_set_level(PA_LOG_INFO);
@@ -1026,7 +1040,7 @@ int main(int argc, char *argv[]) {
#endif
#ifdef HAVE_DBUS
- register_org_pulseaudio(c);
+ dbus = register_dbus(c);
#endif
pa_log_info(_("Daemon startup complete."));
@@ -1038,6 +1052,10 @@ int main(int argc, char *argv[]) {
pa_log_info(_("Daemon shutdown initiated."));
finish:
+#ifdef HAVE_DBUS
+ if (dbus)
+ pa_dbus_connection_unref(dbus);
+#endif
if (autospawn_fd >= 0) {
if (autospawn_locked)
diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c
index f9fb0335..c18c34ef 100644
--- a/src/modules/alsa/alsa-sink.c
+++ b/src/modules/alsa/alsa-sink.c
@@ -1728,7 +1728,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
if (setup_mixer(u, ignore_dB) < 0)
goto fail;
- pa_alsa_dump(u->pcm_handle);
+ pa_alsa_dump(PA_LOG_DEBUG, u->pcm_handle);
if (!(u->thread = pa_thread_new(thread_func, u))) {
pa_log("Failed to create thread.");
diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c
index 9c36211b..9cbd79fa 100644
--- a/src/modules/alsa/alsa-source.c
+++ b/src/modules/alsa/alsa-source.c
@@ -1579,7 +1579,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
if (setup_mixer(u, ignore_dB) < 0)
goto fail;
- pa_alsa_dump(u->pcm_handle);
+ pa_alsa_dump(PA_LOG_DEBUG, u->pcm_handle);
if (!(u->thread = pa_thread_new(thread_func, u))) {
pa_log("Failed to create thread.");
diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c
index 870cf0f1..fbf88b08 100644
--- a/src/modules/alsa/alsa-util.c
+++ b/src/modules/alsa/alsa-util.c
@@ -1297,7 +1297,7 @@ int pa_alsa_calc_mixer_map(snd_mixer_elem_t *elem, const pa_channel_map *channel
return 0;
}
-void pa_alsa_dump(snd_pcm_t *pcm) {
+void pa_alsa_dump(pa_log_level_t level, snd_pcm_t *pcm) {
int err;
snd_output_t *out;
@@ -1306,11 +1306,11 @@ void pa_alsa_dump(snd_pcm_t *pcm) {
pa_assert_se(snd_output_buffer_open(&out) == 0);
if ((err = snd_pcm_dump(pcm, out)) < 0)
- pa_log_debug("snd_pcm_dump(): %s", snd_strerror(err));
+ pa_logl(level, "snd_pcm_dump(): %s", snd_strerror(err));
else {
char *s = NULL;
snd_output_buffer_string(out, &s);
- pa_log_debug("snd_pcm_dump():\n%s", pa_strnull(s));
+ pa_logl(level, "snd_pcm_dump():\n%s", pa_strnull(s));
}
pa_assert_se(snd_output_close(out) == 0);
@@ -1612,6 +1612,7 @@ snd_pcm_sframes_t pa_alsa_safe_avail(snd_pcm_t *pcm, size_t hwbuf_size, const pa
(unsigned long) (pa_bytes_to_usec(k, ss) / PA_USEC_PER_MSEC),
pa_strnull(dn));
pa_xfree(dn);
+ pa_alsa_dump(PA_LOG_ERROR, pcm);
} PA_ONCE_END;
/* Mhmm, let's try not to fail completely */
@@ -1653,6 +1654,7 @@ int pa_alsa_safe_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delay, size_t hwbuf_si
(unsigned long) (pa_bytes_to_usec(abs_k, ss) / PA_USEC_PER_MSEC),
pa_strnull(dn));
pa_xfree(dn);
+ pa_alsa_dump(PA_LOG_ERROR, pcm);
} PA_ONCE_END;
/* Mhmm, let's try not to fail completely */
@@ -1698,6 +1700,7 @@ int pa_alsa_safe_mmap_begin(snd_pcm_t *pcm, const snd_pcm_channel_area_t **areas
(unsigned long) (pa_bytes_to_usec(k, ss) / PA_USEC_PER_MSEC),
pa_strnull(dn));
pa_xfree(dn);
+ pa_alsa_dump(PA_LOG_ERROR, pcm);
} PA_ONCE_END;
return r;
diff --git a/src/modules/alsa/alsa-util.h b/src/modules/alsa/alsa-util.h
index 94f27d14..9fce6daf 100644
--- a/src/modules/alsa/alsa-util.h
+++ b/src/modules/alsa/alsa-util.h
@@ -33,6 +33,7 @@
#include <pulsecore/rtpoll.h>
#include <pulsecore/core.h>
+#include <pulsecore/log.h>
typedef struct pa_alsa_fdlist pa_alsa_fdlist;
@@ -114,7 +115,7 @@ int pa_alsa_probe_profiles(
int pa_alsa_calc_mixer_map(snd_mixer_elem_t *elem, const pa_channel_map *channel_map, snd_mixer_selem_channel_id_t mixer_map[], pa_bool_t playback);
-void pa_alsa_dump(snd_pcm_t *pcm);
+void pa_alsa_dump(pa_log_level_t level, snd_pcm_t *pcm);
void pa_alsa_dump_status(snd_pcm_t *pcm);
void pa_alsa_redirect_errors_inc(void);
diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c
index 6e4344f7..5c7681d4 100644
--- a/src/modules/bluetooth/bluetooth-util.c
+++ b/src/modules/bluetooth/bluetooth-util.c
@@ -773,6 +773,8 @@ void pa_bluetooth_discovery_unref(pa_bluetooth_discovery *y) {
if (y->core)
pa_shared_remove(y->core, "bluetooth-discovery");
+
+ pa_xfree(y);
}
void pa_bluetooth_discovery_sync(pa_bluetooth_discovery *y) {
diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index 4613172e..90f64861 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -166,10 +166,14 @@ struct userdata {
pa_modargs *modargs;
- int stream_write_type, stream_read_type;
+ int stream_write_type;
int service_write_type, service_read_type;
};
+#define FIXED_LATENCY_PLAYBACK_A2DP (25*PA_USEC_PER_MSEC)
+#define FIXED_LATENCY_PLAYBACK_HSP (125*PA_USEC_PER_MSEC)
+#define FIXED_LATENCY_RECORD_HSP (25*PA_USEC_PER_MSEC)
+
#ifdef NOKIA
#define USE_SCO_OVER_PCM(u) (u->profile == PROFILE_HSP && (u->hsp.sco_sink && u->hsp.sco_source))
#endif
@@ -275,6 +279,7 @@ static ssize_t service_expect(struct userdata*u, bt_audio_msg_header_t *rsp, siz
return 0;
}
+/* Run from main thread */
static int parse_caps(struct userdata *u, uint8_t seid, const struct bt_get_capabilities_rsp *rsp) {
uint16_t bytes_left;
const codec_capabilities_t *codec;
@@ -335,6 +340,7 @@ static int parse_caps(struct userdata *u, uint8_t seid, const struct bt_get_capa
return 0;
}
+/* Run from main thread */
static int get_caps(struct userdata *u, uint8_t seid) {
union {
struct bt_get_capabilities_req getcaps_req;
@@ -374,6 +380,7 @@ static int get_caps(struct userdata *u, uint8_t seid) {
return get_caps(u, ret);
}
+/* Run from main thread */
static uint8_t a2dp_default_bitpool(uint8_t freq, uint8_t mode) {
switch (freq) {
@@ -419,6 +426,7 @@ static uint8_t a2dp_default_bitpool(uint8_t freq, uint8_t mode) {
}
}
+/* Run from main thread */
static int setup_a2dp(struct userdata *u) {
sbc_capabilities_t *cap;
int i;
@@ -526,6 +534,7 @@ static int setup_a2dp(struct userdata *u) {
return 0;
}
+/* Run from main thread */
static void setup_sbc(struct a2dp_info *a2dp) {
sbc_capabilities_t *active_capabilities;
@@ -617,6 +626,7 @@ static void setup_sbc(struct a2dp_info *a2dp) {
a2dp->frame_length = sbc_get_frame_length(&a2dp->sbc);
}
+/* Run from main thread */
static int set_conf(struct userdata *u) {
union {
struct bt_open_req open_req;
@@ -705,6 +715,7 @@ static int start_stream_fd(struct userdata *u) {
uint8_t buf[BT_SUGGESTED_BUFFER_SIZE];
} msg;
struct pollfd *pollfd;
+ int one;
pa_assert(u);
pa_assert(u->rtpoll);
@@ -733,13 +744,29 @@ static int start_stream_fd(struct userdata *u) {
pa_make_fd_nonblock(u->stream_fd);
pa_make_socket_low_delay(u->stream_fd);
+ one = 1;
+ if (setsockopt(u->stream_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one)) < 0)
+ pa_log_warn("Failed to enable SO_TIMESTAMP: %s", pa_cstrerror(errno));
+
+ pa_log_debug("Stream properly set up, we're ready to roll!");
+
u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1);
pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
pollfd->fd = u->stream_fd;
pollfd->events = pollfd->revents = 0;
- u->read_index = 0;
- u->write_index = 0;
+ u->read_index = u->write_index = 0;
+ u->started_at = 0;
+
+ if (u->source)
+ u->read_smoother = pa_smoother_new(
+ PA_USEC_PER_SEC,
+ PA_USEC_PER_SEC*2,
+ TRUE,
+ TRUE,
+ 10,
+ pa_rtclock_usec(),
+ TRUE);
return 0;
}
@@ -775,9 +802,15 @@ static int stop_stream_fd(struct userdata *u) {
pa_close(u->stream_fd);
u->stream_fd = -1;
+ if (u->read_smoother) {
+ pa_smoother_free(u->read_smoother);
+ u->read_smoother = NULL;
+ }
+
return r;
}
+/* Run from IO thread */
static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
struct userdata *u = PA_SINK(o)->userdata;
pa_bool_t failed = FALSE;
@@ -785,7 +818,6 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
pa_assert(u->sink == PA_SINK(o));
- pa_log_debug("got message: %d", code);
switch (code) {
case PA_SINK_MESSAGE_SET_STATE:
@@ -813,8 +845,6 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
if (!u->source || u->source->state == PA_SOURCE_SUSPENDED)
if (start_stream_fd(u) < 0)
failed = TRUE;
-
- u->started_at = pa_rtclock_usec();
break;
case PA_SINK_UNLINKED:
@@ -825,7 +855,24 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
break;
case PA_SINK_MESSAGE_GET_LATENCY: {
- *((pa_usec_t*) data) = 0;
+
+ if (u->read_smoother) {
+ pa_usec_t wi, ri;
+
+ ri = pa_smoother_get(u->read_smoother, pa_rtclock_usec());
+ wi = pa_bytes_to_usec(u->write_index + u->block_size, &u->sample_spec);
+
+ *((pa_usec_t*) data) = wi > ri ? wi - ri : 0;
+ } else {
+ pa_usec_t ri, wi;
+
+ ri = pa_rtclock_usec() - u->started_at;
+ wi = pa_bytes_to_usec(u->write_index, &u->sample_spec);
+
+ *((pa_usec_t*) data) = wi > ri ? wi - ri : 0;
+ }
+
+ *((pa_usec_t*) data) += u->sink->fixed_latency;
return 0;
}
}
@@ -835,6 +882,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
return (r < 0 || !failed) ? r : -1;
}
+/* Run from IO thread */
static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
struct userdata *u = PA_SOURCE(o)->userdata;
pa_bool_t failed = FALSE;
@@ -842,7 +890,6 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
pa_assert(u->source == PA_SOURCE(o));
- pa_log_debug("got message: %d", code);
switch (code) {
case PA_SOURCE_MESSAGE_SET_STATE:
@@ -856,7 +903,8 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
if (!u->sink || u->sink->state == PA_SINK_SUSPENDED)
stop_stream_fd(u);
- pa_smoother_pause(u->read_smoother, pa_rtclock_usec());
+ if (u->read_smoother)
+ pa_smoother_pause(u->read_smoother, pa_rtclock_usec());
break;
case PA_SOURCE_IDLE:
@@ -869,7 +917,8 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
if (start_stream_fd(u) < 0)
failed = TRUE;
- pa_smoother_resume(u->read_smoother, pa_rtclock_usec(), TRUE);
+ /* We don't resume the smoother here. Instead we
+ * wait until the first packet arrives */
break;
case PA_SOURCE_UNLINKED:
@@ -880,7 +929,12 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
break;
case PA_SOURCE_MESSAGE_GET_LATENCY: {
- *((pa_usec_t*) data) = 0;
+ pa_usec_t wi, ri;
+
+ wi = pa_smoother_get(u->read_smoother, pa_rtclock_usec());
+ ri = pa_bytes_to_usec(u->read_index, &u->sample_spec);
+
+ *((pa_usec_t*) data) = (wi > ri ? wi - ri : 0) + u->source->fixed_latency;
return 0;
}
@@ -891,6 +945,7 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
return (r < 0 || !failed) ? r : -1;
}
+/* Run from IO thread */
static int hsp_process_render(struct userdata *u) {
int ret = 0;
@@ -947,12 +1002,14 @@ static int hsp_process_render(struct userdata *u) {
pa_memblock_unref(u->write_memchunk.memblock);
pa_memchunk_reset(&u->write_memchunk);
+ ret = 1;
break;
}
return ret;
}
+/* Run from IO thread */
static int hsp_process_push(struct userdata *u) {
int ret = 0;
pa_memchunk memchunk;
@@ -960,6 +1017,7 @@ static int hsp_process_push(struct userdata *u) {
pa_assert(u);
pa_assert(u->profile == PROFILE_HSP);
pa_assert(u->source);
+ pa_assert(u->read_smoother);
memchunk.memblock = pa_memblock_new(u->core->mempool, u->block_size);
memchunk.index = memchunk.length = 0;
@@ -967,9 +1025,26 @@ static int hsp_process_push(struct userdata *u) {
for (;;) {
ssize_t l;
void *p;
+ struct msghdr m;
+ struct cmsghdr *cm;
+ uint8_t aux[1024];
+ struct iovec iov;
+ pa_bool_t found_tstamp = FALSE;
+ pa_usec_t tstamp;
+
+ memset(&m, 0, sizeof(m));
+ memset(&aux, 0, sizeof(aux));
+ memset(&iov, 0, sizeof(iov));
+
+ m.msg_iov = &iov;
+ m.msg_iovlen = 1;
+ m.msg_control = aux;
+ m.msg_controllen = sizeof(aux);
p = pa_memblock_acquire(memchunk.memblock);
- l = pa_read(u->stream_fd, p, pa_memblock_get_length(memchunk.memblock), &u->stream_read_type);
+ iov.iov_base = p;
+ iov.iov_len = pa_memblock_get_length(memchunk.memblock);
+ l = recvmsg(u->stream_fd, &m, 0);
pa_memblock_release(memchunk.memblock);
if (l <= 0) {
@@ -992,7 +1067,26 @@ static int hsp_process_push(struct userdata *u) {
memchunk.length = (size_t) l;
u->read_index += (uint64_t) l;
+ for (cm = CMSG_FIRSTHDR(&m); cm; cm = CMSG_NXTHDR(&m, cm))
+ if (cm->cmsg_level == SOL_SOCKET && cm->cmsg_type == SO_TIMESTAMP) {
+ struct timeval *tv = (struct timeval*) CMSG_DATA(cm);
+ pa_rtclock_from_wallclock(tv);
+ tstamp = pa_timeval_load(tv);
+ found_tstamp = TRUE;
+ break;
+ }
+
+ if (!found_tstamp) {
+ pa_log_warn("Couldn't find SO_TIMESTAMP data in auxiliary recvmsg() data!");
+ tstamp = pa_rtclock_usec();
+ }
+
+ pa_smoother_put(u->read_smoother, tstamp, pa_bytes_to_usec(u->read_index, &u->sample_spec));
+ pa_smoother_resume(u->read_smoother, tstamp, TRUE);
+
pa_source_post(u->source, &memchunk);
+
+ ret = 1;
break;
}
@@ -1001,6 +1095,7 @@ static int hsp_process_push(struct userdata *u) {
return ret;
}
+/* Run from IO thread */
static void a2dp_prepare_buffer(struct userdata *u) {
pa_assert(u);
@@ -1012,6 +1107,7 @@ static void a2dp_prepare_buffer(struct userdata *u) {
u->a2dp.buffer = pa_xmalloc(u->a2dp.buffer_size);
}
+/* Run from IO thread */
static int a2dp_process_render(struct userdata *u) {
struct a2dp_info *a2dp;
struct rtp_header *header;
@@ -1095,7 +1191,7 @@ static int a2dp_process_render(struct userdata *u) {
header->v = 2;
header->pt = 1;
header->sequence_number = htons(a2dp->seq_num++);
- header->timestamp = htonl(u->write_index / pa_frame_size(&u->sink->sample_spec));
+ header->timestamp = htonl(u->write_index / pa_frame_size(&u->sample_spec));
header->ssrc = htonl(1);
payload->frame_count = frame_count;
@@ -1137,6 +1233,8 @@ static int a2dp_process_render(struct userdata *u) {
pa_memblock_unref(u->write_memchunk.memblock);
pa_memchunk_reset(&u->write_memchunk);
+ ret = 1;
+
break;
}
@@ -1145,7 +1243,8 @@ static int a2dp_process_render(struct userdata *u) {
static void thread_func(void *userdata) {
struct userdata *u = userdata;
- pa_bool_t do_write = FALSE, writable = FALSE;
+ unsigned do_write = 0;
+ pa_bool_t writable = FALSE;
pa_assert(u);
@@ -1160,8 +1259,6 @@ static void thread_func(void *userdata) {
pa_thread_mq_install(&u->thread_mq);
pa_rtpoll_install(u->rtpoll);
- pa_smoother_set_time_offset(u->read_smoother, pa_rtclock_usec());
-
for (;;) {
struct pollfd *pollfd;
int ret;
@@ -1171,13 +1268,20 @@ static void thread_func(void *userdata) {
if (u->source && PA_SOURCE_IS_LINKED(u->source->thread_info.state)) {
+ /* We should send two blocks to the device before we expect
+ * a response. */
+
+ if (u->write_index == 0 && u->read_index <= 0)
+ do_write = 2;
+
if (pollfd && (pollfd->revents & POLLIN)) {
+ int n_read;
- if (hsp_process_push(u) < 0)
+ if ((n_read = hsp_process_push(u)) < 0)
goto fail;
/* We just read something, so we are supposed to write something, too */
- do_write = TRUE;
+ do_write += n_read;
}
}
@@ -1190,7 +1294,7 @@ static void thread_func(void *userdata) {
if (pollfd->revents & POLLOUT)
writable = TRUE;
- if ((!u->source || !PA_SOURCE_IS_LINKED(u->source->thread_info.state)) && !do_write && writable) {
+ if ((!u->source || !PA_SOURCE_IS_LINKED(u->source->thread_info.state)) && do_write <= 0 && writable) {
pa_usec_t time_passed;
uint64_t should_have_written;
@@ -1198,36 +1302,37 @@ static void thread_func(void *userdata) {
* to. So let's do things by time */
time_passed = pa_rtclock_usec() - u->started_at;
- should_have_written = pa_usec_to_bytes(time_passed, &u->sink->sample_spec);
+ should_have_written = pa_usec_to_bytes(time_passed, &u->sample_spec);
- do_write = u->write_index <= should_have_written ;
-/* pa_log_debug("Time has come: %s", pa_yes_no(do_write)); */
+ do_write = u->write_index <= should_have_written;
}
- if (writable && do_write) {
- if (u->write_index == 0)
+ if (writable && do_write > 0) {
+ int n_written;
+
+ if (u->write_index <= 0)
u->started_at = pa_rtclock_usec();
if (u->profile == PROFILE_A2DP) {
- if (a2dp_process_render(u) < 0)
+ if ((n_written = a2dp_process_render(u)) < 0)
goto fail;
} else {
- if (hsp_process_render(u) < 0)
+ if ((n_written = hsp_process_render(u)) < 0)
goto fail;
}
- do_write = FALSE;
+ do_write -= n_written;
writable = FALSE;
}
- if ((!u->source || !PA_SOURCE_IS_LINKED(u->source->thread_info.state)) && !do_write) {
+ if ((!u->source || !PA_SOURCE_IS_LINKED(u->source->thread_info.state)) && do_write <= 0) {
pa_usec_t time_passed, next_write_at, sleep_for;
/* Hmm, there is no input stream we could synchronize
* to. So let's estimate when we need to wake up the latest */
time_passed = pa_rtclock_usec() - u->started_at;
- next_write_at = pa_bytes_to_usec(u->write_index, &u->sink->sample_spec);
+ next_write_at = pa_bytes_to_usec(u->write_index, &u->sample_spec);
sleep_for = time_passed < next_write_at ? next_write_at - time_passed : 0;
/* pa_log("Sleeping for %lu; time passed %lu, next write at %lu", (unsigned long) sleep_for, (unsigned long) time_passed, (unsigned long)next_write_at); */
@@ -1255,7 +1360,11 @@ static void thread_func(void *userdata) {
pollfd = u->rtpoll_item ? pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL) : NULL;
if (pollfd && (pollfd->revents & ~(POLLOUT|POLLIN))) {
- pa_log_error("FD error.");
+ pa_log_info("FD error: %s%s%s%s",
+ pollfd->revents & POLLERR ? "POLLERR " :"",
+ pollfd->revents & POLLHUP ? "POLLHUP " :"",
+ pollfd->revents & POLLPRI ? "POLLPRI " :"",
+ pollfd->revents & POLLNVAL ? "POLLNVAL " :"");
goto fail;
}
}
@@ -1270,6 +1379,7 @@ finish:
pa_log_debug("IO thread shutting down");
}
+/* Run from main thread */
static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *userdata) {
DBusError err;
struct userdata *u;
@@ -1302,12 +1412,12 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us
if (u->profile == PROFILE_HSP) {
if (u->sink && dbus_message_is_signal(m, "org.bluez.Headset", "SpeakerGainChanged")) {
- pa_cvolume_set(&v, u->sink->sample_spec.channels, (pa_volume_t) (gain * PA_VOLUME_NORM / 15));
+ pa_cvolume_set(&v, u->sample_spec.channels, (pa_volume_t) (gain * PA_VOLUME_NORM / 15));
pa_sink_volume_changed(u->sink, &v);
} else if (u->source && dbus_message_is_signal(m, "org.bluez.Headset", "MicrophoneGainChanged")) {
- pa_cvolume_set(&v, u->sink->sample_spec.channels, (pa_volume_t) (gain * PA_VOLUME_NORM / 15));
+ pa_cvolume_set(&v, u->sample_spec.channels, (pa_volume_t) (gain * PA_VOLUME_NORM / 15));
pa_source_volume_changed(u->source, &v);
}
}
@@ -1319,6 +1429,7 @@ fail:
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
+/* Run from main thread */
static void sink_set_volume_cb(pa_sink *s) {
struct userdata *u = s->userdata;
DBusMessage *m;
@@ -1334,7 +1445,7 @@ static void sink_set_volume_cb(pa_sink *s) {
if (gain > 15)
gain = 15;
- pa_cvolume_set(&s->virtual_volume, u->sink->sample_spec.channels, (pa_volume_t) (gain * PA_VOLUME_NORM / 15));
+ pa_cvolume_set(&s->virtual_volume, u->sample_spec.channels, (pa_volume_t) (gain * PA_VOLUME_NORM / 15));
pa_assert_se(m = dbus_message_new_method_call("org.bluez", u->path, "org.bluez.Headset", "SetSpeakerGain"));
pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_UINT16, &gain, DBUS_TYPE_INVALID));
@@ -1342,6 +1453,7 @@ static void sink_set_volume_cb(pa_sink *s) {
dbus_message_unref(m);
}
+/* Run from main thread */
static void source_set_volume_cb(pa_source *s) {
struct userdata *u = s->userdata;
DBusMessage *m;
@@ -1357,7 +1469,7 @@ static void source_set_volume_cb(pa_source *s) {
if (gain > 15)
gain = 15;
- pa_cvolume_set(&s->virtual_volume, u->source->sample_spec.channels, (pa_volume_t) (gain * PA_VOLUME_NORM / 15));
+ pa_cvolume_set(&s->virtual_volume, u->sample_spec.channels, (pa_volume_t) (gain * PA_VOLUME_NORM / 15));
pa_assert_se(m = dbus_message_new_method_call("org.bluez", u->path, "org.bluez.Headset", "SetMicrophoneGain"));
pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_UINT16, &gain, DBUS_TYPE_INVALID));
@@ -1365,6 +1477,7 @@ static void source_set_volume_cb(pa_source *s) {
dbus_message_unref(m);
}
+/* Run from main thread */
static char *get_name(const char *type, pa_modargs *ma, const char *device_id, pa_bool_t *namereg_fail) {
char *t;
const char *n;
@@ -1451,6 +1564,7 @@ static pa_hook_result_t source_state_changed_cb(pa_core *c, pa_source *s, struct
#endif
+/* Run from main thread */
static int add_sink(struct userdata *u) {
#ifdef NOKIA
@@ -1492,6 +1606,11 @@ static int add_sink(struct userdata *u) {
u->sink->userdata = u;
u->sink->parent.process_msg = sink_process_msg;
+
+ pa_sink_set_max_request(u->sink, u->block_size);
+ u->sink->fixed_latency =
+ (u->profile == PROFILE_A2DP ? FIXED_LATENCY_PLAYBACK_A2DP : FIXED_LATENCY_PLAYBACK_HSP) +
+ pa_bytes_to_usec(u->block_size, &u->sample_spec);
}
if (u->profile == PROFILE_HSP) {
@@ -1502,12 +1621,13 @@ static int add_sink(struct userdata *u) {
return 0;
}
+/* Run from main thread */
static int add_source(struct userdata *u) {
#ifdef NOKIA
if (USE_SCO_OVER_PCM(u)) {
u->source = u->hsp.sco_source;
- pa_proplist_sets(u->source->proplist, "bluetooth.protocol", "sco");
+ pa_proplist_sets(u->source->proplist, "bluetooth.protocol", "hsp");
if (!u->hsp.source_state_changed_slot)
u->hsp.source_state_changed_slot = pa_hook_connect(&u->core->hooks[PA_CORE_HOOK_SOURCE_STATE_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) source_state_changed_cb, u);
@@ -1523,7 +1643,7 @@ static int add_source(struct userdata *u) {
data.driver = __FILE__;
data.module = u->module;
pa_source_new_data_set_sample_spec(&data, &u->sample_spec);
- pa_proplist_sets(data.proplist, "bluetooth.protocol", u->profile == PROFILE_A2DP ? "a2dp" : "sco");
+ pa_proplist_sets(data.proplist, "bluetooth.protocol", u->profile == PROFILE_A2DP ? "a2dp" : "hsp");
data.card = u->card;
data.name = get_name("source", u->modargs, u->address, &b);
data.namereg_fail = b;
@@ -1538,6 +1658,10 @@ static int add_source(struct userdata *u) {
u->source->userdata = u;
u->source->parent.process_msg = source_process_msg;
+
+ u->source->fixed_latency =
+ (/* u->profile == PROFILE_A2DP ? FIXED_LATENCY_RECORD_A2DP : */ FIXED_LATENCY_RECORD_HSP) +
+ pa_bytes_to_usec(u->block_size, &u->sample_spec);
}
if (u->profile == PROFILE_HSP) {
@@ -1549,6 +1673,7 @@ static int add_source(struct userdata *u) {
return 0;
}
+/* Run from main thread */
static void shutdown_bt(struct userdata *u) {
pa_assert(u);
@@ -1557,12 +1682,12 @@ static void shutdown_bt(struct userdata *u) {
u->stream_fd = -1;
u->stream_write_type = 0;
- u->stream_read_type = 0;
}
if (u->service_fd >= 0) {
pa_close(u->service_fd);
u->service_fd = -1;
+ u->service_write_type = u->service_write_type = 0;
}
if (u->write_memchunk.memblock) {
@@ -1571,12 +1696,13 @@ static void shutdown_bt(struct userdata *u) {
}
}
+/* Run from main thread */
static int init_bt(struct userdata *u) {
pa_assert(u);
shutdown_bt(u);
- u->stream_write_type = u->stream_read_type = 0;
+ u->stream_write_type = 0;
u->service_write_type = u->service_write_type = 0;
if ((u->service_fd = bt_audio_service_open()) < 0) {
@@ -1589,6 +1715,7 @@ static int init_bt(struct userdata *u) {
return 0;
}
+/* Run from main thread */
static int setup_bt(struct userdata *u) {
pa_assert(u);
@@ -1614,6 +1741,7 @@ static int setup_bt(struct userdata *u) {
return 0;
}
+/* Run from main thread */
static int init_profile(struct userdata *u) {
int r = 0;
pa_assert(u);
@@ -1634,6 +1762,7 @@ static int init_profile(struct userdata *u) {
return r;
}
+/* Run from main thread */
static void stop_thread(struct userdata *u) {
pa_assert(u);
@@ -1674,8 +1803,14 @@ static void stop_thread(struct userdata *u) {
pa_rtpoll_free(u->rtpoll);
u->rtpoll = NULL;
}
+
+ if (u->read_smoother) {
+ pa_smoother_free(u->read_smoother);
+ u->read_smoother = NULL;
+ }
}
+/* Run from main thread */
static int start_thread(struct userdata *u) {
pa_assert(u);
pa_assert(!u->thread);
@@ -1724,6 +1859,7 @@ static int start_thread(struct userdata *u) {
return 0;
}
+/* Run from main thread */
static int card_set_profile(pa_card *c, pa_card_profile *new_profile) {
struct userdata *u;
enum profile *d;
@@ -1797,6 +1933,7 @@ static int card_set_profile(pa_card *c, pa_card_profile *new_profile) {
return 0;
}
+/* Run from main thread */
static int add_card(struct userdata *u, const char *default_profile, const pa_bluetooth_device *device) {
pa_card_new_data data;
pa_bool_t b;
@@ -1845,7 +1982,7 @@ static int add_card(struct userdata *u, const char *default_profile, const pa_bl
}
if (pa_bluetooth_uuid_has(device->uuids, HSP_HS_UUID) ||
- pa_bluetooth_uuid_has(device->uuids, HFP_HS_UUID)) {
+ pa_bluetooth_uuid_has(device->uuids, HFP_HS_UUID)) {
p = pa_card_profile_new("hsp", _("Telephony Duplex (HSP/HFP)"), sizeof(enum profile));
p->priority = 20;
p->n_sinks = 1;
@@ -1890,6 +2027,7 @@ static int add_card(struct userdata *u, const char *default_profile, const pa_bl
return 0;
}
+/* Run from main thread */
static const pa_bluetooth_device* find_device(struct userdata *u, const char *address, const char *path) {
const pa_bluetooth_device *d = NULL;
@@ -1926,6 +2064,7 @@ static const pa_bluetooth_device* find_device(struct userdata *u, const char *ad
return d;
}
+/* Run from main thread */
static int setup_dbus(struct userdata *u) {
DBusError err;
@@ -1965,14 +2104,6 @@ int pa__init(pa_module* m) {
u->core = m->core;
u->service_fd = -1;
u->stream_fd = -1;
- u->read_smoother = pa_smoother_new(
- PA_USEC_PER_SEC,
- PA_USEC_PER_SEC*2,
- TRUE,
- TRUE,
- 10,
- 0,
- FALSE);
u->sample_spec = m->core->default_sample_spec;
u->modargs = ma;
diff --git a/src/modules/module-null-sink.c b/src/modules/module-null-sink.c
index 129bc1c3..d9bab6bd 100644
--- a/src/modules/module-null-sink.c
+++ b/src/modules/module-null-sink.c
@@ -119,6 +119,7 @@ static int sink_process_msg(
static void sink_update_requested_latency_cb(pa_sink *s) {
struct userdata *u;
+ size_t nbytes;
pa_sink_assert_ref(s);
pa_assert_se(u = s->userdata);
@@ -127,6 +128,10 @@ static void sink_update_requested_latency_cb(pa_sink *s) {
if (u->block_usec == (pa_usec_t) -1)
u->block_usec = s->thread_info.max_latency;
+
+ nbytes = pa_usec_to_bytes(u->block_usec, &s->sample_spec);
+ pa_sink_set_max_rewind_within_thread(s, nbytes);
+ pa_sink_set_max_request_within_thread(s, nbytes);
}
static void process_rewind(struct userdata *u, pa_usec_t now) {
@@ -284,7 +289,7 @@ int pa__init(pa_module*m) {
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, pa_modargs_get_value(ma, "description", "Null Output"));
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_CLASS, "abstract");
- u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY);
+ u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY);
pa_sink_new_data_done(&data);
if (!u->sink) {
diff --git a/src/modules/reserve-wrap.c b/src/modules/reserve-wrap.c
index 1927342b..02ff29be 100644
--- a/src/modules/reserve-wrap.c
+++ b/src/modules/reserve-wrap.c
@@ -23,6 +23,8 @@
#include <config.h>
#endif
+#include <errno.h>
+
#include <pulse/xmalloc.h>
#include <pulse/i18n.h>
@@ -127,8 +129,13 @@ pa_reserve_wrapper* pa_reserve_wrapper_get(pa_core *c, const char *device_name)
request_cb,
NULL)) < 0) {
- pa_log_error("Failed to acquire reservation lock on device '%s': %s", device_name, pa_cstrerror(-k));
- goto fail;
+ if (k == -EBUSY) {
+ pa_log_error("Device '%s' already locked.", device_name);
+ goto fail;
+ } else {
+ pa_log_warn("Failed to acquire reservation lock on device '%s': %s", device_name, pa_cstrerror(-k));
+ return r;
+ }
}
pa_log_debug("Successfully acquired reservation lock on device '%s'", device_name);
diff --git a/src/modules/rtp/module-rtp-recv.c b/src/modules/rtp/module-rtp-recv.c
index e7749cdd..c61d2d8b 100644
--- a/src/modules/rtp/module-rtp-recv.c
+++ b/src/modules/rtp/module-rtp-recv.c
@@ -52,6 +52,8 @@
#include <pulsecore/rtclock.h>
#include <pulsecore/atomic.h>
#include <pulsecore/time-smoother.h>
+#include <pulsecore/socket-util.h>
+#include <pulsecore/once.h>
#include "module-rtp-recv-symdef.h"
@@ -165,7 +167,7 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
pa_memblockq_rewind(s->memblockq, nbytes);
}
-/* Called from thread context */
+/* Called from I/O thread context */
static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
struct session *s;
@@ -184,11 +186,24 @@ static void sink_input_kill(pa_sink_input* i) {
session_free(s);
}
+/* Called from IO context */
+static void sink_input_suspend_within_thread(pa_sink_input* i, pa_bool_t b) {
+ struct session *s;
+ pa_sink_input_assert_ref(i);
+ pa_assert_se(s = i->userdata);
+
+ if (b) {
+ pa_smoother_pause(s->smoother, pa_rtclock_usec());
+ pa_memblockq_flush_read(s->memblockq);
+ } else
+ s->first_packet = FALSE;
+}
+
/* Called from I/O thread context */
static int rtpoll_work_cb(pa_rtpoll_item *i) {
pa_memchunk chunk;
int64_t k, j, delta;
- struct timeval now;
+ struct timeval now = { 0, 0 };
struct session *s;
struct pollfd *p;
@@ -206,10 +221,11 @@ static int rtpoll_work_cb(pa_rtpoll_item *i) {
p->revents = 0;
- if (pa_rtp_recv(&s->rtp_context, &chunk, s->userdata->module->core->mempool) < 0)
+ if (pa_rtp_recv(&s->rtp_context, &chunk, s->userdata->module->core->mempool, &now) < 0)
return 0;
- if (s->sdp_info.payload != s->rtp_context.payload) {
+ if (s->sdp_info.payload != s->rtp_context.payload ||
+ !PA_SINK_IS_OPENED(s->sink_input->sink->thread_info.state)) {
pa_memblock_unref(chunk.memblock);
return 0;
}
@@ -240,10 +256,19 @@ static int rtpoll_work_cb(pa_rtpoll_item *i) {
pa_memblockq_seek(s->memblockq, delta * (int64_t) s->rtp_context.frame_size, PA_SEEK_RELATIVE, TRUE);
- pa_rtclock_get(&now);
+ if (now.tv_sec == 0) {
+ PA_ONCE_BEGIN {
+ pa_log_warn("Using artificial time instead of timestamp");
+ } PA_ONCE_END;
+ pa_rtclock_get(&now);
+ } else
+ pa_rtclock_from_wallclock(&now);
pa_smoother_put(s->smoother, pa_timeval_load(&now), pa_bytes_to_usec((uint64_t) pa_memblockq_get_write_index(s->memblockq), &s->sink_input->sample_spec));
+ /* Tell the smoother that we are rolling now, in case it is still paused */
+ pa_smoother_resume(s->smoother, pa_timeval_load(&now), TRUE);
+
if (pa_memblockq_push(s->memblockq, &chunk) < 0) {
pa_log_warn("Queue overrun");
pa_memblockq_seek(s->memblockq, (int64_t) chunk.length, PA_SEEK_RELATIVE, TRUE);
@@ -262,14 +287,14 @@ static int rtpoll_work_cb(pa_rtpoll_item *i) {
pa_usec_t wi, ri, render_delay, sink_delay = 0, latency, fix;
unsigned fix_samples;
- pa_log("Updating sample rate");
+ pa_log_debug("Updating sample rate");
wi = pa_smoother_get(s->smoother, pa_timeval_load(&now));
ri = pa_bytes_to_usec((uint64_t) pa_memblockq_get_read_index(s->memblockq), &s->sink_input->sample_spec);
- if (PA_MSGOBJECT(s->sink_input->sink)->process_msg(PA_MSGOBJECT(s->sink_input->sink), PA_SINK_MESSAGE_GET_LATENCY, &sink_delay, 0, NULL) < 0)
- sink_delay = 0;
+ pa_log_debug("wi=%lu ri=%lu", (unsigned long) wi, (unsigned long) ri);
+ sink_delay = pa_sink_get_latency_within_thread(s->sink_input->sink);
render_delay = pa_bytes_to_usec(pa_memblockq_get_length(s->sink_input->thread_info.render_memblockq), &s->sink_input->sink->sample_spec);
if (ri > render_delay+sink_delay)
@@ -294,14 +319,20 @@ static int rtpoll_work_cb(pa_rtpoll_item *i) {
fix_samples = (unsigned) (fix * (pa_usec_t) s->sink_input->thread_info.sample_spec.rate / (pa_usec_t) RATE_UPDATE_INTERVAL);
/* Check if deviation is in bounds */
- if (fix_samples > s->sink_input->sample_spec.rate*.20)
+ if (fix_samples > s->sink_input->sample_spec.rate*.50)
pa_log_debug("Hmmm, rate fix is too large (%lu Hz), not applying.", (unsigned long) fix_samples);
+ else {
+ /* Fix up rate */
+ if (latency < s->intended_latency)
+ s->sink_input->sample_spec.rate -= fix_samples;
+ else
+ s->sink_input->sample_spec.rate += fix_samples;
+
+ if (s->sink_input->sample_spec.rate > PA_RATE_MAX)
+ s->sink_input->sample_spec.rate = PA_RATE_MAX;
+ }
- /* Fix up rate */
- if (latency < s->intended_latency)
- s->sink_input->sample_spec.rate -= fix_samples;
- else
- s->sink_input->sample_spec.rate += fix_samples;
+ pa_assert(pa_sample_spec_valid(&s->sink_input->sample_spec));
pa_resampler_set_input_rate(s->sink_input->thread_info.resampler, s->sink_input->sample_spec.rate);
@@ -362,6 +393,14 @@ static int mcast_socket(const struct sockaddr* sa, socklen_t salen) {
goto fail;
}
+ pa_make_udp_socket_low_delay(fd);
+
+ one = 1;
+ if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one)) < 0) {
+ pa_log("SO_TIMESTAMP failed: %s", pa_cstrerror(errno));
+ goto fail;
+ }
+
one = 1;
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0) {
pa_log("SO_REUSEADDR failed: %s", pa_cstrerror(errno));
@@ -437,7 +476,7 @@ static struct session *session_new(struct userdata *u, const pa_sdp_info *sdp_in
TRUE,
10,
pa_timeval_load(&now),
- FALSE);
+ TRUE);
s->last_rate_update = pa_timeval_load(&now);
pa_atomic_store(&s->timestamp, (int) now.tv_sec);
@@ -478,6 +517,7 @@ static struct session *session_new(struct userdata *u, const pa_sdp_info *sdp_in
s->sink_input->kill = sink_input_kill;
s->sink_input->attach = sink_input_attach;
s->sink_input->detach = sink_input_detach;
+ s->sink_input->suspend_within_thread = sink_input_suspend_within_thread;
pa_sink_input_get_silence(s->sink_input, &silence);
@@ -654,8 +694,7 @@ int pa__init(pa_module*m) {
if ((fd = mcast_socket(sa, salen)) < 0)
goto fail;
- u = pa_xnew(struct userdata, 1);
- m->userdata = u;
+ m->userdata = u = pa_xnew(struct userdata, 1);
u->module = m;
u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));
diff --git a/src/modules/rtp/module-rtp-send.c b/src/modules/rtp/module-rtp-send.c
index 722d12bd..cdd2c57d 100644
--- a/src/modules/rtp/module-rtp-send.c
+++ b/src/modules/rtp/module-rtp-send.c
@@ -347,10 +347,10 @@ int pa__init(pa_module*m) {
o->push = source_output_push;
o->kill = source_output_kill;
- u = pa_xnew(struct userdata, 1);
- m->userdata = u;
- o->userdata = u;
+ pa_log_info("Configured source latency of %lu ms.",
+ pa_source_output_set_requested_latency(o, pa_bytes_to_usec(mtu, &o->sample_spec)) / PA_USEC_PER_MSEC);
+ m->userdata = o->userdata = u = pa_xnew(struct userdata, 1);
u->module = m;
u->source_output = o;
diff --git a/src/modules/rtp/rtp.c b/src/modules/rtp/rtp.c
index 7537c1f5..6706a10f 100644
--- a/src/modules/rtp/rtp.c
+++ b/src/modules/rtp/rtp.c
@@ -162,13 +162,16 @@ pa_rtp_context* pa_rtp_context_init_recv(pa_rtp_context *c, int fd, size_t frame
return c;
}
-int pa_rtp_recv(pa_rtp_context *c, pa_memchunk *chunk, pa_mempool *pool) {
+int pa_rtp_recv(pa_rtp_context *c, pa_memchunk *chunk, pa_mempool *pool, struct timeval *tstamp) {
int size;
struct msghdr m;
+ struct cmsghdr *cm;
struct iovec iov;
uint32_t header;
unsigned cc;
ssize_t r;
+ uint8_t aux[1024];
+ pa_bool_t found_tstamp = FALSE;
pa_assert(c);
pa_assert(chunk);
@@ -208,8 +211,8 @@ int pa_rtp_recv(pa_rtp_context *c, pa_memchunk *chunk, pa_mempool *pool) {
m.msg_namelen = 0;
m.msg_iov = &iov;
m.msg_iovlen = 1;
- m.msg_control = NULL;
- m.msg_controllen = 0;
+ m.msg_control = aux;
+ m.msg_controllen = sizeof(aux);
m.msg_flags = 0;
r = recvmsg(c->fd, &m, 0);
@@ -275,6 +278,18 @@ int pa_rtp_recv(pa_rtp_context *c, pa_memchunk *chunk, pa_mempool *pool) {
pa_memchunk_reset(&c->memchunk);
}
+ for (cm = CMSG_FIRSTHDR(&m); cm; cm = CMSG_NXTHDR(&m, cm)) {
+ if (cm->cmsg_level == SOL_SOCKET && cm->cmsg_type == SO_TIMESTAMP)
+ memcpy(tstamp, CMSG_DATA(cm), sizeof(struct timeval));
+ found_tstamp = TRUE;
+ break;
+ }
+
+ if (!found_tstamp) {
+ pa_log_warn("Couldn't find SO_TIMESTAMP data in auxiliary recvmsg() data!");
+ memset(tstamp, 0, sizeof(tstamp));
+ }
+
return 0;
fail:
diff --git a/src/modules/rtp/rtp.h b/src/modules/rtp/rtp.h
index eff5e75b..b197e82f 100644
--- a/src/modules/rtp/rtp.h
+++ b/src/modules/rtp/rtp.h
@@ -43,7 +43,7 @@ pa_rtp_context* pa_rtp_context_init_send(pa_rtp_context *c, int fd, uint32_t ssr
int pa_rtp_send(pa_rtp_context *c, size_t size, pa_memblockq *q);
pa_rtp_context* pa_rtp_context_init_recv(pa_rtp_context *c, int fd, size_t frame_size);
-int pa_rtp_recv(pa_rtp_context *c, pa_memchunk *chunk, pa_mempool *pool);
+int pa_rtp_recv(pa_rtp_context *c, pa_memchunk *chunk, pa_mempool *pool, struct timeval *tstamp);
void pa_rtp_context_destroy(pa_rtp_context *c);
diff --git a/src/pulse/stream.h b/src/pulse/stream.h
index 8e99a753..49c132a2 100644
--- a/src/pulse/stream.h
+++ b/src/pulse/stream.h
@@ -424,7 +424,9 @@ int pa_stream_disconnect(pa_stream *s);
* is not copied. If NULL, the data is copied into an internal
* buffer. The client my freely seek around in the output buffer. For
* most applications passing 0 and PA_SEEK_RELATIVE as arguments for
- * offset and seek should be useful.*/
+ * offset and seek should be useful. Afte ther write call succeeded
+ * the write index will be a the position after where this chunk of
+ * data has been written to. */
int pa_stream_write(
pa_stream *p /**< The stream to use */,
const void *data /**< The data to write */,
@@ -519,7 +521,16 @@ void pa_stream_set_event_callback(pa_stream *p, pa_stream_event_cb_t cb, void *u
* pa_stream_set_moved_callback() as well. \since 0.9.15 */
void pa_stream_set_buffer_attr_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata);
-/** Pause (or resume) playback of this stream temporarily. Available on both playback and recording streams. */
+/** Pause (or resume) playback of this stream temporarily. Available
+ * on both playback and recording streams. If b is 1 the stream is
+ * paused. If b is 0 the stream is resumed. The pause/resume operation
+ * is executed as quickly as possible. If a cork is very quickly
+ * followed by an uncork or the other way round this might not
+ * actually have any effect on the stream that is output. You can use
+ * pa_stream_is_corked() to find out whether the stream is currently
+ * paused or not. Normally a stream will be created in uncorked
+ * state. If you pass PA_STREAM_START_CORKED as flag during connection
+ * of the stream it will be created in corked state. */
pa_operation* pa_stream_cork(pa_stream *s, int b, pa_stream_success_cb_t cb, void *userdata);
/** Flush the playback buffer of this stream. Most of the time you're
@@ -542,37 +553,63 @@ pa_operation* pa_stream_set_name(pa_stream *s, const char *name, pa_stream_succe
/** Return the current playback/recording time. This is based on the
* data in the timing info structure returned by
- * pa_stream_get_timing_info(). This function will usually only return
- * new data if a timing info update has been recieved. Only if timing
- * interpolation has been requested (PA_STREAM_INTERPOLATE_TIMING)
- * the data from the last timing update is used for an estimation of
- * the current playback/recording time based on the local time that
- * passed since the timing info structure has been acquired. The time
- * value returned by this function is guaranteed to increase
- * monotonically. (that means: the returned value is always greater or
- * equal to the value returned on the last call) This behaviour can
- * be disabled by using PA_STREAM_NOT_MONOTONIC. This may be
+ * pa_stream_get_timing_info().
+ *
+ * This function will usually only return new data if a timing info
+ * update has been recieved. Only if timing interpolation has been
+ * requested (PA_STREAM_INTERPOLATE_TIMING) the data from the last
+ * timing update is used for an estimation of the current
+ * playback/recording time based on the local time that passed since
+ * the timing info structure has been acquired.
+ *
+ * The time value returned by this function is guaranteed to increase
+ * monotonically. (that means: the returned value is always greater
+ * or equal to the value returned on the last call). This behaviour
+ * can be disabled by using PA_STREAM_NOT_MONOTONIC. This may be
* desirable to deal better with bad estimations of transport
* latencies, but may have strange effects if the application is not
- * able to deal with time going 'backwards'. */
+ * able to deal with time going 'backwards'.
+ *
+ * The time interpolator activated by PA_STREAM_INTERPOLATE_TIMING
+ * favours 'smooth' time graphs over accurate ones to improve the
+ * smoothness of UI operations that are tied to the audio clock. If
+ * accuracy is more important to you you might need to estimate your
+ * timing based on the data from pa_stream_get_timing_info() yourself
+ * or not work with interpolated timing at all and instead always
+ * query on the server side for the most up to date timing with
+ * pa_stream_update_timing_info().
+ *
+ * If no timing information has been
+ * recieved yet this call will return PA_ERR_NODATA. For more details
+ * see pa_stream_get_timing_info(). */
int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec);
/** Return the total stream latency. This function is based on
- * pa_stream_get_time(). In case the stream is a monitoring stream the
- * result can be negative, i.e. the captured samples are not yet
- * played. In this case *negative is set to 1. */
+ * pa_stream_get_time().
+ *
+ * In case the stream is a monitoring stream the result can be
+ * negative, i.e. the captured samples are not yet played. In this
+ * case *negative is set to 1.
+ *
+ * If no timing information has been recieved yet this call will
+ * return PA_ERR_NODATA. For more details see
+ * pa_stream_get_timing_info() and pa_stream_get_time(). */
int pa_stream_get_latency(pa_stream *s, pa_usec_t *r_usec, int *negative);
/** Return the latest raw timing data structure. The returned pointer
* points to an internal read-only instance of the timing
* structure. The user should make a copy of this structure if he
* wants to modify it. An in-place update to this data structure may
- * be requested using pa_stream_update_timing_info(). If no
- * pa_stream_update_timing_info() call was issued before, this
- * function will fail with PA_ERR_NODATA. Please note that the
- * write_index member field (and only this field) is updated on each
- * pa_stream_write() call, not just when a timing update has been
- * recieved. */
+ * be requested using pa_stream_update_timing_info().
+ *
+ * If no timing information has been received before (i.e. by
+ * requesting pa_stream_update_timing_info() or by using
+ * PA_STREAM_AUTO_TIMING_UPDATE), this function will fail with
+ * PA_ERR_NODATA.
+ *
+ * Please note that the write_index member field (and only this field)
+ * is updated on each pa_stream_write() call, not just when a timing
+ * update has been recieved. */
const pa_timing_info* pa_stream_get_timing_info(pa_stream *s);
/** Return a pointer to the stream's sample specification. */
diff --git a/src/pulse/volume.c b/src/pulse/volume.c
index c865058d..ad3b3a49 100644
--- a/src/pulse/volume.c
+++ b/src/pulse/volume.c
@@ -126,7 +126,7 @@ pa_volume_t pa_sw_volume_from_dB(double dB) {
if (isinf(dB) < 0 || dB <= -USER_DECIBEL_RANGE)
return PA_VOLUME_MUTED;
- return (pa_volume_t) lrint((dB/USER_DECIBEL_RANGE+1)*PA_VOLUME_NORM);
+ return (pa_volume_t) lrint(ceil((dB/USER_DECIBEL_RANGE+1.0)*PA_VOLUME_NORM));
}
double pa_sw_volume_to_dB(pa_volume_t v) {
@@ -138,19 +138,19 @@ double pa_sw_volume_to_dB(pa_volume_t v) {
pa_volume_t pa_sw_volume_from_linear(double v) {
- if (v <= 0)
+ if (v <= 0.0)
return PA_VOLUME_MUTED;
if (v > .999 && v < 1.001)
return PA_VOLUME_NORM;
- return pa_sw_volume_from_dB(20*log10(v));
+ return pa_sw_volume_from_dB(20.0*log10(v));
}
double pa_sw_volume_to_linear(pa_volume_t v) {
if (v == PA_VOLUME_MUTED)
- return 0;
+ return 0.0;
return pow(10.0, pa_sw_volume_to_dB(v)/20.0);
}
diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c
index 324f83c8..b0911ef1 100644
--- a/src/pulsecore/cli-text.c
+++ b/src/pulsecore/cli-text.c
@@ -288,7 +288,11 @@ char *pa_sink_list_to_string(pa_core *c) {
(double) pa_sink_get_requested_latency(sink) / (double) PA_USEC_PER_MSEC,
(double) min_latency / PA_USEC_PER_MSEC,
(double) max_latency / PA_USEC_PER_MSEC);
- }
+ } else
+ pa_strbuf_printf(
+ s,
+ "\tfixed latency: %0.2f ms\n",
+ (double) pa_sink_get_requested_latency(sink) / PA_USEC_PER_MSEC);
if (sink->card)
pa_strbuf_printf(s, "\tcard: %u <%s>\n", sink->card->index, sink->card->name);
@@ -382,7 +386,11 @@ char *pa_source_list_to_string(pa_core *c) {
(double) pa_source_get_requested_latency(source) / PA_USEC_PER_MSEC,
(double) min_latency / PA_USEC_PER_MSEC,
(double) max_latency / PA_USEC_PER_MSEC);
- }
+ } else
+ pa_strbuf_printf(
+ s,
+ "\tfixed latency: %0.2f ms\n",
+ (double) pa_source_get_requested_latency(source) / PA_USEC_PER_MSEC);
if (source->monitor_of)
pa_strbuf_printf(s, "\tmonitor_of: %u\n", source->monitor_of->index);
diff --git a/src/pulsecore/dbus-shared.c b/src/pulsecore/dbus-shared.c
index b52c14cb..9d9445b6 100644
--- a/src/pulsecore/dbus-shared.c
+++ b/src/pulsecore/dbus-shared.c
@@ -41,7 +41,7 @@ struct pa_dbus_connection {
const char *property_name;
};
-static pa_dbus_connection* pa_dbus_connection_new(pa_core *c, pa_dbus_wrap_connection *conn, const char *name) {
+static pa_dbus_connection* dbus_connection_new(pa_core *c, pa_dbus_wrap_connection *conn, const char *name) {
pa_dbus_connection *pconn;
pconn = pa_xnew(pa_dbus_connection, 1);
@@ -73,9 +73,7 @@ pa_dbus_connection* pa_dbus_bus_get(pa_core *c, DBusBusType type, DBusError *err
if (!(conn = pa_dbus_wrap_connection_new(c->mainloop, type, error)))
return NULL;
- pconn = pa_dbus_connection_new(c, conn, prop_name[type]);
-
- return pconn;
+ return dbus_connection_new(c, conn, prop_name[type]);
}
DBusConnection* pa_dbus_connection_get(pa_dbus_connection *c){
@@ -93,7 +91,8 @@ void pa_dbus_connection_unref(pa_dbus_connection *c) {
if (PA_REFCNT_DEC(c) > 0)
return;
- /* already disconnected, just free */
+ pa_dbus_wrap_connection_free(c->connection);
+
pa_shared_remove(c->core, c->property_name);
pa_xfree(c);
}
@@ -106,6 +105,3 @@ pa_dbus_connection* pa_dbus_connection_ref(pa_dbus_connection *c) {
return c;
}
-
-
-
diff --git a/src/pulsecore/dbus-util.c b/src/pulsecore/dbus-util.c
index d712bff3..ece36def 100644
--- a/src/pulsecore/dbus-util.c
+++ b/src/pulsecore/dbus-util.c
@@ -28,6 +28,8 @@
#include <pulse/xmalloc.h>
#include <pulse/timeval.h>
+
+#include <pulsecore/core-util.h>
#include <pulsecore/log.h>
#include "dbus-util.h"
@@ -244,7 +246,8 @@ static void wakeup_main(void *userdata) {
pa_dbus_wrap_connection* pa_dbus_wrap_connection_new(pa_mainloop_api *m, DBusBusType type, DBusError *error) {
DBusConnection *conn;
- pa_dbus_wrap_connection *pconn = NULL;
+ pa_dbus_wrap_connection *pconn;
+ char *id;
pa_assert(type == DBUS_BUS_SYSTEM || type == DBUS_BUS_SESSION || type == DBUS_BUS_STARTER);
@@ -263,6 +266,13 @@ pa_dbus_wrap_connection* pa_dbus_wrap_connection_new(pa_mainloop_api *m, DBusBus
pconn->dispatch_event = pconn->mainloop->defer_new(pconn->mainloop, dispatch_cb, conn);
+ pa_log_debug("Successfully connected to D-Bus %s bus %s as %s",
+ type == DBUS_BUS_SYSTEM ? "system" : (type == DBUS_BUS_SESSION ? "session" : "starter"),
+ pa_strnull((id = dbus_connection_get_server_id(conn))),
+ pa_strnull(dbus_bus_get_unique_name(conn)));
+
+ dbus_free(id);
+
return pconn;
}
@@ -273,7 +283,8 @@ void pa_dbus_wrap_connection_free(pa_dbus_wrap_connection* c) {
dbus_connection_close(c->connection);
/* must process remaining messages, bit of a kludge to handle
* both unload and shutdown */
- while (dbus_connection_read_write_dispatch(c->connection, -1));
+ while (dbus_connection_read_write_dispatch(c->connection, -1))
+ ;
}
c->mainloop->defer_free(c->dispatch_event);
@@ -369,8 +380,10 @@ pa_dbus_pending *pa_dbus_pending_new(
void pa_dbus_pending_free(pa_dbus_pending *p) {
pa_assert(p);
- if (p->pending)
- dbus_pending_call_cancel(p->pending); /* p->pending is freed by cancel() */
+ if (p->pending) {
+ dbus_pending_call_cancel(p->pending);
+ dbus_pending_call_unref(p->pending);
+ }
if (p->message)
dbus_message_unref(p->message);
diff --git a/src/pulsecore/log.h b/src/pulsecore/log.h
index 8628bf40..2f379f68 100644
--- a/src/pulsecore/log.h
+++ b/src/pulsecore/log.h
@@ -113,6 +113,7 @@ void pa_log_levelv(
#define pa_log_notice(...) pa_log_level_meta(PA_LOG_NOTICE, __FILE__, __LINE__, __func__, __VA_ARGS__)
#define pa_log_warn(...) pa_log_level_meta(PA_LOG_WARN, __FILE__, __LINE__, __func__, __VA_ARGS__)
#define pa_log_error(...) pa_log_level_meta(PA_LOG_ERROR, __FILE__, __LINE__, __func__, __VA_ARGS__)
+#define pa_logl(level, ...) pa_log_level_meta(level, __FILE__, __LINE__, __func__, __VA_ARGS__)
#else
diff --git a/src/pulsecore/memblockq.c b/src/pulsecore/memblockq.c
index d12d13a8..77f9efc9 100644
--- a/src/pulsecore/memblockq.c
+++ b/src/pulsecore/memblockq.c
@@ -90,8 +90,8 @@ pa_memblockq* pa_memblockq_new(
pa_memblockq_set_maxlength(bq, maxlength);
pa_memblockq_set_tlength(bq, tlength);
- pa_memblockq_set_prebuf(bq, prebuf);
pa_memblockq_set_minreq(bq, minreq);
+ pa_memblockq_set_prebuf(bq, prebuf);
pa_memblockq_set_maxrewind(bq, maxrewind);
pa_log_debug("memblockq sanitized: maxlength=%lu, tlength=%lu, base=%lu, prebuf=%lu, minreq=%lu maxrewind=%lu",
@@ -784,16 +784,13 @@ void pa_memblockq_set_maxlength(pa_memblockq *bq, size_t maxlength) {
if (bq->tlength > bq->maxlength)
pa_memblockq_set_tlength(bq, bq->maxlength);
-
- if (bq->prebuf > bq->maxlength)
- pa_memblockq_set_prebuf(bq, bq->maxlength);
}
void pa_memblockq_set_tlength(pa_memblockq *bq, size_t tlength) {
size_t old_tlength;
pa_assert(bq);
- if (tlength <= 0)
+ if (tlength <= 0 || tlength == (size_t) -1)
tlength = bq->maxlength;
old_tlength = bq->tlength;
@@ -802,49 +799,46 @@ void pa_memblockq_set_tlength(pa_memblockq *bq, size_t tlength) {
if (bq->tlength > bq->maxlength)
bq->tlength = bq->maxlength;
- if (bq->prebuf > bq->tlength)
- pa_memblockq_set_prebuf(bq, bq->tlength);
-
if (bq->minreq > bq->tlength)
pa_memblockq_set_minreq(bq, bq->tlength);
+ if (bq->prebuf > bq->tlength+bq->base-bq->minreq)
+ pa_memblockq_set_prebuf(bq, bq->tlength+bq->base-bq->minreq);
+
bq->missing += (int64_t) bq->tlength - (int64_t) old_tlength;
}
+void pa_memblockq_set_minreq(pa_memblockq *bq, size_t minreq) {
+ pa_assert(bq);
+
+ bq->minreq = (minreq/bq->base)*bq->base;
+
+ if (bq->minreq > bq->tlength)
+ bq->minreq = bq->tlength;
+
+ if (bq->minreq < bq->base)
+ bq->minreq = bq->base;
+
+ if (bq->prebuf > bq->tlength+bq->base-bq->minreq)
+ pa_memblockq_set_prebuf(bq, bq->tlength+bq->base-bq->minreq);
+}
+
void pa_memblockq_set_prebuf(pa_memblockq *bq, size_t prebuf) {
pa_assert(bq);
if (prebuf == (size_t) -1)
- prebuf = bq->tlength;
+ prebuf = bq->tlength+bq->base-bq->minreq;
bq->prebuf = ((prebuf+bq->base-1)/bq->base)*bq->base;
if (prebuf > 0 && bq->prebuf < bq->base)
bq->prebuf = bq->base;
- if (bq->prebuf > bq->tlength)
- bq->prebuf = bq->tlength;
+ if (bq->prebuf > bq->tlength+bq->base-bq->minreq)
+ bq->prebuf = bq->tlength+bq->base-bq->minreq;
if (bq->prebuf <= 0 || pa_memblockq_get_length(bq) >= bq->prebuf)
bq->in_prebuf = FALSE;
-
- if (bq->minreq > bq->prebuf)
- pa_memblockq_set_minreq(bq, bq->prebuf);
-}
-
-void pa_memblockq_set_minreq(pa_memblockq *bq, size_t minreq) {
- pa_assert(bq);
-
- bq->minreq = (minreq/bq->base)*bq->base;
-
- if (bq->minreq > bq->tlength)
- bq->minreq = bq->tlength;
-
- if (bq->minreq > bq->prebuf)
- bq->minreq = bq->prebuf;
-
- if (bq->minreq < bq->base)
- bq->minreq = bq->base;
}
void pa_memblockq_set_maxrewind(pa_memblockq *bq, size_t maxrewind) {
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index 59e5d80e..7c2183d8 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -850,7 +850,7 @@ static int playback_stream_process_msg(pa_msgobject *o, int code, void*userdata,
/* Called from main context */
static void fix_playback_buffer_attr(playback_stream *s) {
- size_t frame_size;
+ size_t frame_size, max_prebuf;
pa_usec_t orig_tlength_usec, tlength_usec, orig_minreq_usec, minreq_usec, sink_usec;
pa_assert(s);
@@ -976,8 +976,11 @@ static void fix_playback_buffer_attr(playback_stream *s) {
if (s->buffer_attr.tlength <= s->buffer_attr.minreq)
s->buffer_attr.tlength = s->buffer_attr.minreq*2 + (uint32_t) frame_size;
- if (s->buffer_attr.prebuf == (uint32_t) -1 || s->buffer_attr.prebuf > s->buffer_attr.tlength)
- s->buffer_attr.prebuf = s->buffer_attr.tlength;
+ max_prebuf = s->buffer_attr.tlength + (uint32_t)frame_size - s->buffer_attr.minreq;
+
+ if (s->buffer_attr.prebuf == (uint32_t) -1 ||
+ s->buffer_attr.prebuf > max_prebuf)
+ s->buffer_attr.prebuf = max_prebuf;
}
/* Called from main context */
@@ -1465,7 +1468,8 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
if (pa_memblockq_is_readable(s->memblockq))
s->is_underrun = FALSE;
else {
- pa_log_debug("Underrun on '%s', %lu bytes in queue.", pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_MEDIA_NAME)), (unsigned long) pa_memblockq_get_length(s->memblockq));
+ if (!s->is_underrun)
+ pa_log_debug("Underrun on '%s', %lu bytes in queue.", pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_MEDIA_NAME)), (unsigned long) pa_memblockq_get_length(s->memblockq));
if (s->drain_request && pa_sink_input_safe_to_remove(i)) {
s->drain_request = FALSE;
@@ -4241,7 +4245,7 @@ static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t o
pa_native_connection_assert_ref(c);
if (!(stream = OUTPUT_STREAM(pa_idxset_get_by_index(c->output_streams, channel)))) {
- pa_log("client sent block for invalid stream.");
+ pa_log_debug("Client sent block for invalid stream.");
/* Ignoring */
return;
}
diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index 1fdb3fa6..b1b9fb56 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -117,6 +117,7 @@ static void reset_callbacks(pa_sink_input *i) {
i->attach = NULL;
i->detach = NULL;
i->suspend = NULL;
+ i->suspend_within_thread = NULL;
i->moving = NULL;
i->kill = NULL;
i->get_latency = NULL;
@@ -287,6 +288,7 @@ int pa_sink_input_new(
i->volume_factor = data->volume_factor;
pa_cvolume_init(&i->soft_volume);
+ memset(i->relative_volume, 0, sizeof(i->relative_volume));
i->save_volume = data->save_volume;
i->save_sink = data->save_sink;
i->save_muted = data->save_muted;
@@ -333,8 +335,8 @@ int pa_sink_input_new(
0,
&i->sink->silence);
- 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_assert_se(pa_idxset_put(core->sink_inputs, i, &i->index) == 0);
+ pa_assert_se(pa_idxset_put(i->sink->inputs, pa_sink_input_ref(i), NULL) == 0);
if (i->client)
pa_assert_se(pa_idxset_put(i->client->sink_inputs, i, NULL) >= 0);
@@ -529,7 +531,7 @@ void pa_sink_input_put(pa_sink_input *i) {
pa_sink_update_flat_volume(i->sink, &new_volume);
pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE);
} else
- pa_sw_cvolume_multiply(&i->soft_volume, &i->virtual_volume, &i->volume_factor);
+ pa_sink_input_set_relative_volume(i, &i->virtual_volume);
i->thread_info.soft_volume = i->soft_volume;
i->thread_info.muted = i->muted;
@@ -822,6 +824,9 @@ void pa_sink_input_update_max_request(pa_sink_input *i, size_t nbytes /* in the
pa_usec_t pa_sink_input_set_requested_latency_within_thread(pa_sink_input *i, pa_usec_t usec) {
pa_sink_input_assert_ref(i);
+ if (!(i->sink->flags & PA_SINK_DYNAMIC_LATENCY))
+ usec = i->sink->fixed_latency;
+
if (usec != (pa_usec_t) -1)
usec = PA_CLAMP(usec, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency);
@@ -833,8 +838,6 @@ pa_usec_t pa_sink_input_set_requested_latency_within_thread(pa_sink_input *i, pa
/* Called from main context */
pa_usec_t pa_sink_input_set_requested_latency(pa_sink_input *i, pa_usec_t usec) {
- pa_usec_t min_latency, max_latency;
-
pa_sink_input_assert_ref(i);
if (PA_SINK_INPUT_IS_LINKED(i->state) && i->sink) {
@@ -846,10 +849,14 @@ pa_usec_t pa_sink_input_set_requested_latency(pa_sink_input *i, pa_usec_t usec)
* we have to touch the thread info data directly */
if (i->sink) {
- pa_sink_get_latency_range(i->sink, &min_latency, &max_latency);
+ if (!(i->sink->flags & PA_SINK_DYNAMIC_LATENCY))
+ usec = i->sink->fixed_latency;
- if (usec != (pa_usec_t) -1)
+ if (usec != (pa_usec_t) -1) {
+ pa_usec_t min_latency, max_latency;
+ pa_sink_get_latency_range(i->sink, &min_latency, &max_latency);
usec = PA_CLAMP(usec, min_latency, max_latency);
+ }
}
i->thread_info.requested_sink_latency = usec;
@@ -900,11 +907,12 @@ void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, pa_boo
/* OK, we are in normal volume mode. The volume only affects
* ourselves */
- pa_sw_cvolume_multiply(&i->soft_volume, volume, &i->volume_factor);
+ pa_sink_input_set_relative_volume(i, volume);
/* Hooks have the ability to play games with i->soft_volume */
pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_SET_VOLUME], i);
+ /* Copy the new soft_volume to the thread_info struct */
pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_SOFT_VOLUME, NULL, 0, NULL) == 0);
}
@@ -922,24 +930,50 @@ const pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i) {
/* Called from main context */
pa_cvolume *pa_sink_input_get_relative_volume(pa_sink_input *i, pa_cvolume *v) {
+ unsigned c;
+
pa_sink_input_assert_ref(i);
pa_assert(v);
pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
- *v = i->virtual_volume;
-
/* This always returns a relative volume, even in flat volume mode */
- if (i->sink->flags & PA_SINK_FLAT_VOLUME) {
- pa_cvolume sv;
+ v->channels = i->sample_spec.channels;
+
+ for (c = 0; c < v->channels; c++)
+ v->values[c] = pa_sw_volume_from_linear(i->relative_volume[c]);
+
+ return v;
+}
+
+/* Called from main context */
+void pa_sink_input_set_relative_volume(pa_sink_input *i, const pa_cvolume *v) {
+ unsigned c;
+ pa_cvolume _v;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
+ pa_assert(!v || pa_cvolume_compatible(v, &i->sample_spec));
- sv = *pa_sink_get_volume(i->sink, FALSE);
+ if (!v)
+ v = pa_cvolume_reset(&_v, i->sample_spec.channels);
- pa_sw_cvolume_divide(v, v,
- pa_cvolume_remap(&sv, &i->sink->channel_map, &i->channel_map));
+ /* This basically calculates:
+ *
+ * i->relative_volume := v
+ * i->soft_volume := i->relative_volume * i->volume_factor */
+
+ i->soft_volume.channels = i->sample_spec.channels;
+
+ for (c = 0; c < i->sample_spec.channels; c++) {
+ i->relative_volume[c] = pa_sw_volume_to_linear(v->values[c]);
+
+ i->soft_volume.values[c] = pa_sw_volume_from_linear(
+ i->relative_volume[c] *
+ pa_sw_volume_to_linear(i->volume_factor.values[c]));
}
- return v;
+ /* We don't copy the data to the thread_info data. That's left for someone else to do */
}
/* Called from main context */
@@ -1109,9 +1143,11 @@ int pa_sink_input_start_move(pa_sink_input *i) {
if (i->sink->flags & PA_SINK_FLAT_VOLUME) {
pa_cvolume new_volume;
- /* Make the absolute volume relative */
- i->virtual_volume = i->soft_volume;
- i->soft_volume = i->volume_factor;
+ /* Make the virtual volume relative */
+ pa_sink_input_get_relative_volume(i, &i->virtual_volume);
+
+ /* And reset the the relative volume */
+ pa_sink_input_set_relative_volume(i, NULL);
/* We might need to update the sink's volume if we are in flat
* volume mode. */
@@ -1124,6 +1160,8 @@ int pa_sink_input_start_move(pa_sink_input *i) {
pa_sink_update_status(i->sink);
i->sink = NULL;
+ pa_sink_input_unref(i);
+
return 0;
}
@@ -1171,7 +1209,7 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
i->sink = dest;
i->save_sink = save;
- pa_idxset_put(dest->inputs, i, NULL);
+ pa_idxset_put(dest->inputs, pa_sink_input_ref(i), NULL);
if (pa_sink_input_get_state(i) == PA_SINK_INPUT_CORKED)
i->sink->n_corked++;
@@ -1236,11 +1274,19 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
if (!pa_sink_input_may_move_to(i, dest))
return -PA_ERR_NOTSUPPORTED;
- if ((r = pa_sink_input_start_move(i)) < 0)
+ pa_sink_input_ref(i);
+
+ if ((r = pa_sink_input_start_move(i)) < 0) {
+ pa_sink_input_unref(i);
return r;
+ }
- if ((r = pa_sink_input_finish_move(i, dest, save)) < 0)
+ if ((r = pa_sink_input_finish_move(i, dest, save)) < 0) {
+ pa_sink_input_unref(i);
return r;
+ }
+
+ pa_sink_input_unref(i);
return 0;
}
diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h
index 0dd5e9aa..96ad2baf 100644
--- a/src/pulsecore/sink-input.h
+++ b/src/pulsecore/sink-input.h
@@ -91,7 +91,10 @@ struct pa_sink_input {
pa_sink_input *sync_prev, *sync_next;
- pa_cvolume virtual_volume, soft_volume, volume_factor;
+ pa_cvolume virtual_volume; /* The volume clients are informed about */
+ pa_cvolume volume_factor; /* An internally used volume factor that can be used by modules to apply effects and suchlike without having that visible to the outside */
+ double relative_volume[PA_CHANNELS_MAX]; /* The calculated volume relative to the sink volume as linear factors. */
+ pa_cvolume soft_volume; /* The internal software volume we apply to all PCM data while it passes through. Usually calculated as relative_volume * volume_factor */
pa_bool_t muted:1;
/* if TRUE then the source we are connected to and/or the volume
@@ -148,6 +151,10 @@ struct pa_sink_input {
* to suspends or resumes. Called from main context */
void (*suspend) (pa_sink_input *i, pa_bool_t b); /* may be NULL */
+ /* If non-NULL called whenever the sink this input is attached
+ * to suspends or resumes. Called from IO context */
+ void (*suspend_within_thread) (pa_sink_input *i, pa_bool_t b); /* may be NULL */
+
/* If non-NULL called whenever the sink input is moved to a new
* sink. Called from main context after the sink input has been
* detached from the old sink and before it has been attached to
@@ -345,4 +352,7 @@ pa_bool_t pa_sink_input_safe_to_remove(pa_sink_input *i);
pa_memchunk* pa_sink_input_get_silence(pa_sink_input *i, pa_memchunk *ret);
+/* To be used by sink.c only */
+void pa_sink_input_set_relative_volume(pa_sink_input *i, const pa_cvolume *v);
+
#endif
diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index a0f0ea7e..93800d14 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -50,6 +50,7 @@
#define MIX_BUFFER_LENGTH (PA_PAGE_SIZE)
#define ABSOLUTE_MIN_LATENCY (500)
#define ABSOLUTE_MAX_LATENCY (10*PA_USEC_PER_SEC)
+#define DEFAULT_FIXED_LATENCY (250*PA_USEC_PER_MSEC)
static PA_DEFINE_CHECK_TYPE(pa_sink, pa_msgobject);
@@ -208,6 +209,8 @@ pa_sink* pa_sink_new(
s->muted = data->muted;
s->refresh_volume = s->refresh_muted = FALSE;
+ s->fixed_latency = flags & PA_SINK_DYNAMIC_LATENCY ? 0 : DEFAULT_FIXED_LATENCY;
+
reset_callbacks(s);
s->userdata = NULL;
@@ -363,8 +366,13 @@ void pa_sink_put(pa_sink* s) {
if (s->flags & PA_SINK_LATENCY)
s->monitor_source->flags |= PA_SOURCE_LATENCY;
- if (s->flags & PA_SINK_DYNAMIC_LATENCY)
+ if (s->flags & PA_SINK_DYNAMIC_LATENCY) {
s->monitor_source->flags |= PA_SOURCE_DYNAMIC_LATENCY;
+ s->fixed_latency = 0;
+ } else if (s->fixed_latency <= 0)
+ s->fixed_latency = DEFAULT_FIXED_LATENCY;
+
+ s->monitor_source->fixed_latency = s->fixed_latency;
pa_assert_se(sink_set_state(s, PA_SINK_IDLE) == 0);
@@ -515,8 +523,12 @@ pa_queue *pa_sink_move_all_start(pa_sink *s) {
for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = n) {
n = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx));
+ pa_sink_input_ref(i);
+
if (pa_sink_input_start_move(i) >= 0)
- pa_queue_push(q, pa_sink_input_ref(i));
+ pa_queue_push(q, i);
+ else
+ pa_sink_input_unref(i);
}
return q;
@@ -984,6 +996,46 @@ pa_usec_t pa_sink_get_latency_within_thread(pa_sink *s) {
return usec;
}
+static void compute_new_soft_volume(pa_sink_input *i, const pa_cvolume *new_volume) {
+ unsigned c;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert(new_volume->channels == i->sample_spec.channels);
+
+ /*
+ * This basically calculates:
+ *
+ * i->relative_volume := i->virtual_volume / new_volume
+ * i->soft_volume := i->relative_volume * i->volume_factor
+ */
+
+ /* The new sink volume passed in here must already be remapped to
+ * the sink input's channel map! */
+
+ i->soft_volume.channels = i->sample_spec.channels;
+
+ for (c = 0; c < i->sample_spec.channels; c++)
+
+ if (new_volume->values[c] <= PA_VOLUME_MUTED)
+ /* We leave i->relative_volume untouched */
+ i->soft_volume.values[c] = PA_VOLUME_MUTED;
+ else {
+ i->relative_volume[c] =
+ pa_sw_volume_to_linear(i->virtual_volume.values[c]) /
+ pa_sw_volume_to_linear(new_volume->values[c]);
+
+ i->soft_volume.values[c] = pa_sw_volume_from_linear(
+ i->relative_volume[c] *
+ pa_sw_volume_to_linear(i->volume_factor.values[c]));
+ }
+
+ /* Hooks have the ability to play games with i->soft_volume */
+ pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_SET_VOLUME], i);
+
+ /* We don't copy the soft_volume to the thread_info data
+ * here. That must be done by the caller */
+}
+
/* Called from main thread */
void pa_sink_update_flat_volume(pa_sink *s, pa_cvolume *new_volume) {
pa_sink_input *i;
@@ -998,7 +1050,7 @@ void pa_sink_update_flat_volume(pa_sink *s, pa_cvolume *new_volume) {
* might need to fix up the sink volume accordingly. Please note
* that we don't actually update the sinks volume here, we only
* return how it needs to be updated. The caller should then call
- * pa_sink_set_flat_volume().*/
+ * pa_sink_set_volume().*/
if (pa_idxset_isempty(s->inputs)) {
/* In the special case that we have no sink input we leave the
@@ -1030,26 +1082,22 @@ void pa_sink_update_flat_volume(pa_sink *s, pa_cvolume *new_volume) {
remapped_new_volume = *new_volume;
pa_cvolume_remap(&remapped_new_volume, &s->channel_map, &i->channel_map);
- pa_sw_cvolume_divide(&i->soft_volume, &i->virtual_volume, &remapped_new_volume);
- pa_sw_cvolume_multiply(&i->soft_volume, &i->soft_volume, &i->volume_factor);
-
- /* Hooks have the ability to play games with i->soft_volume */
- pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_INPUT_SET_VOLUME], i);
+ compute_new_soft_volume(i, &remapped_new_volume);
- /* We don't issue PA_SINK_INPUT_MESSAGE_SET_VOLUME because
- * we want the update to have atomically with the sink
- * volume update, hence we do it within the
- * pa_sink_set_flat_volume() call below*/
+ /* We don't copy soft_volume to the thread_info data here
+ * (i.e. issue PA_SINK_INPUT_MESSAGE_SET_VOLUME) because we
+ * want the update to be atomically with the sink volume
+ * update, hence we do it within the pa_sink_set_volume() call
+ * below */
}
}
/* Called from main thread */
-void pa_sink_propagate_flat_volume(pa_sink *s, const pa_cvolume *old_volume) {
+void pa_sink_propagate_flat_volume(pa_sink *s) {
pa_sink_input *i;
uint32_t idx;
pa_sink_assert_ref(s);
- pa_assert(old_volume);
pa_assert(PA_SINK_IS_LINKED(s->state));
pa_assert(s->flags & PA_SINK_FLAT_VOLUME);
@@ -1058,39 +1106,43 @@ void pa_sink_propagate_flat_volume(pa_sink *s, const pa_cvolume *old_volume) {
* sink input volumes accordingly */
for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx))) {
- pa_cvolume remapped_old_volume, remapped_new_volume, fixed_volume;
+ pa_cvolume sink_volume, new_virtual_volume;
unsigned c;
- remapped_new_volume = s->virtual_volume;
- pa_cvolume_remap(&remapped_new_volume, &s->channel_map, &i->channel_map);
+ /* This basically calculates i->virtual_volume := i->relative_volume * s->virtual_volume */
- remapped_old_volume = *old_volume;
- pa_cvolume_remap(&remapped_old_volume, &s->channel_map, &i->channel_map);
+ sink_volume = s->virtual_volume;
+ pa_cvolume_remap(&sink_volume, &s->channel_map, &i->channel_map);
for (c = 0; c < i->sample_spec.channels; c++)
+ new_virtual_volume.values[c] = pa_sw_volume_from_linear(
+ i->relative_volume[c] *
+ pa_sw_volume_to_linear(sink_volume.values[c]));
- if (remapped_old_volume.values[c] == PA_VOLUME_MUTED)
- fixed_volume.values[c] = PA_VOLUME_MUTED;
- else
- fixed_volume.values[c] = (pa_volume_t)
- ((uint64_t) i->virtual_volume.values[c] *
- (uint64_t) remapped_new_volume.values[c] /
- (uint64_t) remapped_old_volume.values[c]);
+ new_virtual_volume.channels = i->sample_spec.channels;
- fixed_volume.channels = i->virtual_volume.channels;
+ if (!pa_cvolume_equal(&new_virtual_volume, &i->virtual_volume)) {
+ i->virtual_volume = new_virtual_volume;
- if (!pa_cvolume_equal(&fixed_volume, &i->virtual_volume)) {
- i->virtual_volume = fixed_volume;
+ /* Hmm, the soft volume might no longer actually match
+ * what has been chosen as new virtual volume here,
+ * especially when the old volume was
+ * PA_VOLUME_MUTED. Hence let's recalculate the soft
+ * volumes here. */
+ compute_new_soft_volume(i, &sink_volume);
/* The virtual volume changed, let's tell people so */
pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
}
}
+
+ /* If the soft_volume of any of the sink inputs got changed, let's
+ * make sure the thread copies are synced up. */
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SYNC_VOLUMES, NULL, 0, NULL) == 0);
}
/* Called from main thread */
void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume, pa_bool_t propagate, pa_bool_t sendmsg) {
- pa_cvolume old_virtual_volume;
pa_bool_t virtual_volume_changed;
pa_sink_assert_ref(s);
@@ -1099,14 +1151,13 @@ void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume, pa_bool_t propagat
pa_assert(pa_cvolume_valid(volume));
pa_assert(pa_cvolume_compatible(volume, &s->sample_spec));
- old_virtual_volume = s->virtual_volume;
+ virtual_volume_changed = !pa_cvolume_equal(volume, &s->virtual_volume);
s->virtual_volume = *volume;
- virtual_volume_changed = !pa_cvolume_equal(&old_virtual_volume, &s->virtual_volume);
/* Propagate this volume change back to the inputs */
if (virtual_volume_changed)
if (propagate && (s->flags & PA_SINK_FLAT_VOLUME))
- pa_sink_propagate_flat_volume(s, &old_virtual_volume);
+ pa_sink_propagate_flat_volume(s);
if (s->set_volume) {
/* If we have a function set_volume(), then we do not apply a
@@ -1157,7 +1208,7 @@ const pa_cvolume *pa_sink_get_volume(pa_sink *s, pa_bool_t force_refresh) {
if (!pa_cvolume_equal(&old_virtual_volume, &s->virtual_volume)) {
if (s->flags & PA_SINK_FLAT_VOLUME)
- pa_sink_propagate_flat_volume(s, &old_virtual_volume);
+ pa_sink_propagate_flat_volume(s);
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
}
@@ -1565,9 +1616,13 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
pa_sink_request_rewind(s, (size_t) -1);
}
- if (s->flags & PA_SINK_FLAT_VOLUME)
- sync_input_volumes_within_thread(s);
+ if (!(s->flags & PA_SINK_FLAT_VOLUME))
+ return 0;
+
+ /* Fall through ... */
+ case PA_SINK_MESSAGE_SYNC_VOLUMES:
+ sync_input_volumes_within_thread(s);
return 0;
case PA_SINK_MESSAGE_GET_VOLUME:
@@ -1585,7 +1640,11 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
case PA_SINK_MESSAGE_GET_MUTE:
return 0;
- case PA_SINK_MESSAGE_SET_STATE:
+ case PA_SINK_MESSAGE_SET_STATE: {
+
+ pa_bool_t suspend_change =
+ (s->thread_info.state == PA_SINK_SUSPENDED && PA_SINK_IS_OPENED(PA_PTR_TO_UINT(userdata))) ||
+ (PA_SINK_IS_OPENED(s->thread_info.state) && PA_PTR_TO_UINT(userdata) == PA_SINK_SUSPENDED);
s->thread_info.state = PA_PTR_TO_UINT(userdata);
@@ -1594,7 +1653,17 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
s->thread_info.rewind_requested = FALSE;
}
+ if (suspend_change) {
+ pa_sink_input *i;
+ void *state = NULL;
+
+ while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
+ if (i->suspend_within_thread)
+ i->suspend_within_thread(i, s->thread_info.state == PA_SINK_SUSPENDED);
+ }
+
return 0;
+ }
case PA_SINK_MESSAGE_DETACH:
@@ -1763,6 +1832,9 @@ pa_usec_t pa_sink_get_requested_latency_within_thread(pa_sink *s) {
pa_sink_assert_ref(s);
+ if (!(s->flags & PA_SINK_DYNAMIC_LATENCY))
+ return PA_CLAMP(s->fixed_latency, s->thread_info.min_latency, s->thread_info.max_latency);
+
if (s->thread_info.requested_latency_valid)
return s->thread_info.requested_latency;
@@ -1778,13 +1850,8 @@ pa_usec_t pa_sink_get_requested_latency_within_thread(pa_sink *s) {
(result == (pa_usec_t) -1 || result > monitor_latency))
result = monitor_latency;
- if (result != (pa_usec_t) -1) {
- if (result > s->thread_info.max_latency)
- result = s->thread_info.max_latency;
-
- if (result < s->thread_info.min_latency)
- result = s->thread_info.min_latency;
- }
+ if (result != (pa_usec_t) -1)
+ result = PA_CLAMP(result, s->thread_info.min_latency, s->thread_info.max_latency);
s->thread_info.requested_latency = result;
s->thread_info.requested_latency_valid = TRUE;
@@ -1873,6 +1940,9 @@ void pa_sink_invalidate_requested_latency(pa_sink *s) {
pa_sink_assert_ref(s);
+ if (!(s->flags & PA_SINK_DYNAMIC_LATENCY))
+ return;
+
s->thread_info.requested_latency_valid = FALSE;
if (PA_SINK_IS_LINKED(s->thread_info.state)) {
diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h
index 634bf3ef..cb4697f9 100644
--- a/src/pulsecore/sink.h
+++ b/src/pulsecore/sink.h
@@ -74,7 +74,8 @@ struct pa_sink {
pa_volume_t base_volume; /* shall be constant */
unsigned n_volume_steps; /* shall be constant */
- pa_cvolume virtual_volume, soft_volume;
+ pa_cvolume virtual_volume; /* The volume clients are informed about */
+ pa_cvolume soft_volume; /* The internal software volume we apply to all PCM data while it passes through */
pa_bool_t muted:1;
pa_bool_t refresh_volume:1;
@@ -85,6 +86,8 @@ struct pa_sink {
pa_memchunk silence;
+ pa_usec_t fixed_latency; /* for sinks with PA_SINK_DYNAMIC_LATENCY this is 0 */
+
/* Called when the main loop requests a state change. Called from
* main loop context. If returns -1 the state change will be
* inhibited */
@@ -159,6 +162,7 @@ typedef enum pa_sink_message {
PA_SINK_MESSAGE_REMOVE_INPUT,
PA_SINK_MESSAGE_GET_VOLUME,
PA_SINK_MESSAGE_SET_VOLUME,
+ PA_SINK_MESSAGE_SYNC_VOLUMES,
PA_SINK_MESSAGE_GET_MUTE,
PA_SINK_MESSAGE_SET_MUTE,
PA_SINK_MESSAGE_GET_LATENCY,
@@ -249,7 +253,7 @@ int pa_sink_suspend(pa_sink *s, pa_bool_t suspend);
int pa_sink_suspend_all(pa_core *c, pa_bool_t suspend);
void pa_sink_update_flat_volume(pa_sink *s, pa_cvolume *new_volume);
-void pa_sink_propagate_flat_volume(pa_sink *s, const pa_cvolume *old_volume);
+void pa_sink_propagate_flat_volume(pa_sink *s);
void pa_sink_set_volume(pa_sink *sink, const pa_cvolume *volume, pa_bool_t propagate, pa_bool_t sendmsg);
const pa_cvolume *pa_sink_get_volume(pa_sink *sink, pa_bool_t force_refresh);
diff --git a/src/pulsecore/socket-server.c b/src/pulsecore/socket-server.c
index 8147b27f..e660700c 100644
--- a/src/pulsecore/socket-server.c
+++ b/src/pulsecore/socket-server.c
@@ -536,6 +536,7 @@ char *pa_socket_server_get_address(pa_socket_server *s, char *c, size_t l) {
return NULL;
pa_snprintf(c, l, "{%s}unix:%s", id, s->filename);
+ pa_xfree(id);
return c;
}
diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c
index 1c37be93..3ee26735 100644
--- a/src/pulsecore/source-output.c
+++ b/src/pulsecore/source-output.c
@@ -87,6 +87,7 @@ static void reset_callbacks(pa_source_output *o) {
o->attach = NULL;
o->detach = NULL;
o->suspend = NULL;
+ o->suspend_within_thread = NULL;
o->moving = NULL;
o->kill = NULL;
o->get_latency = NULL;
@@ -519,6 +520,9 @@ void pa_source_output_update_max_rewind(pa_source_output *o, size_t nbytes /* i
pa_usec_t pa_source_output_set_requested_latency_within_thread(pa_source_output *o, pa_usec_t usec) {
pa_source_output_assert_ref(o);
+ if (!(o->source->flags & PA_SOURCE_DYNAMIC_LATENCY))
+ usec = o->source->fixed_latency;
+
if (usec != (pa_usec_t) -1)
usec = PA_CLAMP(usec, o->source->thread_info.min_latency, o->source->thread_info.max_latency);
@@ -530,8 +534,6 @@ pa_usec_t pa_source_output_set_requested_latency_within_thread(pa_source_output
/* Called from main context */
pa_usec_t pa_source_output_set_requested_latency(pa_source_output *o, pa_usec_t usec) {
- pa_usec_t min_latency, max_latency;
-
pa_source_output_assert_ref(o);
if (PA_SOURCE_OUTPUT_IS_LINKED(o->state) && o->source) {
@@ -543,10 +545,14 @@ pa_usec_t pa_source_output_set_requested_latency(pa_source_output *o, pa_usec_t
* have to touch the thread info data directly */
if (o->source) {
- pa_source_get_latency_range(o->source, &min_latency, &max_latency);
+ if (!(o->source->flags & PA_SOURCE_DYNAMIC_LATENCY))
+ usec = o->source->fixed_latency;
- if (usec != (pa_usec_t) -1)
+ if (usec != (pa_usec_t) -1) {
+ pa_usec_t min_latency, max_latency;
+ pa_source_get_latency_range(o->source, &min_latency, &max_latency);
usec = PA_CLAMP(usec, min_latency, max_latency);
+ }
}
o->thread_info.requested_source_latency = usec;
@@ -704,6 +710,8 @@ int pa_source_output_start_move(pa_source_output *o) {
pa_source_update_status(o->source);
o->source = NULL;
+ pa_source_output_unref(o);
+
return 0;
}
@@ -751,7 +759,7 @@ int pa_source_output_finish_move(pa_source_output *o, pa_source *dest, pa_bool_t
o->source = dest;
o->save_source = save;
- pa_idxset_put(o->source->outputs, o, NULL);
+ pa_idxset_put(o->source->outputs, pa_source_output_ref(o), NULL);
if (pa_source_output_get_state(o) == PA_SOURCE_OUTPUT_CORKED)
o->source->n_corked++;
@@ -803,11 +811,19 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest, pa_bool_t sav
if (!pa_source_output_may_move_to(o, dest))
return -PA_ERR_NOTSUPPORTED;
- if ((r = pa_source_output_start_move(o)) < 0)
+ pa_source_output_ref(o);
+
+ if ((r = pa_source_output_start_move(o)) < 0) {
+ pa_source_output_unref(o);
return r;
+ }
- if ((r = pa_source_output_finish_move(o, dest, save)) < 0)
+ if ((r = pa_source_output_finish_move(o, dest, save)) < 0) {
+ pa_source_output_unref(o);
return r;
+ }
+
+ pa_source_output_unref(o);
return 0;
}
diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h
index 9f5f7744..9824e160 100644
--- a/src/pulsecore/source-output.h
+++ b/src/pulsecore/source-output.h
@@ -120,6 +120,10 @@ struct pa_source_output {
* to suspends or resumes. Called from main context */
void (*suspend) (pa_source_output *o, pa_bool_t b); /* may be NULL */
+ /* If non-NULL called whenever the source this output is attached
+ * to suspends or resumes. Called from IO context */
+ void (*suspend_within_thread) (pa_source_output *o, pa_bool_t b); /* may be NULL */
+
/* If non-NULL called whenever the source output is moved to a new
* source. Called from main context after the stream was detached
* from the old source and before it is attached to the new
diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
index 252e23ab..21902509 100644
--- a/src/pulsecore/source.c
+++ b/src/pulsecore/source.c
@@ -43,6 +43,7 @@
#define ABSOLUTE_MIN_LATENCY (500)
#define ABSOLUTE_MAX_LATENCY (10*PA_USEC_PER_SEC)
+#define DEFAULT_FIXED_LATENCY (250*PA_USEC_PER_MSEC)
static PA_DEFINE_CHECK_TYPE(pa_source, pa_msgobject);
@@ -199,6 +200,8 @@ pa_source* pa_source_new(
s->muted = data->muted;
s->refresh_volume = s->refresh_muted = FALSE;
+ s->fixed_latency = flags & PA_SOURCE_DYNAMIC_LATENCY ? 0 : DEFAULT_FIXED_LATENCY;
+
reset_callbacks(s);
s->userdata = NULL;
@@ -303,8 +306,7 @@ void pa_source_put(pa_source *s) {
/* The following fields must be initialized properly when calling _put() */
pa_assert(s->asyncmsgq);
pa_assert(s->rtpoll);
- pa_assert(!s->thread_info.min_latency || !s->thread_info.max_latency ||
- s->thread_info.min_latency <= s->thread_info.max_latency);
+ pa_assert(s->thread_info.min_latency <= s->thread_info.max_latency);
if (!(s->flags & PA_SOURCE_HW_VOLUME_CTRL)) {
s->flags |= PA_SOURCE_DECIBEL_VOLUME;
@@ -316,6 +318,11 @@ void pa_source_put(pa_source *s) {
if (s->flags & PA_SOURCE_DECIBEL_VOLUME)
s->n_volume_steps = PA_VOLUME_NORM+1;
+ if (s->flags & PA_SOURCE_DYNAMIC_LATENCY)
+ s->fixed_latency = 0;
+ else if (s->fixed_latency <= 0)
+ s->fixed_latency = DEFAULT_FIXED_LATENCY;
+
pa_assert_se(source_set_state(s, PA_SOURCE_IDLE) == 0);
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_NEW, s->index);
@@ -466,8 +473,12 @@ pa_queue *pa_source_move_all_start(pa_source *s) {
for (o = PA_SOURCE_OUTPUT(pa_idxset_first(s->outputs, &idx)); o; o = n) {
n = PA_SOURCE_OUTPUT(pa_idxset_next(s->outputs, &idx));
+ pa_source_output_ref(o);
+
if (pa_source_output_start_move(o) >= 0)
- pa_queue_push(q, pa_source_output_ref(o));
+ pa_queue_push(q, o);
+ else
+ pa_source_output_unref(o);
}
return q;
@@ -933,9 +944,26 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_
case PA_SOURCE_MESSAGE_GET_MUTE:
return 0;
- case PA_SOURCE_MESSAGE_SET_STATE:
+ case PA_SOURCE_MESSAGE_SET_STATE: {
+
+ pa_bool_t suspend_change =
+ (s->thread_info.state == PA_SOURCE_SUSPENDED && PA_SOURCE_IS_OPENED(PA_PTR_TO_UINT(userdata))) ||
+ (PA_SOURCE_IS_OPENED(s->thread_info.state) && PA_PTR_TO_UINT(userdata) == PA_SOURCE_SUSPENDED);
+
s->thread_info.state = PA_PTR_TO_UINT(userdata);
+
+ if (suspend_change) {
+ pa_source_output *o;
+ void *state = NULL;
+
+ while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
+ if (o->suspend_within_thread)
+ o->suspend_within_thread(o, s->thread_info.state == PA_SOURCE_SUSPENDED);
+ }
+
+
return 0;
+ }
case PA_SOURCE_MESSAGE_DETACH:
@@ -1075,6 +1103,9 @@ pa_usec_t pa_source_get_requested_latency_within_thread(pa_source *s) {
pa_source_assert_ref(s);
+ if (!(s->flags & PA_SOURCE_DYNAMIC_LATENCY))
+ return PA_CLAMP(s->fixed_latency, s->thread_info.min_latency, s->thread_info.max_latency);
+
if (s->thread_info.requested_latency_valid)
return s->thread_info.requested_latency;
@@ -1084,13 +1115,8 @@ pa_usec_t pa_source_get_requested_latency_within_thread(pa_source *s) {
(result == (pa_usec_t) -1 || result > o->thread_info.requested_source_latency))
result = o->thread_info.requested_source_latency;
- if (result != (pa_usec_t) -1) {
- if (s->thread_info.max_latency > 0 && result > s->thread_info.max_latency)
- result = s->thread_info.max_latency;
-
- if (s->thread_info.min_latency > 0 && result < s->thread_info.min_latency)
- result = s->thread_info.min_latency;
- }
+ if (result != (pa_usec_t) -1)
+ result = PA_CLAMP(result, s->thread_info.min_latency, s->thread_info.max_latency);
s->thread_info.requested_latency = result;
s->thread_info.requested_latency_valid = TRUE;
@@ -1100,7 +1126,7 @@ pa_usec_t pa_source_get_requested_latency_within_thread(pa_source *s) {
/* Called from main thread */
pa_usec_t pa_source_get_requested_latency(pa_source *s) {
- pa_usec_t usec;
+ pa_usec_t usec = 0;
pa_source_assert_ref(s);
pa_assert(PA_SOURCE_IS_LINKED(s->state));
@@ -1148,6 +1174,9 @@ void pa_source_invalidate_requested_latency(pa_source *s) {
pa_source_assert_ref(s);
+ if (!(s->flags & PA_SOURCE_DYNAMIC_LATENCY))
+ return;
+
s->thread_info.requested_latency_valid = FALSE;
if (PA_SOURCE_IS_LINKED(s->thread_info.state)) {
@@ -1217,7 +1246,7 @@ void pa_source_get_latency_range(pa_source *s, pa_usec_t *min_latency, pa_usec_t
}
}
-/* Called from IO thread, and from main thread before pa_sink_put() is called */
+/* Called from IO thread, and from main thread before pa_source_put() is called */
void pa_source_set_latency_range_within_thread(pa_source *s, pa_usec_t min_latency, pa_usec_t max_latency) {
void *state = NULL;
diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h
index 652783ef..b502c228 100644
--- a/src/pulsecore/source.h
+++ b/src/pulsecore/source.h
@@ -87,6 +87,8 @@ struct pa_source {
pa_memchunk silence;
+ pa_usec_t fixed_latency; /* for sources with PA_SOURCE_DYNAMIC_LATENCY this is 0 */
+
/* Called when the main loop requests a state change. Called from
* main loop context. If returns -1 the state change will be
* inhibited */
diff --git a/src/pulsecore/time-smoother.c b/src/pulsecore/time-smoother.c
index 55ac8687..9d5a0705 100644
--- a/src/pulsecore/time-smoother.c
+++ b/src/pulsecore/time-smoother.c
@@ -291,7 +291,8 @@ static void estimate(pa_smoother *s, pa_usec_t x, pa_usec_t *y, double *deriv) {
pa_assert(s);
pa_assert(y);
- if (!s->smoothing || x >= s->px) {
+ if (x >= s->px) {
+ /* Linear interpolation right from px */
int64_t t;
/* The requested point is right of the point where we wanted
@@ -307,7 +308,22 @@ static void estimate(pa_smoother *s, pa_usec_t x, pa_usec_t *y, double *deriv) {
if (deriv)
*deriv = s->dp;
+ } else if (x <= s->ex) {
+ /* Linear interpolation left from ex */
+ int64_t t;
+
+ t = (int64_t) s->ey - (int64_t) llrint(s->de * (double) (s->ex - x));
+
+ if (t < 0)
+ t = 0;
+
+ *y = (pa_usec_t) t;
+
+ if (deriv)
+ *deriv = s->de;
+
} else {
+ /* Spline interpolation between ex and px */
double tx, ty;
/* Ok, we're not yet on track, thus let's interpolate, and
@@ -381,7 +397,9 @@ void pa_smoother_put(pa_smoother *s, pa_usec_t x, pa_usec_t y) {
s->abc_valid = FALSE;
-/* pa_log_debug("put(%llu | %llu) = %llu", (unsigned long long) (x + s->time_offset), (unsigned long long) x, (unsigned long long) y); */
+#ifdef DEBUG_DATA
+ pa_log_debug("%p, put(%llu | %llu) = %llu", s, (unsigned long long) (x + s->time_offset), (unsigned long long) x, (unsigned long long) y);
+#endif
}
pa_usec_t pa_smoother_get(pa_smoother *s, pa_usec_t x) {
@@ -412,7 +430,9 @@ pa_usec_t pa_smoother_get(pa_smoother *s, pa_usec_t x) {
s->last_y = y;
}
-/* pa_log_debug("get(%llu | %llu) = %llu", (unsigned long long) (x + s->time_offset), (unsigned long long) x, (unsigned long long) y); */
+#ifdef DEBUG_DATA
+ pa_log_debug("%p, get(%llu | %llu) = %llu", s, (unsigned long long) (x + s->time_offset), (unsigned long long) x, (unsigned long long) y);
+#endif
return y;
}
@@ -422,7 +442,9 @@ void pa_smoother_set_time_offset(pa_smoother *s, pa_usec_t offset) {
s->time_offset = offset;
-/* pa_log_debug("offset(%llu)", (unsigned long long) offset); */
+#ifdef DEBUG_DATA
+ pa_log_debug("offset(%llu)", (unsigned long long) offset);
+#endif
}
void pa_smoother_pause(pa_smoother *s, pa_usec_t x) {
@@ -431,7 +453,9 @@ void pa_smoother_pause(pa_smoother *s, pa_usec_t x) {
if (s->paused)
return;
-/* pa_log_debug("pause(%llu)", (unsigned long long) x); */
+#ifdef DEBUG_DATA
+ pa_log_debug("pause(%llu)", (unsigned long long) x);
+#endif
s->paused = TRUE;
s->pause_time = x;
@@ -446,7 +470,9 @@ void pa_smoother_resume(pa_smoother *s, pa_usec_t x, pa_bool_t fix_now) {
if (x < s->pause_time)
x = s->pause_time;
-/* pa_log_debug("resume(%llu)", (unsigned long long) x); */
+#ifdef DEBUG_DATA
+ pa_log_debug("resume(%llu)", (unsigned long long) x);
+#endif
s->paused = FALSE;
s->time_offset += x - s->pause_time;
@@ -481,7 +507,9 @@ pa_usec_t pa_smoother_translate(pa_smoother *s, pa_usec_t x, pa_usec_t y_delay)
if (s->dp > nde)
nde = s->dp;
-/* pa_log_debug("translate(%llu) = %llu (%0.2f)", (unsigned long long) y_delay, (unsigned long long) ((double) y_delay / nde), nde); */
+#ifdef DEBUG_DATA
+ pa_log_debug("translate(%llu) = %llu (%0.2f)", (unsigned long long) y_delay, (unsigned long long) ((double) y_delay / nde), nde);
+#endif
return (pa_usec_t) llrint((double) y_delay / nde);
}
diff --git a/src/tests/interpol-test.c b/src/tests/interpol-test.c
index c103a493..0c906d3e 100644
--- a/src/tests/interpol-test.c
+++ b/src/tests/interpol-test.c
@@ -37,6 +37,7 @@
#include <pulsecore/thread.h>
#define INTERPOLATE
+//#define CORK
static pa_context *context = NULL;
static pa_stream *stream = NULL;
@@ -125,7 +126,9 @@ int main(int argc, char *argv[]) {
int k, r;
struct timeval start, last_info = { 0, 0 };
pa_usec_t old_t = 0, old_rtc = 0;
+#ifdef CORK
pa_bool_t corked = FALSE;
+#endif
pa_log_set_level(PA_LOG_DEBUG);
@@ -150,7 +153,12 @@ int main(int argc, char *argv[]) {
r = pa_threaded_mainloop_start(m);
assert(r >= 0);
- for (k = 0; k < 20000; k++) {
+/* #ifdef CORK */
+ for (k = 0; k < 20000; k++)
+/* #else */
+/* for (k = 0; k < 2000; k++) */
+/* #endif */
+ {
pa_bool_t success = FALSE, changed = FALSE;
pa_usec_t t, rtc;
struct timeval now, tv;
@@ -179,8 +187,9 @@ int main(int argc, char *argv[]) {
pa_gettimeofday(&now);
if (success) {
+#ifdef CORK
pa_bool_t cork_now;
-
+#endif
rtc = pa_timeval_diff(&now, &start);
printf("%i\t%llu\t%llu\t%llu\t%llu\t%lli\t%u\t%u\n", k,
(unsigned long long) rtc,
@@ -195,6 +204,7 @@ int main(int argc, char *argv[]) {
old_t = t;
old_rtc = rtc;
+#ifdef CORK
cork_now = (rtc / (2*PA_USEC_PER_SEC)) % 2 == 1;
if (corked != cork_now) {
@@ -206,6 +216,7 @@ int main(int argc, char *argv[]) {
corked = cork_now;
}
+#endif
}
/* Spin loop, ugly but normal usleep() is just too badly grained */
diff --git a/src/tests/voltest.c b/src/tests/voltest.c
index 0c6d2ea6..2dcfa53c 100644
--- a/src/tests/voltest.c
+++ b/src/tests/voltest.c
@@ -9,6 +9,9 @@ int main(int argc, char *argv[]) {
float b;
pa_channel_map map;
+ printf("Attenuation of sample 1 against 32767: %g dB\n", 20.0*log10(1.0/32767.0));
+ printf("Smallest possible attenutation > 0 applied to 32767: %li\n", lrint(32767.0*pa_sw_volume_to_linear(1)));
+
for (v = PA_VOLUME_MUTED; v <= PA_VOLUME_NORM*2; v += 256) {
double dB = pa_sw_volume_to_dB(v);