Merge branch 'master' of git://0pointer.de/pulseaudio into dbus-work

Conflicts:
	src/Makefile.am
	src/daemon/daemon-conf.c
	src/daemon/daemon.conf.in
	src/modules/module-stream-restore.c
	src/pulse/client-conf.c
	src/pulsecore/namereg.c
This commit is contained in:
Tanu Kaskinen 2009-08-16 21:25:48 +03:00
commit bcaba0b1b4
134 changed files with 15076 additions and 12812 deletions

View file

@ -94,17 +94,18 @@ int pa_client_conf_load(pa_client_conf *c, const char *filename) {
/* Prepare the configuration parse table */
pa_config_item table[] = {
{ "daemon-binary", pa_config_parse_string, &c->daemon_binary, NULL },
{ "extra-arguments", pa_config_parse_string, &c->extra_arguments, NULL },
{ "default-sink", pa_config_parse_string, &c->default_sink, NULL },
{ "default-source", pa_config_parse_string, &c->default_source, NULL },
{ "default-server", pa_config_parse_string, &c->default_server, NULL },
{ "default-dbus-server", pa_config_parse_string, &c->default_dbus_server, NULL },
{ "autospawn", pa_config_parse_bool, &c->autospawn, NULL },
{ "cookie-file", pa_config_parse_string, &c->cookie_file, NULL },
{ "disable-shm", pa_config_parse_bool, &c->disable_shm, NULL },
{ "shm-size-bytes", pa_config_parse_size, &c->shm_size, NULL },
{ NULL, NULL, NULL, NULL },
{ "daemon-binary", pa_config_parse_string, &c->daemon_binary, NULL },
{ "extra-arguments", pa_config_parse_string, &c->extra_arguments, NULL },
{ "default-sink", pa_config_parse_string, &c->default_sink, NULL },
{ "default-source", pa_config_parse_string, &c->default_source, NULL },
{ "default-server", pa_config_parse_string, &c->default_server, NULL },
{ "default-dbus-server", pa_config_parse_string, &c->default_dbus_server, NULL },
{ "autospawn", pa_config_parse_bool, &c->autospawn, NULL },
{ "cookie-file", pa_config_parse_string, &c->cookie_file, NULL },
{ "disable-shm", pa_config_parse_bool, &c->disable_shm, NULL },
{ "enable-shm", pa_config_parse_not_bool, &c->disable_shm, NULL },
{ "shm-size-bytes", pa_config_parse_size, &c->shm_size, NULL },
{ NULL, NULL, NULL, NULL },
};
if (filename) {

View file

@ -30,5 +30,5 @@
; cookie-file =
; disable-shm = no
; enable-shm = yes
; shm-size-bytes = 0 # setting this 0 will use the system-default, usually 64 MiB

View file

@ -668,11 +668,24 @@ static pa_strlist *prepend_per_user(pa_strlist *l) {
static int context_autospawn(pa_context *c) {
pid_t pid;
int status, r;
pa_log_debug("Trying to autospawn...");
struct sigaction sa;
pa_context_ref(c);
if (sigaction(SIGCHLD, NULL, &sa) < 0) {
pa_log_debug("sigaction() failed: %s", pa_cstrerror(errno));
pa_context_fail(c, PA_ERR_INTERNAL);
goto fail;
}
if ((sa.sa_flags & SA_NOCLDWAIT) || sa.sa_handler == SIG_IGN) {
pa_log_debug("Process disabled waitpid(), cannot autospawn.");
pa_context_fail(c, PA_ERR_CONNECTIONREFUSED);
goto fail;
}
pa_log_debug("Trying to autospawn...");
if (c->spawn_api.prefork)
c->spawn_api.prefork();
@ -688,23 +701,23 @@ static int context_autospawn(pa_context *c) {
/* Child */
const char *state = NULL;
#define MAX_ARGS 64
const char * argv[MAX_ARGS+1];
int n;
const char * argv[32];
unsigned n = 0;
if (c->spawn_api.atfork)
c->spawn_api.atfork();
/* We leave most of the cleaning up of the process environment
* to the executable. We only clean up the file descriptors to
* make sure the executable can actually be loaded
* correctly. */
pa_close_all(-1);
/* Setup argv */
n = 0;
argv[n++] = c->conf->daemon_binary;
argv[n++] = "--start";
while (n < MAX_ARGS) {
while (n < PA_ELEMENTSOF(argv)-1) {
char *a;
if (!(a = pa_split_spaces(c->conf->extra_arguments, &state)))
@ -714,10 +727,10 @@ static int context_autospawn(pa_context *c) {
}
argv[n++] = NULL;
pa_assert(n <= PA_ELEMENTSOF(argv));
execv(argv[0], (char * const *) argv);
_exit(1);
#undef MAX_ARGS
}
/* Parent */
@ -730,9 +743,16 @@ static int context_autospawn(pa_context *c) {
} while (r < 0 && errno == EINTR);
if (r < 0) {
pa_log(_("waitpid(): %s"), pa_cstrerror(errno));
pa_context_fail(c, PA_ERR_INTERNAL);
goto fail;
if (errno != ESRCH) {
pa_log(_("waitpid(): %s"), pa_cstrerror(errno));
pa_context_fail(c, PA_ERR_INTERNAL);
goto fail;
}
/* hmm, something already reaped our child, so we assume
* startup worked, even if we cannot know */
} else if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
pa_context_fail(c, PA_ERR_CONNECTIONREFUSED);
goto fail;
@ -761,22 +781,33 @@ static void track_pulseaudio_on_dbus(pa_context *c, DBusBusType type, pa_dbus_wr
pa_assert(conn);
dbus_error_init(&error);
if (!(*conn = pa_dbus_wrap_connection_new(c->mainloop, c->use_rtclock, type, &error)) || dbus_error_is_set(&error)) {
pa_log_warn("Unable to contact DBUS: %s: %s", error.name, error.message);
goto finish;
goto fail;
}
if (!dbus_connection_add_filter(pa_dbus_wrap_connection_get(*conn), filter_cb, c, NULL)) {
pa_log_warn("Failed to add filter function");
goto finish;
goto fail;
}
if (pa_dbus_add_matches(
pa_dbus_wrap_connection_get(*conn), &error,
"type='signal',sender='" DBUS_SERVICE_DBUS "',interface='" DBUS_INTERFACE_DBUS "',member='NameOwnerChanged',arg0='org.pulseaudio.Server',arg1=''", NULL) < 0)
pa_log_warn("Unable to track org.pulseaudio.Server: %s: %s", error.name, error.message);
"type='signal',sender='" DBUS_SERVICE_DBUS "',interface='" DBUS_INTERFACE_DBUS "',member='NameOwnerChanged',arg0='org.pulseaudio.Server',arg1=''", NULL) < 0) {
pa_log_warn("Unable to track org.pulseaudio.Server: %s: %s", error.name, error.message);
goto fail;
}
return;
fail:
if (*conn) {
pa_dbus_wrap_connection_free(*conn);
*conn = NULL;
}
finish:
dbus_error_free(&error);
}
#endif
@ -861,7 +892,7 @@ static void on_connection(pa_socket_client *client, pa_iochannel*io, void *userd
c->client = NULL;
if (!io) {
/* Try the item in the list */
/* Try the next item in the list */
if (saved_errno == ECONNREFUSED ||
saved_errno == ETIMEDOUT ||
saved_errno == EHOSTUNREACH) {
@ -897,7 +928,7 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, vo
/* FIXME: We probably should check if this is actually the NameOwnerChanged we were looking for */
is_session = c->session_bus && bus == pa_dbus_wrap_connection_get(c->session_bus);
pa_log_debug("Rock!! PulseAudio is back on %s bus", is_session ? "session" : "system");
pa_log_debug("Rock!! PulseAudio might be back on %s bus", is_session ? "session" : "system");
if (is_session)
/* The user instance via PF_LOCAL */
@ -937,7 +968,7 @@ int pa_context_connect(
pa_context_ref(c);
c->no_fail = flags & PA_CONTEXT_NOFAIL;
c->no_fail = !!(flags & PA_CONTEXT_NOFAIL);
c->server_specified = !!server;
pa_assert(!c->server_list);
@ -954,10 +985,7 @@ int pa_context_connect(
/* Follow the X display */
if ((d = getenv("DISPLAY"))) {
char *e;
d = pa_xstrdup(d);
if ((e = strchr(d, ':')))
*e = 0;
d = pa_xstrndup(d, strcspn(d, ":"));
if (*d)
c->server_list = pa_strlist_prepend(c->server_list, d);

View file

@ -151,6 +151,11 @@ struct pa_stream {
uint32_t device_index;
char *device_name;
/* playback */
pa_memblock *write_memblock;
void *write_data;
/* recording */
pa_memchunk peek_memchunk;
void *peek_data;
pa_memblockq *record_memblockq;

View file

@ -201,42 +201,44 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, u
goto finish;
}
if (i.n_ports > 0) {
i.ports = pa_xnew(pa_sink_port_info*, i.n_ports+1);
i.ports[0] = pa_xnew(pa_sink_port_info, i.n_ports);
if (o->context->version >= 16) {
if (i.n_ports > 0) {
i.ports = pa_xnew(pa_sink_port_info*, i.n_ports+1);
i.ports[0] = pa_xnew(pa_sink_port_info, i.n_ports);
for (j = 0; j < i.n_ports; j++) {
if (pa_tagstruct_gets(t, &i.ports[0][j].name) < 0 ||
pa_tagstruct_gets(t, &i.ports[0][j].description) < 0 ||
pa_tagstruct_getu32(t, &i.ports[0][j].priority) < 0) {
for (j = 0; j < i.n_ports; j++) {
if (pa_tagstruct_gets(t, &i.ports[0][j].name) < 0 ||
pa_tagstruct_gets(t, &i.ports[0][j].description) < 0 ||
pa_tagstruct_getu32(t, &i.ports[0][j].priority) < 0) {
pa_context_fail(o->context, PA_ERR_PROTOCOL);
pa_xfree(i.ports);
pa_xfree(i.ports[0]);
pa_proplist_free(i.proplist);
goto finish;
pa_context_fail(o->context, PA_ERR_PROTOCOL);
pa_xfree(i.ports[0]);
pa_xfree(i.ports);
pa_proplist_free(i.proplist);
goto finish;
}
i.ports[j] = &i.ports[0][j];
}
i.ports[j] = &i.ports[0][j];
i.ports[j] = NULL;
}
i.ports[j] = NULL;
}
if (pa_tagstruct_gets(t, &ap) < 0) {
pa_context_fail(o->context, PA_ERR_PROTOCOL);
pa_xfree(i.ports[0]);
pa_xfree(i.ports);
pa_proplist_free(i.proplist);
goto finish;
}
if (pa_tagstruct_gets(t, &ap) < 0) {
pa_context_fail(o->context, PA_ERR_PROTOCOL);
pa_xfree(i.ports[0]);
pa_xfree(i.ports);
pa_proplist_free(i.proplist);
goto finish;
}
if (ap) {
for (j = 0; j < i.n_ports; j++)
if (pa_streq(i.ports[j]->name, ap)) {
i.active_port = i.ports[j];
break;
}
if (ap) {
for (j = 0; j < i.n_ports; j++)
if (pa_streq(i.ports[j]->name, ap)) {
i.active_port = i.ports[j];
break;
}
}
}
i.mute = (int) mute;
@ -248,6 +250,10 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, u
cb(o->context, &i, 0, o->userdata);
}
if (i.ports) {
pa_xfree(i.ports[0]);
pa_xfree(i.ports);
}
pa_proplist_free(i.proplist);
}
}
@ -428,42 +434,44 @@ static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command,
goto finish;
}
if (i.n_ports > 0) {
i.ports = pa_xnew(pa_source_port_info*, i.n_ports+1);
i.ports[0] = pa_xnew(pa_source_port_info, i.n_ports);
if (o->context->version >= 16) {
if (i.n_ports > 0) {
i.ports = pa_xnew(pa_source_port_info*, i.n_ports+1);
i.ports[0] = pa_xnew(pa_source_port_info, i.n_ports);
for (j = 0; j < i.n_ports; j++) {
if (pa_tagstruct_gets(t, &i.ports[0][j].name) < 0 ||
pa_tagstruct_gets(t, &i.ports[0][j].description) < 0 ||
pa_tagstruct_getu32(t, &i.ports[0][j].priority) < 0) {
for (j = 0; j < i.n_ports; j++) {
if (pa_tagstruct_gets(t, &i.ports[0][j].name) < 0 ||
pa_tagstruct_gets(t, &i.ports[0][j].description) < 0 ||
pa_tagstruct_getu32(t, &i.ports[0][j].priority) < 0) {
pa_context_fail(o->context, PA_ERR_PROTOCOL);
pa_xfree(i.ports[0]);
pa_xfree(i.ports);
pa_proplist_free(i.proplist);
goto finish;
pa_context_fail(o->context, PA_ERR_PROTOCOL);
pa_xfree(i.ports[0]);
pa_xfree(i.ports);
pa_proplist_free(i.proplist);
goto finish;
}
i.ports[j] = &i.ports[0][j];
}
i.ports[j] = &i.ports[0][j];
i.ports[j] = NULL;
}
i.ports[j] = NULL;
}
if (pa_tagstruct_gets(t, &ap) < 0) {
pa_context_fail(o->context, PA_ERR_PROTOCOL);
pa_xfree(i.ports[0]);
pa_xfree(i.ports);
pa_proplist_free(i.proplist);
goto finish;
}
if (pa_tagstruct_gets(t, &ap) < 0) {
pa_context_fail(o->context, PA_ERR_PROTOCOL);
pa_xfree(i.ports[0]);
pa_xfree(i.ports);
pa_proplist_free(i.proplist);
goto finish;
}
if (ap) {
for (j = 0; j < i.n_ports; j++)
if (pa_streq(i.ports[j]->name, ap)) {
i.active_port = i.ports[j];
break;
}
if (ap) {
for (j = 0; j < i.n_ports; j++)
if (pa_streq(i.ports[j]->name, ap)) {
i.active_port = i.ports[j];
break;
}
}
}
i.mute = (int) mute;
@ -475,6 +483,10 @@ static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command,
cb(o->context, &i, 0, o->userdata);
}
if (i.ports) {
pa_xfree(i.ports[0]);
pa_xfree(i.ports);
}
pa_proplist_free(i.proplist);
}
}

View file

@ -765,23 +765,22 @@ static pa_time_event* find_next_time_event(pa_mainloop *m) {
static int calc_next_timeout(pa_mainloop *m) {
pa_time_event *t;
pa_usec_t usec;
pa_usec_t clock_now;
if (!m->n_enabled_time_events)
return -1;
t = find_next_time_event(m);
pa_assert(t);
pa_assert_se(t = find_next_time_event(m));
if (t->time == 0)
if (t->time <= 0)
return 0;
usec = t->time - pa_rtclock_now();
clock_now = pa_rtclock_now();
if (usec <= 0)
if (t->time <= clock_now)
return 0;
return (int) (usec / 1000); /* in milliseconds */
return (int) ((t->time - clock_now) / 1000); /* in milliseconds */
}
static int dispatch_timeout(pa_mainloop *m) {

View file

@ -40,7 +40,11 @@ pa_operation *pa_operation_ref(pa_operation *o);
/** Decrease the reference count by one */
void pa_operation_unref(pa_operation *o);
/** Cancel the operation. Beware! This will not necessarily cancel the execution of the operation on the server side. */
/** Cancel the operation. Beware! This will not necessarily cancel the
* execution of the operation on the server side. However it will make
* sure that the callback associated with this operation will not be
* called anymore, effectively disabling the operation from the client
* side's view. */
void pa_operation_cancel(pa_operation *o);
/** Return the current status of the operation */

View file

@ -70,8 +70,8 @@ struct pa_simple {
#define CHECK_DEAD_GOTO(p, rerror, label) \
do { \
if (!(p)->context || pa_context_get_state((p)->context) != PA_CONTEXT_READY || \
!(p)->stream || pa_stream_get_state((p)->stream) != PA_STREAM_READY) { \
if (!(p)->context || !PA_CONTEXT_IS_GOOD(pa_context_get_state((p)->context)) || \
!(p)->stream || !PA_STREAM_IS_GOOD(pa_stream_get_state((p)->stream))) { \
if (((p)->context && pa_context_get_state((p)->context) == PA_CONTEXT_FAILED) || \
((p)->stream && pa_stream_get_state((p)->stream) == PA_STREAM_FAILED)) { \
if (rerror) \
@ -157,12 +157,8 @@ pa_simple* pa_simple_new(
CHECK_VALIDITY_RETURN_ANY(rerror, ss && pa_sample_spec_valid(ss), PA_ERR_INVALID, NULL);
CHECK_VALIDITY_RETURN_ANY(rerror, !map || (pa_channel_map_valid(map) && map->channels == ss->channels), PA_ERR_INVALID, NULL)
p = pa_xnew(pa_simple, 1);
p->context = NULL;
p->stream = NULL;
p = pa_xnew0(pa_simple, 1);
p->direction = dir;
p->read_data = NULL;
p->read_index = p->read_length = 0;
if (!(p->mainloop = pa_threaded_mainloop_new()))
goto fail;
@ -182,12 +178,21 @@ pa_simple* pa_simple_new(
if (pa_threaded_mainloop_start(p->mainloop) < 0)
goto unlock_and_fail;
/* Wait until the context is ready */
pa_threaded_mainloop_wait(p->mainloop);
for (;;) {
pa_context_state_t state;
if (pa_context_get_state(p->context) != PA_CONTEXT_READY) {
error = pa_context_errno(p->context);
goto unlock_and_fail;
state = pa_context_get_state(p->context);
if (state == PA_CONTEXT_READY)
break;
if (!PA_CONTEXT_IS_GOOD(state)) {
error = pa_context_errno(p->context);
goto unlock_and_fail;
}
/* Wait until the context is ready */
pa_threaded_mainloop_wait(p->mainloop);
}
if (!(p->stream = pa_stream_new(p->context, stream_name, ss, map))) {
@ -216,13 +221,21 @@ pa_simple* pa_simple_new(
goto unlock_and_fail;
}
/* Wait until the stream is ready */
pa_threaded_mainloop_wait(p->mainloop);
for (;;) {
pa_stream_state_t state;
/* Wait until the stream is ready */
if (pa_stream_get_state(p->stream) != PA_STREAM_READY) {
error = pa_context_errno(p->context);
goto unlock_and_fail;
state = pa_stream_get_state(p->stream);
if (state == PA_STREAM_READY)
break;
if (!PA_STREAM_IS_GOOD(state)) {
error = pa_context_errno(p->context);
goto unlock_and_fail;
}
/* Wait until the stream is ready */
pa_threaded_mainloop_wait(p->mainloop);
}
pa_threaded_mainloop_unlock(p->mainloop);
@ -248,8 +261,10 @@ void pa_simple_free(pa_simple *s) {
if (s->stream)
pa_stream_unref(s->stream);
if (s->context)
if (s->context) {
pa_context_disconnect(s->context);
pa_context_unref(s->context);
}
if (s->mainloop)
pa_threaded_mainloop_free(s->mainloop);
@ -261,7 +276,8 @@ int pa_simple_write(pa_simple *p, const void*data, size_t length, int *rerror) {
pa_assert(p);
CHECK_VALIDITY_RETURN_ANY(rerror, p->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE, -1);
CHECK_VALIDITY_RETURN_ANY(rerror, data && length, PA_ERR_INVALID, -1);
CHECK_VALIDITY_RETURN_ANY(rerror, data, PA_ERR_INVALID, -1);
CHECK_VALIDITY_RETURN_ANY(rerror, length > 0, PA_ERR_INVALID, -1);
pa_threaded_mainloop_lock(p->mainloop);
@ -300,7 +316,8 @@ int pa_simple_read(pa_simple *p, void*data, size_t length, int *rerror) {
pa_assert(p);
CHECK_VALIDITY_RETURN_ANY(rerror, p->direction == PA_STREAM_RECORD, PA_ERR_BADSTATE, -1);
CHECK_VALIDITY_RETURN_ANY(rerror, data && length, PA_ERR_INVALID, -1);
CHECK_VALIDITY_RETURN_ANY(rerror, data, PA_ERR_INVALID, -1);
CHECK_VALIDITY_RETURN_ANY(rerror, length > 0, PA_ERR_INVALID, -1);
pa_threaded_mainloop_lock(p->mainloop);
@ -375,7 +392,7 @@ int pa_simple_drain(pa_simple *p, int *rerror) {
CHECK_SUCCESS_GOTO(p, rerror, o, unlock_and_fail);
p->operation_success = 0;
while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
while (pa_operation_get_state(o) == PA_OPERATION_RUNNING) {
pa_threaded_mainloop_wait(p->mainloop);
CHECK_DEAD_GOTO(p, rerror, unlock_and_fail);
}
@ -411,7 +428,7 @@ int pa_simple_flush(pa_simple *p, int *rerror) {
CHECK_SUCCESS_GOTO(p, rerror, o, unlock_and_fail);
p->operation_success = 0;
while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
while (pa_operation_get_state(o) == PA_OPERATION_RUNNING) {
pa_threaded_mainloop_wait(p->mainloop);
CHECK_DEAD_GOTO(p, rerror, unlock_and_fail);
}

View file

@ -144,12 +144,13 @@ pa_stream *pa_stream_new_with_proplist(
s->suspended = FALSE;
s->corked = FALSE;
s->write_memblock = NULL;
s->write_data = NULL;
pa_memchunk_reset(&s->peek_memchunk);
s->peek_data = NULL;
s->record_memblockq = NULL;
memset(&s->timing_info, 0, sizeof(s->timing_info));
s->timing_info_valid = FALSE;
@ -221,6 +222,11 @@ static void stream_free(pa_stream *s) {
stream_unlink(s);
if (s->write_memblock) {
pa_memblock_release(s->write_memblock);
pa_memblock_unref(s->write_data);
}
if (s->peek_memchunk.memblock) {
if (s->peek_data)
pa_memblock_release(s->peek_memchunk.memblock);
@ -821,7 +827,7 @@ static void create_stream_complete(pa_stream *s) {
if (s->flags & PA_STREAM_AUTO_TIMING_UPDATE) {
s->auto_timing_interval_usec = AUTO_TIMING_INTERVAL_START_USEC;
pa_assert(!s->auto_timing_update_event);
s->auto_timing_update_event = pa_context_rttime_new(s->context, pa_rtclock_now() + s->auto_timing_interval_usec, &auto_timing_update_callback, s);
s->auto_timing_update_event = pa_context_rttime_new(s->context, pa_rtclock_now() + s->auto_timing_interval_usec, &auto_timing_update_callback, s);
request_auto_timing_update(s, TRUE);
}
@ -861,7 +867,7 @@ static void automatic_buffer_attr(pa_stream *s, pa_buffer_attr *attr, const pa_s
void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
pa_stream *s = userdata;
uint32_t requested_bytes;
uint32_t requested_bytes = 0;
pa_assert(pd);
pa_assert(s);
@ -1166,7 +1172,7 @@ int pa_stream_connect_playback(
const char *dev,
const pa_buffer_attr *attr,
pa_stream_flags_t flags,
pa_cvolume *volume,
const pa_cvolume *volume,
pa_stream *sync_stream) {
pa_assert(s);
@ -1187,20 +1193,71 @@ int pa_stream_connect_record(
return create_stream(PA_STREAM_RECORD, s, dev, attr, flags, NULL, NULL);
}
int pa_stream_begin_write(
pa_stream *s,
void **data,
size_t *nbytes) {
pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1);
PA_CHECK_VALIDITY(s->context, !pa_detect_fork(), PA_ERR_FORKED);
PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_PLAYBACK || s->direction == PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY(s->context, data, PA_ERR_INVALID);
PA_CHECK_VALIDITY(s->context, nbytes && *nbytes != 0, PA_ERR_INVALID);
if (*nbytes != (size_t) -1) {
size_t m, fs;
m = pa_mempool_block_size_max(s->context->mempool);
fs = pa_frame_size(&s->sample_spec);
m = (m / fs) * fs;
if (*nbytes > m)
*nbytes = m;
}
if (!s->write_memblock) {
s->write_memblock = pa_memblock_new(s->context->mempool, *nbytes);
s->write_data = pa_memblock_acquire(s->write_memblock);
}
*data = s->write_data;
*nbytes = pa_memblock_get_length(s->write_memblock);
return 0;
}
int pa_stream_cancel_write(
pa_stream *s) {
pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1);
PA_CHECK_VALIDITY(s->context, !pa_detect_fork(), PA_ERR_FORKED);
PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_PLAYBACK || s->direction == PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY(s->context, s->write_memblock, PA_ERR_BADSTATE);
pa_assert(s->write_data);
pa_memblock_release(s->write_memblock);
pa_memblock_unref(s->write_memblock);
s->write_memblock = NULL;
s->write_data = NULL;
return 0;
}
int pa_stream_write(
pa_stream *s,
const void *data,
size_t length,
void (*free_cb)(void *p),
pa_free_cb_t free_cb,
int64_t offset,
pa_seek_mode_t seek) {
pa_memchunk chunk;
pa_seek_mode_t t_seek;
int64_t t_offset;
size_t t_length;
const void *t_data;
pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1);
pa_assert(data);
@ -1210,47 +1267,72 @@ int pa_stream_write(
PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_PLAYBACK || s->direction == PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY(s->context, seek <= PA_SEEK_RELATIVE_END, PA_ERR_INVALID);
PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_PLAYBACK || (seek == PA_SEEK_RELATIVE && offset == 0), PA_ERR_INVALID);
PA_CHECK_VALIDITY(s->context,
!s->write_memblock ||
((data >= s->write_data) &&
((const char*) data + length <= (const char*) s->write_data + pa_memblock_get_length(s->write_memblock))),
PA_ERR_INVALID);
PA_CHECK_VALIDITY(s->context, !free_cb || !s->write_memblock, PA_ERR_INVALID);
if (length <= 0)
return 0;
if (s->write_memblock) {
pa_memchunk chunk;
t_seek = seek;
t_offset = offset;
t_length = length;
t_data = data;
/* pa_stream_write_begin() was called before */
while (t_length > 0) {
pa_memblock_release(s->write_memblock);
chunk.index = 0;
chunk.memblock = s->write_memblock;
chunk.index = (const char *) data - (const char *) s->write_data;
chunk.length = length;
if (free_cb && !pa_pstream_get_shm(s->context->pstream)) {
chunk.memblock = pa_memblock_new_user(s->context->mempool, (void*) t_data, t_length, free_cb, 1);
chunk.length = t_length;
} else {
void *d;
s->write_memblock = NULL;
s->write_data = NULL;
chunk.length = PA_MIN(t_length, pa_mempool_block_size_max(s->context->mempool));
chunk.memblock = pa_memblock_new(s->context->mempool, chunk.length);
pa_pstream_send_memblock(s->context->pstream, s->channel, offset, seek, &chunk);
pa_memblock_unref(chunk.memblock);
d = pa_memblock_acquire(chunk.memblock);
memcpy(d, t_data, chunk.length);
pa_memblock_release(chunk.memblock);
} else {
pa_seek_mode_t t_seek = seek;
int64_t t_offset = offset;
size_t t_length = length;
const void *t_data = data;
/* pa_stream_write_begin() was not called before */
while (t_length > 0) {
pa_memchunk chunk;
chunk.index = 0;
if (free_cb && !pa_pstream_get_shm(s->context->pstream)) {
chunk.memblock = pa_memblock_new_user(s->context->mempool, (void*) t_data, t_length, free_cb, 1);
chunk.length = t_length;
} else {
void *d;
chunk.length = PA_MIN(t_length, pa_mempool_block_size_max(s->context->mempool));
chunk.memblock = pa_memblock_new(s->context->mempool, chunk.length);
d = pa_memblock_acquire(chunk.memblock);
memcpy(d, t_data, chunk.length);
pa_memblock_release(chunk.memblock);
}
pa_pstream_send_memblock(s->context->pstream, s->channel, t_offset, t_seek, &chunk);
t_offset = 0;
t_seek = PA_SEEK_RELATIVE;
t_data = (const uint8_t*) t_data + chunk.length;
t_length -= chunk.length;
pa_memblock_unref(chunk.memblock);
}
pa_pstream_send_memblock(s->context->pstream, s->channel, t_offset, t_seek, &chunk);
t_offset = 0;
t_seek = PA_SEEK_RELATIVE;
t_data = (const uint8_t*) t_data + chunk.length;
t_length -= chunk.length;
pa_memblock_unref(chunk.memblock);
if (free_cb && pa_pstream_get_shm(s->context->pstream))
free_cb((void*) data);
}
if (free_cb && pa_pstream_get_shm(s->context->pstream))
free_cb((void*) data);
/* This is obviously wrong since we ignore the seeking index . But
* that's OK, the server side applies the same error */
s->requested_bytes -= (seek == PA_SEEK_RELATIVE ? offset : 0) + (int64_t) length;

View file

@ -405,7 +405,7 @@ int pa_stream_connect_playback(
const char *dev /**< Name of the sink to connect to, or NULL for default */ ,
const pa_buffer_attr *attr /**< Buffering attributes, or NULL for default */,
pa_stream_flags_t flags /**< Additional flags, or 0 for default */,
pa_cvolume *volume /**< Initial volume, or NULL for default */,
const pa_cvolume *volume /**< Initial volume, or NULL for default */,
pa_stream *sync_stream /**< Synchronize this stream with the specified one, or NULL for a standalone stream*/);
/** Connect the stream to a source */
@ -418,15 +418,71 @@ int pa_stream_connect_record(
/** Disconnect a stream from a source/sink */
int pa_stream_disconnect(pa_stream *s);
/** Write some data to the server (for playback sinks), if free_cb is
* non-NULL this routine is called when all data has been written out
* and an internal reference to the specified data is kept, the data
* is not copied. If NULL, the data is copied into an internal
* buffer. The client my freely seek around in the output buffer. For
/** Prepare writing data to the server (for playback streams). This
* function may be used to optimize the number of memory copies when
* doing playback ("zero-copy"). It is recommended to call this
* function before each call to pa_stream_write(). Pass in the address
* to a pointer and an address of the number of bytes you want to
* write. On return the two values will contain a pointer where you
* can place the data to write and the maximum number of bytes you can
* write. On return *nbytes can be smaller or have the same value as
* you passed in. You need to be able to handle both cases. Accessing
* memory beyond the returned *nbytes value is invalid. Acessing the
* memory returned after the following pa_stream_write() or
* pa_stream_cancel_write() is invalid. On invocation only *nbytes
* needs to be initialized, on return both *data and *nbytes will be
* valid. If you place (size_t) -1 in *nbytes on invocation the memory
* size will be chosen automatically (which is recommended to
* do). After placing your data in the memory area returned call
* pa_stream_write() with data set to an address within this memory
* area and an nbytes value that is smaller or equal to what was
* returned by this function to actually execute the write. An
* invocation of pa_stream_write() should follow "quickly" on
* pa_stream_begin_write(). It is not recommended letting an unbounded
* amount of time pass after calling pa_stream_begin_write() and
* before calling pa_stream_write(). If you want to cancel a
* previously called pa_stream_begin_write() without calling
* pa_stream_write() use pa_stream_cancel_write(). Calling
* pa_stream_begin_write() twice without calling pa_stream_write() or
* pa_stream_cancel_write() in between will return exactly the same
* pointer/nbytes values.\since 0.9.16 */
int pa_stream_begin_write(
pa_stream *p,
void **data,
size_t *nbytes);
/** Reverses the effect of pa_stream_begin_write() dropping all data
* that has already been placed in the memory area returned by
* pa_stream_begin_write(). Only valid to call if
* pa_stream_begin_write() was called before and neither
* pa_stream_cancel_write() nor pa_stream_write() have been called
* yet. Accessing the memory previously returned by
* pa_stream_begin_write() after this call is invalid. Any further
* explicit freeing of the memory area is not necessary. \since
* 0.9.16 */
int pa_stream_cancel_write(
pa_stream *p);
/** Write some data to the server (for playback streams), if free_cb
* is non-NULL this routine is called when all data has been written
* out and an internal reference to the specified data is kept, the
* data is not copied. If NULL, the data is copied into an internal
* buffer. The client may 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. Afte ther write call succeeded
* the write index will be a the position after where this chunk of
* data has been written to. */
* data has been written to.
*
* As an optimization for avoiding needless memory copies you may call
* pa_stream_begin_write() before this call and then place your audio
* data directly in the memory area returned by that call. Then, pass
* a pointer to that memory area to pa_stream_write(). After the
* invocation of pa_stream_write() the memory area may no longer be
* accessed. Any further explicit freeing of the memory area is not
* necessary. It is OK to write the memory area returned by
* pa_stream_begin_write() only partially with this call, skipping
* bytes both at the end and at the beginning of the reserved memory
* area.*/
int pa_stream_write(
pa_stream *p /**< The stream to use */,
const void *data /**< The data to write */,
@ -435,11 +491,12 @@ int pa_stream_write(
int64_t offset, /**< Offset for seeking, must be 0 for upload streams */
pa_seek_mode_t seek /**< Seek mode, must be PA_SEEK_RELATIVE for upload streams */);
/** Read the next fragment from the buffer (for recording).
* data will point to the actual data and length will contain the size
* of the data in bytes (which can be less than a complete framgnet).
* Use pa_stream_drop() to actually remove the data from the
* buffer. If no data is available will return a NULL pointer */
/** Read the next fragment from the buffer (for recording streams).
* data will point to the actual data and nbytes will contain the size
* of the data in bytes (which can be less or more than a complete
* fragment). Use pa_stream_drop() to actually remove the data from
* the buffer. If no data is available this will return a NULL
* pointer */
int pa_stream_peek(
pa_stream *p /**< The stream to use */,
const void **data /**< Pointer to pointer that will point to data */,
@ -455,7 +512,9 @@ size_t pa_stream_writable_size(pa_stream *p);
/** Return the number of bytes that may be read using pa_stream_peek()*/
size_t pa_stream_readable_size(pa_stream *p);
/** Drain a playback stream. Use this for notification when the buffer is empty */
/** Drain a playback stream. Use this for notification when the buffer
* is empty. Please note that only one drain operation per stream may
* be issued at a time. */
pa_operation* pa_stream_drain(pa_stream *s, pa_stream_success_cb_t cb, void *userdata);
/** Request a timing info structure update for a stream. Use

View file

@ -51,7 +51,7 @@
struct pa_threaded_mainloop {
pa_mainloop *real_mainloop;
int n_waiting;
int n_waiting, n_waiting_for_accept;
pa_thread* thread;
pa_mutex* mutex;
@ -190,8 +190,12 @@ void pa_threaded_mainloop_signal(pa_threaded_mainloop *m, int wait_for_accept) {
pa_cond_signal(m->cond, 1);
if (wait_for_accept && m->n_waiting > 0)
pa_cond_wait(m->accept_cond, m->mutex);
if (wait_for_accept) {
m->n_waiting_for_accept ++;
while (m->n_waiting_for_accept > 0)
pa_cond_wait(m->accept_cond, m->mutex);
}
}
void pa_threaded_mainloop_wait(pa_threaded_mainloop *m) {
@ -214,6 +218,9 @@ void pa_threaded_mainloop_accept(pa_threaded_mainloop *m) {
/* Make sure that this function is not called from the helper thread */
pa_assert(!m->thread || !pa_thread_is_running(m->thread) || !in_worker(m));
pa_assert(m->n_waiting_for_accept > 0);
m->n_waiting_for_accept --;
pa_cond_signal(m->accept_cond, 0);
}

View file

@ -137,15 +137,19 @@ PA_C_DECL_BEGIN
* The main function, my_drain_stream_func(), will wait for the callback to
* be called using pa_threaded_mainloop_wait().
*
* If your application is multi-threaded, then this waiting must be done
* inside a while loop. The reason for this is that multiple threads might be
* using pa_threaded_mainloop_wait() at the same time. Each thread must
* therefore verify that it was its callback that was invoked.
* If your application is multi-threaded, then this waiting must be
* done inside a while loop. The reason for this is that multiple
* threads might be using pa_threaded_mainloop_wait() at the same
* time. Each thread must therefore verify that it was its callback
* that was invoked. Also the underlying OS synchronization primitives
* are usually not free of spurious wake-ups, so a
* pa_threaded_mainloop_wait() must be called within a loop even if
* you have only one thread waiting.
*
* The callback, my_drain_callback(), indicates to the main function that it
* has been called using pa_threaded_mainloop_signal().
*
* As you can see, both pa_threaded_mainloop_wait() may only be called with
* As you can see, pa_threaded_mainloop_wait() may only be called with
* the lock held. The same thing is true for pa_threaded_mainloop_signal(),
* but as the lock is held before the callback is invoked, you do not have to
* deal with that.
@ -274,7 +278,9 @@ void pa_threaded_mainloop_unlock(pa_threaded_mainloop *m);
* inside the event loop thread. Prior to this call the event loop
* object needs to be locked using pa_threaded_mainloop_lock(). While
* waiting the lock will be released, immediately before returning it
* will be acquired again. */
* will be acquired again. This function may spuriously wake up even
* without _signal() being called. You need to make sure to handle
* that! */
void pa_threaded_mainloop_wait(pa_threaded_mainloop *m);
/** Signal all threads waiting for a signalling event in

View file

@ -205,9 +205,12 @@ pa_volume_t pa_sw_volume_from_linear(double v) {
*
* http://www.robotplanet.dk/audio/audio_gui_design/
* http://lists.linuxaudio.org/pipermail/linux-audio-dev/2009-May/thread.html#23151
*
* We make sure that the conversion to linear and back yields the
* same volume value! That's why we need the lround() below!
*/
return (pa_volume_t) (cbrt(v) * PA_VOLUME_NORM);
return (pa_volume_t) lround(cbrt(v) * PA_VOLUME_NORM);
}
double pa_sw_volume_to_linear(pa_volume_t v) {