mirror of
https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
synced 2025-11-08 13:29:59 -05:00
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:
commit
bcaba0b1b4
134 changed files with 15076 additions and 12812 deletions
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue