big s/polyp/pulse/g

git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1033 fefdeb5f-60dc-0310-8127-8f9354f1896f
This commit is contained in:
Lennart Poettering 2006-06-19 21:53:48 +00:00
parent dd21f11ded
commit f44ba09265
303 changed files with 2724 additions and 2719 deletions

1
src/pulsecore/Makefile Symbolic link
View file

@ -0,0 +1 @@
../pulse/Makefile

View file

@ -0,0 +1,89 @@
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <assert.h>
#include <string.h>
#include <pulse/xmalloc.h>
#include <pulsecore/props.h>
#include <pulsecore/log.h>
#include "authkey-prop.h"
struct authkey_data {
int ref;
size_t length;
};
int pa_authkey_prop_get(pa_core *c, const char *name, void *data, size_t len) {
struct authkey_data *a;
assert(c && name && data && len > 0);
if (!(a = pa_property_get(c, name)))
return -1;
assert(a->length == len);
memcpy(data, a+1, len);
return 0;
}
int pa_authkey_prop_put(pa_core *c, const char *name, const void *data, size_t len) {
struct authkey_data *a;
assert(c && name);
if (pa_property_get(c, name))
return -1;
a = pa_xmalloc(sizeof(struct authkey_data) + len);
a->ref = 1;
a->length = len;
memcpy(a+1, data, len);
pa_property_set(c, name, a);
return 0;
}
void pa_authkey_prop_ref(pa_core *c, const char *name) {
struct authkey_data *a;
assert(c && name);
a = pa_property_get(c, name);
assert(a && a->ref >= 1);
a->ref++;
}
void pa_authkey_prop_unref(pa_core *c, const char *name) {
struct authkey_data *a;
assert(c && name);
a = pa_property_get(c, name);
assert(a && a->ref >= 1);
if (!(--a->ref)) {
pa_property_remove(c, name);
pa_xfree(a);
}
}

View file

@ -0,0 +1,43 @@
#ifndef fooauthkeyprophfoo
#define fooauthkeyprophfoo
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <pulsecore/core.h>
/* The authkey-prop uses a central property to store a previously
* loaded cookie in memory. Useful for sharing the same cookie between
* several modules. */
/* Return the data of the specified authorization key property. Doesn't alter the refernce count of the key */
int pa_authkey_prop_get(pa_core *c, const char *name, void *data, size_t len);
/* Store data in the specified authorization key property. The initial reference count is set to 1 */
int pa_authkey_prop_put(pa_core *c, const char *name, const void *data, size_t len);
/* Increase the reference count of the specified authorization key */
void pa_authkey_prop_ref(pa_core *c, const char *name);
/* Decrease the reference count of the specified authorization key */
void pa_authkey_prop_unref(pa_core *c, const char *name);
#endif

209
src/pulsecore/authkey.c Normal file
View file

@ -0,0 +1,209 @@
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <assert.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <inttypes.h>
#include <stdlib.h>
#include <time.h>
#include <limits.h>
#include <sys/stat.h>
#include <pulse/util.h>
#include <pulsecore/core-error.h>
#include <pulsecore/core-util.h>
#include <pulsecore/log.h>
#include <pulsecore/random.h>
#include "authkey.h"
/* Generate a new authorization key, store it in file fd and return it in *data */
static int generate(int fd, void *ret_data, size_t length) {
ssize_t r;
assert(fd >= 0 && ret_data && length);
pa_random(ret_data, length);
lseek(fd, 0, SEEK_SET);
ftruncate(fd, 0);
if ((r = pa_loop_write(fd, ret_data, length)) < 0 || (size_t) r != length) {
pa_log(__FILE__": failed to write cookie file: %s", pa_cstrerror(errno));
return -1;
}
return 0;
}
#ifndef O_BINARY
#define O_BINARY 0
#endif
/* Load an euthorization cookie from file fn and store it in data. If
* the cookie file doesn't exist, create it */
static int load(const char *fn, void *data, size_t length) {
int fd = -1;
int writable = 1;
int unlock = 0, ret = -1;
ssize_t r;
assert(fn && data && length);
if ((fd = open(fn, O_RDWR|O_CREAT|O_BINARY, S_IRUSR|S_IWUSR)) < 0) {
if (errno != EACCES || (fd = open(fn, O_RDONLY|O_BINARY)) < 0) {
pa_log(__FILE__": failed to open cookie file '%s': %s", fn, pa_cstrerror(errno));
goto finish;
} else
writable = 0;
}
unlock = pa_lock_fd(fd, 1) >= 0;
if ((r = pa_loop_read(fd, data, length)) < 0) {
pa_log(__FILE__": failed to read cookie file '%s': %s", fn, pa_cstrerror(errno));
goto finish;
}
if ((size_t) r != length) {
pa_log_debug(__FILE__": got %d bytes from cookie file '%s', expected %d", (int)r, fn, (int)length);
if (!writable) {
pa_log(__FILE__": unable to write cookie to read only file");
goto finish;
}
if (generate(fd, data, length) < 0)
goto finish;
}
ret = 0;
finish:
if (fd >= 0) {
if (unlock)
pa_lock_fd(fd, 0);
close(fd);
}
return ret;
}
/* Load a cookie from a cookie file. If the file doesn't exist, create it. */
int pa_authkey_load(const char *path, void *data, size_t length) {
int ret;
assert(path && data && length);
ret = load(path, data, length);
if (ret < 0)
pa_log(__FILE__": Failed to load authorization key '%s': %s", path,
(ret == -1) ? pa_cstrerror(errno) : "file corrupt");
return ret;
}
/* If the specified file path starts with / return it, otherwise
* return path prepended with home directory */
static const char *normalize_path(const char *fn, char *s, size_t l) {
assert(fn && s && l > 0);
#ifndef OS_IS_WIN32
if (fn[0] != '/') {
#else
if (strlen(fn) < 3 || !isalpha(fn[0]) || fn[1] != ':' || fn[2] != '\\') {
#endif
char homedir[PATH_MAX];
if (!pa_get_home_dir(homedir, sizeof(homedir)))
return NULL;
#ifndef OS_IS_WIN32
snprintf(s, l, "%s/%s", homedir, fn);
#else
snprintf(s, l, "%s\\%s", homedir, fn);
#endif
return s;
}
return fn;
}
/* Load a cookie from a file in the home directory. If the specified
* path starts with /, use it as absolute path instead. */
int pa_authkey_load_auto(const char *fn, void *data, size_t length) {
char path[PATH_MAX];
const char *p;
assert(fn && data && length);
if (!(p = normalize_path(fn, path, sizeof(path))))
return -2;
return pa_authkey_load(p, data, length);
}
/* Store the specified cookie in the speicified cookie file */
int pa_authkey_save(const char *fn, const void *data, size_t length) {
int fd = -1;
int unlock = 0, ret = -1;
ssize_t r;
char path[PATH_MAX];
const char *p;
assert(fn && data && length);
if (!(p = normalize_path(fn, path, sizeof(path))))
return -2;
if ((fd = open(p, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR)) < 0) {
pa_log(__FILE__": failed to open cookie file '%s': %s", fn, pa_cstrerror(errno));
goto finish;
}
unlock = pa_lock_fd(fd, 1) >= 0;
if ((r = pa_loop_write(fd, data, length)) < 0 || (size_t) r != length) {
pa_log(__FILE__": failed to read cookie file '%s': %s", fn, pa_cstrerror(errno));
goto finish;
}
ret = 0;
finish:
if (fd >= 0) {
if (unlock)
pa_lock_fd(fd, 0);
close(fd);
}
return ret;
}

32
src/pulsecore/authkey.h Normal file
View file

@ -0,0 +1,32 @@
#ifndef fooauthkeyhfoo
#define fooauthkeyhfoo
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <sys/types.h>
int pa_authkey_load(const char *path, void *data, size_t len);
int pa_authkey_load_auto(const char *fn, void *data, size_t length);
int pa_authkey_save(const char *path, const void *data, size_t length);
#endif

182
src/pulsecore/autoload.c Normal file
View file

@ -0,0 +1,182 @@
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <pulse/xmalloc.h>
#include <pulsecore/module.h>
#include <pulsecore/memchunk.h>
#include <pulsecore/sound-file.h>
#include <pulsecore/log.h>
#include <pulsecore/core-scache.h>
#include <pulsecore/core-subscribe.h>
#include "autoload.h"
static void entry_free(pa_autoload_entry *e) {
assert(e);
pa_subscription_post(e->core, PA_SUBSCRIPTION_EVENT_AUTOLOAD|PA_SUBSCRIPTION_EVENT_REMOVE, PA_INVALID_INDEX);
pa_xfree(e->name);
pa_xfree(e->module);
pa_xfree(e->argument);
pa_xfree(e);
}
static void entry_remove_and_free(pa_autoload_entry *e) {
assert(e && e->core);
pa_idxset_remove_by_data(e->core->autoload_idxset, e, NULL);
pa_hashmap_remove(e->core->autoload_hashmap, e->name);
entry_free(e);
}
static pa_autoload_entry* entry_new(pa_core *c, const char *name) {
pa_autoload_entry *e = NULL;
assert(c && name);
if (c->autoload_hashmap && (e = pa_hashmap_get(c->autoload_hashmap, name)))
return NULL;
e = pa_xmalloc(sizeof(pa_autoload_entry));
e->core = c;
e->name = pa_xstrdup(name);
e->module = e->argument = NULL;
e->in_action = 0;
if (!c->autoload_hashmap)
c->autoload_hashmap = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
assert(c->autoload_hashmap);
pa_hashmap_put(c->autoload_hashmap, e->name, e);
if (!c->autoload_idxset)
c->autoload_idxset = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
pa_idxset_put(c->autoload_idxset, e, &e->index);
pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_AUTOLOAD|PA_SUBSCRIPTION_EVENT_NEW, e->index);
return e;
}
int pa_autoload_add(pa_core *c, const char*name, pa_namereg_type_t type, const char*module, const char *argument, uint32_t *idx) {
pa_autoload_entry *e = NULL;
assert(c && name && module && (type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE));
if (!(e = entry_new(c, name)))
return -1;
e->module = pa_xstrdup(module);
e->argument = pa_xstrdup(argument);
e->type = type;
if (idx)
*idx = e->index;
return 0;
}
int pa_autoload_remove_by_name(pa_core *c, const char*name, pa_namereg_type_t type) {
pa_autoload_entry *e;
assert(c && name && type);
if (!c->autoload_hashmap || !(e = pa_hashmap_get(c->autoload_hashmap, name)) || e->type != type)
return -1;
entry_remove_and_free(e);
return 0;
}
int pa_autoload_remove_by_index(pa_core *c, uint32_t idx) {
pa_autoload_entry *e;
assert(c && idx != PA_IDXSET_INVALID);
if (!c->autoload_idxset || !(e = pa_idxset_get_by_index(c->autoload_idxset, idx)))
return -1;
entry_remove_and_free(e);
return 0;
}
void pa_autoload_request(pa_core *c, const char *name, pa_namereg_type_t type) {
pa_autoload_entry *e;
pa_module *m;
assert(c && name);
if (!c->autoload_hashmap || !(e = pa_hashmap_get(c->autoload_hashmap, name)) || (e->type != type))
return;
if (e->in_action)
return;
e->in_action = 1;
if (type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE) {
if ((m = pa_module_load(c, e->module, e->argument)))
m->auto_unload = 1;
}
e->in_action = 0;
}
static void free_func(void *p, PA_GCC_UNUSED void *userdata) {
pa_autoload_entry *e = p;
pa_idxset_remove_by_data(e->core->autoload_idxset, e, NULL);
entry_free(e);
}
void pa_autoload_free(pa_core *c) {
if (c->autoload_hashmap) {
pa_hashmap_free(c->autoload_hashmap, free_func, NULL);
c->autoload_hashmap = NULL;
}
if (c->autoload_idxset) {
pa_idxset_free(c->autoload_idxset, NULL, NULL);
c->autoload_idxset = NULL;
}
}
const pa_autoload_entry* pa_autoload_get_by_name(pa_core *c, const char*name, pa_namereg_type_t type) {
pa_autoload_entry *e;
assert(c && name);
if (!c->autoload_hashmap || !(e = pa_hashmap_get(c->autoload_hashmap, name)) || e->type != type)
return NULL;
return e;
}
const pa_autoload_entry* pa_autoload_get_by_index(pa_core *c, uint32_t idx) {
pa_autoload_entry *e;
assert(c && idx != PA_IDXSET_INVALID);
if (!c->autoload_idxset || !(e = pa_idxset_get_by_index(c->autoload_idxset, idx)))
return NULL;
return e;
}

58
src/pulsecore/autoload.h Normal file
View file

@ -0,0 +1,58 @@
#ifndef fooautoloadhfoo
#define fooautoloadhfoo
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <pulsecore/namereg.h>
/* Using the autoloading facility, modules by be loaded on-demand and
* synchronously. The user may register a "ghost sink" or "ghost
* source". Whenever this sink/source is requested but not available a
* specified module is loaded. */
/* An autoload entry, or "ghost" sink/source */
typedef struct pa_autoload_entry {
pa_core *core;
uint32_t index;
char *name;
pa_namereg_type_t type; /* Type of the autoload entry */
int in_action; /* Currently loaded */
char *module, *argument;
} pa_autoload_entry;
/* Add a new autoload entry of the given time, with the speicified
* sink/source name, module name and argument. Return the entry's
* index in *index */
int pa_autoload_add(pa_core *c, const char*name, pa_namereg_type_t type, const char*module, const char *argument, uint32_t *idx);
/* Free all autoload entries */
void pa_autoload_free(pa_core *c);
int pa_autoload_remove_by_name(pa_core *c, const char*name, pa_namereg_type_t type);
int pa_autoload_remove_by_index(pa_core *c, uint32_t idx);
/* Request an autoload entry by its name, effectively causing a module to be loaded */
void pa_autoload_request(pa_core *c, const char *name, pa_namereg_type_t type);
const pa_autoload_entry* pa_autoload_get_by_name(pa_core *c, const char*name, pa_namereg_type_t type);
const pa_autoload_entry* pa_autoload_get_by_index(pa_core *c, uint32_t idx);
#endif

947
src/pulsecore/cli-command.c Normal file
View file

@ -0,0 +1,947 @@
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <stdlib.h>
#include <errno.h>
#include <pulse/xmalloc.h>
#include <pulsecore/module.h>
#include <pulsecore/sink.h>
#include <pulsecore/source.h>
#include <pulsecore/client.h>
#include <pulsecore/sink-input.h>
#include <pulsecore/source-output.h>
#include <pulsecore/tokenizer.h>
#include <pulsecore/strbuf.h>
#include <pulsecore/namereg.h>
#include <pulsecore/cli-text.h>
#include <pulsecore/core-scache.h>
#include <pulsecore/sample-util.h>
#include <pulsecore/sound-file.h>
#include <pulsecore/play-memchunk.h>
#include <pulsecore/autoload.h>
#include <pulsecore/sound-file-stream.h>
#include <pulsecore/props.h>
#include <pulsecore/core-util.h>
#include <pulsecore/core-error.h>
#include "cli-command.h"
struct command {
const char *name;
int (*proc) (pa_core *c, pa_tokenizer*t, pa_strbuf *buf, int *fail);
const char *help;
unsigned args;
};
#define INCLUDE_META ".include"
#define FAIL_META ".fail"
#define NOFAIL_META ".nofail"
/* Prototypes for all available commands */
static int pa_cli_command_exit(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_help(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_modules(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_clients(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_sinks(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_sources(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_sink_inputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_source_outputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_info(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_unload(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_source_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_sink_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_source_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_sink_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_source_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_kill_client(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_kill_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_kill_source_output(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_scache_play(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_scache_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_scache_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_scache_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_scache_load_dir(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_play_file(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_autoload_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_autoload_add(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_autoload_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
static int pa_cli_command_list_props(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail);
/* A method table for all available commands */
static const struct command commands[] = {
{ "exit", pa_cli_command_exit, "Terminate the daemon", 1 },
{ "help", pa_cli_command_help, "Show this help", 1 },
{ "list-modules", pa_cli_command_modules, "List loaded modules", 1 },
{ "list-sinks", pa_cli_command_sinks, "List loaded sinks", 1 },
{ "list-sources", pa_cli_command_sources, "List loaded sources", 1 },
{ "list-clients", pa_cli_command_clients, "List loaded clients", 1 },
{ "list-sink-inputs", pa_cli_command_sink_inputs, "List sink inputs", 1 },
{ "list-source-outputs", pa_cli_command_source_outputs, "List source outputs", 1 },
{ "stat", pa_cli_command_stat, "Show memory block statistics", 1 },
{ "info", pa_cli_command_info, "Show comprehensive status", 1 },
{ "ls", pa_cli_command_info, NULL, 1 },
{ "list", pa_cli_command_info, NULL, 1 },
{ "load-module", pa_cli_command_load, "Load a module (args: name, arguments)", 3},
{ "unload-module", pa_cli_command_unload, "Unload a module (args: index)", 2},
{ "set-sink-volume", pa_cli_command_sink_volume, "Set the volume of a sink (args: index|name, volume)", 3},
{ "set-sink-input-volume", pa_cli_command_sink_input_volume, "Set the volume of a sink input (args: index|name, volume)", 3},
{ "set-source-volume", pa_cli_command_source_volume, "Set the volume of a source (args: index|name, volume)", 3},
{ "set-sink-mute", pa_cli_command_sink_mute, "Set the mute switch of a sink (args: index|name, mute)", 3},
{ "set-source-mute", pa_cli_command_source_mute, "Set the mute switch of a source (args: index|name, mute)", 3},
{ "set-default-sink", pa_cli_command_sink_default, "Set the default sink (args: index|name)", 2},
{ "set-default-source", pa_cli_command_source_default, "Set the default source (args: index|name)", 2},
{ "kill-client", pa_cli_command_kill_client, "Kill a client (args: index)", 2},
{ "kill-sink-input", pa_cli_command_kill_sink_input, "Kill a sink input (args: index)", 2},
{ "kill-source-output", pa_cli_command_kill_source_output, "Kill a source output (args: index)", 2},
{ "list-samples", pa_cli_command_scache_list, "List all entries in the sample cache", 1},
{ "play-sample", pa_cli_command_scache_play, "Play a sample from the sample cache (args: name, sink|index)", 3},
{ "remove-sample", pa_cli_command_scache_remove, "Remove a sample from the sample cache (args: name)", 2},
{ "load-sample", pa_cli_command_scache_load, "Load a sound file into the sample cache (args: name, filename)", 3},
{ "load-sample-lazy", pa_cli_command_scache_load, "Lazily load a sound file into the sample cache (args: name, filename)", 3},
{ "load-sample-dir-lazy", pa_cli_command_scache_load_dir, "Lazily load all files in a directory into the sample cache (args: pathname)", 2},
{ "play-file", pa_cli_command_play_file, "Play a sound file (args: filename, sink|index)", 3},
{ "list-autoload", pa_cli_command_autoload_list, "List autoload entries", 1},
{ "add-autoload-sink", pa_cli_command_autoload_add, "Add autoload entry for a sink (args: sink, module name, arguments)", 4},
{ "add-autoload-source", pa_cli_command_autoload_add, "Add autoload entry for a source (args: source, module name, arguments)", 4},
{ "remove-autoload-sink", pa_cli_command_autoload_remove, "Remove autoload entry for a sink (args: name)", 2},
{ "remove-autoload-source", pa_cli_command_autoload_remove, "Remove autoload entry for a source (args: name)", 2},
{ "dump", pa_cli_command_dump, "Dump daemon configuration", 1},
{ "list-props", pa_cli_command_list_props, NULL, 1},
{ NULL, NULL, NULL, 0 }
};
static const char whitespace[] = " \t\n\r";
static const char linebreak[] = "\n\r";
static uint32_t parse_index(const char *n) {
uint32_t idx;
if (pa_atou(n, &idx) < 0)
return (uint32_t) PA_IDXSET_INVALID;
return idx;
}
static int pa_cli_command_exit(pa_core *c, pa_tokenizer *t, PA_GCC_UNUSED pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
assert(c && c->mainloop && t);
c->mainloop->quit(c->mainloop, 0);
return 0;
}
static int pa_cli_command_help(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
const struct command*command;
assert(c && t && buf);
pa_strbuf_puts(buf, "Available commands:\n");
for (command = commands; command->name; command++)
if (command->help)
pa_strbuf_printf(buf, " %-25s %s\n", command->name, command->help);
return 0;
}
static int pa_cli_command_modules(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
char *s;
assert(c && t);
s = pa_module_list_to_string(c);
assert(s);
pa_strbuf_puts(buf, s);
pa_xfree(s);
return 0;
}
static int pa_cli_command_clients(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
char *s;
assert(c && t);
s = pa_client_list_to_string(c);
assert(s);
pa_strbuf_puts(buf, s);
pa_xfree(s);
return 0;
}
static int pa_cli_command_sinks(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
char *s;
assert(c && t);
s = pa_sink_list_to_string(c);
assert(s);
pa_strbuf_puts(buf, s);
pa_xfree(s);
return 0;
}
static int pa_cli_command_sources(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
char *s;
assert(c && t);
s = pa_source_list_to_string(c);
assert(s);
pa_strbuf_puts(buf, s);
pa_xfree(s);
return 0;
}
static int pa_cli_command_sink_inputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
char *s;
assert(c && t);
s = pa_sink_input_list_to_string(c);
assert(s);
pa_strbuf_puts(buf, s);
pa_xfree(s);
return 0;
}
static int pa_cli_command_source_outputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
char *s;
assert(c && t);
s = pa_source_output_list_to_string(c);
assert(s);
pa_strbuf_puts(buf, s);
pa_xfree(s);
return 0;
}
static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
char s[256];
assert(c && t);
pa_bytes_snprint(s, sizeof(s), c->memblock_stat->total_size);
pa_strbuf_printf(buf, "Memory blocks currently allocated: %u, size: %s.\n",
c->memblock_stat->total,
s);
pa_bytes_snprint(s, sizeof(s), c->memblock_stat->allocated_size);
pa_strbuf_printf(buf, "Memory blocks allocated during the whole lifetime: %u, size: %s.\n",
c->memblock_stat->allocated,
s);
pa_bytes_snprint(s, sizeof(s), pa_scache_total_size(c));
pa_strbuf_printf(buf, "Total sample cache size: %s.\n", s);
pa_sample_spec_snprint(s, sizeof(s), &c->default_sample_spec);
pa_strbuf_printf(buf, "Default sample spec: %s\n", s);
pa_strbuf_printf(buf, "Default sink name: %s\n"
"Default source name: %s\n",
pa_namereg_get_default_sink_name(c),
pa_namereg_get_default_source_name(c));
return 0;
}
static int pa_cli_command_info(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
assert(c && t);
pa_cli_command_stat(c, t, buf, fail);
pa_cli_command_modules(c, t, buf, fail);
pa_cli_command_sinks(c, t, buf, fail);
pa_cli_command_sources(c, t, buf, fail);
pa_cli_command_clients(c, t, buf, fail);
pa_cli_command_sink_inputs(c, t, buf, fail);
pa_cli_command_source_outputs(c, t, buf, fail);
pa_cli_command_scache_list(c, t, buf, fail);
pa_cli_command_autoload_list(c, t, buf, fail);
return 0;
}
static int pa_cli_command_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
pa_module *m;
const char *name;
assert(c && t);
if (!(name = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify the module name and optionally arguments.\n");
return -1;
}
if (!(m = pa_module_load(c, name, pa_tokenizer_get(t, 2)))) {
pa_strbuf_puts(buf, "Module load failed.\n");
return -1;
}
return 0;
}
static int pa_cli_command_unload(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
pa_module *m;
uint32_t idx;
const char *i;
char *e;
assert(c && t);
if (!(i = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify the module index.\n");
return -1;
}
idx = (uint32_t) strtoul(i, &e, 10);
if (*e || !(m = pa_idxset_get_by_index(c->modules, idx))) {
pa_strbuf_puts(buf, "Invalid module index.\n");
return -1;
}
pa_module_unload_request(m);
return 0;
}
static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
const char *n, *v;
pa_sink *sink;
uint32_t volume;
pa_cvolume cvolume;
if (!(n = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a sink either by its name or its index.\n");
return -1;
}
if (!(v = pa_tokenizer_get(t, 2))) {
pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x100 is normal volume)\n");
return -1;
}
if (pa_atou(v, &volume) < 0) {
pa_strbuf_puts(buf, "Failed to parse volume.\n");
return -1;
}
if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK, 1))) {
pa_strbuf_puts(buf, "No sink found by this name or index.\n");
return -1;
}
pa_cvolume_set(&cvolume, sink->sample_spec.channels, volume);
pa_sink_set_volume(sink, PA_MIXER_HARDWARE, &cvolume);
return 0;
}
static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
const char *n, *v;
pa_sink_input *si;
pa_volume_t volume;
pa_cvolume cvolume;
uint32_t idx;
if (!(n = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a sink input by its index.\n");
return -1;
}
if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
pa_strbuf_puts(buf, "Failed to parse index.\n");
return -1;
}
if (!(v = pa_tokenizer_get(t, 2))) {
pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x100 is normal volume)\n");
return -1;
}
if (pa_atou(v, &volume) < 0) {
pa_strbuf_puts(buf, "Failed to parse volume.\n");
return -1;
}
if (!(si = pa_idxset_get_by_index(c->sink_inputs, (uint32_t) idx))) {
pa_strbuf_puts(buf, "No sink input found with this index.\n");
return -1;
}
pa_cvolume_set(&cvolume, si->sample_spec.channels, volume);
pa_sink_input_set_volume(si, &cvolume);
return 0;
}
static int pa_cli_command_source_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
const char *n, *v;
pa_source *source;
uint32_t volume;
pa_cvolume cvolume;
if (!(n = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a source either by its name or its index.\n");
return -1;
}
if (!(v = pa_tokenizer_get(t, 2))) {
pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x100 is normal volume)\n");
return -1;
}
if (pa_atou(v, &volume) < 0) {
pa_strbuf_puts(buf, "Failed to parse volume.\n");
return -1;
}
if (!(source = pa_namereg_get(c, n, PA_NAMEREG_SOURCE, 1))) {
pa_strbuf_puts(buf, "No source found by this name or index.\n");
return -1;
}
pa_cvolume_set(&cvolume, source->sample_spec.channels, volume);
pa_source_set_volume(source, PA_MIXER_HARDWARE, &cvolume);
return 0;
}
static int pa_cli_command_sink_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
const char *n, *m;
pa_sink *sink;
int mute;
if (!(n = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a sink either by its name or its index.\n");
return -1;
}
if (!(m = pa_tokenizer_get(t, 2))) {
pa_strbuf_puts(buf, "You need to specify a mute switch setting (0/1).\n");
return -1;
}
if (pa_atoi(m, &mute) < 0) {
pa_strbuf_puts(buf, "Failed to parse mute switch.\n");
return -1;
}
if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK, 1))) {
pa_strbuf_puts(buf, "No sink found by this name or index.\n");
return -1;
}
pa_sink_set_mute(sink, PA_MIXER_HARDWARE, mute);
return 0;
}
static int pa_cli_command_source_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
const char *n, *m;
pa_source *source;
int mute;
if (!(n = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a source either by its name or its index.\n");
return -1;
}
if (!(m = pa_tokenizer_get(t, 2))) {
pa_strbuf_puts(buf, "You need to specify a mute switch setting (0/1).\n");
return -1;
}
if (pa_atoi(m, &mute) < 0) {
pa_strbuf_puts(buf, "Failed to parse mute switch.\n");
return -1;
}
if (!(source = pa_namereg_get(c, n, PA_NAMEREG_SOURCE, 1))) {
pa_strbuf_puts(buf, "No sink found by this name or index.\n");
return -1;
}
pa_source_set_mute(source, PA_MIXER_HARDWARE, mute);
return 0;
}
static int pa_cli_command_sink_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
const char *n;
assert(c && t);
if (!(n = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a sink either by its name or its index.\n");
return -1;
}
pa_namereg_set_default(c, n, PA_NAMEREG_SINK);
return 0;
}
static int pa_cli_command_source_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
const char *n;
assert(c && t);
if (!(n = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a source either by its name or its index.\n");
return -1;
}
pa_namereg_set_default(c, n, PA_NAMEREG_SOURCE);
return 0;
}
static int pa_cli_command_kill_client(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
const char *n;
pa_client *client;
uint32_t idx;
assert(c && t);
if (!(n = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a client by its index.\n");
return -1;
}
if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
pa_strbuf_puts(buf, "Failed to parse index.\n");
return -1;
}
if (!(client = pa_idxset_get_by_index(c->clients, idx))) {
pa_strbuf_puts(buf, "No client found by this index.\n");
return -1;
}
pa_client_kill(client);
return 0;
}
static int pa_cli_command_kill_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
const char *n;
pa_sink_input *sink_input;
uint32_t idx;
assert(c && t);
if (!(n = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a sink input by its index.\n");
return -1;
}
if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
pa_strbuf_puts(buf, "Failed to parse index.\n");
return -1;
}
if (!(sink_input = pa_idxset_get_by_index(c->sink_inputs, idx))) {
pa_strbuf_puts(buf, "No sink input found by this index.\n");
return -1;
}
pa_sink_input_kill(sink_input);
return 0;
}
static int pa_cli_command_kill_source_output(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
const char *n;
pa_source_output *source_output;
uint32_t idx;
assert(c && t);
if (!(n = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a source output by its index.\n");
return -1;
}
if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
pa_strbuf_puts(buf, "Failed to parse index.\n");
return -1;
}
if (!(source_output = pa_idxset_get_by_index(c->source_outputs, idx))) {
pa_strbuf_puts(buf, "No source output found by this index.\n");
return -1;
}
pa_source_output_kill(source_output);
return 0;
}
static int pa_cli_command_scache_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
char *s;
assert(c && t);
s = pa_scache_list_to_string(c);
assert(s);
pa_strbuf_puts(buf, s);
pa_xfree(s);
return 0;
}
static int pa_cli_command_scache_play(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
const char *n, *sink_name;
pa_sink *sink;
assert(c && t && buf && fail);
if (!(n = pa_tokenizer_get(t, 1)) || !(sink_name = pa_tokenizer_get(t, 2))) {
pa_strbuf_puts(buf, "You need to specify a sample name and a sink name.\n");
return -1;
}
if (!(sink = pa_namereg_get(c, sink_name, PA_NAMEREG_SINK, 1))) {
pa_strbuf_puts(buf, "No sink by that name.\n");
return -1;
}
if (pa_scache_play_item(c, n, sink, PA_VOLUME_NORM) < 0) {
pa_strbuf_puts(buf, "Failed to play sample.\n");
return -1;
}
return 0;
}
static int pa_cli_command_scache_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
const char *n;
assert(c && t && buf && fail);
if (!(n = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a sample name.\n");
return -1;
}
if (pa_scache_remove_item(c, n) < 0) {
pa_strbuf_puts(buf, "Failed to remove sample.\n");
return -1;
}
return 0;
}
static int pa_cli_command_scache_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
const char *fname, *n;
int r;
assert(c && t && buf && fail);
if (!(fname = pa_tokenizer_get(t, 2)) || !(n = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a file name and a sample name.\n");
return -1;
}
if (strstr(pa_tokenizer_get(t, 0), "lazy"))
r = pa_scache_add_file_lazy(c, n, fname, NULL);
else
r = pa_scache_add_file(c, n, fname, NULL);
if (r < 0)
pa_strbuf_puts(buf, "Failed to load sound file.\n");
return 0;
}
static int pa_cli_command_scache_load_dir(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
const char *pname;
assert(c && t && buf && fail);
if (!(pname = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a path name.\n");
return -1;
}
if (pa_scache_add_directory_lazy(c, pname) < 0) {
pa_strbuf_puts(buf, "Failed to load directory.\n");
return -1;
}
return 0;
}
static int pa_cli_command_play_file(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
const char *fname, *sink_name;
pa_sink *sink;
assert(c && t && buf && fail);
if (!(fname = pa_tokenizer_get(t, 1)) || !(sink_name = pa_tokenizer_get(t, 2))) {
pa_strbuf_puts(buf, "You need to specify a file name and a sink name.\n");
return -1;
}
if (!(sink = pa_namereg_get(c, sink_name, PA_NAMEREG_SINK, 1))) {
pa_strbuf_puts(buf, "No sink by that name.\n");
return -1;
}
return pa_play_file(sink, fname, NULL);
}
static int pa_cli_command_autoload_add(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
const char *a, *b;
assert(c && t && buf && fail);
if (!(a = pa_tokenizer_get(t, 1)) || !(b = pa_tokenizer_get(t, 2))) {
pa_strbuf_puts(buf, "You need to specify a device name, a filename or a module name and optionally module arguments\n");
return -1;
}
pa_autoload_add(c, a, strstr(pa_tokenizer_get(t, 0), "sink") ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE, b, pa_tokenizer_get(t, 3), NULL);
return 0;
}
static int pa_cli_command_autoload_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) {
const char *name;
assert(c && t && buf && fail);
if (!(name = pa_tokenizer_get(t, 1))) {
pa_strbuf_puts(buf, "You need to specify a device name\n");
return -1;
}
if (pa_autoload_remove_by_name(c, name, strstr(pa_tokenizer_get(t, 0), "sink") ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE) < 0) {
pa_strbuf_puts(buf, "Failed to remove autload entry\n");
return -1;
}
return 0;
}
static int pa_cli_command_autoload_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
char *s;
assert(c && t);
s = pa_autoload_list_to_string(c);
assert(s);
pa_strbuf_puts(buf, s);
pa_xfree(s);
return 0;
}
static int pa_cli_command_list_props(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
assert(c && t);
pa_property_dump(c, buf);
return 0;
}
static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) {
pa_module *m;
pa_sink *sink;
pa_source *source;
int nl;
const char *p;
uint32_t idx;
char txt[256];
time_t now;
void *i;
pa_autoload_entry *a;
assert(c && t);
time(&now);
#ifdef HAVE_CTIME_R
pa_strbuf_printf(buf, "### Configuration dump generated at %s\n", ctime_r(&now, txt));
#else
pa_strbuf_printf(buf, "### Configuration dump generated at %s\n", ctime(&now));
#endif
for (m = pa_idxset_first(c->modules, &idx); m; m = pa_idxset_next(c->modules, &idx)) {
if (m->auto_unload)
continue;
pa_strbuf_printf(buf, "load-module %s", m->name);
if (m->argument)
pa_strbuf_printf(buf, " %s", m->argument);
pa_strbuf_puts(buf, "\n");
}
nl = 0;
for (sink = pa_idxset_first(c->sinks, &idx); sink; sink = pa_idxset_next(c->sinks, &idx)) {
if (sink->owner && sink->owner->auto_unload)
continue;
if (!nl) {
pa_strbuf_puts(buf, "\n");
nl = 1;
}
pa_strbuf_printf(buf, "set-sink-volume %s 0x%03x\n", sink->name, pa_cvolume_avg(pa_sink_get_volume(sink, PA_MIXER_HARDWARE)));
pa_strbuf_printf(buf, "set-sink-mute %s %d\n", sink->name, pa_sink_get_mute(sink, PA_MIXER_HARDWARE));
}
for (source = pa_idxset_first(c->sources, &idx); source; source = pa_idxset_next(c->sources, &idx)) {
if (source->owner && source->owner->auto_unload)
continue;
if (!nl) {
pa_strbuf_puts(buf, "\n");
nl = 1;
}
pa_strbuf_printf(buf, "set-source-volume %s 0x%03x\n", source->name, pa_cvolume_avg(pa_source_get_volume(source, PA_MIXER_HARDWARE)));
pa_strbuf_printf(buf, "set-source-mute %s %d\n", source->name, pa_source_get_mute(source, PA_MIXER_HARDWARE));
}
if (c->autoload_hashmap) {
nl = 0;
i = NULL;
while ((a = pa_hashmap_iterate(c->autoload_hashmap, &i, NULL))) {
if (!nl) {
pa_strbuf_puts(buf, "\n");
nl = 1;
}
pa_strbuf_printf(buf, "add-autoload-%s %s %s", a->type == PA_NAMEREG_SINK ? "sink" : "source", a->name, a->module);
if (a->argument)
pa_strbuf_printf(buf, " %s", a->argument);
pa_strbuf_puts(buf, "\n");
}
}
nl = 0;
if ((p = pa_namereg_get_default_sink_name(c))) {
if (!nl) {
pa_strbuf_puts(buf, "\n");
nl = 1;
}
pa_strbuf_printf(buf, "set-default-sink %s\n", p);
}
if ((p = pa_namereg_get_default_source_name(c))) {
if (!nl) {
pa_strbuf_puts(buf, "\n");
nl = 1;
}
pa_strbuf_printf(buf, "set-default-source %s\n", p);
}
pa_strbuf_puts(buf, "\n### EOF\n");
return 0;
}
int pa_cli_command_execute_line(pa_core *c, const char *s, pa_strbuf *buf, int *fail) {
const char *cs;
cs = s+strspn(s, whitespace);
if (*cs == '#' || !*cs)
return 0;
else if (*cs == '.') {
if (!strcmp(cs, FAIL_META))
*fail = 1;
else if (!strcmp(cs, NOFAIL_META))
*fail = 0;
else {
size_t l;
l = strcspn(cs, whitespace);
if (l == sizeof(INCLUDE_META)-1 && !strncmp(cs, INCLUDE_META, l)) {
const char *filename = cs+l+strspn(cs+l, whitespace);
if (pa_cli_command_execute_file(c, filename, buf, fail) < 0)
if (*fail) return -1;
} else {
pa_strbuf_printf(buf, "Invalid meta command: %s\n", cs);
if (*fail) return -1;
}
}
} else {
const struct command*command;
int unknown = 1;
size_t l;
l = strcspn(cs, whitespace);
for (command = commands; command->name; command++)
if (strlen(command->name) == l && !strncmp(cs, command->name, l)) {
int ret;
pa_tokenizer *t = pa_tokenizer_new(cs, command->args);
assert(t);
ret = command->proc(c, t, buf, fail);
pa_tokenizer_free(t);
unknown = 0;
if (ret < 0 && *fail)
return -1;
break;
}
if (unknown) {
pa_strbuf_printf(buf, "Unknown command: %s\n", cs);
if (*fail)
return -1;
}
}
return 0;
}
int pa_cli_command_execute_file(pa_core *c, const char *fn, pa_strbuf *buf, int *fail) {
char line[256];
FILE *f = NULL;
int ret = -1;
assert(c && fn && buf);
if (!(f = fopen(fn, "r"))) {
pa_strbuf_printf(buf, "open('%s') failed: %s\n", fn, pa_cstrerror(errno));
if (!*fail)
ret = 0;
goto fail;
}
while (fgets(line, sizeof(line), f)) {
char *e = line + strcspn(line, linebreak);
*e = 0;
if (pa_cli_command_execute_line(c, line, buf, fail) < 0 && *fail)
goto fail;
}
ret = 0;
fail:
if (f)
fclose(f);
return ret;
}
int pa_cli_command_execute(pa_core *c, const char *s, pa_strbuf *buf, int *fail) {
const char *p;
assert(c && s && buf && fail);
p = s;
while (*p) {
size_t l = strcspn(p, linebreak);
char *line = pa_xstrndup(p, l);
if (pa_cli_command_execute_line(c, line, buf, fail) < 0&& *fail) {
pa_xfree(line);
return -1;
}
pa_xfree(line);
p += l;
p += strspn(p, linebreak);
}
return 0;
}

View file

@ -0,0 +1,40 @@
#ifndef fooclicommandhfoo
#define fooclicommandhfoo
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <pulsecore/strbuf.h>
#include <pulsecore/core.h>
/* Execute a single CLI command. Write the results to the string
* buffer *buf. If *fail is non-zero the function will return -1 when
* one or more of the executed commands failed. *fail
* may be modified by the function call. */
int pa_cli_command_execute_line(pa_core *c, const char *s, pa_strbuf *buf, int *fail);
/* Execute a whole file of CLI commands */
int pa_cli_command_execute_file(pa_core *c, const char *fn, pa_strbuf *buf, int *fail);
/* Split the specified string into lines and run pa_cli_command_execute_line() for each. */
int pa_cli_command_execute(pa_core *c, const char *s, pa_strbuf *buf, int *fail);
#endif

387
src/pulsecore/cli-text.c Normal file
View file

@ -0,0 +1,387 @@
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <assert.h>
#include <string.h>
#include <pulse/volume.h>
#include <pulse/xmalloc.h>
#include <pulsecore/module.h>
#include <pulsecore/client.h>
#include <pulsecore/sink.h>
#include <pulsecore/source.h>
#include <pulsecore/sink-input.h>
#include <pulsecore/source-output.h>
#include <pulsecore/strbuf.h>
#include <pulsecore/sample-util.h>
#include <pulsecore/core-scache.h>
#include <pulsecore/autoload.h>
#include "cli-text.h"
char *pa_module_list_to_string(pa_core *c) {
pa_strbuf *s;
pa_module *m;
uint32_t idx = PA_IDXSET_INVALID;
assert(c);
s = pa_strbuf_new();
assert(s);
pa_strbuf_printf(s, "%u module(s) loaded.\n", pa_idxset_size(c->modules));
for (m = pa_idxset_first(c->modules, &idx); m; m = pa_idxset_next(c->modules, &idx))
pa_strbuf_printf(s, " index: %u\n\tname: <%s>\n\targument: <%s>\n\tused: %i\n\tauto unload: %s\n", m->index, m->name, m->argument, m->n_used, m->auto_unload ? "yes" : "no");
return pa_strbuf_tostring_free(s);
}
char *pa_client_list_to_string(pa_core *c) {
pa_strbuf *s;
pa_client *client;
uint32_t idx = PA_IDXSET_INVALID;
assert(c);
s = pa_strbuf_new();
assert(s);
pa_strbuf_printf(s, "%u client(s) logged in.\n", pa_idxset_size(c->clients));
for (client = pa_idxset_first(c->clients, &idx); client; client = pa_idxset_next(c->clients, &idx)) {
pa_strbuf_printf(s, " index: %u\n\tname: <%s>\n\tdriver: <%s>\n", client->index, client->name, client->driver);
if (client->owner)
pa_strbuf_printf(s, "\towner module: <%u>\n", client->owner->index);
}
return pa_strbuf_tostring_free(s);
}
char *pa_sink_list_to_string(pa_core *c) {
pa_strbuf *s;
pa_sink *sink;
uint32_t idx = PA_IDXSET_INVALID;
assert(c);
s = pa_strbuf_new();
assert(s);
pa_strbuf_printf(s, "%u sink(s) available.\n", pa_idxset_size(c->sinks));
for (sink = pa_idxset_first(c->sinks, &idx); sink; sink = pa_idxset_next(c->sinks, &idx)) {
char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
pa_strbuf_printf(
s,
" %c index: %u\n"
"\tname: <%s>\n"
"\tdriver: <%s>\n"
"\tvolume: <%s>\n"
"\tlatency: <%0.0f usec>\n"
"\tmonitor_source: <%u>\n"
"\tsample spec: <%s>\n"
"\tchannel map: <%s>\n",
c->default_sink_name && !strcmp(sink->name, c->default_sink_name) ? '*' : ' ',
sink->index, sink->name,
sink->driver,
pa_cvolume_snprint(cv, sizeof(cv), pa_sink_get_volume(sink, PA_MIXER_HARDWARE)),
(double) pa_sink_get_latency(sink),
sink->monitor_source->index,
pa_sample_spec_snprint(ss, sizeof(ss), &sink->sample_spec),
pa_channel_map_snprint(cm, sizeof(cm), &sink->channel_map));
if (sink->owner)
pa_strbuf_printf(s, "\towner module: <%u>\n", sink->owner->index);
if (sink->description)
pa_strbuf_printf(s, "\tdescription: <%s>\n", sink->description);
}
return pa_strbuf_tostring_free(s);
}
char *pa_source_list_to_string(pa_core *c) {
pa_strbuf *s;
pa_source *source;
uint32_t idx = PA_IDXSET_INVALID;
assert(c);
s = pa_strbuf_new();
assert(s);
pa_strbuf_printf(s, "%u source(s) available.\n", pa_idxset_size(c->sources));
for (source = pa_idxset_first(c->sources, &idx); source; source = pa_idxset_next(c->sources, &idx)) {
char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
pa_strbuf_printf(
s,
" %c index: %u\n"
"\tname: <%s>\n"
"\tdriver: <%s>\n"
"\tlatency: <%0.0f usec>\n"
"\tsample spec: <%s>\n"
"\tchannel map: <%s>\n",
c->default_source_name && !strcmp(source->name, c->default_source_name) ? '*' : ' ',
source->index,
source->name,
source->driver,
(double) pa_source_get_latency(source),
pa_sample_spec_snprint(ss, sizeof(ss), &source->sample_spec),
pa_channel_map_snprint(cm, sizeof(cm), &source->channel_map));
if (source->monitor_of)
pa_strbuf_printf(s, "\tmonitor_of: <%u>\n", source->monitor_of->index);
if (source->owner)
pa_strbuf_printf(s, "\towner module: <%u>\n", source->owner->index);
if (source->description)
pa_strbuf_printf(s, "\tdescription: <%s>\n", source->description);
}
return pa_strbuf_tostring_free(s);
}
char *pa_source_output_list_to_string(pa_core *c) {
pa_strbuf *s;
pa_source_output *o;
uint32_t idx = PA_IDXSET_INVALID;
static const char* const state_table[] = {
"RUNNING",
"CORKED",
"DISCONNECTED"
};
assert(c);
s = pa_strbuf_new();
assert(s);
pa_strbuf_printf(s, "%u source outputs(s) available.\n", pa_idxset_size(c->source_outputs));
for (o = pa_idxset_first(c->source_outputs, &idx); o; o = pa_idxset_next(c->source_outputs, &idx)) {
char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
assert(o->source);
pa_strbuf_printf(
s,
" index: %u\n"
"\tname: '%s'\n"
"\tdriver: <%s>\n"
"\tstate: %s\n"
"\tsource: <%u> '%s'\n"
"\tsample spec: <%s>\n"
"\tchannel map: <%s>\n"
"\tresample method: %s\n",
o->index,
o->name,
o->driver,
state_table[o->state],
o->source->index, o->source->name,
pa_sample_spec_snprint(ss, sizeof(ss), &o->sample_spec),
pa_channel_map_snprint(cm, sizeof(cm), &o->channel_map),
pa_resample_method_to_string(pa_source_output_get_resample_method(o)));
if (o->owner)
pa_strbuf_printf(s, "\towner module: <%u>\n", o->owner->index);
if (o->client)
pa_strbuf_printf(s, "\tclient: <%u> '%s'\n", o->client->index, o->client->name);
}
return pa_strbuf_tostring_free(s);
}
char *pa_sink_input_list_to_string(pa_core *c) {
pa_strbuf *s;
pa_sink_input *i;
uint32_t idx = PA_IDXSET_INVALID;
static const char* const state_table[] = {
"RUNNING",
"CORKED",
"DISCONNECTED"
};
assert(c);
s = pa_strbuf_new();
assert(s);
pa_strbuf_printf(s, "%u sink input(s) available.\n", pa_idxset_size(c->sink_inputs));
for (i = pa_idxset_first(c->sink_inputs, &idx); i; i = pa_idxset_next(c->sink_inputs, &idx)) {
char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
assert(i->sink);
pa_strbuf_printf(
s,
" index: %u\n"
"\tname: <%s>\n"
"\tdriver: <%s>\n"
"\tstate: %s\n"
"\tsink: <%u> '%s'\n"
"\tvolume: <%s>\n"
"\tlatency: <%0.0f usec>\n"
"\tsample spec: <%s>\n"
"\tchannel map: <%s>\n"
"\tresample method: %s\n",
i->index,
i->name,
i->driver,
state_table[i->state],
i->sink->index, i->sink->name,
pa_cvolume_snprint(cv, sizeof(cv), pa_sink_input_get_volume(i)),
(double) pa_sink_input_get_latency(i),
pa_sample_spec_snprint(ss, sizeof(ss), &i->sample_spec),
pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),
pa_resample_method_to_string(pa_sink_input_get_resample_method(i)));
if (i->owner)
pa_strbuf_printf(s, "\towner module: <%u>\n", i->owner->index);
if (i->client)
pa_strbuf_printf(s, "\tclient: <%u> '%s'\n", i->client->index, i->client->name);
}
return pa_strbuf_tostring_free(s);
}
char *pa_scache_list_to_string(pa_core *c) {
pa_strbuf *s;
assert(c);
s = pa_strbuf_new();
assert(s);
pa_strbuf_printf(s, "%u cache entries available.\n", c->scache ? pa_idxset_size(c->scache) : 0);
if (c->scache) {
pa_scache_entry *e;
uint32_t idx = PA_IDXSET_INVALID;
for (e = pa_idxset_first(c->scache, &idx); e; e = pa_idxset_next(c->scache, &idx)) {
double l = 0;
char ss[PA_SAMPLE_SPEC_SNPRINT_MAX] = "n/a", cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX] = "n/a";
if (e->memchunk.memblock) {
pa_sample_spec_snprint(ss, sizeof(ss), &e->sample_spec);
pa_channel_map_snprint(cm, sizeof(cm), &e->channel_map);
l = (double) e->memchunk.length / pa_bytes_per_second(&e->sample_spec);
}
pa_strbuf_printf(
s,
" name: <%s>\n"
"\tindex: <%u>\n"
"\tsample spec: <%s>\n"
"\tchannel map: <%s>\n"
"\tlength: <%lu>\n"
"\tduration: <%0.1fs>\n"
"\tvolume: <%s>\n"
"\tlazy: %s\n"
"\tfilename: %s\n",
e->name,
e->index,
ss,
cm,
(long unsigned)(e->memchunk.memblock ? e->memchunk.length : 0),
l,
pa_cvolume_snprint(cv, sizeof(cv), &e->volume),
e->lazy ? "yes" : "no",
e->filename ? e->filename : "n/a");
}
}
return pa_strbuf_tostring_free(s);
}
char *pa_autoload_list_to_string(pa_core *c) {
pa_strbuf *s;
assert(c);
s = pa_strbuf_new();
assert(s);
pa_strbuf_printf(s, "%u autoload entries available.\n", c->autoload_hashmap ? pa_hashmap_size(c->autoload_hashmap) : 0);
if (c->autoload_hashmap) {
pa_autoload_entry *e;
void *state = NULL;
while ((e = pa_hashmap_iterate(c->autoload_hashmap, &state, NULL))) {
pa_strbuf_printf(
s, " name: <%s>\n\ttype: <%s>\n\tindex: <%u>\n\tmodule_name: <%s>\n\targuments: <%s>\n",
e->name,
e->type == PA_NAMEREG_SOURCE ? "source" : "sink",
e->index,
e->module,
e->argument);
}
}
return pa_strbuf_tostring_free(s);
}
char *pa_full_status_string(pa_core *c) {
pa_strbuf *s;
int i;
s = pa_strbuf_new();
for (i = 0; i < 8; i++) {
char *t = NULL;
switch (i) {
case 0:
t = pa_sink_list_to_string(c);
break;
case 1:
t = pa_source_list_to_string(c);
break;
case 2:
t = pa_sink_input_list_to_string(c);
break;
case 3:
t = pa_source_output_list_to_string(c);
break;
case 4:
t = pa_client_list_to_string(c);
break;
case 5:
t = pa_module_list_to_string(c);
break;
case 6:
t = pa_scache_list_to_string(c);
break;
case 7:
t = pa_autoload_list_to_string(c);
break;
}
pa_strbuf_puts(s, t);
pa_xfree(t);
}
return pa_strbuf_tostring_free(s);
}

42
src/pulsecore/cli-text.h Normal file
View file

@ -0,0 +1,42 @@
#ifndef fooclitexthfoo
#define fooclitexthfoo
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <pulsecore/core.h>
/* Some functions to generate pretty formatted listings of
* entities. The returned strings have to be freed manually. */
char *pa_sink_input_list_to_string(pa_core *c);
char *pa_source_output_list_to_string(pa_core *c);
char *pa_sink_list_to_string(pa_core *core);
char *pa_source_list_to_string(pa_core *c);
char *pa_client_list_to_string(pa_core *c);
char *pa_module_list_to_string(pa_core *c);
char *pa_scache_list_to_string(pa_core *c);
char *pa_autoload_list_to_string(pa_core *c);
char *pa_full_status_string(pa_core *c);
#endif

149
src/pulsecore/cli.c Normal file
View file

@ -0,0 +1,149 @@
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <stdlib.h>
#include <pulse/xmalloc.h>
#include <pulsecore/ioline.h>
#include <pulsecore/module.h>
#include <pulsecore/sink.h>
#include <pulsecore/source.h>
#include <pulsecore/client.h>
#include <pulsecore/sink-input.h>
#include <pulsecore/source-output.h>
#include <pulsecore/tokenizer.h>
#include <pulsecore/strbuf.h>
#include <pulsecore/namereg.h>
#include <pulsecore/cli-text.h>
#include <pulsecore/cli-command.h>
#include <pulsecore/log.h>
#include "cli.h"
#define PROMPT ">>> "
struct pa_cli {
pa_core *core;
pa_ioline *line;
void (*eof_callback)(pa_cli *c, void *userdata);
void *userdata;
pa_client *client;
int fail, kill_requested, defer_kill;
};
static void line_callback(pa_ioline *line, const char *s, void *userdata);
static void client_kill(pa_client *c);
pa_cli* pa_cli_new(pa_core *core, pa_iochannel *io, pa_module *m) {
char cname[256];
pa_cli *c;
assert(io);
c = pa_xmalloc(sizeof(pa_cli));
c->core = core;
c->line = pa_ioline_new(io);
assert(c->line);
c->userdata = NULL;
c->eof_callback = NULL;
pa_iochannel_socket_peer_to_string(io, cname, sizeof(cname));
c->client = pa_client_new(core, __FILE__, cname);
assert(c->client);
c->client->kill = client_kill;
c->client->userdata = c;
c->client->owner = m;
pa_ioline_set_callback(c->line, line_callback, c);
pa_ioline_puts(c->line, "Welcome to pulseaudio! Use \"help\" for usage information.\n"PROMPT);
c->fail = c->kill_requested = c->defer_kill = 0;
return c;
}
void pa_cli_free(pa_cli *c) {
assert(c);
pa_ioline_close(c->line);
pa_ioline_unref(c->line);
pa_client_free(c->client);
pa_xfree(c);
}
static void client_kill(pa_client *client) {
pa_cli *c;
assert(client && client->userdata);
c = client->userdata;
pa_log_debug(__FILE__": CLI client killed.");
if (c->defer_kill)
c->kill_requested = 1;
else {
if (c->eof_callback)
c->eof_callback(c, c->userdata);
}
}
static void line_callback(pa_ioline *line, const char *s, void *userdata) {
pa_strbuf *buf;
pa_cli *c = userdata;
char *p;
assert(line && c);
if (!s) {
pa_log_debug(__FILE__": CLI got EOF from user.");
if (c->eof_callback)
c->eof_callback(c, c->userdata);
return;
}
buf = pa_strbuf_new();
assert(buf);
c->defer_kill++;
pa_cli_command_execute_line(c->core, s, buf, &c->fail);
c->defer_kill--;
pa_ioline_puts(line, p = pa_strbuf_tostring_free(buf));
pa_xfree(p);
if (c->kill_requested) {
if (c->eof_callback)
c->eof_callback(c, c->userdata);
} else
pa_ioline_puts(line, PROMPT);
}
void pa_cli_set_eof_callback(pa_cli *c, void (*cb)(pa_cli*c, void *userdata), void *userdata) {
assert(c);
c->eof_callback = cb;
c->userdata = userdata;
}

38
src/pulsecore/cli.h Normal file
View file

@ -0,0 +1,38 @@
#ifndef fooclihfoo
#define fooclihfoo
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <pulsecore/iochannel.h>
#include <pulsecore/core.h>
#include <pulsecore/module.h>
typedef struct pa_cli pa_cli;
/* Create a new command line session on the specified io channel owned by the specified module */
pa_cli* pa_cli_new(pa_core *core, pa_iochannel *io, pa_module *m);
void pa_cli_free(pa_cli *cli);
/* Set a callback function that is called whenever the command line session is terminated */
void pa_cli_set_eof_callback(pa_cli *cli, void (*cb)(pa_cli*c, void *userdata), void *userdata);
#endif

96
src/pulsecore/client.c Normal file
View file

@ -0,0 +1,96 @@
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <pulse/xmalloc.h>
#include <pulsecore/core-subscribe.h>
#include <pulsecore/log.h>
#include "client.h"
pa_client *pa_client_new(pa_core *core, const char *driver, const char *name) {
pa_client *c;
int r;
assert(core);
c = pa_xmalloc(sizeof(pa_client));
c->name = pa_xstrdup(name);
c->driver = pa_xstrdup(driver);
c->owner = NULL;
c->core = core;
c->kill = NULL;
c->userdata = NULL;
r = pa_idxset_put(core->clients, c, &c->index);
assert(c->index != PA_IDXSET_INVALID && r >= 0);
pa_log_info(__FILE__": created %u \"%s\"", c->index, c->name);
pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_NEW, c->index);
pa_core_check_quit(core);
return c;
}
void pa_client_free(pa_client *c) {
assert(c && c->core);
pa_idxset_remove_by_data(c->core->clients, c, NULL);
pa_core_check_quit(c->core);
pa_log_info(__FILE__": freed %u \"%s\"", c->index, c->name);
pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_REMOVE, c->index);
pa_xfree(c->name);
pa_xfree(c->driver);
pa_xfree(c);
}
void pa_client_kill(pa_client *c) {
assert(c);
if (!c->kill) {
pa_log_warn(__FILE__": kill() operation not implemented for client %u", c->index);
return;
}
c->kill(c);
}
void pa_client_set_name(pa_client *c, const char *name) {
assert(c);
pa_log_info(__FILE__": client %u changed name from \"%s\" to \"%s\"", c->index, c->name, name);
pa_xfree(c->name);
c->name = pa_xstrdup(name);
pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_CHANGE, c->index);
}

57
src/pulsecore/client.h Normal file
View file

@ -0,0 +1,57 @@
#ifndef fooclienthfoo
#define fooclienthfoo
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <pulsecore/core.h>
#include <pulsecore/module.h>
/* Every connection to the server should have a pa_client
* attached. That way the user may generate a listing of all connected
* clients easily and kill them if he wants.*/
typedef struct pa_client pa_client;
struct pa_client {
uint32_t index;
pa_module *owner;
char *name, *driver;
pa_core *core;
void (*kill)(pa_client *c);
void *userdata;
};
pa_client *pa_client_new(pa_core *c, const char *driver, const char *name);
/* This function should be called only by the code that created the client */
void pa_client_free(pa_client *c);
/* Code that didn't create the client should call this function to
* request destruction of the client */
void pa_client_kill(pa_client *c);
/* Rename the client */
void pa_client_set_name(pa_client *c, const char *name);
#endif

181
src/pulsecore/conf-parser.c Normal file
View file

@ -0,0 +1,181 @@
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <assert.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <pulse/xmalloc.h>
#include <pulsecore/core-error.h>
#include <pulsecore/log.h>
#include <pulsecore/core-util.h>
#include "conf-parser.h"
#define WHITESPACE " \t\n"
#define COMMENTS "#;\n"
/* Run the user supplied parser for an assignment */
static int next_assignment(const char *filename, unsigned line, const pa_config_item *t, const char *lvalue, const char *rvalue, void *userdata) {
assert(filename && t && lvalue && rvalue);
for (; t->parse; t++)
if (!strcmp(lvalue, t->lvalue))
return t->parse(filename, line, lvalue, rvalue, t->data, userdata);
pa_log(__FILE__": [%s:%u] Unknown lvalue '%s'.", filename, line, lvalue);
return -1;
}
/* Returns non-zero when c is contained in s */
static int in_string(char c, const char *s) {
assert(s);
for (; *s; s++)
if (*s == c)
return 1;
return 0;
}
/* Remove all whitepsapce from the beginning and the end of *s. *s may
* be modified. */
static char *strip(char *s) {
char *b = s+strspn(s, WHITESPACE);
char *e, *l = NULL;
for (e = b; *e; e++)
if (!in_string(*e, WHITESPACE))
l = e;
if (l)
*(l+1) = 0;
return b;
}
/* Parse a variable assignment line */
static int parse_line(const char *filename, unsigned line, const pa_config_item *t, char *l, void *userdata) {
char *e, *c, *b = l+strspn(l, WHITESPACE);
if ((c = strpbrk(b, COMMENTS)))
*c = 0;
if (!*b)
return 0;
if (!(e = strchr(b, '='))) {
pa_log(__FILE__": [%s:%u] Missing '='.", filename, line);
return -1;
}
*e = 0;
e++;
return next_assignment(filename, line, t, strip(b), strip(e), userdata);
}
/* Go through the file and parse each line */
int pa_config_parse(const char *filename, FILE *f, const pa_config_item *t, void *userdata) {
int r = -1;
unsigned line = 0;
int do_close = !f;
assert(filename && t);
if (!f && !(f = fopen(filename, "r"))) {
if (errno == ENOENT) {
r = 0;
goto finish;
}
pa_log_warn(__FILE__": WARNING: failed to open configuration file '%s': %s",
filename, pa_cstrerror(errno));
goto finish;
}
while (!feof(f)) {
char l[256];
if (!fgets(l, sizeof(l), f)) {
if (feof(f))
break;
pa_log_warn(__FILE__": WARNING: failed to read configuration file '%s': %s",
filename, pa_cstrerror(errno));
goto finish;
}
if (parse_line(filename, ++line, t, l, userdata) < 0)
goto finish;
}
r = 0;
finish:
if (do_close && f)
fclose(f);
return r;
}
int pa_config_parse_int(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) {
int *i = data;
int32_t k;
assert(filename && lvalue && rvalue && data);
if (pa_atoi(rvalue, &k) < 0) {
pa_log(__FILE__": [%s:%u] Failed to parse numeric value: %s", filename, line, rvalue);
return -1;
}
*i = (int) k;
return 0;
}
int pa_config_parse_bool(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) {
int *b = data, k;
assert(filename && lvalue && rvalue && data);
if ((k = pa_parse_boolean(rvalue)) < 0) {
pa_log(__FILE__": [%s:%u] Failed to parse boolean value: %s", filename, line, rvalue);
return -1;
}
*b = k;
return 0;
}
int pa_config_parse_string(const char *filename, PA_GCC_UNUSED unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) {
char **s = data;
assert(filename && lvalue && rvalue && data);
pa_xfree(*s);
*s = *rvalue ? pa_xstrdup(rvalue) : NULL;
return 0;
}

View file

@ -0,0 +1,47 @@
#ifndef fooconfparserhfoo
#define fooconfparserhfoo
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <stdio.h>
/* An abstract parser for simple, line based, shallow configuration
* files consisting of variable assignments only. */
/* Wraps info for parsing a specific configuration variable */
typedef struct pa_config_item {
const char *lvalue; /* name of the variable */
int (*parse)(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata); /* Function that is called to parse the variable's value */
void *data; /* Where to store the variable's data */
} pa_config_item;
/* The configuration file parsing routine. Expects a table of
* pa_config_items in *t that is terminated by an item where lvalue is
* NULL */
int pa_config_parse(const char *filename, FILE *f, const pa_config_item *t, void *userdata);
/* Generic parsers for integers, booleans and strings */
int pa_config_parse_int(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata);
int pa_config_parse_bool(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata);
int pa_config_parse_string(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata);
#endif

30
src/pulsecore/core-def.h Normal file
View file

@ -0,0 +1,30 @@
#ifndef foocoredefhfoo
#define foocoredefhfoo
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
typedef enum pa_mixer {
PA_MIXER_SOFTWARE,
PA_MIXER_HARDWARE
} pa_mixer_t;
#endif

205
src/pulsecore/core-error.c Normal file
View file

@ -0,0 +1,205 @@
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_PTHREAD
#include <pthread.h>
#endif
#ifdef HAVE_WINDOWS_H
#include <windows.h>
#endif
#include <pulse/utf8.h>
#include <pulse/xmalloc.h>
#include <pulsecore/core-util.h>
#include <pulsecore/native-common.h>
#include "core-error.h"
#ifdef HAVE_PTHREAD
static pthread_once_t cstrerror_once = PTHREAD_ONCE_INIT;
static pthread_key_t tlsstr_key;
static void inittls(void) {
int ret;
ret = pthread_key_create(&tlsstr_key, pa_xfree);
if (ret) {
fprintf(stderr, __FILE__ ": CRITICAL: Unable to allocate TLS key (%d)\n", errno);
exit(-1);
}
}
#elif HAVE_WINDOWS_H
static DWORD tlsstr_key = TLS_OUT_OF_INDEXES;
static DWORD monitor_key = TLS_OUT_OF_INDEXES;
static void inittls(void) {
HANDLE mutex;
char name[64];
sprintf(name, "pulseaudio%d", (int)GetCurrentProcessId());
mutex = CreateMutex(NULL, FALSE, name);
if (!mutex) {
fprintf(stderr, __FILE__ ": CRITICAL: Unable to create named mutex (%d)\n", (int)GetLastError());
exit(-1);
}
WaitForSingleObject(mutex, INFINITE);
if (tlsstr_key == TLS_OUT_OF_INDEXES) {
tlsstr_key = TlsAlloc();
monitor_key = TlsAlloc();
if ((tlsstr_key == TLS_OUT_OF_INDEXES) || (monitor_key == TLS_OUT_OF_INDEXES)) {
fprintf(stderr, __FILE__ ": CRITICAL: Unable to allocate TLS key (%d)\n", (int)GetLastError());
exit(-1);
}
}
ReleaseMutex(mutex);
CloseHandle(mutex);
}
/*
* This is incredibly brain dead, but this is necessary when dealing with
* the hell that is Win32.
*/
struct monitor_data {
HANDLE thread;
void *data;
};
static DWORD WINAPI monitor_thread(LPVOID param) {
struct monitor_data *data;
data = (struct monitor_data*)param;
assert(data);
WaitForSingleObject(data->thread, INFINITE);
CloseHandle(data->thread);
pa_xfree(data->data);
pa_xfree(data);
return 0;
}
static void start_monitor(void) {
HANDLE thread;
struct monitor_data *data;
data = pa_xnew(struct monitor_data, 1);
assert(data);
DuplicateHandle(GetCurrentProcess(), GetCurrentThread(),
GetCurrentProcess(), &data->thread, 0, FALSE, DUPLICATE_SAME_ACCESS);
thread = CreateThread(NULL, 0, monitor_thread, data, 0, NULL);
assert(thread);
TlsSetValue(monitor_key, data);
CloseHandle(thread);
}
#else
/* Unsafe, but we have no choice */
static char *tlsstr;
#endif
const char* pa_cstrerror(int errnum) {
const char *origbuf;
#ifdef HAVE_STRERROR_R
char errbuf[128];
#endif
#ifdef HAVE_PTHREAD
char *tlsstr;
pthread_once(&cstrerror_once, inittls);
tlsstr = pthread_getspecific(tlsstr_key);
#elif defined(HAVE_WINDOWS_H)
char *tlsstr;
struct monitor_data *data;
inittls();
tlsstr = TlsGetValue(tlsstr_key);
if (!tlsstr)
start_monitor();
data = TlsGetValue(monitor_key);
#endif
if (tlsstr)
pa_xfree(tlsstr);
#ifdef HAVE_STRERROR_R
#ifdef __GLIBC__
origbuf = strerror_r(errnum, errbuf, sizeof(errbuf));
if (origbuf == NULL)
origbuf = "";
#else
if (strerror_r(errnum, errbuf, sizeof(errbuf)) == 0) {
origbuf = errbuf;
errbuf[sizeof(errbuf) - 1] = '\0';
} else
origbuf = "";
#endif
#else
/* This might not be thread safe, but we hope for the best */
origbuf = strerror(errnum);
#endif
tlsstr = pa_locale_to_utf8(origbuf);
if (!tlsstr) {
fprintf(stderr, "Unable to convert, filtering\n");
tlsstr = pa_utf8_filter(origbuf);
}
#ifdef HAVE_PTHREAD
pthread_setspecific(tlsstr_key, tlsstr);
#elif defined(HAVE_WINDOWS_H)
TlsSetValue(tlsstr_key, tlsstr);
data->data = tlsstr;
#endif
return tlsstr;
}

View file

@ -0,0 +1,41 @@
#ifndef foocoreerrorhfoo
#define foocoreerrorhfoo
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <inttypes.h>
#include <pulse/cdecl.h>
/** \file
* Error management */
PA_C_DECL_BEGIN
/** A wrapper around the standard strerror() function that converts the
* string to UTF-8. The function is thread safe but the returned string is
* only guaranteed to exist until the thread exits or pa_cstrerror() is
* called again from the same thread. */
const char* pa_cstrerror(int errnum);
PA_C_DECL_END
#endif

412
src/pulsecore/core-scache.c Normal file
View file

@ -0,0 +1,412 @@
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <errno.h>
#include <limits.h>
#ifdef HAVE_GLOB_H
#include <glob.h>
#endif
#ifdef HAVE_WINDOWS_H
#include <windows.h>
#endif
#include <pulse/mainloop.h>
#include <pulse/channelmap.h>
#include <pulse/timeval.h>
#include <pulse/util.h>
#include <pulse/volume.h>
#include <pulse/xmalloc.h>
#include <pulsecore/sink-input.h>
#include <pulsecore/sample-util.h>
#include <pulsecore/play-memchunk.h>
#include <pulsecore/core-subscribe.h>
#include <pulsecore/namereg.h>
#include <pulsecore/sound-file.h>
#include <pulsecore/core-util.h>
#include <pulsecore/log.h>
#include <pulsecore/core-error.h>
#include "core-scache.h"
#define UNLOAD_POLL_TIME 2
static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) {
pa_core *c = userdata;
struct timeval ntv;
assert(c && c->mainloop == m && c->scache_auto_unload_event == e);
pa_scache_unload_unused(c);
pa_gettimeofday(&ntv);
ntv.tv_sec += UNLOAD_POLL_TIME;
m->time_restart(e, &ntv);
}
static void free_entry(pa_scache_entry *e) {
assert(e);
pa_namereg_unregister(e->core, e->name);
pa_subscription_post(e->core, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_REMOVE, e->index);
pa_xfree(e->name);
pa_xfree(e->filename);
if (e->memchunk.memblock)
pa_memblock_unref(e->memchunk.memblock);
pa_xfree(e);
}
static pa_scache_entry* scache_add_item(pa_core *c, const char *name) {
pa_scache_entry *e;
assert(c && name);
if ((e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, 0))) {
if (e->memchunk.memblock)
pa_memblock_unref(e->memchunk.memblock);
pa_xfree(e->filename);
assert(e->core == c);
pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_CHANGE, e->index);
} else {
e = pa_xmalloc(sizeof(pa_scache_entry));
if (!pa_namereg_register(c, name, PA_NAMEREG_SAMPLE, e, 1)) {
pa_xfree(e);
return NULL;
}
e->name = pa_xstrdup(name);
e->core = c;
if (!c->scache) {
c->scache = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
assert(c->scache);
}
pa_idxset_put(c->scache, e, &e->index);
pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_NEW, e->index);
}
e->last_used_time = 0;
e->memchunk.memblock = NULL;
e->memchunk.index = e->memchunk.length = 0;
e->filename = NULL;
e->lazy = 0;
e->last_used_time = 0;
memset(&e->sample_spec, 0, sizeof(e->sample_spec));
pa_channel_map_init(&e->channel_map);
pa_cvolume_reset(&e->volume, PA_CHANNELS_MAX);
return e;
}
int pa_scache_add_item(pa_core *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map, const pa_memchunk *chunk, uint32_t *idx) {
pa_scache_entry *e;
assert(c && name);
if (chunk && chunk->length > PA_SCACHE_ENTRY_SIZE_MAX)
return -1;
if (!(e = scache_add_item(c, name)))
return -1;
if (ss) {
e->sample_spec = *ss;
pa_channel_map_init_auto(&e->channel_map, ss->channels, PA_CHANNEL_MAP_DEFAULT);
e->volume.channels = e->sample_spec.channels;
}
if (map)
e->channel_map = *map;
if (chunk) {
e->memchunk = *chunk;
pa_memblock_ref(e->memchunk.memblock);
}
if (idx)
*idx = e->index;
return 0;
}
int pa_scache_add_file(pa_core *c, const char *name, const char *filename, uint32_t *idx) {
pa_sample_spec ss;
pa_channel_map map;
pa_memchunk chunk;
int r;
#ifdef OS_IS_WIN32
char buf[MAX_PATH];
if (ExpandEnvironmentStrings(filename, buf, MAX_PATH))
filename = buf;
#endif
if (pa_sound_file_load(filename, &ss, &map, &chunk, c->memblock_stat) < 0)
return -1;
r = pa_scache_add_item(c, name, &ss, &map, &chunk, idx);
pa_memblock_unref(chunk.memblock);
return r;
}
int pa_scache_add_file_lazy(pa_core *c, const char *name, const char *filename, uint32_t *idx) {
pa_scache_entry *e;
#ifdef OS_IS_WIN32
char buf[MAX_PATH];
if (ExpandEnvironmentStrings(filename, buf, MAX_PATH))
filename = buf;
#endif
assert(c && name);
if (!(e = scache_add_item(c, name)))
return -1;
e->lazy = 1;
e->filename = pa_xstrdup(filename);
if (!c->scache_auto_unload_event) {
struct timeval ntv;
pa_gettimeofday(&ntv);
ntv.tv_sec += UNLOAD_POLL_TIME;
c->scache_auto_unload_event = c->mainloop->time_new(c->mainloop, &ntv, timeout_callback, c);
}
if (idx)
*idx = e->index;
return 0;
}
int pa_scache_remove_item(pa_core *c, const char *name) {
pa_scache_entry *e;
assert(c && name);
if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, 0)))
return -1;
if (pa_idxset_remove_by_data(c->scache, e, NULL) != e)
assert(0);
free_entry(e);
return 0;
}
static void free_cb(void *p, PA_GCC_UNUSED void *userdata) {
pa_scache_entry *e = p;
assert(e);
free_entry(e);
}
void pa_scache_free(pa_core *c) {
assert(c);
if (c->scache) {
pa_idxset_free(c->scache, free_cb, NULL);
c->scache = NULL;
}
if (c->scache_auto_unload_event)
c->mainloop->time_free(c->scache_auto_unload_event);
}
int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t volume) {
pa_scache_entry *e;
char *t;
pa_cvolume r;
assert(c);
assert(name);
assert(sink);
if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, 1)))
return -1;
if (e->lazy && !e->memchunk.memblock) {
if (pa_sound_file_load(e->filename, &e->sample_spec, &e->channel_map, &e->memchunk, c->memblock_stat) < 0)
return -1;
pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_CHANGE, e->index);
if (e->volume.channels > e->sample_spec.channels)
e->volume.channels = e->sample_spec.channels;
}
if (!e->memchunk.memblock)
return -1;
t = pa_sprintf_malloc("sample:%s", name);
pa_cvolume_set(&r, e->volume.channels, volume);
pa_sw_cvolume_multiply(&r, &r, &e->volume);
if (pa_play_memchunk(sink, t, &e->sample_spec, &e->channel_map, &e->memchunk, &r) < 0) {
pa_xfree(t);
return -1;
}
pa_xfree(t);
if (e->lazy)
time(&e->last_used_time);
return 0;
}
const char * pa_scache_get_name_by_id(pa_core *c, uint32_t id) {
pa_scache_entry *e;
assert(c && id != PA_IDXSET_INVALID);
if (!c->scache || !(e = pa_idxset_get_by_index(c->scache, id)))
return NULL;
return e->name;
}
uint32_t pa_scache_get_id_by_name(pa_core *c, const char *name) {
pa_scache_entry *e;
assert(c && name);
if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, 0)))
return PA_IDXSET_INVALID;
return e->index;
}
uint32_t pa_scache_total_size(pa_core *c) {
pa_scache_entry *e;
uint32_t idx, sum = 0;
assert(c);
if (!c->scache || !pa_idxset_size(c->scache))
return 0;
for (e = pa_idxset_first(c->scache, &idx); e; e = pa_idxset_next(c->scache, &idx))
if (e->memchunk.memblock)
sum += e->memchunk.length;
return sum;
}
void pa_scache_unload_unused(pa_core *c) {
pa_scache_entry *e;
time_t now;
uint32_t idx;
assert(c);
if (!c->scache || !pa_idxset_size(c->scache))
return;
time(&now);
for (e = pa_idxset_first(c->scache, &idx); e; e = pa_idxset_next(c->scache, &idx)) {
if (!e->lazy || !e->memchunk.memblock)
continue;
if (e->last_used_time + c->scache_idle_time > now)
continue;
pa_memblock_unref(e->memchunk.memblock);
e->memchunk.memblock = NULL;
e->memchunk.index = e->memchunk.length = 0;
pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_CHANGE, e->index);
}
}
static void add_file(pa_core *c, const char *pathname) {
struct stat st;
const char *e;
e = pa_path_get_filename(pathname);
if (stat(pathname, &st) < 0) {
pa_log(__FILE__": stat('%s'): %s", pathname, pa_cstrerror(errno));
return;
}
#if defined(S_ISREG) && defined(S_ISLNK)
if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode))
#endif
pa_scache_add_file_lazy(c, e, pathname, NULL);
}
int pa_scache_add_directory_lazy(pa_core *c, const char *pathname) {
DIR *dir;
assert(c && pathname);
/* First try to open this as directory */
if (!(dir = opendir(pathname))) {
#ifdef HAVE_GLOB_H
glob_t p;
unsigned int i;
/* If that fails, try to open it as shell glob */
if (glob(pathname, GLOB_ERR|GLOB_NOSORT, NULL, &p) < 0) {
pa_log(__FILE__": failed to open directory '%s': %s", pathname, pa_cstrerror(errno));
return -1;
}
for (i = 0; i < p.gl_pathc; i++)
add_file(c, p.gl_pathv[i]);
globfree(&p);
#else
return -1;
#endif
} else {
struct dirent *e;
while ((e = readdir(dir))) {
char p[PATH_MAX];
if (e->d_name[0] == '.')
continue;
snprintf(p, sizeof(p), "%s/%s", pathname, e->d_name);
add_file(c, p);
}
}
closedir(dir);
return 0;
}

View file

@ -0,0 +1,64 @@
#ifndef foocorescachehfoo
#define foocorescachehfoo
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <pulsecore/core.h>
#include <pulsecore/memchunk.h>
#include <pulsecore/sink.h>
#define PA_SCACHE_ENTRY_SIZE_MAX (1024*1024*2)
typedef struct pa_scache_entry {
pa_core *core;
uint32_t index;
char *name;
pa_cvolume volume;
pa_sample_spec sample_spec;
pa_channel_map channel_map;
pa_memchunk memchunk;
char *filename;
int lazy;
time_t last_used_time;
} pa_scache_entry;
int pa_scache_add_item(pa_core *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map, const pa_memchunk *chunk, uint32_t *idx);
int pa_scache_add_file(pa_core *c, const char *name, const char *filename, uint32_t *idx);
int pa_scache_add_file_lazy(pa_core *c, const char *name, const char *filename, uint32_t *idx);
int pa_scache_add_directory_lazy(pa_core *c, const char *pathname);
int pa_scache_remove_item(pa_core *c, const char *name);
int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t volume);
void pa_scache_free(pa_core *c);
const char *pa_scache_get_name_by_id(pa_core *c, uint32_t id);
uint32_t pa_scache_get_id_by_name(pa_core *c, const char *name);
uint32_t pa_scache_total_size(pa_core *c);
void pa_scache_unload_unused(pa_core *c);
#endif

View file

@ -0,0 +1,233 @@
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <assert.h>
#include <pulse/xmalloc.h>
#include <pulsecore/queue.h>
#include <pulsecore/log.h>
#include "core-subscribe.h"
/* The subscription subsystem may be used to be notified whenever an
* entity (sink, source, ...) is created or deleted. Modules may
* register a callback function that is called whenever an event
* matching a subscription mask happens. The execution of the callback
* function is postponed to the next main loop iteration, i.e. is not
* called from within the stack frame the entity was created in. */
struct pa_subscription {
pa_core *core;
int dead;
void (*callback)(pa_core *c, pa_subscription_event_type_t t, uint32_t index, void *userdata);
void *userdata;
pa_subscription_mask_t mask;
pa_subscription *prev, *next;
};
struct pa_subscription_event {
pa_subscription_event_type_t type;
uint32_t index;
};
static void sched_event(pa_core *c);
/* Allocate a new subscription object for the given subscription mask. Use the specified callback function and user data */
pa_subscription* pa_subscription_new(pa_core *c, pa_subscription_mask_t m, void (*callback)(pa_core *c, pa_subscription_event_type_t t, uint32_t index, void *userdata), void *userdata) {
pa_subscription *s;
assert(c);
s = pa_xmalloc(sizeof(pa_subscription));
s->core = c;
s->dead = 0;
s->callback = callback;
s->userdata = userdata;
s->mask = m;
if ((s->next = c->subscriptions))
s->next->prev = s;
s->prev = NULL;
c->subscriptions = s;
return s;
}
/* Free a subscription object, effectively marking it for deletion */
void pa_subscription_free(pa_subscription*s) {
assert(s && !s->dead);
s->dead = 1;
sched_event(s->core);
}
static void free_item(pa_subscription *s) {
assert(s && s->core);
if (s->prev)
s->prev->next = s->next;
else
s->core->subscriptions = s->next;
if (s->next)
s->next->prev = s->prev;
pa_xfree(s);
}
/* Free all subscription objects */
void pa_subscription_free_all(pa_core *c) {
pa_subscription_event *e;
assert(c);
while (c->subscriptions)
free_item(c->subscriptions);
if (c->subscription_event_queue) {
while ((e = pa_queue_pop(c->subscription_event_queue)))
pa_xfree(e);
pa_queue_free(c->subscription_event_queue, NULL, NULL);
c->subscription_event_queue = NULL;
}
if (c->subscription_defer_event) {
c->mainloop->defer_free(c->subscription_defer_event);
c->subscription_defer_event = NULL;
}
}
#if 0
static void dump_event(pa_subscription_event*e) {
switch (e->type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) {
case PA_SUBSCRIPTION_EVENT_SINK:
pa_log(__FILE__": SINK_EVENT");
break;
case PA_SUBSCRIPTION_EVENT_SOURCE:
pa_log(__FILE__": SOURCE_EVENT");
break;
case PA_SUBSCRIPTION_EVENT_SINK_INPUT:
pa_log(__FILE__": SINK_INPUT_EVENT");
break;
case PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT:
pa_log(__FILE__": SOURCE_OUTPUT_EVENT");
break;
case PA_SUBSCRIPTION_EVENT_MODULE:
pa_log(__FILE__": MODULE_EVENT");
break;
case PA_SUBSCRIPTION_EVENT_CLIENT:
pa_log(__FILE__": CLIENT_EVENT");
break;
default:
pa_log(__FILE__": OTHER");
break;
}
switch (e->type & PA_SUBSCRIPTION_EVENT_TYPE_MASK) {
case PA_SUBSCRIPTION_EVENT_NEW:
pa_log(__FILE__": NEW");
break;
case PA_SUBSCRIPTION_EVENT_CHANGE:
pa_log(__FILE__": CHANGE");
break;
case PA_SUBSCRIPTION_EVENT_REMOVE:
pa_log(__FILE__": REMOVE");
break;
default:
pa_log(__FILE__": OTHER");
break;
}
pa_log(__FILE__": %u", e->index);
}
#endif
/* Deferred callback for dispatching subscirption events */
static void defer_cb(pa_mainloop_api *m, pa_defer_event *de, void *userdata) {
pa_core *c = userdata;
pa_subscription *s;
assert(c && c->subscription_defer_event == de && c->mainloop == m);
c->mainloop->defer_enable(c->subscription_defer_event, 0);
/* Dispatch queued events */
if (c->subscription_event_queue) {
pa_subscription_event *e;
while ((e = pa_queue_pop(c->subscription_event_queue))) {
for (s = c->subscriptions; s; s = s->next) {
if (!s->dead && pa_subscription_match_flags(s->mask, e->type))
s->callback(c, e->type, e->index, s->userdata);
}
pa_xfree(e);
}
}
/* Remove dead subscriptions */
s = c->subscriptions;
while (s) {
pa_subscription *n = s->next;
if (s->dead)
free_item(s);
s = n;
}
}
/* Schedule an mainloop event so that a pending subscription event is dispatched */
static void sched_event(pa_core *c) {
assert(c);
if (!c->subscription_defer_event) {
c->subscription_defer_event = c->mainloop->defer_new(c->mainloop, defer_cb, c);
assert(c->subscription_defer_event);
}
c->mainloop->defer_enable(c->subscription_defer_event, 1);
}
/* Append a new subscription event to the subscription event queue and schedule a main loop event */
void pa_subscription_post(pa_core *c, pa_subscription_event_type_t t, uint32_t index) {
pa_subscription_event *e;
assert(c);
e = pa_xmalloc(sizeof(pa_subscription_event));
e->type = t;
e->index = index;
if (!c->subscription_event_queue) {
c->subscription_event_queue = pa_queue_new();
assert(c->subscription_event_queue);
}
pa_queue_push(c->subscription_event_queue, e);
sched_event(c);
}

View file

@ -0,0 +1,37 @@
#ifndef foocoresubscribehfoo
#define foocoresubscribehfoo
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
typedef struct pa_subscription pa_subscription;
typedef struct pa_subscription_event pa_subscription_event;
#include <pulsecore/core.h>
#include <pulsecore/native-common.h>
pa_subscription* pa_subscription_new(pa_core *c, pa_subscription_mask_t m, void (*callback)(pa_core *c, pa_subscription_event_type_t t, uint32_t index, void *userdata), void *userdata);
void pa_subscription_free(pa_subscription*s);
void pa_subscription_free_all(pa_core *c);
void pa_subscription_post(pa_core *c, pa_subscription_event_type_t t, uint32_t idx);
#endif

1023
src/pulsecore/core-util.c Normal file

File diff suppressed because it is too large Load diff

88
src/pulsecore/core-util.h Normal file
View file

@ -0,0 +1,88 @@
#ifndef foocoreutilhfoo
#define foocoreutilhfoo
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <sys/types.h>
#include <inttypes.h>
#include <stdarg.h>
#include <stdio.h>
#include <pulsecore/gccmacro.h>
struct timeval;
void pa_make_nonblock_fd(int fd);
int pa_make_secure_dir(const char* dir);
int pa_make_secure_parent_dir(const char *fn);
ssize_t pa_read(int fd, void *buf, size_t count);
ssize_t pa_write(int fd, const void *buf, size_t count);
ssize_t pa_loop_read(int fd, void*data, size_t size);
ssize_t pa_loop_write(int fd, const void*data, size_t size);
void pa_check_signal_is_blocked(int sig);
char *pa_sprintf_malloc(const char *format, ...) PA_GCC_PRINTF_ATTR(1,2);
char *pa_vsprintf_malloc(const char *format, va_list ap);
char *pa_strlcpy(char *b, const char *s, size_t l);
char *pa_parent_dir(const char *fn);
void pa_raise_priority(void);
void pa_reset_priority(void);
int pa_fd_set_cloexec(int fd, int b);
int pa_parse_boolean(const char *s);
char *pa_split(const char *c, const char*delimiters, const char **state);
char *pa_split_spaces(const char *c, const char **state);
char *pa_strip_nl(char *s);
const char *pa_strsignal(int sig);
int pa_own_uid_in_group(const char *name, gid_t *gid);
int pa_uid_in_group(uid_t uid, const char *name);
int pa_lock_fd(int fd, int b);
int pa_lock_lockfile(const char *fn);
int pa_unlock_lockfile(const char *fn, int fd);
FILE *pa_open_config_file(const char *global, const char *local, const char *env, char **result, const char *mode);
char *pa_hexstr(const uint8_t* d, size_t dlength, char *s, size_t slength);
size_t pa_parsehex(const char *p, uint8_t *d, size_t dlength);
int pa_startswith(const char *s, const char *pfx);
int pa_endswith(const char *s, const char *sfx);
char *pa_runtime_path(const char *fn, char *s, size_t l);
int pa_atoi(const char *s, int32_t *ret_i);
int pa_atou(const char *s, uint32_t *ret_u);
#endif

160
src/pulsecore/core.c Normal file
View file

@ -0,0 +1,160 @@
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdlib.h>
#include <assert.h>
#include <stdio.h>
#include <signal.h>
#include <pulse/timeval.h>
#include <pulse/xmalloc.h>
#include <pulsecore/module.h>
#include <pulsecore/sink.h>
#include <pulsecore/source.h>
#include <pulsecore/namereg.h>
#include <pulsecore/core-util.h>
#include <pulsecore/core-scache.h>
#include <pulsecore/autoload.h>
#include <pulsecore/core-subscribe.h>
#include <pulsecore/props.h>
#include <pulsecore/random.h>
#include "core.h"
pa_core* pa_core_new(pa_mainloop_api *m) {
pa_core* c;
c = pa_xmalloc(sizeof(pa_core));
c->mainloop = m;
c->clients = pa_idxset_new(NULL, NULL);
c->sinks = pa_idxset_new(NULL, NULL);
c->sources = pa_idxset_new(NULL, NULL);
c->source_outputs = pa_idxset_new(NULL, NULL);
c->sink_inputs = pa_idxset_new(NULL, NULL);
c->default_source_name = c->default_sink_name = NULL;
c->modules = NULL;
c->namereg = NULL;
c->scache = NULL;
c->autoload_idxset = NULL;
c->autoload_hashmap = NULL;
c->running_as_daemon = 0;
c->default_sample_spec.format = PA_SAMPLE_S16NE;
c->default_sample_spec.rate = 44100;
c->default_sample_spec.channels = 2;
c->module_auto_unload_event = NULL;
c->module_defer_unload_event = NULL;
c->scache_auto_unload_event = NULL;
c->subscription_defer_event = NULL;
c->subscription_event_queue = NULL;
c->subscriptions = NULL;
c->memblock_stat = pa_memblock_stat_new();
c->disallow_module_loading = 0;
c->quit_event = NULL;
c->exit_idle_time = -1;
c->module_idle_time = 20;
c->scache_idle_time = 20;
c->resample_method = PA_RESAMPLER_SRC_SINC_FASTEST;
pa_property_init(c);
pa_random(&c->cookie, sizeof(c->cookie));
#ifdef SIGPIPE
pa_check_signal_is_blocked(SIGPIPE);
#endif
return c;
}
void pa_core_free(pa_core *c) {
assert(c);
pa_module_unload_all(c);
assert(!c->modules);
assert(pa_idxset_isempty(c->clients));
pa_idxset_free(c->clients, NULL, NULL);
assert(pa_idxset_isempty(c->sinks));
pa_idxset_free(c->sinks, NULL, NULL);
assert(pa_idxset_isempty(c->sources));
pa_idxset_free(c->sources, NULL, NULL);
assert(pa_idxset_isempty(c->source_outputs));
pa_idxset_free(c->source_outputs, NULL, NULL);
assert(pa_idxset_isempty(c->sink_inputs));
pa_idxset_free(c->sink_inputs, NULL, NULL);
pa_scache_free(c);
pa_namereg_free(c);
pa_autoload_free(c);
pa_subscription_free_all(c);
if (c->quit_event)
c->mainloop->time_free(c->quit_event);
pa_xfree(c->default_source_name);
pa_xfree(c->default_sink_name);
pa_memblock_stat_unref(c->memblock_stat);
pa_property_cleanup(c);
pa_xfree(c);
}
static void quit_callback(pa_mainloop_api*m, pa_time_event *e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) {
pa_core *c = userdata;
assert(c->quit_event = e);
m->quit(m, 0);
}
void pa_core_check_quit(pa_core *c) {
assert(c);
if (!c->quit_event && c->exit_idle_time >= 0 && pa_idxset_size(c->clients) == 0) {
struct timeval tv;
pa_gettimeofday(&tv);
tv.tv_sec+= c->exit_idle_time;
c->quit_event = c->mainloop->time_new(c->mainloop, &tv, quit_callback, c);
} else if (c->quit_event && pa_idxset_size(c->clients) > 0) {
c->mainloop->time_free(c->quit_event);
c->quit_event = NULL;
}
}

82
src/pulsecore/core.h Normal file
View file

@ -0,0 +1,82 @@
#ifndef foocorehfoo
#define foocorehfoo
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
typedef struct pa_core pa_core;
#include <pulsecore/idxset.h>
#include <pulsecore/hashmap.h>
#include <pulse/mainloop-api.h>
#include <pulse/sample.h>
#include <pulsecore/memblock.h>
#include <pulsecore/resampler.h>
#include <pulsecore/queue.h>
#include <pulsecore/core-subscribe.h>
/* The core structure of pulseaudio. Every pulseaudio daemon contains
* exactly one of these. It is used for storing kind of global
* variables for the daemon. */
struct pa_core {
/* A random value which may be used to identify this instance of
* pulseaudio. Not cryptographically secure in any way. */
uint32_t cookie;
pa_mainloop_api *mainloop;
/* idxset of all kinds of entities */
pa_idxset *clients, *sinks, *sources, *sink_inputs, *source_outputs, *modules, *scache, *autoload_idxset;
/* Some hashmaps for all sorts of entities */
pa_hashmap *namereg, *autoload_hashmap, *properties;
/* The name of the default sink/source */
char *default_source_name, *default_sink_name;
pa_sample_spec default_sample_spec;
pa_time_event *module_auto_unload_event;
pa_defer_event *module_defer_unload_event;
pa_defer_event *subscription_defer_event;
pa_queue *subscription_event_queue;
pa_subscription *subscriptions;
pa_memblock_stat *memblock_stat;
int disallow_module_loading, running_as_daemon;
int exit_idle_time, module_idle_time, scache_idle_time;
pa_time_event *quit_event;
pa_time_event *scache_auto_unload_event;
pa_resample_method_t resample_method;
};
pa_core* pa_core_new(pa_mainloop_api *m);
void pa_core_free(pa_core*c);
/* Check whether noone is connected to this core */
void pa_core_check_quit(pa_core *c);
#endif

55
src/pulsecore/dllmain.c Normal file
View file

@ -0,0 +1,55 @@
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef OS_IS_WIN32
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <windows.h>
extern pa_set_root(HANDLE handle);
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
WSADATA data;
switch (fdwReason) {
case DLL_PROCESS_ATTACH:
if (!pa_set_root(hinstDLL))
return FALSE;
WSAStartup(MAKEWORD(2, 0), &data);
break;
case DLL_PROCESS_DETACH:
WSACleanup();
break;
}
return TRUE;
}
#endif /* OS_IS_WIN32 */

103
src/pulsecore/dynarray.c Normal file
View file

@ -0,0 +1,103 @@
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <string.h>
#include <assert.h>
#include <stdlib.h>
#include <pulse/xmalloc.h>
#include "dynarray.h"
/* If the array becomes to small, increase its size by 100 entries */
#define INCREASE_BY 100
struct pa_dynarray {
void **data;
unsigned n_allocated, n_entries;
};
pa_dynarray* pa_dynarray_new(void) {
pa_dynarray *a;
a = pa_xnew(pa_dynarray, 1);
a->data = NULL;
a->n_entries = 0;
a->n_allocated = 0;
return a;
}
void pa_dynarray_free(pa_dynarray* a, void (*func)(void *p, void *userdata), void *userdata) {
unsigned i;
assert(a);
if (func)
for (i = 0; i < a->n_entries; i++)
if (a->data[i])
func(a->data[i], userdata);
pa_xfree(a->data);
pa_xfree(a);
}
void pa_dynarray_put(pa_dynarray*a, unsigned i, void *p) {
assert(a);
if (i >= a->n_allocated) {
unsigned n;
if (!p)
return;
n = i+INCREASE_BY;
a->data = pa_xrealloc(a->data, sizeof(void*)*n);
memset(a->data+a->n_allocated, 0, sizeof(void*)*(n-a->n_allocated));
a->n_allocated = n;
}
a->data[i] = p;
if (i >= a->n_entries)
a->n_entries = i+1;
}
unsigned pa_dynarray_append(pa_dynarray*a, void *p) {
unsigned i = a->n_entries;
pa_dynarray_put(a, i, p);
return i;
}
void *pa_dynarray_get(pa_dynarray*a, unsigned i) {
assert(a);
if (i >= a->n_allocated)
return NULL;
assert(a->data);
return a->data[i];
}
unsigned pa_dynarray_size(pa_dynarray*a) {
assert(a);
return a->n_entries;
}

49
src/pulsecore/dynarray.h Normal file
View file

@ -0,0 +1,49 @@
#ifndef foodynarrayhfoo
#define foodynarrayhfoo
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
typedef struct pa_dynarray pa_dynarray;
/* Implementation of a simple dynamically sized array. The array
* expands if required, but doesn't shrink if possible. Memory
* management of the array's entries is the user's job. */
pa_dynarray* pa_dynarray_new(void);
/* Free the array calling the specified function for every entry in
* the array. The function may be NULL. */
void pa_dynarray_free(pa_dynarray* a, void (*func)(void *p, void *userdata), void *userdata);
/* Store p at position i in the array */
void pa_dynarray_put(pa_dynarray*a, unsigned i, void *p);
/* Store p a the first free position in the array. Returns the index
* of that entry. If entries are removed from the array their position
* are not filled any more by this function. */
unsigned pa_dynarray_append(pa_dynarray*a, void *p);
void *pa_dynarray_get(pa_dynarray*a, unsigned i);
unsigned pa_dynarray_size(pa_dynarray*a);
#endif

View file

@ -0,0 +1,77 @@
#ifndef fooendianmacroshfoo
#define fooendianmacroshfoo
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <inttypes.h>
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#define INT16_SWAP(x) ( (int16_t) ( ((uint16_t) x >> 8) | ((uint16_t) x << 8) ) )
#define UINT16_SWAP(x) ( (uint16_t) ( ((uint16_t) x >> 8) | ((uint16_t) x << 8) ) )
#define INT32_SWAP(x) ( (int32_t) ( ((uint32_t) x >> 24) | ((uint32_t) x << 24) | (((uint32_t) x & 0xFF00) << 8) | ((((uint32_t) x) >> 8) & 0xFF00) ) )
#define UINT32_SWAP(x) ( (uint32_t) ( ((uint32_t) x >> 24) | ((uint32_t) x << 24) | (((uint32_t) x & 0xFF00) << 8) | ((((uint32_t) x) >> 8) & 0xFF00) ) )
#define MAYBE_INT32_SWAP(c,x) ((c) ? INT32_SWAP(x) : x)
#define MAYBE_UINT32_SWAP(c,x) ((c) ? UINT32_SWAP(x) : x)
#ifdef WORDS_BIGENDIAN
#define INT16_FROM_LE(x) INT16_SWAP(x)
#define INT16_FROM_BE(x) ((int16_t)(x))
#define INT16_TO_LE(x) INT16_SWAP(x)
#define INT16_TO_BE(x) ((int16_t)(x))
#define UINT16_FROM_LE(x) UINT16_SWAP(x)
#define UINT16_FROM_BE(x) ((uint16_t)(x))
#define INT32_FROM_LE(x) INT32_SWAP(x)
#define INT32_FROM_BE(x) ((int32_t)(x))
#define UINT32_FROM_LE(x) UINT32_SWAP(x)
#define UINT32_FROM_BE(x) ((uint32_t)(x))
#define UINT32_TO_LE(x) UINT32_SWAP(x)
#define UINT32_TO_BE(x) ((uint32_t)(x))
#else
#define INT16_FROM_LE(x) ((int16_t)(x))
#define INT16_FROM_BE(x) INT16_SWAP(x)
#define INT16_TO_LE(x) ((int16_t)(x))
#define INT16_TO_BE(x) INT16_SWAP(x)
#define UINT16_FROM_LE(x) ((uint16_t)(x))
#define UINT16_FROM_BE(x) UINT16_SWAP(x)
#define INT32_FROM_LE(x) ((int32_t)(x))
#define INT32_FROM_BE(x) INT32_SWAP(x)
#define UINT32_FROM_LE(x) ((uint32_t)(x))
#define UINT32_FROM_BE(x) UINT32_SWAP(x)
#define UINT32_TO_LE(x) ((uint32_t)(x))
#define UINT32_TO_BE(x) UINT32_SWAP(x)
#endif
#endif

209
src/pulsecore/esound.h Normal file
View file

@ -0,0 +1,209 @@
#ifndef fooesoundhfoo
#define fooesoundhfoo
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
/* Most of the following is blatantly stolen from esound. */
/* path and name of the default EsounD domain socket */
#define ESD_UNIX_SOCKET_DIR "/tmp/.esd"
#define ESD_UNIX_SOCKET_NAME "/tmp/.esd/socket"
/* length of the audio buffer size */
#define ESD_BUF_SIZE (4 * 1024)
/* maximum size we can write(). Otherwise we might overflow */
#define ESD_MAX_WRITE_SIZE (21 * 4096)
/* length of the authorization key, octets */
#define ESD_KEY_LEN (16)
/* default port for the EsounD server */
#define ESD_DEFAULT_PORT (16001)
/* default sample rate for the EsounD server */
#define ESD_DEFAULT_RATE (44100)
/* maximum length of a stream/sample name */
#define ESD_NAME_MAX (128)
/* a magic number to identify the relative endianness of a client */
#define ESD_ENDIAN_KEY ((uint32_t) (('E' << 24) + ('N' << 16) + ('D' << 8) + ('N')))
#define ESD_VOLUME_BASE (256)
/*************************************/
/* what can we do to/with the EsounD */
enum esd_proto {
ESD_PROTO_CONNECT, /* implied on inital client connection */
/* pseudo "security" functionality */
ESD_PROTO_LOCK, /* disable "foreign" client connections */
ESD_PROTO_UNLOCK, /* enable "foreign" client connections */
/* stream functionality: play, record, monitor */
ESD_PROTO_STREAM_PLAY, /* play all following data as a stream */
ESD_PROTO_STREAM_REC, /* record data from card as a stream */
ESD_PROTO_STREAM_MON, /* send mixed buffer output as a stream */
/* sample functionality: cache, free, play, loop, EOL, kill */
ESD_PROTO_SAMPLE_CACHE, /* cache a sample in the server */
ESD_PROTO_SAMPLE_FREE, /* release a sample in the server */
ESD_PROTO_SAMPLE_PLAY, /* play a cached sample */
ESD_PROTO_SAMPLE_LOOP, /* loop a cached sample, til eoloop */
ESD_PROTO_SAMPLE_STOP, /* stop a looping sample when done */
ESD_PROTO_SAMPLE_KILL, /* stop the looping sample immed. */
/* free and reclaim /dev/dsp functionality */
ESD_PROTO_STANDBY, /* release /dev/dsp and ignore all data */
ESD_PROTO_RESUME, /* reclaim /dev/dsp and play sounds again */
/* TODO: move these to a more logical place. NOTE: will break the protocol */
ESD_PROTO_SAMPLE_GETID, /* get the ID for an already-cached sample */
ESD_PROTO_STREAM_FILT, /* filter mixed buffer output as a stream */
/* esd remote management */
ESD_PROTO_SERVER_INFO, /* get server info (ver, sample rate, format) */
ESD_PROTO_ALL_INFO, /* get all info (server info, players, samples) */
ESD_PROTO_SUBSCRIBE, /* track new and removed players and samples */
ESD_PROTO_UNSUBSCRIBE, /* stop tracking updates */
/* esd remote control */
ESD_PROTO_STREAM_PAN, /* set stream panning */
ESD_PROTO_SAMPLE_PAN, /* set default sample panning */
/* esd status */
ESD_PROTO_STANDBY_MODE, /* see if server is in standby, autostandby, etc */
/* esd latency */
ESD_PROTO_LATENCY, /* retrieve latency between write()'s and output */
ESD_PROTO_MAX /* for bounds checking */
};
/******************/
/* The EsounD api */
/* the properties of a sound buffer are logically or'd */
/* bits of stream/sample data */
#define ESD_MASK_BITS ( 0x000F )
#define ESD_BITS8 ( 0x0000 )
#define ESD_BITS16 ( 0x0001 )
/* how many interleaved channels of data */
#define ESD_MASK_CHAN ( 0x00F0 )
#define ESD_MONO ( 0x0010 )
#define ESD_STEREO ( 0x0020 )
/* whether it's a stream or a sample */
#define ESD_MASK_MODE ( 0x0F00 )
#define ESD_STREAM ( 0x0000 )
#define ESD_SAMPLE ( 0x0100 )
#define ESD_ADPCM ( 0x0200 ) /* TODO: anyone up for this? =P */
/* the function of the stream/sample, and common functions */
#define ESD_MASK_FUNC ( 0xF000 )
#define ESD_PLAY ( 0x1000 )
/* functions for streams only */
#define ESD_MONITOR ( 0x0000 )
#define ESD_RECORD ( 0x2000 )
/* functions for samples only */
#define ESD_STOP ( 0x0000 )
#define ESD_LOOP ( 0x2000 )
typedef int esd_format_t;
typedef int esd_proto_t;
/*******************************************************************/
/* esdmgr.c - functions to implement a "sound manager" for esd */
/* structures to retrieve information about streams/samples from the server */
typedef struct esd_server_info {
int version; /* server version encoded as an int */
esd_format_t format; /* magic int with the format info */
int rate; /* sample rate */
} esd_server_info_t;
typedef struct esd_player_info {
struct esd_player_info *next; /* point to next entry in list */
esd_server_info_t *server; /* the server that contains this stream */
int source_id; /* either a stream fd or sample id */
char name[ ESD_NAME_MAX ]; /* name of stream for remote control */
int rate; /* sample rate */
int left_vol_scale; /* volume scaling */
int right_vol_scale;
esd_format_t format; /* magic int with the format info */
} esd_player_info_t;
typedef struct esd_sample_info {
struct esd_sample_info *next; /* point to next entry in list */
esd_server_info_t *server; /* the server that contains this sample */
int sample_id; /* either a stream fd or sample id */
char name[ ESD_NAME_MAX ]; /* name of stream for remote control */
int rate; /* sample rate */
int left_vol_scale; /* volume scaling */
int right_vol_scale;
esd_format_t format; /* magic int with the format info */
int length; /* total buffer length */
} esd_sample_info_t;
typedef struct esd_info {
esd_server_info_t *server;
esd_player_info_t *player_list;
esd_sample_info_t *sample_list;
} esd_info_t;
enum esd_standby_mode {
ESM_ERROR, ESM_ON_STANDBY, ESM_ON_AUTOSTANDBY, ESM_RUNNING
};
typedef int esd_standby_mode_t;
enum esd_client_state {
ESD_STREAMING_DATA, /* data from here on is streamed data */
ESD_CACHING_SAMPLE, /* midway through caching a sample */
ESD_NEEDS_REQDATA, /* more data needed to complere request */
ESD_NEXT_REQUEST, /* proceed to next request */
ESD_CLIENT_STATE_MAX /* place holder */
};
typedef int esd_client_state_t;
/* the endian key is transferred in binary, if it's read into int, */
/* and matches ESD_ENDIAN_KEY (ENDN), then the endianness of the */
/* server and the client match; if it's SWAP_ENDIAN_KEY, swap data */
#define ESD_SWAP_ENDIAN_KEY (UINT32_SWAP(ESD_ENDIAN_KEY))
#endif

2531
src/pulsecore/g711.c Normal file

File diff suppressed because it is too large Load diff

40
src/pulsecore/g711.h Normal file
View file

@ -0,0 +1,40 @@
#ifndef foog711hfoo
#define foog711hfoo
/* g711.h - include for G711 u-law and a-law conversion routines
**
** Copyright (C) 2001 Chris Bagwell
**
** Permission to use, copy, modify, and distribute this software and its
** documentation for any purpose and without fee is hereby granted, provided
** that the above copyright notice appear in all copies and that both that
** copyright notice and this permission notice appear in supporting
** documentation. This software is provided "as is" without express or
** implied warranty.
*/
/** Copied from sox -- Lennart Poettring*/
#include <inttypes.h>
#ifdef FAST_ALAW_CONVERSION
extern uint8_t _st_13linear2alaw[0x2000];
extern int16_t _st_alaw2linear16[256];
#define st_13linear2alaw(sw) (_st_13linear2alaw[(sw + 0x1000)])
#define st_alaw2linear16(uc) (_st_alaw2linear16[uc])
#else
unsigned char st_13linear2alaw(int16_t pcm_val);
int16_t st_alaw2linear16(unsigned char);
#endif
#ifdef FAST_ULAW_CONVERSION
extern uint8_t _st_14linear2ulaw[0x4000];
extern int16_t _st_ulaw2linear16[256];
#define st_14linear2ulaw(sw) (_st_14linear2ulaw[(sw + 0x2000)])
#define st_ulaw2linear16(uc) (_st_ulaw2linear16[uc])
#else
unsigned char st_14linear2ulaw(int16_t pcm_val);
int16_t st_ulaw2linear16(unsigned char);
#endif
#endif

53
src/pulsecore/gccmacro.h Normal file
View file

@ -0,0 +1,53 @@
#ifndef foogccmacrohfoo
#define foogccmacrohfoo
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef __GNUC__
#define PA_GCC_PRINTF_ATTR(a,b) __attribute__ ((format (printf, a, b)))
#else
/** If we're in GNU C, use some magic for detecting invalid format strings */
#define PA_GCC_PRINTF_ATTR(a,b)
#endif
#if defined(__GNUC__) && (__GNUC__ >= 4)
#define PA_GCC_SENTINEL __attribute__ ((sentinel))
#else
/** Macro for usage of GCC's sentinel compilation warnings */
#define PA_GCC_SENTINEL
#endif
#ifdef __GNUC__
#define PA_GCC_NORETURN __attribute__((noreturn))
#else
/** Macro for no-return functions */
#define PA_GCC_NORETURN
#endif
#ifdef __GNUC__
#define PA_GCC_UNUSED __attribute__ ((unused))
#else
/** Macro for not used parameter */
#define PA_GCC_UNUSED
#endif
#endif

196
src/pulsecore/hashmap.c Normal file
View file

@ -0,0 +1,196 @@
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <pulse/xmalloc.h>
#include <pulsecore/idxset.h>
#include <pulsecore/log.h>
#include "hashmap.h"
#define BUCKETS 1023
struct hashmap_entry {
struct hashmap_entry *next, *previous, *bucket_next, *bucket_previous;
unsigned hash;
const void *key;
void *value;
};
struct pa_hashmap {
unsigned size;
struct hashmap_entry **data;
struct hashmap_entry *first_entry;
unsigned n_entries;
unsigned (*hash_func) (const void *p);
int (*compare_func) (const void*a, const void*b);
};
pa_hashmap *pa_hashmap_new(unsigned (*hash_func) (const void *p), int (*compare_func) (const void*a, const void*b)) {
pa_hashmap *h;
h = pa_xmalloc(sizeof(pa_hashmap));
h->data = pa_xmalloc0(sizeof(struct hashmap_entry*)*(h->size = BUCKETS));
h->first_entry = NULL;
h->n_entries = 0;
h->hash_func = hash_func ? hash_func : pa_idxset_trivial_hash_func;
h->compare_func = compare_func ? compare_func : pa_idxset_trivial_compare_func;
return h;
}
static void remove(pa_hashmap *h, struct hashmap_entry *e) {
assert(e);
if (e->next)
e->next->previous = e->previous;
if (e->previous)
e->previous->next = e->next;
else
h->first_entry = e->next;
if (e->bucket_next)
e->bucket_next->bucket_previous = e->bucket_previous;
if (e->bucket_previous)
e->bucket_previous->bucket_next = e->bucket_next;
else {
assert(e->hash < h->size);
h->data[e->hash] = e->bucket_next;
}
pa_xfree(e);
h->n_entries--;
}
void pa_hashmap_free(pa_hashmap*h, void (*free_func)(void *p, void *userdata), void *userdata) {
assert(h);
while (h->first_entry) {
if (free_func)
free_func(h->first_entry->value, userdata);
remove(h, h->first_entry);
}
pa_xfree(h->data);
pa_xfree(h);
}
static struct hashmap_entry *get(pa_hashmap *h, unsigned hash, const void *key) {
struct hashmap_entry *e;
assert(h && hash < h->size);
for (e = h->data[hash]; e; e = e->bucket_next)
if (h->compare_func(e->key, key) == 0)
return e;
return NULL;
}
int pa_hashmap_put(pa_hashmap *h, const void *key, void *value) {
struct hashmap_entry *e;
unsigned hash;
assert(h);
hash = h->hash_func(key) % h->size;
if ((e = get(h, hash, key)))
return -1;
e = pa_xmalloc(sizeof(struct hashmap_entry));
e->hash = hash;
e->key = key;
e->value = value;
e->previous = NULL;
e->next = h->first_entry;
if (h->first_entry)
h->first_entry->previous = e;
h->first_entry = e;
e->bucket_previous = NULL;
e->bucket_next = h->data[hash];
if (h->data[hash])
h->data[hash]->bucket_previous = e;
h->data[hash] = e;
h->n_entries ++;
return 0;
}
void* pa_hashmap_get(pa_hashmap *h, const void *key) {
unsigned hash;
struct hashmap_entry *e;
assert(h && key);
hash = h->hash_func(key) % h->size;
if (!(e = get(h, hash, key)))
return NULL;
return e->value;
}
void* pa_hashmap_remove(pa_hashmap *h, const void *key) {
struct hashmap_entry *e;
unsigned hash;
void *data;
assert(h && key);
hash = h->hash_func(key) % h->size;
if (!(e = get(h, hash, key)))
return NULL;
data = e->value;
remove(h, e);
return data;
}
unsigned pa_hashmap_size(pa_hashmap *h) {
return h->n_entries;
}
void *pa_hashmap_iterate(pa_hashmap *h, void **state, const void **key) {
assert(h && state);
if (!*state)
*state = h->first_entry;
else
*state = ((struct hashmap_entry*) *state)->next;
if (!*state) {
if (key)
*key = NULL;
return NULL;
}
if (key)
*key = ((struct hashmap_entry*) *state)->key;
return ((struct hashmap_entry*) *state)->value;
}

53
src/pulsecore/hashmap.h Normal file
View file

@ -0,0 +1,53 @@
#ifndef foohashmaphfoo
#define foohashmaphfoo
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
/* Simple Implementation of a hash table. Memory management is the
* user's job. It's a good idea to have the key pointer point to a
* string in the value data. */
typedef struct pa_hashmap pa_hashmap;
/* Create a new hashmap. Use the specified functions for hashing and comparing objects in the map */
pa_hashmap *pa_hashmap_new(unsigned (*hash_func) (const void *p), int (*compare_func) (const void*a, const void*b));
/* Free the hash table. Calls the specified function for every value in the table. The function may be NULL */
void pa_hashmap_free(pa_hashmap*, void (*free_func)(void *p, void *userdata), void *userdata);
/* Returns non-zero when the entry already exists */
int pa_hashmap_put(pa_hashmap *h, const void *key, void *value);
void* pa_hashmap_get(pa_hashmap *h, const void *key);
/* Returns the data of the entry while removing */
void* pa_hashmap_remove(pa_hashmap *h, const void *key);
unsigned pa_hashmap_size(pa_hashmap *h);
/* May be used to iterate through the hashmap. Initially the opaque
pointer *state has to be set to NULL. The hashmap may not be
modified during iteration. The key of the entry is returned in
*key, if key is non-NULL. After the last entry in the hashmap NULL
is returned. */
void *pa_hashmap_iterate(pa_hashmap *h, void **state, const void**key);
#endif

391
src/pulsecore/idxset.c Normal file
View file

@ -0,0 +1,391 @@
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <pulse/xmalloc.h>
#include "idxset.h"
typedef struct idxset_entry {
void *data;
uint32_t index;
unsigned hash_value;
struct idxset_entry *hash_prev, *hash_next;
struct idxset_entry* iterate_prev, *iterate_next;
} idxset_entry;
struct pa_idxset {
unsigned (*hash_func) (const void *p);
int (*compare_func)(const void *a, const void *b);
unsigned hash_table_size, n_entries;
idxset_entry **hash_table, **array, *iterate_list_head, *iterate_list_tail;
uint32_t index, start_index, array_size;
};
unsigned pa_idxset_string_hash_func(const void *p) {
unsigned hash = 0;
const char *c;
for (c = p; *c; c++)
hash = 31 * hash + *c;
return hash;
}
int pa_idxset_string_compare_func(const void *a, const void *b) {
return strcmp(a, b);
}
unsigned pa_idxset_trivial_hash_func(const void *p) {
return (unsigned) (long) p;
}
int pa_idxset_trivial_compare_func(const void *a, const void *b) {
return a != b;
}
pa_idxset* pa_idxset_new(unsigned (*hash_func) (const void *p), int (*compare_func) (const void*a, const void*b)) {
pa_idxset *s;
s = pa_xnew(pa_idxset, 1);
s->hash_func = hash_func ? hash_func : pa_idxset_trivial_hash_func;
s->compare_func = compare_func ? compare_func : pa_idxset_trivial_compare_func;
s->hash_table_size = 1023;
s->hash_table = pa_xmalloc0(sizeof(idxset_entry*)*s->hash_table_size);
s->array = NULL;
s->array_size = 0;
s->index = 0;
s->start_index = 0;
s->n_entries = 0;
s->iterate_list_head = s->iterate_list_tail = NULL;
return s;
}
void pa_idxset_free(pa_idxset *s, void (*free_func) (void *p, void *userdata), void *userdata) {
assert(s);
while (s->iterate_list_head) {
idxset_entry *e = s->iterate_list_head;
s->iterate_list_head = s->iterate_list_head->iterate_next;
if (free_func)
free_func(e->data, userdata);
pa_xfree(e);
}
pa_xfree(s->hash_table);
pa_xfree(s->array);
pa_xfree(s);
}
static idxset_entry* hash_scan(pa_idxset *s, idxset_entry* e, const void *p) {
assert(p);
assert(s->compare_func);
for (; e; e = e->hash_next)
if (s->compare_func(e->data, p) == 0)
return e;
return NULL;
}
static void extend_array(pa_idxset *s, uint32_t idx) {
uint32_t i, j, l;
idxset_entry** n;
assert(idx >= s->start_index);
if (idx < s->start_index + s->array_size)
return;
for (i = 0; i < s->array_size; i++)
if (s->array[i])
break;
l = idx - s->start_index - i + 100;
n = pa_xnew0(idxset_entry*, l);
for (j = 0; j < s->array_size-i; j++)
n[j] = s->array[i+j];
pa_xfree(s->array);
s->array = n;
s->array_size = l;
s->start_index += i;
}
static idxset_entry** array_index(pa_idxset*s, uint32_t idx) {
if (idx >= s->start_index + s->array_size)
return NULL;
if (idx < s->start_index)
return NULL;
return s->array + (idx - s->start_index);
}
int pa_idxset_put(pa_idxset*s, void *p, uint32_t *idx) {
unsigned h;
idxset_entry *e, **a;
assert(s && p);
assert(s->hash_func);
h = s->hash_func(p) % s->hash_table_size;
assert(s->hash_table);
if ((e = hash_scan(s, s->hash_table[h], p))) {
if (idx)
*idx = e->index;
return -1;
}
e = pa_xmalloc(sizeof(idxset_entry));
e->data = p;
e->index = s->index++;
e->hash_value = h;
/* Insert into hash table */
e->hash_next = s->hash_table[h];
e->hash_prev = NULL;
if (s->hash_table[h])
s->hash_table[h]->hash_prev = e;
s->hash_table[h] = e;
/* Insert into array */
extend_array(s, e->index);
a = array_index(s, e->index);
assert(a && !*a);
*a = e;
/* Insert into linked list */
e->iterate_next = NULL;
e->iterate_prev = s->iterate_list_tail;
if (s->iterate_list_tail) {
assert(s->iterate_list_head);
s->iterate_list_tail->iterate_next = e;
} else {
assert(!s->iterate_list_head);
s->iterate_list_head = e;
}
s->iterate_list_tail = e;
s->n_entries++;
assert(s->n_entries >= 1);
if (idx)
*idx = e->index;
return 0;
}
void* pa_idxset_get_by_index(pa_idxset*s, uint32_t idx) {
idxset_entry **a;
assert(s);
if (!(a = array_index(s, idx)))
return NULL;
if (!*a)
return NULL;
return (*a)->data;
}
void* pa_idxset_get_by_data(pa_idxset*s, const void *p, uint32_t *idx) {
unsigned h;
idxset_entry *e;
assert(s && p);
assert(s->hash_func);
h = s->hash_func(p) % s->hash_table_size;
assert(s->hash_table);
if (!(e = hash_scan(s, s->hash_table[h], p)))
return NULL;
if (idx)
*idx = e->index;
return e->data;
}
static void remove_entry(pa_idxset *s, idxset_entry *e) {
idxset_entry **a;
assert(s && e);
/* Remove from array */
a = array_index(s, e->index);
assert(a && *a && *a == e);
*a = NULL;
/* Remove from linked list */
if (e->iterate_next)
e->iterate_next->iterate_prev = e->iterate_prev;
else
s->iterate_list_tail = e->iterate_prev;
if (e->iterate_prev)
e->iterate_prev->iterate_next = e->iterate_next;
else
s->iterate_list_head = e->iterate_next;
/* Remove from hash table */
if (e->hash_next)
e->hash_next->hash_prev = e->hash_prev;
if (e->hash_prev)
e->hash_prev->hash_next = e->hash_next;
else
s->hash_table[e->hash_value] = e->hash_next;
pa_xfree(e);
assert(s->n_entries >= 1);
s->n_entries--;
}
void* pa_idxset_remove_by_index(pa_idxset*s, uint32_t idx) {
idxset_entry **a;
void *data;
assert(s);
if (!(a = array_index(s, idx)))
return NULL;
data = (*a)->data;
remove_entry(s, *a);
return data;
}
void* pa_idxset_remove_by_data(pa_idxset*s, const void *data, uint32_t *idx) {
idxset_entry *e;
unsigned h;
void *r;
assert(s->hash_func);
h = s->hash_func(data) % s->hash_table_size;
assert(s->hash_table);
if (!(e = hash_scan(s, s->hash_table[h], data)))
return NULL;
r = e->data;
if (idx)
*idx = e->index;
remove_entry(s, e);
return r;
}
void* pa_idxset_rrobin(pa_idxset *s, uint32_t *idx) {
idxset_entry **a, *e = NULL;
assert(s && idx);
if ((a = array_index(s, *idx)) && *a)
e = (*a)->iterate_next;
if (!e)
e = s->iterate_list_head;
if (!e)
return NULL;
*idx = e->index;
return e->data;
}
void* pa_idxset_first(pa_idxset *s, uint32_t *idx) {
assert(s);
if (!s->iterate_list_head)
return NULL;
if (idx)
*idx = s->iterate_list_head->index;
return s->iterate_list_head->data;
}
void *pa_idxset_next(pa_idxset *s, uint32_t *idx) {
idxset_entry **a, *e = NULL;
assert(s && idx);
if ((a = array_index(s, *idx)) && *a)
e = (*a)->iterate_next;
if (e) {
*idx = e->index;
return e->data;
} else {
*idx = PA_IDXSET_INVALID;
return NULL;
}
}
int pa_idxset_foreach(pa_idxset*s, int (*func)(void *p, uint32_t idx, int *del, void*userdata), void *userdata) {
idxset_entry *e;
assert(s && func);
e = s->iterate_list_head;
while (e) {
int del = 0, r;
idxset_entry *n = e->iterate_next;
r = func(e->data, e->index, &del, userdata);
if (del)
remove_entry(s, e);
if (r < 0)
return r;
e = n;
}
return 0;
}
unsigned pa_idxset_size(pa_idxset*s) {
assert(s);
return s->n_entries;
}
int pa_idxset_isempty(pa_idxset *s) {
assert(s);
return s->n_entries == 0;
}

94
src/pulsecore/idxset.h Normal file
View file

@ -0,0 +1,94 @@
#ifndef fooidxsethfoo
#define fooidxsethfoo
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <inttypes.h>
/* A combination of a set and a dynamic array. Entries are indexable
* both through a numeric automatically generated index and the entry's
* data pointer. As usual, memory management is the user's job. */
/* A special index value denoting the invalid index. */
#define PA_IDXSET_INVALID ((uint32_t) -1)
/* Generic implementations for hash and comparison functions. Just
* compares the pointer or calculates the hash value directly from the
* pointer value. */
unsigned pa_idxset_trivial_hash_func(const void *p);
int pa_idxset_trivial_compare_func(const void *a, const void *b);
/* Generic implementations for hash and comparison functions for strings. */
unsigned pa_idxset_string_hash_func(const void *p);
int pa_idxset_string_compare_func(const void *a, const void *b);
typedef struct pa_idxset pa_idxset;
/* Instantiate a new idxset with the specified hash and comparison functions */
pa_idxset* pa_idxset_new(unsigned (*hash_func) (const void *p), int (*compare_func) (const void*a, const void*b));
/* Free the idxset. When the idxset is not empty the specified function is called for every entry contained */
void pa_idxset_free(pa_idxset *s, void (*free_func) (void *p, void *userdata), void *userdata);
/* Store a new item in the idxset. The index of the item is returned in *idx */
int pa_idxset_put(pa_idxset*s, void *p, uint32_t *idx);
/* Get the entry by its idx */
void* pa_idxset_get_by_index(pa_idxset*s, uint32_t idx);
/* Get the entry by its data. The idx is returned in *index */
void* pa_idxset_get_by_data(pa_idxset*s, const void *p, uint32_t *idx);
/* Similar to pa_idxset_get_by_index(), but removes the entry from the idxset. */
void* pa_idxset_remove_by_index(pa_idxset*s, uint32_t idx);
/* Similar to pa_idxset_get_by_data(), but removes the entry from the idxset */
void* pa_idxset_remove_by_data(pa_idxset*s, const void *p, uint32_t *idx);
/* This may be used to iterate through all entries. When called with
an invalid index value it returns the first entry, otherwise the
next following. The function is best called with *idx =
PA_IDXSET_VALID first. It is safe to manipulate the idxset between
the calls. It is not guaranteed that all entries have already been
returned before the an entry is returned the second time.*/
void* pa_idxset_rrobin(pa_idxset *s, uint32_t *idx);
/* Return the oldest entry in the idxset. Fill in its index in *idx. */
void* pa_idxset_first(pa_idxset *s, uint32_t *idx);
/* Return the entry following the entry indexed by *idx. After the
* call *index contains the index of the returned
* object. pa_idxset_first() and pa_idxset_next() may be used to
* iterate through the set.*/
void *pa_idxset_next(pa_idxset *s, uint32_t *idx);
/* Call a function for every item in the set. If the callback function
returns -1, the loop is terminated. If *del is set to non-zero that
specific item is removed. It is not safe to call any other
functions on the idxset while pa_idxset_foreach is executed. */
int pa_idxset_foreach(pa_idxset*s, int (*func)(void *p, uint32_t idx, int *del, void*userdata), void *userdata);
unsigned pa_idxset_size(pa_idxset*s);
int pa_idxset_isempty(pa_idxset *s);
#endif

80
src/pulsecore/inet_ntop.c Normal file
View file

@ -0,0 +1,80 @@
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <errno.h>
#include <assert.h>
#ifndef HAVE_INET_NTOP
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#include "winsock.h"
#include "inet_ntop.h"
const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt) {
struct in_addr *in = (struct in_addr*)src;
struct in6_addr *in6 = (struct in6_addr*)src;
assert(src && dst);
switch (af) {
case AF_INET:
snprintf(dst, cnt, "%d.%d.%d.%d",
#ifdef WORDS_BIGENDIAN
(int)(in->s_addr >> 24) & 0xff,
(int)(in->s_addr >> 16) & 0xff,
(int)(in->s_addr >> 8) & 0xff,
(int)(in->s_addr >> 0) & 0xff);
#else
(int)(in->s_addr >> 0) & 0xff,
(int)(in->s_addr >> 8) & 0xff,
(int)(in->s_addr >> 16) & 0xff,
(int)(in->s_addr >> 24) & 0xff);
#endif
break;
case AF_INET6:
snprintf(dst, cnt, "%x:%x:%x:%x:%x:%x:%x:%x",
in6->s6_addr[ 0] << 8 | in6->s6_addr[ 1],
in6->s6_addr[ 2] << 8 | in6->s6_addr[ 3],
in6->s6_addr[ 4] << 8 | in6->s6_addr[ 5],
in6->s6_addr[ 6] << 8 | in6->s6_addr[ 7],
in6->s6_addr[ 8] << 8 | in6->s6_addr[ 9],
in6->s6_addr[10] << 8 | in6->s6_addr[11],
in6->s6_addr[12] << 8 | in6->s6_addr[13],
in6->s6_addr[14] << 8 | in6->s6_addr[15]);
break;
default:
errno = EAFNOSUPPORT;
return NULL;
}
return dst;
}
#endif /* INET_NTOP */

12
src/pulsecore/inet_ntop.h Normal file
View file

@ -0,0 +1,12 @@
#ifndef fooinet_ntophfoo
#define fooinet_ntophfoo
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#include "winsock.h"
const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt);
#endif

62
src/pulsecore/inet_pton.c Normal file
View file

@ -0,0 +1,62 @@
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <errno.h>
#include <assert.h>
#ifndef HAVE_INET_PTON
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#include "winsock.h"
#include "inet_pton.h"
int inet_pton(int af, const char *src, void *dst) {
struct in_addr *in = (struct in_addr*)dst;
struct in6_addr *in6 = (struct in6_addr*)dst;
assert(src && dst);
switch (af) {
case AF_INET:
in->s_addr = inet_addr(src);
if (in->s_addr == INADDR_NONE)
return 0;
break;
case AF_INET6:
/* FIXME */
default:
errno = EAFNOSUPPORT;
return -1;
}
return 1;
}
#endif /* INET_PTON */

12
src/pulsecore/inet_pton.h Normal file
View file

@ -0,0 +1,12 @@
#ifndef fooinet_ptonhfoo
#define fooinet_ptonhfoo
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#include "winsock.h"
int inet_pton(int af, const char *src, void *dst);
#endif

417
src/pulsecore/iochannel.c Normal file
View file

@ -0,0 +1,417 @@
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdlib.h>
#include <assert.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef HAVE_SYS_UN_H
#include <sys/un.h>
#endif
#include "winsock.h"
#include <pulse/xmalloc.h>
#include <pulsecore/core-error.h>
#include <pulsecore/core-util.h>
#include <pulsecore/socket-util.h>
#include <pulsecore/log.h>
#include "iochannel.h"
struct pa_iochannel {
int ifd, ofd;
pa_mainloop_api* mainloop;
pa_iochannel_cb_t callback;
void*userdata;
int readable;
int writable;
int hungup;
int no_close;
pa_io_event* input_event, *output_event;
};
static void enable_mainloop_sources(pa_iochannel *io) {
assert(io);
if (io->input_event == io->output_event && io->input_event) {
pa_io_event_flags_t f = PA_IO_EVENT_NULL;
assert(io->input_event);
if (!io->readable)
f |= PA_IO_EVENT_INPUT;
if (!io->writable)
f |= PA_IO_EVENT_OUTPUT;
io->mainloop->io_enable(io->input_event, f);
} else {
if (io->input_event)
io->mainloop->io_enable(io->input_event, io->readable ? PA_IO_EVENT_NULL : PA_IO_EVENT_INPUT);
if (io->output_event)
io->mainloop->io_enable(io->output_event, io->writable ? PA_IO_EVENT_NULL : PA_IO_EVENT_OUTPUT);
}
}
static void callback(pa_mainloop_api* m, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
pa_iochannel *io = userdata;
int changed = 0;
assert(m);
assert(e);
assert(fd >= 0);
assert(userdata);
if ((f & (PA_IO_EVENT_HANGUP|PA_IO_EVENT_ERROR)) && !io->hungup) {
io->hungup = 1;
changed = 1;
}
if ((f & PA_IO_EVENT_INPUT) && !io->readable) {
io->readable = 1;
changed = 1;
assert(e == io->input_event);
}
if ((f & PA_IO_EVENT_OUTPUT) && !io->writable) {
io->writable = 1;
changed = 1;
assert(e == io->output_event);
}
if (changed) {
enable_mainloop_sources(io);
if (io->callback)
io->callback(io, io->userdata);
}
}
pa_iochannel* pa_iochannel_new(pa_mainloop_api*m, int ifd, int ofd) {
pa_iochannel *io;
assert(m);
assert(ifd >= 0 || ofd >= 0);
io = pa_xnew(pa_iochannel, 1);
io->ifd = ifd;
io->ofd = ofd;
io->mainloop = m;
io->userdata = NULL;
io->callback = NULL;
io->readable = 0;
io->writable = 0;
io->hungup = 0;
io->no_close = 0;
io->input_event = io->output_event = NULL;
if (ifd == ofd) {
assert(ifd >= 0);
pa_make_nonblock_fd(io->ifd);
io->input_event = io->output_event = m->io_new(m, ifd, PA_IO_EVENT_INPUT|PA_IO_EVENT_OUTPUT, callback, io);
} else {
if (ifd >= 0) {
pa_make_nonblock_fd(io->ifd);
io->input_event = m->io_new(m, ifd, PA_IO_EVENT_INPUT, callback, io);
}
if (ofd >= 0) {
pa_make_nonblock_fd(io->ofd);
io->output_event = m->io_new(m, ofd, PA_IO_EVENT_OUTPUT, callback, io);
}
}
return io;
}
void pa_iochannel_free(pa_iochannel*io) {
assert(io);
if (io->input_event)
io->mainloop->io_free(io->input_event);
if (io->output_event && (io->output_event != io->input_event))
io->mainloop->io_free(io->output_event);
if (!io->no_close) {
if (io->ifd >= 0)
close(io->ifd);
if (io->ofd >= 0 && io->ofd != io->ifd)
close(io->ofd);
}
pa_xfree(io);
}
int pa_iochannel_is_readable(pa_iochannel*io) {
assert(io);
return io->readable || io->hungup;
}
int pa_iochannel_is_writable(pa_iochannel*io) {
assert(io);
return io->writable && !io->hungup;
}
int pa_iochannel_is_hungup(pa_iochannel*io) {
assert(io);
return io->hungup;
}
ssize_t pa_iochannel_write(pa_iochannel*io, const void*data, size_t l) {
ssize_t r;
assert(io);
assert(data);
assert(l);
assert(io->ofd >= 0);
r = pa_write(io->ofd, data, l);
if (r >= 0) {
io->writable = 0;
enable_mainloop_sources(io);
}
return r;
}
ssize_t pa_iochannel_read(pa_iochannel*io, void*data, size_t l) {
ssize_t r;
assert(io);
assert(data);
assert(io->ifd >= 0);
r = pa_read(io->ifd, data, l);
if (r >= 0) {
io->readable = 0;
enable_mainloop_sources(io);
}
return r;
}
#ifdef SCM_CREDENTIALS
int pa_iochannel_creds_supported(pa_iochannel *io) {
struct sockaddr_un sa;
socklen_t l;
assert(io);
assert(io->ifd >= 0);
assert(io->ofd == io->ifd);
l = sizeof(sa);
if (getsockname(io->ifd, (struct sockaddr*) &sa, &l) < 0)
return 0;
return sa.sun_family == AF_UNIX;
}
int pa_iochannel_creds_enable(pa_iochannel *io) {
int t = 1;
assert(io);
assert(io->ifd >= 0);
if (setsockopt(io->ifd, SOL_SOCKET, SO_PASSCRED, &t, sizeof(t)) < 0) {
pa_log_error("setsockopt(SOL_SOCKET, SO_PASSCRED): %s", pa_cstrerror(errno));
return -1;
}
return 0;
}
ssize_t pa_iochannel_write_with_creds(pa_iochannel*io, const void*data, size_t l) {
ssize_t r;
struct msghdr mh;
struct iovec iov;
uint8_t cmsg_data[CMSG_SPACE(sizeof(struct ucred))];
struct ucred *ucred;
struct cmsghdr *cmsg;
assert(io);
assert(data);
assert(l);
assert(io->ofd >= 0);
memset(&iov, 0, sizeof(iov));
iov.iov_base = (void*) data;
iov.iov_len = l;
memset(cmsg_data, 0, sizeof(cmsg_data));
cmsg = (struct cmsghdr*) cmsg_data;
cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_CREDENTIALS;
ucred = (struct ucred*) CMSG_DATA(cmsg);
ucred->pid = getpid();
ucred->uid = getuid();
ucred->gid = getgid();
memset(&mh, 0, sizeof(mh));
mh.msg_name = NULL;
mh.msg_namelen = 0;
mh.msg_iov = &iov;
mh.msg_iovlen = 1;
mh.msg_control = cmsg_data;
mh.msg_controllen = sizeof(cmsg_data);
mh.msg_flags = 0;
if ((r = sendmsg(io->ofd, &mh, MSG_NOSIGNAL)) >= 0) {
io->writable = 0;
enable_mainloop_sources(io);
}
return r;
}
ssize_t pa_iochannel_read_with_creds(pa_iochannel*io, void*data, size_t l, struct ucred *ucred, int *creds_valid) {
ssize_t r;
struct msghdr mh;
struct iovec iov;
uint8_t cmsg_data[CMSG_SPACE(sizeof(struct ucred))];
assert(io);
assert(data);
assert(l);
assert(io->ifd >= 0);
assert(ucred);
assert(creds_valid);
memset(&iov, 0, sizeof(iov));
iov.iov_base = data;
iov.iov_len = l;
memset(cmsg_data, 0, sizeof(cmsg_data));
memset(&mh, 0, sizeof(mh));
mh.msg_name = NULL;
mh.msg_namelen = 0;
mh.msg_iov = &iov;
mh.msg_iovlen = 1;
mh.msg_control = cmsg_data;
mh.msg_controllen = sizeof(cmsg_data);
mh.msg_flags = 0;
if ((r = recvmsg(io->ifd, &mh, MSG_NOSIGNAL)) >= 0) {
struct cmsghdr *cmsg;
*creds_valid = 0;
for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg)) {
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDENTIALS) {
assert(cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred)));
memcpy(ucred, CMSG_DATA(cmsg), sizeof(struct ucred));
*creds_valid = 1;
break;
}
}
io->readable = 0;
enable_mainloop_sources(io);
}
return r;
}
#else /* SCM_CREDENTIALS */
int pa_iochannel_creds_supported(pa_iochannel *io) {
return 0;
}
int pa_iochannel_creds_enable(pa_iochannel *io) {
return -1;
}
ssize_t pa_iochannel_write_with_creds(pa_iochannel*io, const void*data, size_t l) {
pa_log_error("pa_iochannel_write_with_creds() not supported.");
return -1;
}
ssize_t pa_iochannel_read_with_creds(pa_iochannel*io, void*data, size_t l, struct ucred *ucred, int *creds_valid) {
pa_log_error("pa_iochannel_read_with_creds() not supported.");
return -1;
}
#endif /* SCM_CREDENTIALS */
void pa_iochannel_set_callback(pa_iochannel*io, pa_iochannel_cb_t _callback, void *userdata) {
assert(io);
io->callback = _callback;
io->userdata = userdata;
}
void pa_iochannel_set_noclose(pa_iochannel*io, int b) {
assert(io);
io->no_close = b;
}
void pa_iochannel_socket_peer_to_string(pa_iochannel*io, char*s, size_t l) {
assert(io);
assert(s);
assert(l);
pa_socket_peer_to_string(io->ifd, s, l);
}
int pa_iochannel_socket_set_rcvbuf(pa_iochannel *io, size_t l) {
assert(io);
return pa_socket_set_rcvbuf(io->ifd, l);
}
int pa_iochannel_socket_set_sndbuf(pa_iochannel *io, size_t l) {
assert(io);
return pa_socket_set_sndbuf(io->ofd, l);
}
pa_mainloop_api* pa_iochannel_get_mainloop_api(pa_iochannel *io) {
assert(io);
return io->mainloop;
}

81
src/pulsecore/iochannel.h Normal file
View file

@ -0,0 +1,81 @@
#ifndef fooiochannelhfoo
#define fooiochannelhfoo
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <sys/types.h>
#include <pulse/mainloop-api.h>
/* A wrapper around UNIX file descriptors for attaching them to the a
main event loop. Everytime new data may be read or be written to
the channel a callback function is called. It is safe to destroy
the calling iochannel object from the callback */
/* When pa_iochannel_is_readable() returns non-zero, the user has to
* call this function in a loop until it is no longer set or EOF
* reached. Otherwise strange things may happen when an EOF is
* reached. */
typedef struct pa_iochannel pa_iochannel;
/* Create a new IO channel for the specified file descriptors for
input resp. output. It is safe to pass the same file descriptor for
both parameters (in case of full-duplex channels). For a simplex
channel specify -1 for the other direction. */
pa_iochannel* pa_iochannel_new(pa_mainloop_api*m, int ifd, int ofd);
void pa_iochannel_free(pa_iochannel*io);
ssize_t pa_iochannel_write(pa_iochannel*io, const void*data, size_t l);
ssize_t pa_iochannel_read(pa_iochannel*io, void*data, size_t l);
int pa_iochannel_creds_supported(pa_iochannel *io);
int pa_iochannel_creds_enable(pa_iochannel *io);
struct ucred;
ssize_t pa_iochannel_write_with_creds(pa_iochannel*io, const void*data, size_t l);
ssize_t pa_iochannel_read_with_creds(pa_iochannel*io, void*data, size_t l, struct ucred *ucred, int *creds_valid);
int pa_iochannel_is_readable(pa_iochannel*io);
int pa_iochannel_is_writable(pa_iochannel*io);
int pa_iochannel_is_hungup(pa_iochannel*io);
/* Don't close the file descirptors when the io channel is freed. By
* default the file descriptors are closed. */
void pa_iochannel_set_noclose(pa_iochannel*io, int b);
/* Set the callback function that is called whenever data becomes available for read or write */
typedef void (*pa_iochannel_cb_t)(pa_iochannel*io, void *userdata);
void pa_iochannel_set_callback(pa_iochannel*io, pa_iochannel_cb_t callback, void *userdata);
/* In case the file descriptor is a socket, return a pretty-printed string in *s which describes the peer connected */
void pa_iochannel_socket_peer_to_string(pa_iochannel*io, char*s, size_t l);
/* Use setsockopt() to tune the recieve and send buffers of TCP sockets */
int pa_iochannel_socket_set_rcvbuf(pa_iochannel*io, size_t l);
int pa_iochannel_socket_set_sndbuf(pa_iochannel*io, size_t l);
pa_mainloop_api* pa_iochannel_get_mainloop_api(pa_iochannel *io);
#endif

394
src/pulsecore/ioline.c Normal file
View file

@ -0,0 +1,394 @@
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <errno.h>
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <pulse/xmalloc.h>
#include <pulsecore/core-error.h>
#include <pulsecore/log.h>
#include "ioline.h"
#define BUFFER_LIMIT (64*1024)
#define READ_SIZE (1024)
struct pa_ioline {
pa_iochannel *io;
pa_defer_event *defer_event;
pa_mainloop_api *mainloop;
int ref;
int dead;
char *wbuf;
size_t wbuf_length, wbuf_index, wbuf_valid_length;
char *rbuf;
size_t rbuf_length, rbuf_index, rbuf_valid_length;
void (*callback)(pa_ioline*io, const char *s, void *userdata);
void *userdata;
int defer_close;
};
static void io_callback(pa_iochannel*io, void *userdata);
static void defer_callback(pa_mainloop_api*m, pa_defer_event*e, void *userdata);
pa_ioline* pa_ioline_new(pa_iochannel *io) {
pa_ioline *l;
assert(io);
l = pa_xnew(pa_ioline, 1);
l->io = io;
l->dead = 0;
l->wbuf = NULL;
l->wbuf_length = l->wbuf_index = l->wbuf_valid_length = 0;
l->rbuf = NULL;
l->rbuf_length = l->rbuf_index = l->rbuf_valid_length = 0;
l->callback = NULL;
l->userdata = NULL;
l->ref = 1;
l->mainloop = pa_iochannel_get_mainloop_api(io);
l->defer_event = l->mainloop->defer_new(l->mainloop, defer_callback, l);
l->mainloop->defer_enable(l->defer_event, 0);
l->defer_close = 0;
pa_iochannel_set_callback(io, io_callback, l);
return l;
}
static void ioline_free(pa_ioline *l) {
assert(l);
if (l->io)
pa_iochannel_free(l->io);
if (l->defer_event)
l->mainloop->defer_free(l->defer_event);
pa_xfree(l->wbuf);
pa_xfree(l->rbuf);
pa_xfree(l);
}
void pa_ioline_unref(pa_ioline *l) {
assert(l);
assert(l->ref >= 1);
if ((--l->ref) <= 0)
ioline_free(l);
}
pa_ioline* pa_ioline_ref(pa_ioline *l) {
assert(l);
assert(l->ref >= 1);
l->ref++;
return l;
}
void pa_ioline_close(pa_ioline *l) {
assert(l);
assert(l->ref >= 1);
l->dead = 1;
if (l->io) {
pa_iochannel_free(l->io);
l->io = NULL;
}
if (l->defer_event) {
l->mainloop->defer_free(l->defer_event);
l->defer_event = NULL;
}
if (l->callback)
l->callback = NULL;
}
void pa_ioline_puts(pa_ioline *l, const char *c) {
size_t len;
assert(l);
assert(l->ref >= 1);
assert(c);
if (l->dead)
return;
len = strlen(c);
if (len > BUFFER_LIMIT - l->wbuf_valid_length)
len = BUFFER_LIMIT - l->wbuf_valid_length;
if (len) {
assert(l->wbuf_length >= l->wbuf_valid_length);
/* In case the allocated buffer is too small, enlarge it. */
if (l->wbuf_valid_length + len > l->wbuf_length) {
size_t n = l->wbuf_valid_length+len;
char *new = pa_xmalloc(n);
if (l->wbuf) {
memcpy(new, l->wbuf+l->wbuf_index, l->wbuf_valid_length);
pa_xfree(l->wbuf);
}
l->wbuf = new;
l->wbuf_length = n;
l->wbuf_index = 0;
} else if (l->wbuf_index + l->wbuf_valid_length + len > l->wbuf_length) {
/* In case the allocated buffer fits, but the current index is too far from the start, move it to the front. */
memmove(l->wbuf, l->wbuf+l->wbuf_index, l->wbuf_valid_length);
l->wbuf_index = 0;
}
assert(l->wbuf_index + l->wbuf_valid_length + len <= l->wbuf_length);
/* Append the new string */
memcpy(l->wbuf + l->wbuf_index + l->wbuf_valid_length, c, len);
l->wbuf_valid_length += len;
l->mainloop->defer_enable(l->defer_event, 1);
}
}
void pa_ioline_set_callback(pa_ioline*l, void (*callback)(pa_ioline*io, const char *s, void *userdata), void *userdata) {
assert(l);
assert(l->ref >= 1);
l->callback = callback;
l->userdata = userdata;
}
static void failure(pa_ioline *l, int process_leftover) {
assert(l);
assert(l->ref >= 1);
assert(!l->dead);
if (process_leftover && l->rbuf_valid_length > 0) {
/* Pass the last missing bit to the client */
if (l->callback) {
char *p = pa_xstrndup(l->rbuf+l->rbuf_index, l->rbuf_valid_length);
l->callback(l, p, l->userdata);
pa_xfree(p);
}
}
if (l->callback) {
l->callback(l, NULL, l->userdata);
l->callback = NULL;
}
pa_ioline_close(l);
}
static void scan_for_lines(pa_ioline *l, size_t skip) {
assert(l && l->ref >= 1 && skip < l->rbuf_valid_length);
while (!l->dead && l->rbuf_valid_length > skip) {
char *e, *p;
size_t m;
if (!(e = memchr(l->rbuf + l->rbuf_index + skip, '\n', l->rbuf_valid_length - skip)))
break;
*e = 0;
p = l->rbuf + l->rbuf_index;
m = strlen(p);
l->rbuf_index += m+1;
l->rbuf_valid_length -= m+1;
/* A shortcut for the next time */
if (l->rbuf_valid_length == 0)
l->rbuf_index = 0;
if (l->callback)
l->callback(l, p, l->userdata);
skip = 0;
}
/* If the buffer became too large and still no newline was found, drop it. */
if (l->rbuf_valid_length >= BUFFER_LIMIT)
l->rbuf_index = l->rbuf_valid_length = 0;
}
static int do_write(pa_ioline *l);
static int do_read(pa_ioline *l) {
assert(l && l->ref >= 1);
while (!l->dead && pa_iochannel_is_readable(l->io)) {
ssize_t r;
size_t len;
len = l->rbuf_length - l->rbuf_index - l->rbuf_valid_length;
/* Check if we have to enlarge the read buffer */
if (len < READ_SIZE) {
size_t n = l->rbuf_valid_length+READ_SIZE;
if (n >= BUFFER_LIMIT)
n = BUFFER_LIMIT;
if (l->rbuf_length >= n) {
/* The current buffer is large enough, let's just move the data to the front */
if (l->rbuf_valid_length)
memmove(l->rbuf, l->rbuf+l->rbuf_index, l->rbuf_valid_length);
} else {
/* Enlarge the buffer */
char *new = pa_xmalloc(n);
if (l->rbuf_valid_length)
memcpy(new, l->rbuf+l->rbuf_index, l->rbuf_valid_length);
pa_xfree(l->rbuf);
l->rbuf = new;
l->rbuf_length = n;
}
l->rbuf_index = 0;
}
len = l->rbuf_length - l->rbuf_index - l->rbuf_valid_length;
assert(len >= READ_SIZE);
/* Read some data */
if ((r = pa_iochannel_read(l->io, l->rbuf+l->rbuf_index+l->rbuf_valid_length, len)) <= 0) {
if (r < 0) {
pa_log(__FILE__": read(): %s", pa_cstrerror(errno));
failure(l, 0);
} else
failure(l, 1);
return -1;
}
l->rbuf_valid_length += r;
/* Look if a line has been terminated in the newly read data */
scan_for_lines(l, l->rbuf_valid_length - r);
}
return 0;
}
/* Try to flush the buffer */
static int do_write(pa_ioline *l) {
ssize_t r;
assert(l && l->ref >= 1);
while (!l->dead && pa_iochannel_is_writable(l->io) && l->wbuf_valid_length) {
if ((r = pa_iochannel_write(l->io, l->wbuf+l->wbuf_index, l->wbuf_valid_length)) < 0) {
pa_log(__FILE__": write(): %s", r < 0 ? pa_cstrerror(errno) : "EOF");
failure(l, 0);
return -1;
}
l->wbuf_index += r;
l->wbuf_valid_length -= r;
/* A shortcut for the next time */
if (l->wbuf_valid_length == 0)
l->wbuf_index = 0;
}
return 0;
}
/* Try to flush read/write data */
static void do_work(pa_ioline *l) {
assert(l);
assert(l->ref >= 1);
pa_ioline_ref(l);
l->mainloop->defer_enable(l->defer_event, 0);
if (!l->dead)
do_read(l);
if (!l->dead)
do_write(l);
if (l->defer_close && !l->wbuf_valid_length)
failure(l, 1);
pa_ioline_unref(l);
}
static void io_callback(pa_iochannel*io, void *userdata) {
pa_ioline *l = userdata;
assert(io && l && l->ref >= 1);
do_work(l);
}
static void defer_callback(pa_mainloop_api*m, pa_defer_event*e, void *userdata) {
pa_ioline *l = userdata;
assert(l && l->ref >= 1 && l->mainloop == m && l->defer_event == e);
do_work(l);
}
void pa_ioline_defer_close(pa_ioline *l) {
assert(l);
assert(l->ref >= 1);
l->defer_close = 1;
if (!l->wbuf_valid_length)
l->mainloop->defer_enable(l->defer_event, 1);
}
void pa_ioline_printf(pa_ioline *l, const char *format, ...) {
char *t;
va_list ap;
assert(l);
assert(l->ref >= 1);
va_start(ap, format);
t = pa_vsprintf_malloc(format, ap);
va_end(ap);
pa_ioline_puts(l, t);
pa_xfree(t);
}

51
src/pulsecore/ioline.h Normal file
View file

@ -0,0 +1,51 @@
#ifndef fooiolinehfoo
#define fooiolinehfoo
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <pulsecore/iochannel.h>
#include <pulsecore/core-util.h>
/* An ioline wraps an iochannel for line based communication. A
* callback function is called whenever a new line has been recieved
* from the client */
typedef struct pa_ioline pa_ioline;
pa_ioline* pa_ioline_new(pa_iochannel *io);
void pa_ioline_unref(pa_ioline *l);
pa_ioline* pa_ioline_ref(pa_ioline *l);
void pa_ioline_close(pa_ioline *l);
/* Write a string to the channel */
void pa_ioline_puts(pa_ioline *s, const char *c);
/* Write a string to the channel */
void pa_ioline_printf(pa_ioline *s, const char *format, ...) PA_GCC_PRINTF_ATTR(2,3);
/* Set the callback function that is called for every recieved line */
void pa_ioline_set_callback(pa_ioline*io, void (*callback)(pa_ioline*io, const char *s, void *userdata), void *userdata);
/* Make sure to close the ioline object as soon as the send buffer is emptied */
void pa_ioline_defer_close(pa_ioline *io);
#endif

79
src/pulsecore/llist.h Normal file
View file

@ -0,0 +1,79 @@
#ifndef foollistfoo
#define foollistfoo
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
/* Some macros for maintaining doubly linked lists */
/* The head of the linked list. Use this in the structure that shall
* contain the head of the linked list */
#define PA_LLIST_HEAD(t,name) t *name
/* The pointers in the linked list's items. Use this in the item structure */
#define PA_LLIST_FIELDS(t) t *next, *prev
/* Initialize the list's head */
#define PA_LLIST_HEAD_INIT(t,item) do { (item) = NULL; } while(0)
/* Initialize a list item */
#define PA_LLIST_INIT(t,item) do { \
t *_item = (item); \
assert(_item); \
_item->prev = _item->next = NULL; \
} while(0)
/* Prepend an item to the list */
#define PA_LLIST_PREPEND(t,head,item) do { \
t **_head = &(head), *_item = (item); \
assert(_item); \
if ((_item->next = *_head)) \
_item->next->prev = _item; \
_item->prev = NULL; \
*_head = _item; \
} while (0)
/* Remove an item from the list */
#define PA_LLIST_REMOVE(t,head,item) do { \
t **_head = &(head), *_item = (item); \
assert(_item); \
if (_item->next) \
_item->next->prev = _item->prev; \
if (_item->prev) \
_item->prev->next = _item->next; \
else {\
assert(*_head == _item); \
*_head = _item->next; \
} \
_item->next = _item->prev = NULL; \
} while(0)
#define PA_LLIST_FIND_HEAD(t,item,head) \
do { \
t **_head = (head), *_item = (item); \
*_head = _item; \
assert(_head); \
while ((*_head)->prev) \
*_head = (*_head)->prev; \
} while (0) \
#endif

211
src/pulsecore/log.c Normal file
View file

@ -0,0 +1,211 @@
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <assert.h>
#include <stdarg.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#ifdef HAVE_SYSLOG_H
#include <syslog.h>
#endif
#include <pulse/utf8.h>
#include <pulse/xmalloc.h>
#include <pulsecore/core-util.h>
#include "log.h"
#define ENV_LOGLEVEL "POLYP_LOG"
static char *log_ident = NULL, *log_ident_local = NULL;
static pa_log_target_t log_target = PA_LOG_STDERR;
static void (*user_log_func)(pa_log_level_t l, const char *s) = NULL;
static pa_log_level_t maximal_level = PA_LOG_NOTICE;
#ifdef HAVE_SYSLOG_H
static const int level_to_syslog[] = {
[PA_LOG_ERROR] = LOG_ERR,
[PA_LOG_WARN] = LOG_WARNING,
[PA_LOG_NOTICE] = LOG_NOTICE,
[PA_LOG_INFO] = LOG_INFO,
[PA_LOG_DEBUG] = LOG_DEBUG
};
#endif
void pa_log_set_ident(const char *p) {
if (log_ident)
pa_xfree(log_ident);
if (log_ident_local)
pa_xfree(log_ident_local);
log_ident = pa_xstrdup(p);
log_ident_local = pa_utf8_to_locale(log_ident);
if (!log_ident_local)
log_ident_local = pa_xstrdup(log_ident);
}
void pa_log_set_maximal_level(pa_log_level_t l) {
assert(l < PA_LOG_LEVEL_MAX);
maximal_level = l;
}
void pa_log_set_target(pa_log_target_t t, void (*func)(pa_log_level_t l, const char*s)) {
assert(t == PA_LOG_USER || !func);
log_target = t;
user_log_func = func;
}
void pa_log_levelv(pa_log_level_t level, const char *format, va_list ap) {
const char *e;
char *text, *t, *n;
assert(level < PA_LOG_LEVEL_MAX);
if ((e = getenv(ENV_LOGLEVEL)))
maximal_level = atoi(e);
if (level > maximal_level)
return;
text = pa_vsprintf_malloc(format, ap);
if (!pa_utf8_valid(text))
pa_log_level(level, __FILE__": invalid UTF-8 string following below:");
for (t = text; t; t = n) {
if ((n = strchr(t, '\n'))) {
*n = 0;
n++;
}
if (!*t)
continue;
switch (log_target) {
case PA_LOG_STDERR: {
const char *prefix = "", *suffix = "";
char *local_t;
#ifndef OS_IS_WIN32
/* Yes indeed. Useless, but fun! */
if (isatty(STDERR_FILENO)) {
if (level <= PA_LOG_ERROR) {
prefix = "\x1B[1;31m";
suffix = "\x1B[0m";
} else if (level <= PA_LOG_WARN) {
prefix = "\x1B[1m";
suffix = "\x1B[0m";
}
}
#endif
local_t = pa_utf8_to_locale(t);
if (!local_t)
fprintf(stderr, "%s%s%s\n", prefix, t, suffix);
else {
fprintf(stderr, "%s%s%s\n", prefix, local_t, suffix);
pa_xfree(local_t);
}
break;
}
#ifdef HAVE_SYSLOG_H
case PA_LOG_SYSLOG: {
char *local_t;
openlog(log_ident_local ? log_ident_local : "???", LOG_PID, LOG_USER);
local_t = pa_utf8_to_locale(t);
if (!local_t)
syslog(level_to_syslog[level], "%s", t);
else {
syslog(level_to_syslog[level], "%s", local_t);
pa_xfree(local_t);
}
closelog();
break;
}
#endif
case PA_LOG_USER:
user_log_func(level, t);
break;
case PA_LOG_NULL:
default:
break;
}
}
pa_xfree(text);
}
void pa_log_level(pa_log_level_t level, const char *format, ...) {
va_list ap;
va_start(ap, format);
pa_log_levelv(level, format, ap);
va_end(ap);
}
void pa_log_debug(const char *format, ...) {
va_list ap;
va_start(ap, format);
pa_log_levelv(PA_LOG_DEBUG, format, ap);
va_end(ap);
}
void pa_log_info(const char *format, ...) {
va_list ap;
va_start(ap, format);
pa_log_levelv(PA_LOG_INFO, format, ap);
va_end(ap);
}
void pa_log_notice(const char *format, ...) {
va_list ap;
va_start(ap, format);
pa_log_levelv(PA_LOG_INFO, format, ap);
va_end(ap);
}
void pa_log_warn(const char *format, ...) {
va_list ap;
va_start(ap, format);
pa_log_levelv(PA_LOG_WARN, format, ap);
va_end(ap);
}
void pa_log_error(const char *format, ...) {
va_list ap;
va_start(ap, format);
pa_log_levelv(PA_LOG_ERROR, format, ap);
va_end(ap);
}

69
src/pulsecore/log.h Normal file
View file

@ -0,0 +1,69 @@
#ifndef foologhfoo
#define foologhfoo
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <stdarg.h>
#include <pulsecore/gccmacro.h>
/* A simple logging subsystem */
/* Where to log to */
typedef enum pa_log_target {
PA_LOG_STDERR, /* default */
PA_LOG_SYSLOG,
PA_LOG_USER, /* to user specified function */
PA_LOG_NULL /* to /dev/null */
} pa_log_target_t;
typedef enum pa_log_level {
PA_LOG_ERROR = 0, /* Error messages */
PA_LOG_WARN = 1, /* Warning messages */
PA_LOG_NOTICE = 2, /* Notice messages */
PA_LOG_INFO = 3, /* Info messages */
PA_LOG_DEBUG = 4, /* debug message */
PA_LOG_LEVEL_MAX
} pa_log_level_t;
/* Set an identification for the current daemon. Used when logging to syslog. */
void pa_log_set_ident(const char *p);
/* Set another log target. If t is PA_LOG_USER you may specify a function that is called every log string */
void pa_log_set_target(pa_log_target_t t, void (*func)(pa_log_level_t t, const char*s));
/* Minimal log level */
void pa_log_set_maximal_level(pa_log_level_t l);
/* Do a log line */
void pa_log_debug(const char *format, ...) PA_GCC_PRINTF_ATTR(1,2);
void pa_log_info(const char *format, ...) PA_GCC_PRINTF_ATTR(1,2);
void pa_log_notice(const char *format, ...) PA_GCC_PRINTF_ATTR(1,2);
void pa_log_warn(const char *format, ...) PA_GCC_PRINTF_ATTR(1,2);
void pa_log_error(const char *format, ...) PA_GCC_PRINTF_ATTR(1,2);
void pa_log_level(pa_log_level_t level, const char *format, ...) PA_GCC_PRINTF_ATTR(2,3);
void pa_log_levelv(pa_log_level_t level, const char *format, va_list ap);
#define pa_log pa_log_error
#endif

206
src/pulsecore/mcalign.c Normal file
View file

@ -0,0 +1,206 @@
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <pulse/xmalloc.h>
#include "mcalign.h"
struct pa_mcalign {
size_t base;
pa_memchunk leftover, current;
pa_memblock_stat *memblock_stat;
};
pa_mcalign *pa_mcalign_new(size_t base, pa_memblock_stat *s) {
pa_mcalign *m;
assert(base);
m = pa_xnew(pa_mcalign, 1);
m->base = base;
pa_memchunk_reset(&m->leftover);
pa_memchunk_reset(&m->current);
m->memblock_stat = s;
return m;
}
void pa_mcalign_free(pa_mcalign *m) {
assert(m);
if (m->leftover.memblock)
pa_memblock_unref(m->leftover.memblock);
if (m->current.memblock)
pa_memblock_unref(m->current.memblock);
pa_xfree(m);
}
void pa_mcalign_push(pa_mcalign *m, const pa_memchunk *c) {
assert(m);
assert(c);
assert(c->memblock);
assert(c->length > 0);
assert(!m->current.memblock);
/* Append to the leftover memory block */
if (m->leftover.memblock) {
/* Try to merge */
if (m->leftover.memblock == c->memblock &&
m->leftover.index + m->leftover.length == c->index) {
/* Merge */
m->leftover.length += c->length;
/* If the new chunk is larger than m->base, move it to current */
if (m->leftover.length >= m->base) {
m->current = m->leftover;
pa_memchunk_reset(&m->leftover);
}
} else {
size_t l;
/* We have to copy */
assert(m->leftover.length < m->base);
l = m->base - m->leftover.length;
if (l > c->length)
l = c->length;
/* Can we use the current block? */
pa_memchunk_make_writable(&m->leftover, m->memblock_stat, m->base);
memcpy((uint8_t*) m->leftover.memblock->data + m->leftover.index + m->leftover.length, (uint8_t*) c->memblock->data + c->index, l);
m->leftover.length += l;
assert(m->leftover.length <= m->base && m->leftover.length <= m->leftover.memblock->length);
if (c->length > l) {
/* Save the remainder of the memory block */
m->current = *c;
m->current.index += l;
m->current.length -= l;
pa_memblock_ref(m->current.memblock);
}
}
} else {
/* Nothing to merge or copy, just store it */
if (c->length >= m->base)
m->current = *c;
else
m->leftover = *c;
pa_memblock_ref(c->memblock);
}
}
int pa_mcalign_pop(pa_mcalign *m, pa_memchunk *c) {
assert(m);
assert(c);
/* First test if there's a leftover memory block available */
if (m->leftover.memblock) {
assert(m->leftover.length > 0 && m->leftover.length <= m->base);
/* The leftover memory block is not yet complete */
if (m->leftover.length < m->base)
return -1;
/* Return the leftover memory block */
*c = m->leftover;
pa_memchunk_reset(&m->leftover);
/* If the current memblock is too small move it the leftover */
if (m->current.memblock && m->current.length < m->base) {
m->leftover = m->current;
pa_memchunk_reset(&m->current);
}
return 0;
}
/* Now let's see if there is other data available */
if (m->current.memblock) {
size_t l;
assert(m->current.length >= m->base);
/* The length of the returned memory block */
l = m->current.length;
l /= m->base;
l *= m->base;
assert(l > 0);
/* Prepare the returned block */
*c = m->current;
pa_memblock_ref(c->memblock);
c->length = l;
/* Drop that from the current memory block */
assert(l <= m->current.length);
m->current.index += l;
m->current.length -= l;
/* In case the whole block was dropped ... */
if (m->current.length == 0)
pa_memblock_unref(m->current.memblock);
else {
/* Move the raimainder to leftover */
assert(m->current.length < m->base && !m->leftover.memblock);
m->leftover = m->current;
}
pa_memchunk_reset(&m->current);
return 0;
}
/* There's simply nothing */
return -1;
}
size_t pa_mcalign_csize(pa_mcalign *m, size_t l) {
assert(m);
assert(l > 0);
assert(!m->current.memblock);
if (m->leftover.memblock)
l += m->leftover.length;
return (l/m->base)*m->base;
}

80
src/pulsecore/mcalign.h Normal file
View file

@ -0,0 +1,80 @@
#ifndef foomcalignhfoo
#define foomcalignhfoo
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <pulsecore/memblock.h>
#include <pulsecore/memchunk.h>
/* An alignment object, used for aligning memchunks to multiples of
* the frame size. */
/* Method of operation: the user creates a new mcalign object by
* calling pa_mcalign_new() with the appropriate aligning
* granularity. After that he may call pa_mcalign_push() for an input
* memchunk. After exactly one memchunk the user has to call
* pa_mcalign_pop() until it returns -1. If pa_mcalign_pop() returns
* 0, the memchunk *c is valid and aligned to the granularity. Some
* pseudocode illustrating this:
*
* pa_mcalign *a = pa_mcalign_new(4, NULL);
*
* for (;;) {
* pa_memchunk input;
*
* ... fill input ...
*
* pa_mcalign_push(m, &input);
* pa_memblock_unref(input.memblock);
*
* for (;;) {
* pa_memchunk output;
*
* if (pa_mcalign_pop(m, &output) < 0)
* break;
*
* ... consume output ...
*
* pa_memblock_unref(output.memblock);
* }
* }
*
* pa_memchunk_free(a);
* */
typedef struct pa_mcalign pa_mcalign;
pa_mcalign *pa_mcalign_new(size_t base, pa_memblock_stat *s);
void pa_mcalign_free(pa_mcalign *m);
/* Push a new memchunk into the aligner. The caller of this routine
* has to free the memchunk by himself. */
void pa_mcalign_push(pa_mcalign *m, const pa_memchunk *c);
/* Pop a new memchunk from the aligner. Returns 0 when sucessful,
* nonzero otherwise. */
int pa_mcalign_pop(pa_mcalign *m, pa_memchunk *c);
/* If we pass l bytes in now, how many bytes would we get out? */
size_t pa_mcalign_csize(pa_mcalign *m, size_t l);
#endif

173
src/pulsecore/memblock.c Normal file
View file

@ -0,0 +1,173 @@
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <pulse/xmalloc.h>
#include "memblock.h"
static void stat_add(pa_memblock*m, pa_memblock_stat *s) {
assert(m);
if (!s) {
m->stat = NULL;
return;
}
m->stat = pa_memblock_stat_ref(s);
s->total++;
s->allocated++;
s->total_size += m->length;
s->allocated_size += m->length;
}
static void stat_remove(pa_memblock *m) {
assert(m);
if (!m->stat)
return;
m->stat->total--;
m->stat->total_size -= m->length;
pa_memblock_stat_unref(m->stat);
m->stat = NULL;
}
pa_memblock *pa_memblock_new(size_t length, pa_memblock_stat*s) {
pa_memblock *b = pa_xmalloc(sizeof(pa_memblock)+length);
b->type = PA_MEMBLOCK_APPENDED;
b->ref = 1;
b->length = length;
b->data = b+1;
b->free_cb = NULL;
b->read_only = 0;
stat_add(b, s);
return b;
}
pa_memblock *pa_memblock_new_dynamic(void *d, size_t length, pa_memblock_stat*s) {
pa_memblock *b = pa_xmalloc(sizeof(pa_memblock));
b->type = PA_MEMBLOCK_DYNAMIC;
b->ref = 1;
b->length = length;
b->data = d;
b->free_cb = NULL;
b->read_only = 0;
stat_add(b, s);
return b;
}
pa_memblock *pa_memblock_new_fixed(void *d, size_t length, int read_only, pa_memblock_stat*s) {
pa_memblock *b = pa_xmalloc(sizeof(pa_memblock));
b->type = PA_MEMBLOCK_FIXED;
b->ref = 1;
b->length = length;
b->data = d;
b->free_cb = NULL;
b->read_only = read_only;
stat_add(b, s);
return b;
}
pa_memblock *pa_memblock_new_user(void *d, size_t length, void (*free_cb)(void *p), int read_only, pa_memblock_stat*s) {
pa_memblock *b;
assert(d && length && free_cb);
b = pa_xmalloc(sizeof(pa_memblock));
b->type = PA_MEMBLOCK_USER;
b->ref = 1;
b->length = length;
b->data = d;
b->free_cb = free_cb;
b->read_only = read_only;
stat_add(b, s);
return b;
}
pa_memblock* pa_memblock_ref(pa_memblock*b) {
assert(b);
assert(b->ref >= 1);
b->ref++;
return b;
}
void pa_memblock_unref(pa_memblock*b) {
assert(b);
assert(b->ref >= 1);
if ((--(b->ref)) == 0) {
stat_remove(b);
if (b->type == PA_MEMBLOCK_USER) {
assert(b->free_cb);
b->free_cb(b->data);
} else if (b->type == PA_MEMBLOCK_DYNAMIC)
pa_xfree(b->data);
pa_xfree(b);
}
}
void pa_memblock_unref_fixed(pa_memblock *b) {
assert(b && b->ref >= 1 && b->type == PA_MEMBLOCK_FIXED);
if (b->ref == 1)
pa_memblock_unref(b);
else {
b->data = pa_xmemdup(b->data, b->length);
b->type = PA_MEMBLOCK_DYNAMIC;
b->ref--;
}
}
pa_memblock_stat* pa_memblock_stat_new(void) {
pa_memblock_stat *s;
s = pa_xmalloc(sizeof(pa_memblock_stat));
s->ref = 1;
s->total = s->total_size = s->allocated = s->allocated_size = 0;
return s;
}
void pa_memblock_stat_unref(pa_memblock_stat *s) {
assert(s && s->ref >= 1);
if (!(--(s->ref))) {
assert(!s->total);
pa_xfree(s);
}
}
pa_memblock_stat * pa_memblock_stat_ref(pa_memblock_stat *s) {
assert(s);
s->ref++;
return s;
}

86
src/pulsecore/memblock.h Normal file
View file

@ -0,0 +1,86 @@
#ifndef foomemblockhfoo
#define foomemblockhfoo
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <sys/types.h>
#include <inttypes.h>
/* A pa_memblock is a reference counted memory block. Polypaudio
* passed references to pa_memblocks around instead of copying
* data. See pa_memchunk for a structure that describes parts of
* memory blocks. */
/* The type of memory this block points to */
typedef enum pa_memblock_type {
PA_MEMBLOCK_FIXED, /* data is a pointer to fixed memory that needs not to be freed */
PA_MEMBLOCK_APPENDED, /* The most common kind: the data is appended to the memory block */
PA_MEMBLOCK_DYNAMIC, /* data is a pointer to some memory allocated with pa_xmalloc() */
PA_MEMBLOCK_USER /* User supplied memory, to be freed with free_cb */
} pa_memblock_type_t;
/* A structure of keeping memory block statistics */
/* Maintains statistics about memory blocks */
typedef struct pa_memblock_stat {
int ref;
unsigned total;
unsigned total_size;
unsigned allocated;
unsigned allocated_size;
} pa_memblock_stat;
typedef struct pa_memblock {
pa_memblock_type_t type;
unsigned ref; /* the reference counter */
int read_only; /* boolean */
size_t length;
void *data;
void (*free_cb)(void *p); /* If type == PA_MEMBLOCK_USER this points to a function for freeing this memory block */
pa_memblock_stat *stat;
} pa_memblock;
/* Allocate a new memory block of type PA_MEMBLOCK_APPENDED */
pa_memblock *pa_memblock_new(size_t length, pa_memblock_stat*s);
/* Allocate a new memory block of type PA_MEMBLOCK_DYNAMIC. The pointer data is to be maintained be the memory block */
pa_memblock *pa_memblock_new_dynamic(void *data, size_t length, pa_memblock_stat*s);
/* Allocate a new memory block of type PA_MEMBLOCK_FIXED */
pa_memblock *pa_memblock_new_fixed(void *data, size_t length, int read_only, pa_memblock_stat*s);
/* Allocate a new memory block of type PA_MEMBLOCK_USER */
pa_memblock *pa_memblock_new_user(void *data, size_t length, void (*free_cb)(void *p), int read_only, pa_memblock_stat*s);
void pa_memblock_unref(pa_memblock*b);
pa_memblock* pa_memblock_ref(pa_memblock*b);
/* This special unref function has to be called by the owner of the
memory of a static memory block when he wants to release all
references to the memory. This causes the memory to be copied and
converted into a PA_MEMBLOCK_DYNAMIC type memory block */
void pa_memblock_unref_fixed(pa_memblock*b);
pa_memblock_stat* pa_memblock_stat_new(void);
void pa_memblock_stat_unref(pa_memblock_stat *s);
pa_memblock_stat * pa_memblock_stat_ref(pa_memblock_stat *s);
#endif

636
src/pulsecore/memblockq.c Normal file
View file

@ -0,0 +1,636 @@
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <sys/time.h>
#include <time.h>
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <pulse/xmalloc.h>
#include <pulsecore/log.h>
#include <pulsecore/mcalign.h>
#include "memblockq.h"
struct memblock_list {
struct memblock_list *next, *prev;
int64_t index;
pa_memchunk chunk;
};
struct pa_memblockq {
struct memblock_list *blocks, *blocks_tail;
unsigned n_blocks;
size_t maxlength, tlength, base, prebuf, minreq;
int64_t read_index, write_index;
enum { PREBUF, RUNNING } state;
pa_memblock_stat *memblock_stat;
pa_memblock *silence;
pa_mcalign *mcalign;
};
pa_memblockq* pa_memblockq_new(
int64_t idx,
size_t maxlength,
size_t tlength,
size_t base,
size_t prebuf,
size_t minreq,
pa_memblock *silence,
pa_memblock_stat *s) {
pa_memblockq* bq;
assert(base > 0);
assert(maxlength >= base);
bq = pa_xnew(pa_memblockq, 1);
bq->blocks = bq->blocks_tail = NULL;
bq->n_blocks = 0;
bq->base = base;
bq->read_index = bq->write_index = idx;
bq->memblock_stat = s;
pa_log_debug(__FILE__": memblockq requested: maxlength=%lu, tlength=%lu, base=%lu, prebuf=%lu, minreq=%lu",
(unsigned long)maxlength, (unsigned long)tlength, (unsigned long)base, (unsigned long)prebuf, (unsigned long)minreq);
bq->maxlength = ((maxlength+base-1)/base)*base;
assert(bq->maxlength >= base);
bq->tlength = ((tlength+base-1)/base)*base;
if (!bq->tlength || bq->tlength >= bq->maxlength)
bq->tlength = bq->maxlength;
bq->prebuf = (prebuf == (size_t) -1) ? bq->tlength/2 : prebuf;
bq->prebuf = ((bq->prebuf+base-1)/base)*base;
if (bq->prebuf > bq->maxlength)
bq->prebuf = bq->maxlength;
bq->minreq = (minreq/base)*base;
if (bq->minreq > bq->tlength - bq->prebuf)
bq->minreq = bq->tlength - bq->prebuf;
if (!bq->minreq)
bq->minreq = 1;
pa_log_debug(__FILE__": memblockq sanitized: maxlength=%lu, tlength=%lu, base=%lu, prebuf=%lu, minreq=%lu",
(unsigned long)bq->maxlength, (unsigned long)bq->tlength, (unsigned long)bq->base, (unsigned long)bq->prebuf, (unsigned long)bq->minreq);
bq->state = bq->prebuf ? PREBUF : RUNNING;
bq->silence = silence ? pa_memblock_ref(silence) : NULL;
bq->mcalign = NULL;
return bq;
}
void pa_memblockq_free(pa_memblockq* bq) {
assert(bq);
pa_memblockq_flush(bq);
if (bq->silence)
pa_memblock_unref(bq->silence);
if (bq->mcalign)
pa_mcalign_free(bq->mcalign);
pa_xfree(bq);
}
static void drop_block(pa_memblockq *bq, struct memblock_list *q) {
assert(bq);
assert(q);
assert(bq->n_blocks >= 1);
if (q->prev)
q->prev->next = q->next;
else
bq->blocks = q->next;
if (q->next)
q->next->prev = q->prev;
else
bq->blocks_tail = q->prev;
pa_memblock_unref(q->chunk.memblock);
pa_xfree(q);
bq->n_blocks--;
}
static int can_push(pa_memblockq *bq, size_t l) {
int64_t end;
assert(bq);
if (bq->read_index > bq->write_index) {
size_t d = bq->read_index - bq->write_index;
if (l > d)
l -= d;
else
return 1;
}
end = bq->blocks_tail ? bq->blocks_tail->index + bq->blocks_tail->chunk.length : 0;
/* Make sure that the list doesn't get too long */
if (bq->write_index + (int64_t)l > end)
if (bq->write_index + l - bq->read_index > bq->maxlength)
return 0;
return 1;
}
int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *uchunk) {
struct memblock_list *q, *n;
pa_memchunk chunk;
assert(bq);
assert(uchunk);
assert(uchunk->memblock);
assert(uchunk->length > 0);
assert(uchunk->index + uchunk->length <= uchunk->memblock->length);
if (uchunk->length % bq->base)
return -1;
if (!can_push(bq, uchunk->length))
return -1;
chunk = *uchunk;
if (bq->read_index > bq->write_index) {
/* We currently have a buffer underflow, we need to drop some
* incoming data */
size_t d = bq->read_index - bq->write_index;
if (chunk.length > d) {
chunk.index += d;
chunk.length -= d;
bq->write_index = bq->read_index;
} else {
/* We drop the incoming data completely */
bq->write_index += chunk.length;
return 0;
}
}
/* We go from back to front to look for the right place to add
* this new entry. Drop data we will overwrite on the way */
q = bq->blocks_tail;
while (q) {
if (bq->write_index >= q->index + (int64_t)q->chunk.length)
/* We found the entry where we need to place the new entry immediately after */
break;
else if (bq->write_index + (int64_t)chunk.length <= q->index) {
/* This entry isn't touched at all, let's skip it */
q = q->prev;
} else if (bq->write_index <= q->index &&
bq->write_index + chunk.length >= q->index + q->chunk.length) {
/* This entry is fully replaced by the new entry, so let's drop it */
struct memblock_list *p;
p = q;
q = q->prev;
drop_block(bq, p);
} else if (bq->write_index >= q->index) {
/* The write index points into this memblock, so let's
* truncate or split it */
if (bq->write_index + chunk.length < q->index + q->chunk.length) {
/* We need to save the end of this memchunk */
struct memblock_list *p;
size_t d;
/* Create a new list entry for the end of thie memchunk */
p = pa_xnew(struct memblock_list, 1);
p->chunk = q->chunk;
pa_memblock_ref(p->chunk.memblock);
/* Calculate offset */
d = bq->write_index + chunk.length - q->index;
assert(d > 0);
/* Drop it from the new entry */
p->index = q->index + d;
p->chunk.length -= d;
/* Add it to the list */
p->prev = q;
if ((p->next = q->next))
q->next->prev = p;
else
bq->blocks_tail = p;
q->next = p;
bq->n_blocks++;
}
/* Truncate the chunk */
if (!(q->chunk.length = bq->write_index - q->index)) {
struct memblock_list *p;
p = q;
q = q->prev;
drop_block(bq, p);
}
/* We had to truncate this block, hence we're now at the right position */
break;
} else {
size_t d;
assert(bq->write_index + (int64_t)chunk.length > q->index &&
bq->write_index + (int64_t)chunk.length < q->index + (int64_t)q->chunk.length &&
bq->write_index < q->index);
/* The job overwrites the current entry at the end, so let's drop the beginning of this entry */
d = bq->write_index + chunk.length - q->index;
q->index += d;
q->chunk.index += d;
q->chunk.length -= d;
q = q->prev;
}
}
if (q) {
assert(bq->write_index >= q->index + (int64_t)q->chunk.length);
assert(!q->next || (bq->write_index + (int64_t)chunk.length <= q->next->index));
/* Try to merge memory blocks */
if (q->chunk.memblock == chunk.memblock &&
q->chunk.index + (int64_t)q->chunk.length == chunk.index &&
bq->write_index == q->index + (int64_t)q->chunk.length) {
q->chunk.length += chunk.length;
bq->write_index += chunk.length;
return 0;
}
} else
assert(!bq->blocks || (bq->write_index + (int64_t)chunk.length <= bq->blocks->index));
n = pa_xnew(struct memblock_list, 1);
n->chunk = chunk;
pa_memblock_ref(n->chunk.memblock);
n->index = bq->write_index;
bq->write_index += n->chunk.length;
n->next = q ? q->next : bq->blocks;
n->prev = q;
if (n->next)
n->next->prev = n;
else
bq->blocks_tail = n;
if (n->prev)
n->prev->next = n;
else
bq->blocks = n;
bq->n_blocks++;
return 0;
}
int pa_memblockq_peek(pa_memblockq* bq, pa_memchunk *chunk) {
assert(bq);
assert(chunk);
if (bq->state == PREBUF) {
/* We need to pre-buffer */
if (pa_memblockq_get_length(bq) < bq->prebuf)
return -1;
bq->state = RUNNING;
} else if (bq->prebuf > 0 && bq->read_index >= bq->write_index) {
/* Buffer underflow protection */
bq->state = PREBUF;
return -1;
}
/* Do we need to spit out silence? */
if (!bq->blocks || bq->blocks->index > bq->read_index) {
size_t length;
/* How much silence shall we return? */
length = bq->blocks ? bq->blocks->index - bq->read_index : 0;
/* We need to return silence, since no data is yet available */
if (bq->silence) {
chunk->memblock = pa_memblock_ref(bq->silence);
if (!length || length > chunk->memblock->length)
length = chunk->memblock->length;
chunk->length = length;
} else {
chunk->memblock = NULL;
chunk->length = length;
}
chunk->index = 0;
return 0;
}
/* Ok, let's pass real data to the caller */
assert(bq->blocks->index == bq->read_index);
*chunk = bq->blocks->chunk;
pa_memblock_ref(chunk->memblock);
return 0;
}
void pa_memblockq_drop(pa_memblockq *bq, const pa_memchunk *chunk, size_t length) {
assert(bq);
assert(length % bq->base == 0);
assert(!chunk || length <= chunk->length);
if (chunk) {
if (bq->blocks && bq->blocks->index == bq->read_index) {
/* The first item in queue is valid */
/* Does the chunk match with what the user supplied us? */
if (memcmp(chunk, &bq->blocks->chunk, sizeof(pa_memchunk)) != 0)
return;
} else {
size_t l;
/* The first item in the queue is not yet relevant */
assert(!bq->blocks || bq->blocks->index > bq->read_index);
l = bq->blocks ? bq->blocks->index - bq->read_index : 0;
if (bq->silence) {
if (!l || l > bq->silence->length)
l = bq->silence->length;
}
/* Do the entries still match? */
if (chunk->index != 0 || chunk->length != l || chunk->memblock != bq->silence)
return;
}
}
while (length > 0) {
if (bq->blocks) {
size_t d;
assert(bq->blocks->index >= bq->read_index);
d = (size_t) (bq->blocks->index - bq->read_index);
if (d >= length) {
/* The first block is too far in the future */
bq->read_index += length;
break;
} else {
length -= d;
bq->read_index += d;
}
assert(bq->blocks->index == bq->read_index);
if (bq->blocks->chunk.length <= length) {
/* We need to drop the full block */
length -= bq->blocks->chunk.length;
bq->read_index += bq->blocks->chunk.length;
drop_block(bq, bq->blocks);
} else {
/* Only the start of this block needs to be dropped */
bq->blocks->chunk.index += length;
bq->blocks->chunk.length -= length;
bq->blocks->index += length;
bq->read_index += length;
break;
}
} else {
/* The list is empty, there's nothing we could drop */
bq->read_index += length;
break;
}
}
}
int pa_memblockq_is_readable(pa_memblockq *bq) {
assert(bq);
if (bq->prebuf > 0) {
size_t l = pa_memblockq_get_length(bq);
if (bq->state == PREBUF && l < bq->prebuf)
return 0;
if (l <= 0)
return 0;
}
return 1;
}
int pa_memblockq_is_writable(pa_memblockq *bq, size_t length) {
assert(bq);
if (length % bq->base)
return 0;
return pa_memblockq_get_length(bq) + length <= bq->tlength;
}
size_t pa_memblockq_get_length(pa_memblockq *bq) {
assert(bq);
if (bq->write_index <= bq->read_index)
return 0;
return (size_t) (bq->write_index - bq->read_index);
}
size_t pa_memblockq_missing(pa_memblockq *bq) {
size_t l;
assert(bq);
if ((l = pa_memblockq_get_length(bq)) >= bq->tlength)
return 0;
l = bq->tlength - l;
return (l >= bq->minreq) ? l : 0;
}
size_t pa_memblockq_get_minreq(pa_memblockq *bq) {
assert(bq);
return bq->minreq;
}
void pa_memblockq_seek(pa_memblockq *bq, int64_t offset, pa_seek_mode_t seek) {
assert(bq);
switch (seek) {
case PA_SEEK_RELATIVE:
bq->write_index += offset;
return;
case PA_SEEK_ABSOLUTE:
bq->write_index = offset;
return;
case PA_SEEK_RELATIVE_ON_READ:
bq->write_index = bq->read_index + offset;
return;
case PA_SEEK_RELATIVE_END:
bq->write_index = (bq->blocks_tail ? bq->blocks_tail->index + (int64_t)bq->blocks_tail->chunk.length : bq->read_index) + offset;
return;
}
assert(0);
}
void pa_memblockq_flush(pa_memblockq *bq) {
assert(bq);
while (bq->blocks)
drop_block(bq, bq->blocks);
assert(bq->n_blocks == 0);
bq->write_index = bq->read_index;
pa_memblockq_prebuf_force(bq);
}
size_t pa_memblockq_get_tlength(pa_memblockq *bq) {
assert(bq);
return bq->tlength;
}
int64_t pa_memblockq_get_read_index(pa_memblockq *bq) {
assert(bq);
return bq->read_index;
}
int64_t pa_memblockq_get_write_index(pa_memblockq *bq) {
assert(bq);
return bq->write_index;
}
int pa_memblockq_push_align(pa_memblockq* bq, const pa_memchunk *chunk) {
pa_memchunk rchunk;
assert(bq);
assert(chunk && bq->base);
if (bq->base == 1)
return pa_memblockq_push(bq, chunk);
if (!bq->mcalign)
bq->mcalign = pa_mcalign_new(bq->base, bq->memblock_stat);
if (!can_push(bq, pa_mcalign_csize(bq->mcalign, chunk->length)))
return -1;
pa_mcalign_push(bq->mcalign, chunk);
while (pa_mcalign_pop(bq->mcalign, &rchunk) >= 0) {
int r;
r = pa_memblockq_push(bq, &rchunk);
pa_memblock_unref(rchunk.memblock);
if (r < 0)
return -1;
}
return 0;
}
void pa_memblockq_shorten(pa_memblockq *bq, size_t length) {
size_t l;
assert(bq);
l = pa_memblockq_get_length(bq);
if (l > length)
pa_memblockq_drop(bq, NULL, l - length);
}
void pa_memblockq_prebuf_disable(pa_memblockq *bq) {
assert(bq);
if (bq->state == PREBUF)
bq->state = RUNNING;
}
void pa_memblockq_prebuf_force(pa_memblockq *bq) {
assert(bq);
if (bq->state == RUNNING && bq->prebuf > 0)
bq->state = PREBUF;
}
size_t pa_memblockq_get_maxlength(pa_memblockq *bq) {
assert(bq);
return bq->maxlength;
}
size_t pa_memblockq_get_prebuf(pa_memblockq *bq) {
assert(bq);
return bq->prebuf;
}

140
src/pulsecore/memblockq.h Normal file
View file

@ -0,0 +1,140 @@
#ifndef foomemblockqhfoo
#define foomemblockqhfoo
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <sys/types.h>
#include <inttypes.h>
#include <pulsecore/memblock.h>
#include <pulsecore/memchunk.h>
#include <pulse/def.h>
/* A memblockq is a queue of pa_memchunks (yepp, the name is not
* perfect). It is similar to the ring buffers used by most other
* audio software. In contrast to a ring buffer this memblockq data
* type doesn't need to copy any data around, it just maintains
* references to reference counted memory blocks. */
typedef struct pa_memblockq pa_memblockq;
/* Parameters:
- idx: start value for both read and write index
- maxlength: maximum length of queue. If more data is pushed into
the queue, the operation will fail. Must not be 0.
- tlength: the target length of the queue. Pass 0 for the default.
- base: a base value for all metrics. Only multiples of this value
are popped from the queue or should be pushed into
it. Must not be 0.
- prebuf: If the queue runs empty wait until this many bytes are in
queue again before passing the first byte out. If set
to 0 pa_memblockq_pop() will return a silence memblock
if no data is in the queue and will never fail. Pass
(size_t) -1 for the default.
- minreq: pa_memblockq_missing() will only return values greater
than this value. Pass 0 for the default.
- silence: return this memblock whzen reading unitialized data
*/
pa_memblockq* pa_memblockq_new(
int64_t idx,
size_t maxlength,
size_t tlength,
size_t base,
size_t prebuf,
size_t minreq,
pa_memblock *silence,
pa_memblock_stat *s);
void pa_memblockq_free(pa_memblockq*bq);
/* Push a new memory chunk into the queue. */
int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *chunk);
/* Push a new memory chunk into the queue, but filter it through a
* pa_mcalign object. Don't mix this with pa_memblockq_seek() unless
* you know what you do. */
int pa_memblockq_push_align(pa_memblockq* bq, const pa_memchunk *chunk);
/* Return a copy of the next memory chunk in the queue. It is not removed from the queue */
int pa_memblockq_peek(pa_memblockq* bq, pa_memchunk *chunk);
/* Drop the specified bytes from the queue, but only if the first
* chunk in the queue matches the one passed here. If NULL is passed,
* this check isn't done. */
void pa_memblockq_drop(pa_memblockq *bq, const pa_memchunk *chunk, size_t length);
/* Test if the pa_memblockq is currently readable, that is, more data than base */
int pa_memblockq_is_readable(pa_memblockq *bq);
/* Test if the pa_memblockq is currently writable for the specified amount of bytes */
int pa_memblockq_is_writable(pa_memblockq *bq, size_t length);
/* Return the length of the queue in bytes */
size_t pa_memblockq_get_length(pa_memblockq *bq);
/* Return how many bytes are missing in queue to the specified fill amount */
size_t pa_memblockq_missing(pa_memblockq *bq);
/* Returns the minimal request value */
size_t pa_memblockq_get_minreq(pa_memblockq *bq);
/* Manipulate the write pointer */
void pa_memblockq_seek(pa_memblockq *bq, int64_t offset, pa_seek_mode_t seek);
/* Set the queue to silence, set write index to read index */
void pa_memblockq_flush(pa_memblockq *bq);
/* Get Target length */
size_t pa_memblockq_get_tlength(pa_memblockq *bq);
/* Return the current read index */
int64_t pa_memblockq_get_read_index(pa_memblockq *bq);
/* Return the current write index */
int64_t pa_memblockq_get_write_index(pa_memblockq *bq);
/* Shorten the pa_memblockq to the specified length by dropping data
* at the read end of the queue. The read index is increased until the
* queue has the specified length */
void pa_memblockq_shorten(pa_memblockq *bq, size_t length);
/* Ignore prebuf for now */
void pa_memblockq_prebuf_disable(pa_memblockq *bq);
/* Force prebuf */
void pa_memblockq_prebuf_force(pa_memblockq *bq);
/* Return the maximum length of the queue in bytes */
size_t pa_memblockq_get_maxlength(pa_memblockq *bq);
/* Return the prebuffer length in bytes */
size_t pa_memblockq_get_prebuf(pa_memblockq *bq);
#endif

59
src/pulsecore/memchunk.c Normal file
View file

@ -0,0 +1,59 @@
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <pulse/xmalloc.h>
#include "memchunk.h"
void pa_memchunk_make_writable(pa_memchunk *c, pa_memblock_stat *s, size_t min) {
pa_memblock *n;
size_t l;
assert(c && c->memblock && c->memblock->ref >= 1);
if (c->memblock->ref == 1 && !c->memblock->read_only && c->memblock->length >= c->index+min)
return;
l = c->length;
if (l < min)
l = min;
n = pa_memblock_new(l, s);
memcpy(n->data, (uint8_t*) c->memblock->data + c->index, c->length);
pa_memblock_unref(c->memblock);
c->memblock = n;
c->index = 0;
}
void pa_memchunk_reset(pa_memchunk *c) {
assert(c);
c->memblock = NULL;
c->length = c->index = 0;
}

45
src/pulsecore/memchunk.h Normal file
View file

@ -0,0 +1,45 @@
#ifndef foomemchunkhfoo
#define foomemchunkhfoo
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <pulsecore/memblock.h>
/* A memchunk describes a part of a memblock. In contrast to the memblock, a
* memchunk is not allocated dynamically or reference counted, instead
* it is usually stored on the stack and copied around */
typedef struct pa_memchunk {
pa_memblock *memblock;
size_t index, length;
} pa_memchunk;
/* Make a memchunk writable, i.e. make sure that the caller may have
* exclusive access to the memblock and it is not read_only. If needed
* the memblock in the structure is replaced by a copy. */
void pa_memchunk_make_writable(pa_memchunk *c, pa_memblock_stat *s, size_t min);
/* Invalidate a memchunk. This does not free the cotaining memblock,
* but sets all members to zero. */
void pa_memchunk_reset(pa_memchunk *c);
#endif

310
src/pulsecore/modargs.c Normal file
View file

@ -0,0 +1,310 @@
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <ctype.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <pulse/xmalloc.h>
#include <pulsecore/hashmap.h>
#include <pulsecore/idxset.h>
#include <pulsecore/sample-util.h>
#include <pulsecore/namereg.h>
#include <pulsecore/sink.h>
#include <pulsecore/source.h>
#include <pulsecore/core-util.h>
#include "modargs.h"
struct entry {
char *key, *value;
};
static int add_key_value(pa_hashmap *map, char *key, char *value, const char* const valid_keys[]) {
struct entry *e;
assert(map && key && value);
if (valid_keys) {
const char*const* v;
for (v = valid_keys; *v; v++)
if (strcmp(*v, key) == 0)
break;
if (!*v) {
pa_xfree(key);
pa_xfree(value);
return -1;
}
}
e = pa_xmalloc(sizeof(struct entry));
e->key = key;
e->value = value;
pa_hashmap_put(map, key, e);
return 0;
}
pa_modargs *pa_modargs_new(const char *args, const char* const* valid_keys) {
pa_hashmap *map = NULL;
map = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
assert(map);
if (args) {
enum { WHITESPACE, KEY, VALUE_START, VALUE_SIMPLE, VALUE_DOUBLE_QUOTES, VALUE_TICKS } state;
const char *p, *key, *value;
size_t key_len = 0, value_len = 0;
key = value = NULL;
state = WHITESPACE;
for (p = args; *p; p++) {
switch (state) {
case WHITESPACE:
if (*p == '=')
goto fail;
else if (!isspace(*p)) {
key = p;
state = KEY;
key_len = 1;
}
break;
case KEY:
if (*p == '=')
state = VALUE_START;
else
key_len++;
break;
case VALUE_START:
if (*p == '\'') {
state = VALUE_TICKS;
value = p+1;
value_len = 0;
} else if (*p == '"') {
state = VALUE_DOUBLE_QUOTES;
value = p+1;
value_len = 0;
} else if (isspace(*p)) {
if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrdup(""), valid_keys) < 0)
goto fail;
state = WHITESPACE;
} else {
state = VALUE_SIMPLE;
value = p;
value_len = 1;
}
break;
case VALUE_SIMPLE:
if (isspace(*p)) {
if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrndup(value, value_len), valid_keys) < 0)
goto fail;
state = WHITESPACE;
} else
value_len++;
break;
case VALUE_DOUBLE_QUOTES:
if (*p == '"') {
if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrndup(value, value_len), valid_keys) < 0)
goto fail;
state = WHITESPACE;
} else
value_len++;
break;
case VALUE_TICKS:
if (*p == '\'') {
if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrndup(value, value_len), valid_keys) < 0)
goto fail;
state = WHITESPACE;
} else
value_len++;
break;
}
}
if (state == VALUE_START) {
if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrdup(""), valid_keys) < 0)
goto fail;
} else if (state == VALUE_SIMPLE) {
if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrdup(value), valid_keys) < 0)
goto fail;
} else if (state != WHITESPACE)
goto fail;
}
return (pa_modargs*) map;
fail:
if (map)
pa_modargs_free((pa_modargs*) map);
return NULL;
}
static void free_func(void *p, PA_GCC_UNUSED void*userdata) {
struct entry *e = p;
assert(e);
pa_xfree(e->key);
pa_xfree(e->value);
pa_xfree(e);
}
void pa_modargs_free(pa_modargs*ma) {
pa_hashmap *map = (pa_hashmap*) ma;
pa_hashmap_free(map, free_func, NULL);
}
const char *pa_modargs_get_value(pa_modargs *ma, const char *key, const char *def) {
pa_hashmap *map = (pa_hashmap*) ma;
struct entry*e;
if (!(e = pa_hashmap_get(map, key)))
return def;
return e->value;
}
int pa_modargs_get_value_u32(pa_modargs *ma, const char *key, uint32_t *value) {
const char *v;
assert(ma && key && value);
if (!(v = pa_modargs_get_value(ma, key, NULL)))
return 0;
if (pa_atou(v, value) < 0)
return -1;
return 0;
}
int pa_modargs_get_value_s32(pa_modargs *ma, const char *key, int32_t *value) {
const char *v;
assert(ma && key && value);
if (!(v = pa_modargs_get_value(ma, key, NULL)))
return 0;
if (pa_atoi(v, value) < 0)
return -1;
return 0;
}
int pa_modargs_get_value_boolean(pa_modargs *ma, const char *key, int *value) {
const char *v;
int r;
assert(ma && key && value);
if (!(v = pa_modargs_get_value(ma, key, NULL)))
return 0;
if (!*v)
return -1;
if ((r = pa_parse_boolean(v)) < 0)
return -1;
*value = r;
return 0;
}
int pa_modargs_get_sample_spec(pa_modargs *ma, pa_sample_spec *rss) {
const char *format;
uint32_t channels;
pa_sample_spec ss;
assert(ma && rss);
/* DEBUG_TRAP;*/
ss = *rss;
if ((pa_modargs_get_value_u32(ma, "rate", &ss.rate)) < 0)
return -1;
channels = ss.channels;
if ((pa_modargs_get_value_u32(ma, "channels", &channels)) < 0)
return -1;
ss.channels = (uint8_t) channels;
if ((format = pa_modargs_get_value(ma, "format", NULL)))
if ((ss.format = pa_parse_sample_format(format)) < 0)
return -1;
if (!pa_sample_spec_valid(&ss))
return -1;
*rss = ss;
return 0;
}
int pa_modargs_get_channel_map(pa_modargs *ma, pa_channel_map *rmap) {
pa_channel_map map;
const char *cm;
assert(ma);
assert(rmap);
map = *rmap;
if ((cm = pa_modargs_get_value(ma, "channel_map", NULL)))
if (!pa_channel_map_parse(&map, cm))
return -1;
if (!pa_channel_map_valid(&map))
return -1;
*rmap = map;
return 0;
}
int pa_modargs_get_sample_spec_and_channel_map(pa_modargs *ma, pa_sample_spec *rss, pa_channel_map *rmap, pa_channel_map_def_t def) {
pa_sample_spec ss;
pa_channel_map map;
assert(ma);
assert(rss);
assert(rmap);
ss = *rss;
if (pa_modargs_get_sample_spec(ma, &ss) < 0)
return -1;
if (!pa_channel_map_init_auto(&map, ss.channels, def))
map.channels = 0;
if (pa_modargs_get_channel_map(ma, &map) < 0)
return -1;
if (map.channels != ss.channels)
return -1;
*rmap = map;
*rss = ss;
return 0;
}

60
src/pulsecore/modargs.h Normal file
View file

@ -0,0 +1,60 @@
#ifndef foomodargshfoo
#define foomodargshfoo
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <inttypes.h>
#include <pulse/sample.h>
#include <pulse/channelmap.h>
#include <pulsecore/core.h>
typedef struct pa_modargs pa_modargs;
/* A generic parser for module arguments */
/* Parse the string args. The NULL-terminated array keys contains all valid arguments. */
pa_modargs *pa_modargs_new(const char *args, const char* const keys[]);
void pa_modargs_free(pa_modargs*ma);
/* Return the module argument for the specified name as a string. If
* the argument was not specified, return def instead.*/
const char *pa_modargs_get_value(pa_modargs *ma, const char *key, const char *def);
/* Return a module argument as unsigned 32bit value in *value */
int pa_modargs_get_value_u32(pa_modargs *ma, const char *key, uint32_t *value);
int pa_modargs_get_value_s32(pa_modargs *ma, const char *key, int32_t *value);
int pa_modargs_get_value_boolean(pa_modargs *ma, const char *key, int *value);
/* Return sample spec data from the three arguments "rate", "format" and "channels" */
int pa_modargs_get_sample_spec(pa_modargs *ma, pa_sample_spec *ss);
/* Return channel map data from the argument "channel_map" */
int pa_modargs_get_channel_map(pa_modargs *ma, pa_channel_map *map);
/* Combination of pa_modargs_get_sample_spec() and
pa_modargs_get_channel_map(). Not always suitable, since this routine
initializes the map parameter based on the channels field of the ss
structure if no channel_map is found, using pa_channel_map_init_auto() */
int pa_modargs_get_sample_spec_and_channel_map(pa_modargs *ma, pa_sample_spec *ss, pa_channel_map *map, pa_channel_map_def_t def);
#endif

93
src/pulsecore/modinfo.c Normal file
View file

@ -0,0 +1,93 @@
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <ltdl.h>
#include <assert.h>
#include <pulse/xmalloc.h>
#include <pulsecore/core-util.h>
#include <pulsecore/log.h>
#include "modinfo.h"
#define PA_SYMBOL_AUTHOR "pa__get_author"
#define PA_SYMBOL_DESCRIPTION "pa__get_description"
#define PA_SYMBOL_USAGE "pa__get_usage"
#define PA_SYMBOL_VERSION "pa__get_version"
/* lt_dlsym() violates ISO C, so confide the breakage into this function to
* avoid warnings. */
typedef void (*fnptr)(void);
static inline fnptr lt_dlsym_fn(lt_dlhandle handle, const char *symbol) {
return (fnptr) (long) lt_dlsym(handle, symbol);
}
pa_modinfo *pa_modinfo_get_by_handle(lt_dlhandle dl) {
pa_modinfo *i;
const char* (*func)(void);
assert(dl);
i = pa_xnew0(pa_modinfo, 1);
if ((func = (const char* (*)(void)) lt_dlsym_fn(dl, PA_SYMBOL_AUTHOR)))
i->author = pa_xstrdup(func());
if ((func = (const char* (*)(void)) lt_dlsym_fn(dl, PA_SYMBOL_DESCRIPTION)))
i->description = pa_xstrdup(func());
if ((func = (const char* (*)(void)) lt_dlsym_fn(dl, PA_SYMBOL_USAGE)))
i->usage = pa_xstrdup(func());
if ((func = (const char* (*)(void)) lt_dlsym_fn(dl, PA_SYMBOL_VERSION)))
i->version = pa_xstrdup(func());
return i;
}
pa_modinfo *pa_modinfo_get_by_name(const char *name) {
lt_dlhandle dl;
pa_modinfo *i;
assert(name);
if (!(dl = lt_dlopenext(name))) {
pa_log(__FILE__": Failed to open module \"%s\": %s", name, lt_dlerror());
return NULL;
}
i = pa_modinfo_get_by_handle(dl);
lt_dlclose(dl);
return i;
}
void pa_modinfo_free(pa_modinfo *i) {
assert(i);
pa_xfree(i->author);
pa_xfree(i->description);
pa_xfree(i->usage);
pa_xfree(i->version);
pa_xfree(i);
}

43
src/pulsecore/modinfo.h Normal file
View file

@ -0,0 +1,43 @@
#ifndef foomodinfohfoo
#define foomodinfohfoo
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
/* Some functions for reading module meta data from Polypaudio modules */
typedef struct pa_modinfo {
char *author;
char *description;
char *usage;
char *version;
} pa_modinfo;
/* Read meta data from an libtool handle */
pa_modinfo *pa_modinfo_get_by_handle(lt_dlhandle dl);
/* Read meta data from a module file */
pa_modinfo *pa_modinfo_get_by_name(const char *name);
/* Free meta data */
void pa_modinfo_free(pa_modinfo *i);
#endif

319
src/pulsecore/module.c Normal file
View file

@ -0,0 +1,319 @@
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
#include <pulse/timeval.h>
#include <pulse/xmalloc.h>
#include <pulsecore/core-subscribe.h>
#include <pulsecore/log.h>
#include <pulsecore/core-util.h>
#include "module.h"
#define PA_SYMBOL_INIT "pa__init"
#define PA_SYMBOL_DONE "pa__done"
#define UNLOAD_POLL_TIME 2
/* lt_dlsym() violates ISO C, so confide the breakage into this function to
* avoid warnings. */
typedef void (*fnptr)(void);
static inline fnptr lt_dlsym_fn(lt_dlhandle handle, const char *symbol) {
return (fnptr) (long) lt_dlsym(handle, symbol);
}
static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) {
pa_core *c = userdata;
struct timeval ntv;
assert(c && c->mainloop == m && c->module_auto_unload_event == e);
pa_module_unload_unused(c);
pa_gettimeofday(&ntv);
ntv.tv_sec += UNLOAD_POLL_TIME;
m->time_restart(e, &ntv);
}
static inline fnptr load_sym(lt_dlhandle handle, const char *module, const char *symbol) {
char *buffer, *ch;
size_t buflen;
fnptr res;
res = lt_dlsym_fn(handle, symbol);
if (res)
return res;
/* As the .la files might have been cleansed from the system, we should
* try with the ltdl prefix as well. */
buflen = strlen(symbol) + strlen(module) + strlen("_LTX_") + 1;
buffer = pa_xmalloc(buflen);
assert(buffer);
strcpy(buffer, module);
for (ch = buffer;*ch != '\0';ch++) {
if (!isalnum(*ch))
*ch = '_';
}
strcat(buffer, "_LTX_");
strcat(buffer, symbol);
res = lt_dlsym_fn(handle, buffer);
pa_xfree(buffer);
return res;
}
pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) {
pa_module *m = NULL;
int r;
assert(c && name);
if (c->disallow_module_loading)
goto fail;
m = pa_xmalloc(sizeof(pa_module));
m->name = pa_xstrdup(name);
m->argument = pa_xstrdup(argument);
if (!(m->dl = lt_dlopenext(name))) {
pa_log(__FILE__": Failed to open module \"%s\": %s", name, lt_dlerror());
goto fail;
}
if (!(m->init = (int (*)(pa_core *_c, pa_module*_m)) load_sym(m->dl, name, PA_SYMBOL_INIT))) {
pa_log(__FILE__": Failed to load module \"%s\": symbol \""PA_SYMBOL_INIT"\" not found.", name);
goto fail;
}
if (!(m->done = (void (*)(pa_core *_c, pa_module*_m)) load_sym(m->dl, name, PA_SYMBOL_DONE))) {
pa_log(__FILE__": Failed to load module \"%s\": symbol \""PA_SYMBOL_DONE"\" not found.", name);
goto fail;
}
m->userdata = NULL;
m->core = c;
m->n_used = -1;
m->auto_unload = 0;
m->unload_requested = 0;
assert(m->init);
if (m->init(c, m) < 0) {
pa_log_error(__FILE__": Failed to load module \"%s\" (argument: \"%s\"): initialization failed.", name, argument ? argument : "");
goto fail;
}
if (!c->modules)
c->modules = pa_idxset_new(NULL, NULL);
if (!c->module_auto_unload_event) {
struct timeval ntv;
pa_gettimeofday(&ntv);
ntv.tv_sec += UNLOAD_POLL_TIME;
c->module_auto_unload_event = c->mainloop->time_new(c->mainloop, &ntv, timeout_callback, c);
}
assert(c->module_auto_unload_event);
assert(c->modules);
r = pa_idxset_put(c->modules, m, &m->index);
assert(r >= 0 && m->index != PA_IDXSET_INVALID);
pa_log_info(__FILE__": Loaded \"%s\" (index: #%u; argument: \"%s\").", m->name, m->index, m->argument ? m->argument : "");
pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_MODULE|PA_SUBSCRIPTION_EVENT_NEW, m->index);
return m;
fail:
if (m) {
pa_xfree(m->argument);
pa_xfree(m->name);
if (m->dl)
lt_dlclose(m->dl);
pa_xfree(m);
}
return NULL;
}
static void pa_module_free(pa_module *m) {
assert(m && m->done && m->core);
if (m->core->disallow_module_loading)
return;
pa_log_info(__FILE__": Unloading \"%s\" (index: #%u).", m->name, m->index);
m->done(m->core, m);
lt_dlclose(m->dl);
pa_log_info(__FILE__": Unloaded \"%s\" (index: #%u).", m->name, m->index);
pa_subscription_post(m->core, PA_SUBSCRIPTION_EVENT_MODULE|PA_SUBSCRIPTION_EVENT_REMOVE, m->index);
pa_xfree(m->name);
pa_xfree(m->argument);
pa_xfree(m);
}
void pa_module_unload(pa_core *c, pa_module *m) {
assert(c && m);
assert(c->modules);
if (!(m = pa_idxset_remove_by_data(c->modules, m, NULL)))
return;
pa_module_free(m);
}
void pa_module_unload_by_index(pa_core *c, uint32_t idx) {
pa_module *m;
assert(c && idx != PA_IDXSET_INVALID);
assert(c->modules);
if (!(m = pa_idxset_remove_by_index(c->modules, idx)))
return;
pa_module_free(m);
}
static void free_callback(void *p, PA_GCC_UNUSED void *userdata) {
pa_module *m = p;
assert(m);
pa_module_free(m);
}
void pa_module_unload_all(pa_core *c) {
assert(c);
if (!c->modules)
return;
pa_idxset_free(c->modules, free_callback, NULL);
c->modules = NULL;
if (c->module_auto_unload_event) {
c->mainloop->time_free(c->module_auto_unload_event);
c->module_auto_unload_event = NULL;
}
if (c->module_defer_unload_event) {
c->mainloop->defer_free(c->module_defer_unload_event);
c->module_defer_unload_event = NULL;
}
}
static int unused_callback(void *p, PA_GCC_UNUSED uint32_t idx, int *del, void *userdata) {
pa_module *m = p;
time_t *now = userdata;
assert(p && del && now);
if (m->n_used == 0 && m->auto_unload && m->last_used_time+m->core->module_idle_time <= *now) {
pa_module_free(m);
*del = 1;
}
return 0;
}
void pa_module_unload_unused(pa_core *c) {
time_t now;
assert(c);
if (!c->modules)
return;
time(&now);
pa_idxset_foreach(c->modules, unused_callback, &now);
}
static int unload_callback(void *p, PA_GCC_UNUSED uint32_t idx, int *del, PA_GCC_UNUSED void *userdata) {
pa_module *m = p;
assert(m);
if (m->unload_requested) {
pa_module_free(m);
*del = 1;
}
return 0;
}
static void defer_cb(pa_mainloop_api*api, pa_defer_event *e, void *userdata) {
pa_core *core = userdata;
api->defer_enable(e, 0);
if (!core->modules)
return;
pa_idxset_foreach(core->modules, unload_callback, NULL);
}
void pa_module_unload_request(pa_module *m) {
assert(m);
m->unload_requested = 1;
if (!m->core->module_defer_unload_event)
m->core->module_defer_unload_event = m->core->mainloop->defer_new(m->core->mainloop, defer_cb, m->core);
m->core->mainloop->defer_enable(m->core->module_defer_unload_event, 1);
}
void pa_module_set_used(pa_module*m, int used) {
assert(m);
if (m->n_used != used)
pa_subscription_post(m->core, PA_SUBSCRIPTION_EVENT_MODULE|PA_SUBSCRIPTION_EVENT_CHANGE, m->index);
if (m->n_used != used && used == 0)
time(&m->last_used_time);
m->n_used = used;
}
pa_modinfo *pa_module_get_info(pa_module *m) {
assert(m);
return pa_modinfo_get_by_handle(m->dl);
}

70
src/pulsecore/module.h Normal file
View file

@ -0,0 +1,70 @@
#ifndef foomodulehfoo
#define foomodulehfoo
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <inttypes.h>
#include <ltdl.h>
#include <pulsecore/core.h>
#include <pulsecore/modinfo.h>
typedef struct pa_module pa_module;
struct pa_module {
pa_core *core;
char *name, *argument;
uint32_t index;
lt_dlhandle dl;
int (*init)(pa_core *c, pa_module*m);
void (*done)(pa_core *c, pa_module*m);
void *userdata;
int n_used;
int auto_unload;
time_t last_used_time;
int unload_requested;
};
pa_module* pa_module_load(pa_core *c, const char *name, const char*argument);
void pa_module_unload(pa_core *c, pa_module *m);
void pa_module_unload_by_index(pa_core *c, uint32_t idx);
void pa_module_unload_all(pa_core *c);
void pa_module_unload_unused(pa_core *c);
void pa_module_unload_request(pa_module *m);
void pa_module_set_used(pa_module*m, int used);
#define PA_MODULE_AUTHOR(s) const char * pa__get_author(void) { return s; }
#define PA_MODULE_DESCRIPTION(s) const char * pa__get_description(void) { return s; }
#define PA_MODULE_USAGE(s) const char * pa__get_usage(void) { return s; }
#define PA_MODULE_VERSION(s) const char * pa__get_version(void) { return s; }
pa_modinfo *pa_module_get_info(pa_module *m);
#endif

214
src/pulsecore/namereg.c Normal file
View file

@ -0,0 +1,214 @@
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <string.h>
#include <stdio.h>
#include <pulse/xmalloc.h>
#include <pulsecore/autoload.h>
#include <pulsecore/source.h>
#include <pulsecore/sink.h>
#include <pulsecore/core-subscribe.h>
#include <pulsecore/core-util.h>
#include "namereg.h"
struct namereg_entry {
pa_namereg_type_t type;
char *name;
void *data;
};
void pa_namereg_free(pa_core *c) {
assert(c);
if (!c->namereg)
return;
assert(pa_hashmap_size(c->namereg) == 0);
pa_hashmap_free(c->namereg, NULL, NULL);
}
const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t type, void *data, int fail) {
struct namereg_entry *e;
char *n = NULL;
int r;
assert(c && name && data);
if (!c->namereg) {
c->namereg = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
assert(c->namereg);
}
if ((e = pa_hashmap_get(c->namereg, name)) && fail)
return NULL;
if (!e)
n = pa_xstrdup(name);
else {
unsigned i;
size_t l = strlen(name);
n = pa_xmalloc(l+3);
for (i = 1; i <= 99; i++) {
snprintf(n, l+2, "%s%u", name, i);
if (!(e = pa_hashmap_get(c->namereg, n)))
break;
}
if (e) {
pa_xfree(n);
return NULL;
}
}
assert(n);
e = pa_xmalloc(sizeof(struct namereg_entry));
e->type = type;
e->name = n;
e->data = data;
r = pa_hashmap_put(c->namereg, e->name, e);
assert (r >= 0);
return e->name;
}
void pa_namereg_unregister(pa_core *c, const char *name) {
struct namereg_entry *e;
assert(c && name);
e = pa_hashmap_remove(c->namereg, name);
assert(e);
pa_xfree(e->name);
pa_xfree(e);
}
void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type, int autoload) {
struct namereg_entry *e;
uint32_t idx;
assert(c);
if (!name) {
if (type == PA_NAMEREG_SOURCE)
name = pa_namereg_get_default_source_name(c);
else if (type == PA_NAMEREG_SINK)
name = pa_namereg_get_default_sink_name(c);
}
if (!name)
return NULL;
if (c->namereg && (e = pa_hashmap_get(c->namereg, name)))
if (e->type == type)
return e->data;
if (pa_atou(name, &idx) < 0) {
if (autoload) {
pa_autoload_request(c, name, type);
if (c->namereg && (e = pa_hashmap_get(c->namereg, name)))
if (e->type == type)
return e->data;
}
return NULL;
}
if (type == PA_NAMEREG_SINK)
return pa_idxset_get_by_index(c->sinks, idx);
else if (type == PA_NAMEREG_SOURCE)
return pa_idxset_get_by_index(c->sources, idx);
else if (type == PA_NAMEREG_SAMPLE && c->scache)
return pa_idxset_get_by_index(c->scache, idx);
return NULL;
}
void pa_namereg_set_default(pa_core*c, const char *name, pa_namereg_type_t type) {
char **s;
assert(c && (type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE));
s = type == PA_NAMEREG_SINK ? &c->default_sink_name : &c->default_source_name;
assert(s);
if (!name && !*s)
return;
if (name && *s && !strcmp(name, *s))
return;
pa_xfree(*s);
*s = pa_xstrdup(name);
pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SERVER|PA_SUBSCRIPTION_EVENT_CHANGE, PA_INVALID_INDEX);
}
const char *pa_namereg_get_default_sink_name(pa_core *c) {
pa_sink *s;
assert(c);
if (c->default_sink_name)
return c->default_sink_name;
if ((s = pa_idxset_first(c->sinks, NULL)))
pa_namereg_set_default(c, s->name, PA_NAMEREG_SINK);
if (c->default_sink_name)
return c->default_sink_name;
return NULL;
}
const char *pa_namereg_get_default_source_name(pa_core *c) {
pa_source *s;
uint32_t idx;
assert(c);
if (c->default_source_name)
return c->default_source_name;
for (s = pa_idxset_first(c->sources, &idx); s; s = pa_idxset_next(c->sources, &idx))
if (!s->monitor_of) {
pa_namereg_set_default(c, s->name, PA_NAMEREG_SOURCE);
break;
}
if (!c->default_source_name)
if ((s = pa_idxset_first(c->sources, NULL)))
pa_namereg_set_default(c, s->name, PA_NAMEREG_SOURCE);
if (c->default_source_name)
return c->default_source_name;
return NULL;
}

43
src/pulsecore/namereg.h Normal file
View file

@ -0,0 +1,43 @@
#ifndef foonamereghfoo
#define foonamereghfoo
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <pulsecore/core.h>
typedef enum pa_namereg_type {
PA_NAMEREG_SINK,
PA_NAMEREG_SOURCE,
PA_NAMEREG_SAMPLE
} pa_namereg_type_t;
void pa_namereg_free(pa_core *c);
const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t type, void *data, int fail);
void pa_namereg_unregister(pa_core *c, const char *name);
void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type, int autoload);
void pa_namereg_set_default(pa_core*c, const char *name, pa_namereg_type_t type);
const char *pa_namereg_get_default_sink_name(pa_core *c);
const char *pa_namereg_get_default_source_name(pa_core *c);
#endif

View file

@ -0,0 +1,127 @@
#ifndef foonativecommonhfoo
#define foonativecommonhfoo
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <pulse/cdecl.h>
#include <pulse/def.h>
PA_C_DECL_BEGIN
enum {
/* Generic commands */
PA_COMMAND_ERROR,
PA_COMMAND_TIMEOUT, /* pseudo command */
PA_COMMAND_REPLY,
/* Commands from client to server */
PA_COMMAND_CREATE_PLAYBACK_STREAM,
PA_COMMAND_DELETE_PLAYBACK_STREAM,
PA_COMMAND_CREATE_RECORD_STREAM,
PA_COMMAND_DELETE_RECORD_STREAM,
PA_COMMAND_EXIT,
PA_COMMAND_AUTH,
PA_COMMAND_SET_CLIENT_NAME,
PA_COMMAND_LOOKUP_SINK,
PA_COMMAND_LOOKUP_SOURCE,
PA_COMMAND_DRAIN_PLAYBACK_STREAM,
PA_COMMAND_STAT,
PA_COMMAND_GET_PLAYBACK_LATENCY,
PA_COMMAND_CREATE_UPLOAD_STREAM,
PA_COMMAND_DELETE_UPLOAD_STREAM,
PA_COMMAND_FINISH_UPLOAD_STREAM,
PA_COMMAND_PLAY_SAMPLE,
PA_COMMAND_REMOVE_SAMPLE,
PA_COMMAND_GET_SERVER_INFO,
PA_COMMAND_GET_SINK_INFO,
PA_COMMAND_GET_SINK_INFO_LIST,
PA_COMMAND_GET_SOURCE_INFO,
PA_COMMAND_GET_SOURCE_INFO_LIST,
PA_COMMAND_GET_MODULE_INFO,
PA_COMMAND_GET_MODULE_INFO_LIST,
PA_COMMAND_GET_CLIENT_INFO,
PA_COMMAND_GET_CLIENT_INFO_LIST,
PA_COMMAND_GET_SINK_INPUT_INFO,
PA_COMMAND_GET_SINK_INPUT_INFO_LIST,
PA_COMMAND_GET_SOURCE_OUTPUT_INFO,
PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST,
PA_COMMAND_GET_SAMPLE_INFO,
PA_COMMAND_GET_SAMPLE_INFO_LIST,
PA_COMMAND_SUBSCRIBE,
PA_COMMAND_SET_SINK_VOLUME,
PA_COMMAND_SET_SINK_INPUT_VOLUME,
PA_COMMAND_SET_SOURCE_VOLUME,
PA_COMMAND_SET_SINK_MUTE,
PA_COMMAND_SET_SOURCE_MUTE,
PA_COMMAND_CORK_PLAYBACK_STREAM,
PA_COMMAND_FLUSH_PLAYBACK_STREAM,
PA_COMMAND_TRIGGER_PLAYBACK_STREAM,
PA_COMMAND_SET_DEFAULT_SINK,
PA_COMMAND_SET_DEFAULT_SOURCE,
PA_COMMAND_SET_PLAYBACK_STREAM_NAME,
PA_COMMAND_SET_RECORD_STREAM_NAME,
PA_COMMAND_KILL_CLIENT,
PA_COMMAND_KILL_SINK_INPUT,
PA_COMMAND_KILL_SOURCE_OUTPUT,
PA_COMMAND_LOAD_MODULE,
PA_COMMAND_UNLOAD_MODULE,
PA_COMMAND_ADD_AUTOLOAD,
PA_COMMAND_REMOVE_AUTOLOAD,
PA_COMMAND_GET_AUTOLOAD_INFO,
PA_COMMAND_GET_AUTOLOAD_INFO_LIST,
PA_COMMAND_GET_RECORD_LATENCY,
PA_COMMAND_CORK_RECORD_STREAM,
PA_COMMAND_FLUSH_RECORD_STREAM,
PA_COMMAND_PREBUF_PLAYBACK_STREAM,
/* Commands from server to client */
PA_COMMAND_REQUEST,
PA_COMMAND_OVERFLOW,
PA_COMMAND_UNDERFLOW,
PA_COMMAND_PLAYBACK_STREAM_KILLED,
PA_COMMAND_RECORD_STREAM_KILLED,
PA_COMMAND_SUBSCRIBE_EVENT,
PA_COMMAND_MAX
};
#define PA_NATIVE_COOKIE_LENGTH 256
#define PA_NATIVE_COOKIE_FILE ".pulseaudio-cookie"
#define PA_NATIVE_DEFAULT_PORT 4713
#define PA_NATIVE_COOKIE_PROPERTY_NAME "protocol-native-cookie"
#define PA_NATIVE_SERVER_PROPERTY_NAME "protocol-native-server"
#define PA_NATIVE_DEFAULT_UNIX_SOCKET "native"
PA_C_DECL_END
#endif

79
src/pulsecore/packet.c Normal file
View file

@ -0,0 +1,79 @@
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <assert.h>
#include <stdlib.h>
#include <pulse/xmalloc.h>
#include "packet.h"
pa_packet* pa_packet_new(size_t length) {
pa_packet *p;
assert(length);
p = pa_xmalloc(sizeof(pa_packet)+length);
p->ref = 1;
p->length = length;
p->data = (uint8_t*) (p+1);
p->type = PA_PACKET_APPENDED;
return p;
}
pa_packet* pa_packet_new_dynamic(void* data, size_t length) {
pa_packet *p;
assert(data);
assert(length);
p = pa_xnew(pa_packet, 1);
p->ref = 1;
p->length = length;
p->data = data;
p->type = PA_PACKET_DYNAMIC;
return p;
}
pa_packet* pa_packet_ref(pa_packet *p) {
assert(p);
assert(p->ref >= 1);
p->ref++;
return p;
}
void pa_packet_unref(pa_packet *p) {
assert(p);
assert(p->ref >= 1);
if (--p->ref == 0) {
if (p->type == PA_PACKET_DYNAMIC)
pa_xfree(p->data);
pa_xfree(p);
}
}

41
src/pulsecore/packet.h Normal file
View file

@ -0,0 +1,41 @@
#ifndef foopackethfoo
#define foopackethfoo
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <sys/types.h>
#include <inttypes.h>
typedef struct pa_packet {
enum { PA_PACKET_APPENDED, PA_PACKET_DYNAMIC } type;
unsigned ref;
size_t length;
uint8_t *data;
} pa_packet;
pa_packet* pa_packet_new(size_t length);
pa_packet* pa_packet_new_dynamic(void* data, size_t length);
pa_packet* pa_packet_ref(pa_packet *p);
void pa_packet_unref(pa_packet *p);
#endif

115
src/pulsecore/parseaddr.c Normal file
View file

@ -0,0 +1,115 @@
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <string.h>
#include <assert.h>
#include <stdlib.h>
#include <pulse/xmalloc.h>
#include <pulse/util.h>
#include <pulsecore/core-util.h>
#include "parseaddr.h"
/* Parse addresses in one of the following forms:
* HOSTNAME
* HOSTNAME:PORT
* [HOSTNAME]
* [HOSTNAME]:PORT
*
* Return a newly allocated string of the hostname and fill in *ret_port if specified */
static char *parse_host(const char *s, uint16_t *ret_port) {
assert(s && ret_port);
if (*s == '[') {
char *e;
if (!(e = strchr(s+1, ']')))
return NULL;
if (e[1] == ':')
*ret_port = atoi(e+2);
else if (e[1] != 0)
return NULL;
return pa_xstrndup(s+1, e-s-1);
} else {
char *e;
if (!(e = strrchr(s, ':')))
return pa_xstrdup(s);
*ret_port = atoi(e+1);
return pa_xstrndup(s, e-s);
}
}
int pa_parse_address(const char *name, pa_parsed_address *ret_p) {
const char *p;
assert(name && ret_p);
memset(ret_p, 0, sizeof(pa_parsed_address));
ret_p->type = PA_PARSED_ADDRESS_TCP_AUTO;
if (*name == '{') {
char hn[256], *pfx;
/* The URL starts with a host specification for detecting local connections */
if (!pa_get_host_name(hn, sizeof(hn)))
return -1;
pfx = pa_sprintf_malloc("{%s}", hn);
if (!pa_startswith(name, pfx)) {
pa_xfree(pfx);
/* Not local */
return -1;
}
p = name + strlen(pfx);
pa_xfree(pfx);
} else
p = name;
if (*p == '/')
ret_p->type = PA_PARSED_ADDRESS_UNIX;
else if (pa_startswith(p, "unix:")) {
ret_p->type = PA_PARSED_ADDRESS_UNIX;
p += sizeof("unix:")-1;
} else if (pa_startswith(p, "tcp:") || pa_startswith(p, "tcp4:")) {
ret_p->type = PA_PARSED_ADDRESS_TCP4;
p += sizeof("tcp:")-1;
} else if (pa_startswith(p, "tcp6:")) {
ret_p->type = PA_PARSED_ADDRESS_TCP6;
p += sizeof("tcp6:")-1;
}
if (ret_p->type == PA_PARSED_ADDRESS_UNIX)
ret_p->path_or_host = pa_xstrdup(p);
else
if (!(ret_p->path_or_host = parse_host(p, &ret_p->port)))
return -1;
return 0;
}

42
src/pulsecore/parseaddr.h Normal file
View file

@ -0,0 +1,42 @@
#ifndef fooparseaddrhfoo
#define fooparseaddrhfoo
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <inttypes.h>
typedef enum pa_parsed_address_type {
PA_PARSED_ADDRESS_UNIX,
PA_PARSED_ADDRESS_TCP4,
PA_PARSED_ADDRESS_TCP6,
PA_PARSED_ADDRESS_TCP_AUTO
} pa_parsed_address_type_t;
typedef struct pa_parsed_address {
pa_parsed_address_type_t type;
char *path_or_host;
uint16_t port;
} pa_parsed_address;
int pa_parse_address(const char *a, pa_parsed_address *ret_p);
#endif

318
src/pulsecore/pdispatch.c Normal file
View file

@ -0,0 +1,318 @@
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <pulse/timeval.h>
#include <pulse/xmalloc.h>
#include <pulsecore/native-common.h>
#include <pulsecore/llist.h>
#include <pulsecore/log.h>
#include <pulsecore/core-util.h>
#include "pdispatch.h"
/*#define DEBUG_OPCODES */
#ifdef DEBUG_OPCODES
static const char *command_names[PA_COMMAND_MAX] = {
[PA_COMMAND_ERROR] = "ERROR",
[PA_COMMAND_TIMEOUT] = "TIMEOUT",
[PA_COMMAND_REPLY] = "REPLY",
[PA_COMMAND_CREATE_PLAYBACK_STREAM] = "CREATE_PLAYBACK_STREAM",
[PA_COMMAND_DELETE_PLAYBACK_STREAM] = "DELETE_PLAYBACK_STREAM",
[PA_COMMAND_CREATE_RECORD_STREAM] = "CREATE_RECORD_STREAM",
[PA_COMMAND_DELETE_RECORD_STREAM] = "DELETE_RECORD_STREAM",
[PA_COMMAND_AUTH] = "AUTH",
[PA_COMMAND_REQUEST] = "REQUEST",
[PA_COMMAND_EXIT] = "EXIT",
[PA_COMMAND_SET_CLIENT_NAME] = "SET_CLIENT_NAME",
[PA_COMMAND_LOOKUP_SINK] = "LOOKUP_SINK",
[PA_COMMAND_LOOKUP_SOURCE] = "LOOKUP_SOURCE",
[PA_COMMAND_DRAIN_PLAYBACK_STREAM] = "DRAIN_PLAYBACK_STREAM",
[PA_COMMAND_PLAYBACK_STREAM_KILLED] = "PLAYBACK_STREAM_KILLED",
[PA_COMMAND_RECORD_STREAM_KILLED] = "RECORD_STREAM_KILLED",
[PA_COMMAND_STAT] = "STAT",
[PA_COMMAND_GET_PLAYBACK_LATENCY] = "PLAYBACK_LATENCY",
[PA_COMMAND_CREATE_UPLOAD_STREAM] = "CREATE_UPLOAD_STREAM",
[PA_COMMAND_DELETE_UPLOAD_STREAM] = "DELETE_UPLOAD_STREAM",
[PA_COMMAND_FINISH_UPLOAD_STREAM] = "FINISH_UPLOAD_STREAM",
[PA_COMMAND_PLAY_SAMPLE] = "PLAY_SAMPLE",
[PA_COMMAND_REMOVE_SAMPLE] = "REMOVE_SAMPLE",
[PA_COMMAND_GET_SERVER_INFO] = "GET_SERVER_INFO",
[PA_COMMAND_GET_SINK_INFO] = "GET_SINK_INFO",
[PA_COMMAND_GET_SINK_INFO_LIST] = "GET_SINK_INFO_LIST",
[PA_COMMAND_GET_SOURCE_INFO] = "GET_SOURCE_INFO",
[PA_COMMAND_GET_SOURCE_INFO_LIST] = "GET_SOURCE_INFO_LIST",
[PA_COMMAND_GET_MODULE_INFO] = "GET_MODULE_INFO",
[PA_COMMAND_GET_MODULE_INFO_LIST] = "GET_MODULE_INFO_LIST",
[PA_COMMAND_GET_CLIENT_INFO] = "GET_CLIENT_INFO",
[PA_COMMAND_GET_CLIENT_INFO_LIST] = "GET_CLIENT_INFO_LIST",
[PA_COMMAND_GET_SAMPLE_INFO] = "GET_SAMPLE_INFO",
[PA_COMMAND_GET_SAMPLE_INFO_LIST] = "GET_SAMPLE_INFO_LIST",
[PA_COMMAND_GET_SINK_INPUT_INFO] = "GET_SINK_INPUT_INFO",
[PA_COMMAND_GET_SINK_INPUT_INFO_LIST] = "GET_SINK_INPUT_INFO_LIST",
[PA_COMMAND_GET_SOURCE_OUTPUT_INFO] = "GET_SOURCE_OUTPUT_INFO",
[PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST] = "GET_SOURCE_OUTPUT_INFO_LIST",
[PA_COMMAND_SUBSCRIBE] = "SUBSCRIBE",
[PA_COMMAND_SUBSCRIBE_EVENT] = "SUBSCRIBE_EVENT",
[PA_COMMAND_SET_SINK_VOLUME] = "SET_SINK_VOLUME",
[PA_COMMAND_SET_SINK_INPUT_VOLUME] = "SET_SINK_INPUT_VOLUME",
[PA_COMMAND_SET_SOURCE_VOLUME] = "SET_SOURCE_VOLME",
[PA_COMMAND_TRIGGER_PLAYBACK_STREAM] = "TRIGGER_PLAYBACK_STREAM",
[PA_COMMAND_FLUSH_PLAYBACK_STREAM] = "FLUSH_PLAYBACK_STREAM",
[PA_COMMAND_CORK_PLAYBACK_STREAM] = "CORK_PLAYBACK_STREAM",
[PA_COMMAND_GET_AUTOLOAD_INFO] = "GET_AUTOLOAD_INFO",
[PA_COMMAND_GET_AUTOLOAD_INFO_LIST] = "GET_AUTOLOAD_INFO_LIST",
};
#endif
struct reply_info {
pa_pdispatch *pdispatch;
PA_LLIST_FIELDS(struct reply_info);
pa_pdispatch_cb_t callback;
void *userdata;
pa_free_cb_t free_cb;
uint32_t tag;
pa_time_event *time_event;
};
struct pa_pdispatch {
int ref;
pa_mainloop_api *mainloop;
const pa_pdispatch_cb_t *callback_table;
unsigned n_commands;
PA_LLIST_HEAD(struct reply_info, replies);
pa_pdispatch_drain_callback drain_callback;
void *drain_userdata;
const void *creds;
};
static void reply_info_free(struct reply_info *r) {
assert(r && r->pdispatch && r->pdispatch->mainloop);
if (r->time_event)
r->pdispatch->mainloop->time_free(r->time_event);
PA_LLIST_REMOVE(struct reply_info, r->pdispatch->replies, r);
pa_xfree(r);
}
pa_pdispatch* pa_pdispatch_new(pa_mainloop_api *mainloop, const pa_pdispatch_cb_t*table, unsigned entries) {
pa_pdispatch *pd;
assert(mainloop);
assert((entries && table) || (!entries && !table));
pd = pa_xmalloc(sizeof(pa_pdispatch));
pd->ref = 1;
pd->mainloop = mainloop;
pd->callback_table = table;
pd->n_commands = entries;
PA_LLIST_HEAD_INIT(pa_reply_info, pd->replies);
pd->drain_callback = NULL;
pd->drain_userdata = NULL;
pd->creds = NULL;
return pd;
}
static void pdispatch_free(pa_pdispatch *pd) {
assert(pd);
while (pd->replies) {
if (pd->replies->free_cb)
pd->replies->free_cb(pd->replies->userdata);
reply_info_free(pd->replies);
}
pa_xfree(pd);
}
static void run_action(pa_pdispatch *pd, struct reply_info *r, uint32_t command, pa_tagstruct *ts) {
pa_pdispatch_cb_t callback;
void *userdata;
uint32_t tag;
assert(r);
pa_pdispatch_ref(pd);
callback = r->callback;
userdata = r->userdata;
tag = r->tag;
reply_info_free(r);
callback(pd, command, tag, ts, userdata);
if (pd->drain_callback && !pa_pdispatch_is_pending(pd))
pd->drain_callback(pd, pd->drain_userdata);
pa_pdispatch_unref(pd);
}
int pa_pdispatch_run(pa_pdispatch *pd, pa_packet*packet, const void *creds, void *userdata) {
uint32_t tag, command;
pa_tagstruct *ts = NULL;
int ret = -1;
assert(pd && packet && packet->data);
pa_pdispatch_ref(pd);
if (packet->length <= 8)
goto finish;
ts = pa_tagstruct_new(packet->data, packet->length);
assert(ts);
if (pa_tagstruct_getu32(ts, &command) < 0 ||
pa_tagstruct_getu32(ts, &tag) < 0)
goto finish;
#ifdef DEBUG_OPCODES
{
char t[256];
char const *p;
if (!(p = command_names[command]))
snprintf((char*) (p = t), sizeof(t), "%u", command);
pa_log(__FILE__": Recieved opcode <%s>", p);
}
#endif
pd->creds = creds;
if (command == PA_COMMAND_ERROR || command == PA_COMMAND_REPLY) {
struct reply_info *r;
for (r = pd->replies; r; r = r->next)
if (r->tag == tag)
break;
if (r)
run_action(pd, r, command, ts);
} else if (pd->callback_table && (command < pd->n_commands) && pd->callback_table[command]) {
const pa_pdispatch_cb_t *c = pd->callback_table+command;
(*c)(pd, command, tag, ts, userdata);
} else {
pa_log(__FILE__": Recieved unsupported command %u", command);
goto finish;
}
ret = 0;
finish:
pd->creds = NULL;
if (ts)
pa_tagstruct_free(ts);
pa_pdispatch_unref(pd);
return ret;
}
static void timeout_callback(pa_mainloop_api*m, pa_time_event*e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) {
struct reply_info*r = userdata;
assert(r && r->time_event == e && r->pdispatch && r->pdispatch->mainloop == m && r->callback);
run_action(r->pdispatch, r, PA_COMMAND_TIMEOUT, NULL);
}
void pa_pdispatch_register_reply(pa_pdispatch *pd, uint32_t tag, int timeout, pa_pdispatch_cb_t cb, void *userdata, pa_free_cb_t free_cb) {
struct reply_info *r;
struct timeval tv;
assert(pd && pd->ref >= 1 && cb);
r = pa_xmalloc(sizeof(struct reply_info));
r->pdispatch = pd;
r->callback = cb;
r->userdata = userdata;
r->free_cb = free_cb;
r->tag = tag;
pa_gettimeofday(&tv);
tv.tv_sec += timeout;
r->time_event = pd->mainloop->time_new(pd->mainloop, &tv, timeout_callback, r);
assert(r->time_event);
PA_LLIST_PREPEND(struct reply_info, pd->replies, r);
}
int pa_pdispatch_is_pending(pa_pdispatch *pd) {
assert(pd);
return !!pd->replies;
}
void pa_pdispatch_set_drain_callback(pa_pdispatch *pd, void (*cb)(pa_pdispatch *pd, void *userdata), void *userdata) {
assert(pd);
assert(!cb || pa_pdispatch_is_pending(pd));
pd->drain_callback = cb;
pd->drain_userdata = userdata;
}
void pa_pdispatch_unregister_reply(pa_pdispatch *pd, void *userdata) {
struct reply_info *r, *n;
assert(pd);
for (r = pd->replies; r; r = n) {
n = r->next;
if (r->userdata == userdata)
reply_info_free(r);
}
}
void pa_pdispatch_unref(pa_pdispatch *pd) {
assert(pd && pd->ref >= 1);
if (!(--(pd->ref)))
pdispatch_free(pd);
}
pa_pdispatch* pa_pdispatch_ref(pa_pdispatch *pd) {
assert(pd && pd->ref >= 1);
pd->ref++;
return pd;
}
const void * pa_pdispatch_creds(pa_pdispatch *pd) {
assert(pd);
assert(pd->ref >= 1);
return pd->creds;
}

53
src/pulsecore/pdispatch.h Normal file
View file

@ -0,0 +1,53 @@
#ifndef foopdispatchhfoo
#define foopdispatchhfoo
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <inttypes.h>
#include <pulse/mainloop-api.h>
#include <pulse/def.h>
#include <pulsecore/tagstruct.h>
#include <pulsecore/packet.h>
typedef struct pa_pdispatch pa_pdispatch;
typedef void (*pa_pdispatch_cb_t)(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
typedef void (*pa_pdispatch_drain_callback)(pa_pdispatch *pd, void *userdata);
pa_pdispatch* pa_pdispatch_new(pa_mainloop_api *m, const pa_pdispatch_cb_t*table, unsigned entries);
void pa_pdispatch_unref(pa_pdispatch *pd);
pa_pdispatch* pa_pdispatch_ref(pa_pdispatch *pd);
int pa_pdispatch_run(pa_pdispatch *pd, pa_packet*p, const void*creds, void *userdata);
void pa_pdispatch_register_reply(pa_pdispatch *pd, uint32_t tag, int timeout, pa_pdispatch_cb_t callback, void *userdata, pa_free_cb_t free_cb);
int pa_pdispatch_is_pending(pa_pdispatch *pd);
void pa_pdispatch_set_drain_callback(pa_pdispatch *pd, pa_pdispatch_drain_callback callback, void *userdata);
/* Remove all reply slots with the give userdata parameter */
void pa_pdispatch_unregister_reply(pa_pdispatch *pd, void *userdata);
const void * pa_pdispatch_creds(pa_pdispatch *pd);
#endif

304
src/pulsecore/pid.c Normal file
View file

@ -0,0 +1,304 @@
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <limits.h>
#include <signal.h>
#ifdef HAVE_WINDOWS_H
#include <windows.h>
#endif
#include <pulse/xmalloc.h>
#include <pulsecore/core-error.h>
#include <pulsecore/core-util.h>
#include <pulsecore/log.h>
#include "pid.h"
/* Read the PID data from the file descriptor fd, and return it. If no
* pid could be read, return 0, on failure (pid_t) -1 */
static pid_t read_pid(const char *fn, int fd) {
ssize_t r;
char t[20], *e;
uint32_t pid;
assert(fn && fd >= 0);
if ((r = pa_loop_read(fd, t, sizeof(t)-1)) < 0) {
pa_log_warn(__FILE__": WARNING: failed to read PID file '%s': %s",
fn, pa_cstrerror(errno));
return (pid_t) -1;
}
if (r == 0)
return (pid_t) 0;
t[r] = 0;
if ((e = strchr(t, '\n')))
*e = 0;
if (pa_atou(t, &pid) < 0) {
pa_log(__FILE__": WARNING: failed to parse PID file '%s'", fn);
return (pid_t) -1;
}
return (pid_t) pid;
}
static int open_pid_file(const char *fn, int mode) {
int fd = -1;
int lock = -1;
for (;;) {
struct stat st;
pa_make_secure_parent_dir(fn);
if ((fd = open(fn, mode, S_IRUSR|S_IWUSR)) < 0) {
if (mode != O_RDONLY || errno != ENOENT)
pa_log_warn(__FILE__": WARNING: failed to open PID file '%s': %s",
fn, pa_cstrerror(errno));
goto fail;
}
/* Try to lock the file. If that fails, go without */
if (pa_lock_fd(fd, 1) < 0)
goto fail;
if (fstat(fd, &st) < 0) {
pa_log_warn(__FILE__": WARNING: failed to fstat() PID file '%s': %s",
fn, pa_cstrerror(errno));
goto fail;
}
/* Does the file still exist in the file system? When ye, w're done, otherwise restart */
if (st.st_nlink >= 1)
break;
if (pa_lock_fd(fd, 0) < 0)
goto fail;
if (close(fd) < 0) {
pa_log_warn(__FILE__": WARNING: failed to close file '%s': %s",
fn, pa_cstrerror(errno));
goto fail;
}
fd = -1;
}
return fd;
fail:
if (fd < 0) {
if (lock >= 0)
pa_lock_fd(fd, 0);
close(fd);
}
return -1;
}
/* Create a new PID file for the current process. */
int pa_pid_file_create(void) {
int fd = -1;
int ret = -1;
char fn[PATH_MAX];
char t[20];
pid_t pid;
size_t l;
#ifdef OS_IS_WIN32
HANDLE process;
#endif
pa_runtime_path("pid", fn, sizeof(fn));
if ((fd = open_pid_file(fn, O_CREAT|O_RDWR)) < 0)
goto fail;
if ((pid = read_pid(fn, fd)) == (pid_t) -1)
pa_log(__FILE__": corrupt PID file, overwriting.");
else if (pid > 0) {
#ifdef OS_IS_WIN32
if ((process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid)) != NULL) {
CloseHandle(process);
#else
if (kill(pid, 0) >= 0 || errno != ESRCH) {
#endif
pa_log(__FILE__": daemon already running.");
goto fail;
}
pa_log(__FILE__": stale PID file, overwriting.");
}
/* Overwrite the current PID file */
if (lseek(fd, 0, SEEK_SET) == (off_t) -1 || ftruncate(fd, 0) < 0) {
pa_log(__FILE__": failed to truncate PID file '%s': %s",
fn, pa_cstrerror(errno));
goto fail;
}
snprintf(t, sizeof(t), "%lu\n", (unsigned long) getpid());
l = strlen(t);
if (pa_loop_write(fd, t, l) != (ssize_t) l) {
pa_log(__FILE__": failed to write PID file.");
goto fail;
}
ret = 0;
fail:
if (fd >= 0) {
pa_lock_fd(fd, 0);
close(fd);
}
return ret;
}
/* Remove the PID file, if it is ours */
int pa_pid_file_remove(void) {
int fd = -1;
char fn[PATH_MAX];
int ret = -1;
pid_t pid;
char *p;
pa_runtime_path("pid", fn, sizeof(fn));
if ((fd = open_pid_file(fn, O_RDWR)) < 0) {
pa_log_warn(__FILE__": WARNING: failed to open PID file '%s': %s",
fn, pa_cstrerror(errno));
goto fail;
}
if ((pid = read_pid(fn, fd)) == (pid_t) -1)
goto fail;
if (pid != getpid()) {
pa_log(__FILE__": WARNING: PID file '%s' not mine!", fn);
goto fail;
}
if (ftruncate(fd, 0) < 0) {
pa_log_warn(__FILE__": WARNING: failed to truncate PID file '%s': %s",
fn, pa_cstrerror(errno));
goto fail;
}
#ifdef OS_IS_WIN32
pa_lock_fd(fd, 0);
close(fd);
fd = -1;
#endif
if (unlink(fn) < 0) {
pa_log_warn(__FILE__": WARNING: failed to remove PID file '%s': %s",
fn, pa_cstrerror(errno));
goto fail;
}
if ((p = pa_parent_dir(fn))) {
rmdir(p);
pa_xfree(p);
}
ret = 0;
fail:
if (fd >= 0) {
pa_lock_fd(fd, 0);
close(fd);
}
return ret;
}
/* Check whether the daemon is currently running, i.e. if a PID file
* exists and the PID therein too. Returns 0 on succcess, -1
* otherwise. If pid is non-NULL and a running daemon was found,
* return its PID therein */
int pa_pid_file_check_running(pid_t *pid) {
return pa_pid_file_kill(0, pid);
}
#ifndef OS_IS_WIN32
/* Kill a current running daemon. Return non-zero on success, -1
* otherwise. If successful *pid contains the PID of the daemon
* process. */
int pa_pid_file_kill(int sig, pid_t *pid) {
int fd = -1;
char fn[PATH_MAX];
int ret = -1;
pid_t _pid;
if (!pid)
pid = &_pid;
pa_runtime_path("pid", fn, sizeof(fn));
if ((fd = open_pid_file(fn, O_RDONLY)) < 0)
goto fail;
if ((*pid = read_pid(fn, fd)) == (pid_t) -1)
goto fail;
ret = kill(*pid, sig);
fail:
if (fd >= 0) {
pa_lock_fd(fd, 0);
close(fd);
}
return ret;
}
#else /* OS_IS_WIN32 */
int pa_pid_file_kill(int sig, pid_t *pid) {
return -1;
}
#endif

30
src/pulsecore/pid.h Normal file
View file

@ -0,0 +1,30 @@
#ifndef foopidhfoo
#define foopidhfoo
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
int pa_pid_file_create(void);
int pa_pid_file_remove(void);
int pa_pid_file_check_running(pid_t *pid);
int pa_pid_file_kill(int sig, pid_t *pid);
#endif

160
src/pulsecore/pipe.c Normal file
View file

@ -0,0 +1,160 @@
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published
by the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with PulseAudio; If not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#include "winsock.h"
#include "pipe.h"
#ifndef HAVE_PIPE
static int set_block(int fd, int blocking) {
#ifdef O_NONBLOCK
int v;
assert(fd >= 0);
if ((v = fcntl(fd, F_GETFL)) < 0)
return -1;
if (blocking)
v &= ~O_NONBLOCK;
else
v |= O_NONBLOCK;
if (fcntl(fd, F_SETFL, v) < 0)
return -1;
return 0;
#elif defined(OS_IS_WIN32)
u_long arg;
arg = !blocking;
if (ioctlsocket(fd, FIONBIO, &arg) < 0)
return -1;
return 0;
#else
return -1;
#endif
}
int pipe(int filedes[2]) {
int listener;
struct sockaddr_in addr, peer;
socklen_t len;
listener = -1;
filedes[0] = -1;
filedes[1] = -1;
listener = socket(PF_INET, SOCK_STREAM, 0);
if (listener < 0)
goto error;
filedes[0] = socket(PF_INET, SOCK_STREAM, 0);
if (filedes[0] < 0)
goto error;
filedes[1] = socket(PF_INET, SOCK_STREAM, 0);
if (filedes[1] < 0)
goto error;
/* Make non-blocking so that connect() won't block */
if (set_block(filedes[0], 0) < 0)
goto error;
addr.sin_family = AF_INET;
addr.sin_port = 0;
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
if (bind(listener, (struct sockaddr*)&addr, sizeof(addr)) < 0)
goto error;
if (listen(listener, 1) < 0)
goto error;
len = sizeof(addr);
if (getsockname(listener, (struct sockaddr*)&addr, &len) < 0)
goto error;
if (connect(filedes[0], (struct sockaddr*)&addr, sizeof(addr)) < 0) {
#ifdef OS_IS_WIN32
if (WSAGetLastError() != EWOULDBLOCK)
#else
if (errno != EINPROGRESS)
#endif
goto error;
}
len = sizeof(peer);
filedes[1] = accept(listener, (struct sockaddr*)&peer, &len);
if (filedes[1] < 0)
goto error;
/* Restore blocking */
if (set_block(filedes[0], 1) < 0)
goto error;
len = sizeof(addr);
if (getsockname(filedes[0], (struct sockaddr*)&addr, &len) < 0)
goto error;
/* Check that someone else didn't steal the connection */
if ((addr.sin_port != peer.sin_port) || (addr.sin_addr.s_addr != peer.sin_addr.s_addr))
goto error;
close(listener);
return 0;
error:
if (listener >= 0)
close(listener);
if (filedes[0] >= 0)
close(filedes[0]);
if (filedes[1] >= 0)
close(filedes[0]);
return -1;
}
#endif /* HAVE_PIPE */

31
src/pulsecore/pipe.h Normal file
View file

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

View file

@ -0,0 +1,117 @@
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdlib.h>
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <pulse/xmalloc.h>
#include <pulsecore/sink-input.h>
#include <pulsecore/gccmacro.h>
#include "play-memchunk.h"
static void sink_input_kill(pa_sink_input *i) {
pa_memchunk *c;
assert(i && i->userdata);
c = i->userdata;
pa_sink_input_disconnect(i);
pa_sink_input_unref(i);
pa_memblock_unref(c->memblock);
pa_xfree(c);
}
static int sink_input_peek(pa_sink_input *i, pa_memchunk *chunk) {
pa_memchunk *c;
assert(i && chunk && i->userdata);
c = i->userdata;
if (c->length <= 0)
return -1;
assert(c->memblock && c->memblock->length);
*chunk = *c;
pa_memblock_ref(c->memblock);
return 0;
}
static void si_kill(PA_GCC_UNUSED pa_mainloop_api *m, void *i) {
sink_input_kill(i);
}
static void sink_input_drop(pa_sink_input *i, const pa_memchunk*chunk, size_t length) {
pa_memchunk *c;
assert(i && length && i->userdata);
c = i->userdata;
assert(!memcmp(chunk, c, sizeof(chunk)));
assert(length <= c->length);
c->length -= length;
c->index += length;
if (c->length <= 0)
pa_mainloop_api_once(i->sink->core->mainloop, si_kill, i);
}
int pa_play_memchunk(
pa_sink *sink,
const char *name,
const pa_sample_spec *ss,
const pa_channel_map *map,
const pa_memchunk *chunk,
pa_cvolume *cvolume) {
pa_sink_input *si;
pa_memchunk *nchunk;
assert(sink);
assert(ss);
assert(chunk);
if (cvolume && pa_cvolume_is_muted(cvolume))
return 0;
if (!(si = pa_sink_input_new(sink, name, __FILE__, ss, map, cvolume, 0, PA_RESAMPLER_INVALID)))
return -1;
si->peek = sink_input_peek;
si->drop = sink_input_drop;
si->kill = sink_input_kill;
si->userdata = nchunk = pa_xnew(pa_memchunk, 1);
*nchunk = *chunk;
pa_memblock_ref(chunk->memblock);
pa_sink_notify(sink);
return 0;
}

View file

@ -0,0 +1,36 @@
#ifndef fooplaychunkhfoo
#define fooplaychunkhfoo
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <pulsecore/sink.h>
#include <pulsecore/memchunk.h>
int pa_play_memchunk(
pa_sink *sink,
const char *name,
const pa_sample_spec *ss,
const pa_channel_map *map,
const pa_memchunk *chunk,
pa_cvolume *cvolume);
#endif

191
src/pulsecore/poll.c Normal file
View file

@ -0,0 +1,191 @@
/* $Id$ */
/***
Copyright (C) 1994, 1996, 1997 Free Software Foundation, Inc.
Copyright (C) 2005, Cendio AB.
This file is part of PulseAudio.
Based on work for the GNU C Library.
PulseAudio is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published
by the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with PulseAudio; If not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
USA.
***/
/* Poll the file descriptors described by the NFDS structures starting at
FDS. If TIMEOUT is nonzero and not -1, allow TIMEOUT milliseconds for
an event to occur; if TIMEOUT is -1, block until an event occurs.
Returns the number of file descriptors with events, zero if timed out,
or -1 for errors. */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <errno.h>
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#include "winsock.h"
#ifndef HAVE_SYS_POLL_H
#include <pulsecore/core-util.h>
#include "poll.h"
int poll (struct pollfd *fds, unsigned long int nfds, int timeout) {
struct timeval tv;
fd_set rset, wset, xset;
struct pollfd *f;
int ready;
int maxfd = 0;
char data[64];
FD_ZERO (&rset);
FD_ZERO (&wset);
FD_ZERO (&xset);
if (nfds == 0) {
if (timeout >= 0) {
pa_msleep(timeout);
return 0;
}
#ifdef OS_IS_WIN32
/*
* Windows does not support signals properly so waiting for them would
* mean a deadlock.
*/
pa_msleep(100);
return 0;
#else
return select(0, NULL, NULL, NULL, NULL);
#endif
}
for (f = fds; f < &fds[nfds]; ++f) {
if (f->fd != -1) {
if (f->events & POLLIN)
FD_SET (f->fd, &rset);
if (f->events & POLLOUT)
FD_SET (f->fd, &wset);
if (f->events & POLLPRI)
FD_SET (f->fd, &xset);
if (f->fd > maxfd && (f->events & (POLLIN|POLLOUT|POLLPRI)))
maxfd = f->fd;
}
}
tv.tv_sec = timeout / 1000;
tv.tv_usec = (timeout % 1000) * 1000;
ready = select ((SELECT_TYPE_ARG1) maxfd + 1, SELECT_TYPE_ARG234 &rset,
SELECT_TYPE_ARG234 &wset, SELECT_TYPE_ARG234 &xset,
SELECT_TYPE_ARG5 (timeout == -1 ? NULL : &tv));
if ((ready == -1) && (errno == EBADF)) {
ready = 0;
FD_ZERO (&rset);
FD_ZERO (&wset);
FD_ZERO (&xset);
maxfd = -1;
for (f = fds; f < &fds[nfds]; ++f) {
if (f->fd != -1) {
fd_set sngl_rset, sngl_wset, sngl_xset;
FD_ZERO (&sngl_rset);
FD_ZERO (&sngl_wset);
FD_ZERO (&sngl_xset);
if (f->events & POLLIN)
FD_SET (f->fd, &sngl_rset);
if (f->events & POLLOUT)
FD_SET (f->fd, &sngl_wset);
if (f->events & POLLPRI)
FD_SET (f->fd, &sngl_xset);
if (f->events & (POLLIN|POLLOUT|POLLPRI)) {
struct timeval singl_tv;
singl_tv.tv_sec = 0;
singl_tv.tv_usec = 0;
if (select((SELECT_TYPE_ARG1) f->fd, SELECT_TYPE_ARG234 &rset,
SELECT_TYPE_ARG234 &wset, SELECT_TYPE_ARG234 &xset,
SELECT_TYPE_ARG5 &singl_tv) != -1) {
if (f->events & POLLIN)
FD_SET (f->fd, &rset);
if (f->events & POLLOUT)
FD_SET (f->fd, &wset);
if (f->events & POLLPRI)
FD_SET (f->fd, &xset);
if (f->fd > maxfd && (f->events & (POLLIN|POLLOUT|POLLPRI)))
maxfd = f->fd;
++ready;
} else if (errno == EBADF)
f->revents |= POLLNVAL;
}
}
}
if (ready) {
/* Linux alters the tv struct... but it shouldn't matter here ...
* as we're going to be a little bit out anyway as we've just eaten
* more than a couple of cpu cycles above */
ready = select ((SELECT_TYPE_ARG1) maxfd + 1, SELECT_TYPE_ARG234 &rset,
SELECT_TYPE_ARG234 &wset, SELECT_TYPE_ARG234 &xset,
SELECT_TYPE_ARG5 (timeout == -1 ? NULL : &tv));
}
}
#ifdef OS_IS_WIN32
errno = WSAGetLastError();
#endif
if (ready > 0) {
ready = 0;
for (f = fds; f < &fds[nfds]; ++f) {
f->revents = 0;
if (f->fd != -1) {
if (FD_ISSET (f->fd, &rset)) {
/* support for POLLHUP. An hung up descriptor does not
increase the return value! */
if (recv (f->fd, data, 64, MSG_PEEK) == -1) {
if (errno == ESHUTDOWN || errno == ECONNRESET ||
errno == ECONNABORTED || errno == ENETRESET) {
fprintf(stderr, "Hangup\n");
f->revents |= POLLHUP;
}
}
if (f->revents == 0)
f->revents |= POLLIN;
}
if (FD_ISSET (f->fd, &wset))
f->revents |= POLLOUT;
if (FD_ISSET (f->fd, &xset))
f->revents |= POLLPRI;
}
if (f->revents)
ready++;
}
}
return ready;
}
#endif /* HAVE_SYS_POLL_H */

57
src/pulsecore/poll.h Normal file
View file

@ -0,0 +1,57 @@
/* $Id$ */
/***
Compatibility definitions for System V `poll' interface.
Copyright (C) 1994,96,97,98,99,2000,2001,2004 Free Software Foundation, Inc.
Copyright (C) 2005, Cendio AB.
This file is part of PulseAudio.
Based on work for the GNU C Library.
PulseAudio is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published
by the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with PulseAudio; If not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
USA.
***/
/* Event types that can be polled for. These bits may be set in `events'
to indicate the interesting event types; they will appear in `revents'
to indicate the status of the file descriptor. */
#define POLLIN 0x001 /* There is data to read. */
#define POLLPRI 0x002 /* There is urgent data to read. */
#define POLLOUT 0x004 /* Writing now will not block. */
/* Event types always implicitly polled for. These bits need not be set in
`events', but they will appear in `revents' to indicate the status of
the file descriptor. */
#define POLLERR 0x008 /* Error condition. */
#define POLLHUP 0x010 /* Hung up. */
#define POLLNVAL 0x020 /* Invalid polling request. */
/* Type used for the number of file descriptors. */
typedef unsigned long int nfds_t;
/* Data structure describing a polling request. */
struct pollfd
{
int fd; /* File descriptor to poll. */
short int events; /* Types of events poller cares about. */
short int revents; /* Types of events that actually occurred. */
};
/* Poll the file descriptors described by the NFDS structures starting at
FDS. If TIMEOUT is nonzero and not -1, allow TIMEOUT milliseconds for
an event to occur; if TIMEOUT is -1, block until an event occurs.
Returns the number of file descriptors with events, zero if timed out,
or -1 for errors. */
extern int poll (struct pollfd *__fds, nfds_t __nfds, int __timeout);

121
src/pulsecore/props.c Normal file
View file

@ -0,0 +1,121 @@
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <assert.h>
#include <pulse/xmalloc.h>
#include <pulsecore/log.h>
#include "props.h"
typedef struct pa_property {
char *name; /* Points to memory allocated by the property subsystem */
void *data; /* Points to memory maintained by the caller */
} pa_property;
/* Allocate a new property object */
static pa_property* property_new(const char *name, void *data) {
pa_property* p;
assert(name && data);
p = pa_xmalloc(sizeof(pa_property));
p->name = pa_xstrdup(name);
p->data = data;
return p;
}
/* Free a property object */
static void property_free(pa_property *p) {
assert(p);
pa_xfree(p->name);
pa_xfree(p);
}
void* pa_property_get(pa_core *c, const char *name) {
pa_property *p;
assert(c && name && c->properties);
if (!(p = pa_hashmap_get(c->properties, name)))
return NULL;
return p->data;
}
int pa_property_set(pa_core *c, const char *name, void *data) {
pa_property *p;
assert(c && name && data && c->properties);
if (pa_hashmap_get(c->properties, name))
return -1;
p = property_new(name, data);
pa_hashmap_put(c->properties, p->name, p);
return 0;
}
int pa_property_remove(pa_core *c, const char *name) {
pa_property *p;
assert(c && name && c->properties);
if (!(p = pa_hashmap_remove(c->properties, name)))
return -1;
property_free(p);
return 0;
}
void pa_property_init(pa_core *c) {
assert(c);
c->properties = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
}
void pa_property_cleanup(pa_core *c) {
assert(c);
if (!c->properties)
return;
assert(!pa_hashmap_size(c->properties));
pa_hashmap_free(c->properties, NULL, NULL);
c->properties = NULL;
}
void pa_property_dump(pa_core *c, pa_strbuf *s) {
void *state = NULL;
pa_property *p;
assert(c && s);
while ((p = pa_hashmap_iterate(c->properties, &state, NULL)))
pa_strbuf_printf(s, "[%s] -> [%p]\n", p->name, p->data);
}
int pa_property_replace(pa_core *c, const char *name, void *data) {
assert(c && name);
pa_property_remove(c, name);
return pa_property_set(c, name, data);
}

58
src/pulsecore/props.h Normal file
View file

@ -0,0 +1,58 @@
#ifndef foopropshfoo
#define foopropshfoo
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <pulsecore/core.h>
#include <pulsecore/strbuf.h>
/* The property subsystem is to be used to share data between
* modules. Consider them to be kind of "global" variables for a
* core. Why not use the hashmap functions directly? The hashmap
* functions copy neither the key nor value, while this property
* system copies the key. Users of this system have to think about
* reference counting themselves. */
/* Return a pointer to the value of the specified property. */
void* pa_property_get(pa_core *c, const char *name);
/* Set the property 'name' to 'data'. This function fails in case a
* property by this name already exists. The property data is not
* copied or reference counted. This is the caller's job. */
int pa_property_set(pa_core *c, const char *name, void *data);
/* Remove the specified property. Return non-zero on failure */
int pa_property_remove(pa_core *c, const char *name);
/* A combination of pa_property_remove() and pa_property_set() */
int pa_property_replace(pa_core *c, const char *name, void *data);
/* Free all memory used by the property system */
void pa_property_cleanup(pa_core *c);
/* Initialize the properties subsystem */
void pa_property_init(pa_core *c);
/* Dump the current set of properties */
void pa_property_dump(pa_core *c, pa_strbuf *s);
#endif

View file

@ -0,0 +1,97 @@
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <assert.h>
#include <stdlib.h>
#include <pulse/xmalloc.h>
#include <pulsecore/cli.h>
#include <pulsecore/log.h>
#include "protocol-cli.h"
/* Don't allow more than this many concurrent connections */
#define MAX_CONNECTIONS 25
struct pa_protocol_cli {
pa_module *module;
pa_core *core;
pa_socket_server*server;
pa_idxset *connections;
};
static void cli_eof_cb(pa_cli*c, void*userdata) {
pa_protocol_cli *p = userdata;
assert(p);
pa_idxset_remove_by_data(p->connections, c, NULL);
pa_cli_free(c);
}
static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata) {
pa_protocol_cli *p = userdata;
pa_cli *c;
assert(s && io && p);
if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) {
pa_log(__FILE__": Warning! Too many connections (%u), dropping incoming connection.", MAX_CONNECTIONS);
pa_iochannel_free(io);
return;
}
c = pa_cli_new(p->core, io, p->module);
assert(c);
pa_cli_set_eof_callback(c, cli_eof_cb, p);
pa_idxset_put(p->connections, c, NULL);
}
pa_protocol_cli* pa_protocol_cli_new(pa_core *core, pa_socket_server *server, pa_module *m, PA_GCC_UNUSED pa_modargs *ma) {
pa_protocol_cli* p;
assert(core && server);
p = pa_xmalloc(sizeof(pa_protocol_cli));
p->module = m;
p->core = core;
p->server = server;
p->connections = pa_idxset_new(NULL, NULL);
pa_socket_server_set_callback(p->server, on_connection, p);
return p;
}
static void free_connection(void *p, PA_GCC_UNUSED void *userdata) {
assert(p);
pa_cli_free(p);
}
void pa_protocol_cli_free(pa_protocol_cli *p) {
assert(p);
pa_idxset_free(p->connections, free_connection, NULL);
pa_socket_server_unref(p->server);
pa_xfree(p);
}

View file

@ -0,0 +1,35 @@
#ifndef fooprotocolclihfoo
#define fooprotocolclihfoo
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <pulsecore/core.h>
#include <pulsecore/socket-server.h>
#include <pulsecore/module.h>
#include <pulsecore/modargs.h>
typedef struct pa_protocol_cli pa_protocol_cli;
pa_protocol_cli* pa_protocol_cli_new(pa_core *core, pa_socket_server *server, pa_module *m, pa_modargs *ma);
void pa_protocol_cli_free(pa_protocol_cli *n);
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,35 @@
#ifndef fooprotocolesoundhfoo
#define fooprotocolesoundhfoo
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <pulsecore/core.h>
#include <pulsecore/socket-server.h>
#include <pulsecore/module.h>
#include <pulsecore/modargs.h>
typedef struct pa_protocol_esound pa_protocol_esound;
pa_protocol_esound* pa_protocol_esound_new(pa_core*core, pa_socket_server *server, pa_module *m, pa_modargs *ma);
void pa_protocol_esound_free(pa_protocol_esound *p);
#endif

View file

@ -0,0 +1,268 @@
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <pulse/util.h>
#include <pulse/xmalloc.h>
#include <pulsecore/ioline.h>
#include <pulsecore/log.h>
#include <pulsecore/namereg.h>
#include <pulsecore/cli-text.h>
#include "protocol-http.h"
/* Don't allow more than this many concurrent connections */
#define MAX_CONNECTIONS 10
#define internal_server_error(c) http_message((c), 500, "Internal Server Error", NULL)
#define URL_ROOT "/"
#define URL_CSS "/style"
#define URL_STATUS "/status"
struct connection {
pa_protocol_http *protocol;
pa_ioline *line;
enum { REQUEST_LINE, MIME_HEADER, DATA } state;
char *url;
};
struct pa_protocol_http {
pa_module *module;
pa_core *core;
pa_socket_server*server;
pa_idxset *connections;
};
static void http_response(struct connection *c, int code, const char *msg, const char *mime) {
char s[256];
assert(c);
assert(msg);
assert(mime);
snprintf(s, sizeof(s),
"HTTP/1.0 %i %s\n"
"Connection: close\n"
"Content-Type: %s\n"
"Cache-Control: no-cache\n"
"Expires: 0\n"
"Server: "PACKAGE_NAME"/"PACKAGE_VERSION"\n"
"\n", code, msg, mime);
pa_ioline_puts(c->line, s);
}
static void http_message(struct connection *c, int code, const char *msg, const char *text) {
char s[256];
assert(c);
http_response(c, code, msg, "text/html");
if (!text)
text = msg;
snprintf(s, sizeof(s),
"<?xml version=\"1.0\"?>\n"
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
"<html xmlns=\"http://www.w3.org/1999/xhtml\"><head><title>%s</title></head>\n"
"<body>%s</body></html>\n",
text, text);
pa_ioline_puts(c->line, s);
pa_ioline_defer_close(c->line);
}
static void connection_free(struct connection *c, int del) {
assert(c);
if (c->url)
pa_xfree(c->url);
if (del)
pa_idxset_remove_by_data(c->protocol->connections, c, NULL);
pa_ioline_unref(c->line);
pa_xfree(c);
}
static void line_callback(pa_ioline *line, const char *s, void *userdata) {
struct connection *c = userdata;
assert(line);
assert(c);
if (!s) {
/* EOF */
connection_free(c, 1);
return;
}
switch (c->state) {
case REQUEST_LINE: {
if (memcmp(s, "GET ", 4))
goto fail;
s +=4;
c->url = pa_xstrndup(s, strcspn(s, " \r\n\t?"));
c->state = MIME_HEADER;
break;
}
case MIME_HEADER: {
/* Ignore MIME headers */
if (strcspn(s, " \r\n") != 0)
break;
/* We're done */
c->state = DATA;
pa_log_info(__FILE__": request for %s", c->url);
if (!strcmp(c->url, URL_ROOT)) {
char txt[256];
http_response(c, 200, "OK", "text/html");
pa_ioline_puts(c->line,
"<?xml version=\"1.0\"?>\n"
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
"<html xmlns=\"http://www.w3.org/1999/xhtml\"><title>"PACKAGE_NAME" "PACKAGE_VERSION"</title>\n"
"<link rel=\"stylesheet\" type=\"text/css\" href=\"style\"/></head><body>\n");
pa_ioline_puts(c->line,
"<h1>"PACKAGE_NAME" "PACKAGE_VERSION"</h1>\n"
"<table>");
#define PRINTF_FIELD(a,b) pa_ioline_printf(c->line, "<tr><td><b>%s</b></td><td>%s</td></tr>\n",(a),(b))
PRINTF_FIELD("User Name:", pa_get_user_name(txt, sizeof(txt)));
PRINTF_FIELD("Fully Qualified Domain Name:", pa_get_fqdn(txt, sizeof(txt)));
PRINTF_FIELD("Default Sample Specification:", pa_sample_spec_snprint(txt, sizeof(txt), &c->protocol->core->default_sample_spec));
PRINTF_FIELD("Default Sink:", pa_namereg_get_default_sink_name(c->protocol->core));
PRINTF_FIELD("Default Source:", pa_namereg_get_default_source_name(c->protocol->core));
pa_ioline_puts(c->line, "</table>");
pa_ioline_puts(c->line, "<p><a href=\"/status\">Click here</a> for an extensive server status report.</p>");
pa_ioline_puts(c->line, "</body></html>\n");
pa_ioline_defer_close(c->line);
} else if (!strcmp(c->url, URL_CSS)) {
http_response(c, 200, "OK", "text/css");
pa_ioline_puts(c->line,
"body { color: black; background-color: white; margin: 0.5cm; }\n"
"a:link, a:visited { color: #900000; }\n"
"p { margin-left: 0.5cm; margin-right: 0.5cm; }\n"
"h1 { color: #00009F; }\n"
"h2 { color: #00009F; }\n"
"ul { margin-left: .5cm; }\n"
"ol { margin-left: .5cm; }\n"
"pre { margin-left: .5cm; background-color: #f0f0f0; padding: 0.4cm;}\n"
".grey { color: #afafaf; }\n"
"table { margin-left: 1cm; border:1px solid lightgrey; padding: 0.2cm; }\n"
"td { padding-left:10px; padding-right:10px; }\n");
pa_ioline_defer_close(c->line);
} else if (!strcmp(c->url, URL_STATUS)) {
char *r;
http_response(c, 200, "OK", "text/plain");
r = pa_full_status_string(c->protocol->core);
pa_ioline_puts(c->line, r);
pa_xfree(r);
pa_ioline_defer_close(c->line);
} else
http_message(c, 404, "Not Found", NULL);
break;
}
default:
;
}
return;
fail:
internal_server_error(c);
}
static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata) {
pa_protocol_http *p = userdata;
struct connection *c;
assert(s && io && p);
if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) {
pa_log_warn(__FILE__": Warning! Too many connections (%u), dropping incoming connection.", MAX_CONNECTIONS);
pa_iochannel_free(io);
return;
}
c = pa_xmalloc(sizeof(struct connection));
c->protocol = p;
c->line = pa_ioline_new(io);
c->state = REQUEST_LINE;
c->url = NULL;
pa_ioline_set_callback(c->line, line_callback, c);
pa_idxset_put(p->connections, c, NULL);
}
pa_protocol_http* pa_protocol_http_new(pa_core *core, pa_socket_server *server, pa_module *m, PA_GCC_UNUSED pa_modargs *ma) {
pa_protocol_http* p;
assert(core && server);
p = pa_xmalloc(sizeof(pa_protocol_http));
p->module = m;
p->core = core;
p->server = server;
p->connections = pa_idxset_new(NULL, NULL);
pa_socket_server_set_callback(p->server, on_connection, p);
return p;
}
static void free_connection(void *p, PA_GCC_UNUSED void *userdata) {
assert(p);
connection_free(p, 0);
}
void pa_protocol_http_free(pa_protocol_http *p) {
assert(p);
pa_idxset_free(p->connections, free_connection, NULL);
pa_socket_server_unref(p->server);
pa_xfree(p);
}

View file

@ -0,0 +1,35 @@
#ifndef fooprotocolhttphfoo
#define fooprotocolhttphfoo
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <pulsecore/core.h>
#include <pulsecore/socket-server.h>
#include <pulsecore/module.h>
#include <pulsecore/modargs.h>
typedef struct pa_protocol_http pa_protocol_http;
pa_protocol_http* pa_protocol_http_new(pa_core *core, pa_socket_server *server, pa_module *m, pa_modargs *ma);
void pa_protocol_http_free(pa_protocol_http *n);
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,37 @@
#ifndef fooprotocolnativehfoo
#define fooprotocolnativehfoo
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <pulsecore/core.h>
#include <pulsecore/socket-server.h>
#include <pulsecore/module.h>
#include <pulsecore/modargs.h>
typedef struct pa_protocol_native pa_protocol_native;
pa_protocol_native* pa_protocol_native_new(pa_core*core, pa_socket_server *server, pa_module *m, pa_modargs *ma);
void pa_protocol_native_free(pa_protocol_native *n);
pa_protocol_native* pa_protocol_native_new_iochannel(pa_core*core, pa_iochannel *io, pa_module *m, pa_modargs *ma);
#endif

View file

@ -0,0 +1,494 @@
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <assert.h>
#include <stdlib.h>
#include <limits.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <pulse/xmalloc.h>
#include <pulsecore/sink-input.h>
#include <pulsecore/source-output.h>
#include <pulsecore/client.h>
#include <pulsecore/sample-util.h>
#include <pulsecore/namereg.h>
#include <pulsecore/log.h>
#include <pulsecore/core-error.h>
#include "protocol-simple.h"
/* Don't allow more than this many concurrent connections */
#define MAX_CONNECTIONS 10
struct connection {
pa_protocol_simple *protocol;
pa_iochannel *io;
pa_sink_input *sink_input;
pa_source_output *source_output;
pa_client *client;
pa_memblockq *input_memblockq, *output_memblockq;
pa_defer_event *defer_event;
int dead;
struct {
pa_memblock *current_memblock;
size_t memblock_index, fragment_size;
} playback;
};
struct pa_protocol_simple {
pa_module *module;
pa_core *core;
pa_socket_server*server;
pa_idxset *connections;
enum {
RECORD = 1,
PLAYBACK = 2,
DUPLEX = 3
} mode;
pa_sample_spec sample_spec;
char *source_name, *sink_name;
};
#define PLAYBACK_BUFFER_SECONDS (.5)
#define PLAYBACK_BUFFER_FRAGMENTS (10)
#define RECORD_BUFFER_SECONDS (5)
#define RECORD_BUFFER_FRAGMENTS (100)
static void connection_free(struct connection *c) {
assert(c);
pa_idxset_remove_by_data(c->protocol->connections, c, NULL);
if (c->playback.current_memblock)
pa_memblock_unref(c->playback.current_memblock);
if (c->sink_input) {
pa_sink_input_disconnect(c->sink_input);
pa_sink_input_unref(c->sink_input);
}
if (c->source_output) {
pa_source_output_disconnect(c->source_output);
pa_source_output_unref(c->source_output);
}
if (c->client)
pa_client_free(c->client);
if (c->io)
pa_iochannel_free(c->io);
if (c->input_memblockq)
pa_memblockq_free(c->input_memblockq);
if (c->output_memblockq)
pa_memblockq_free(c->output_memblockq);
if (c->defer_event)
c->protocol->core->mainloop->defer_free(c->defer_event);
pa_xfree(c);
}
static int do_read(struct connection *c) {
pa_memchunk chunk;
ssize_t r;
size_t l;
if (!c->sink_input || !(l = pa_memblockq_missing(c->input_memblockq)))
return 0;
if (l > c->playback.fragment_size)
l = c->playback.fragment_size;
if (c->playback.current_memblock)
if (c->playback.current_memblock->length - c->playback.memblock_index < l) {
pa_memblock_unref(c->playback.current_memblock);
c->playback.current_memblock = NULL;
c->playback.memblock_index = 0;
}
if (!c->playback.current_memblock) {
c->playback.current_memblock = pa_memblock_new(c->playback.fragment_size*2, c->protocol->core->memblock_stat);
assert(c->playback.current_memblock && c->playback.current_memblock->length >= l);
c->playback.memblock_index = 0;
}
if ((r = pa_iochannel_read(c->io, (uint8_t*) c->playback.current_memblock->data+c->playback.memblock_index, l)) <= 0) {
pa_log_debug(__FILE__": read(): %s", r == 0 ? "EOF" : pa_cstrerror(errno));
return -1;
}
chunk.memblock = c->playback.current_memblock;
chunk.index = c->playback.memblock_index;
chunk.length = r;
assert(chunk.memblock);
c->playback.memblock_index += r;
assert(c->input_memblockq);
pa_memblockq_push_align(c->input_memblockq, &chunk);
assert(c->sink_input);
pa_sink_notify(c->sink_input->sink);
return 0;
}
static int do_write(struct connection *c) {
pa_memchunk chunk;
ssize_t r;
if (!c->source_output)
return 0;
assert(c->output_memblockq);
if (pa_memblockq_peek(c->output_memblockq, &chunk) < 0)
return 0;
assert(chunk.memblock && chunk.length);
if ((r = pa_iochannel_write(c->io, (uint8_t*) chunk.memblock->data+chunk.index, chunk.length)) < 0) {
pa_memblock_unref(chunk.memblock);
pa_log(__FILE__": write(): %s", pa_cstrerror(errno));
return -1;
}
pa_memblockq_drop(c->output_memblockq, &chunk, r);
pa_memblock_unref(chunk.memblock);
pa_source_notify(c->source_output->source);
return 0;
}
static void do_work(struct connection *c) {
assert(c);
assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable);
c->protocol->core->mainloop->defer_enable(c->defer_event, 0);
if (c->dead)
return;
if (pa_iochannel_is_readable(c->io)) {
if (do_read(c) < 0)
goto fail;
} else if (pa_iochannel_is_hungup(c->io))
goto fail;
if (pa_iochannel_is_writable(c->io)) {
if (do_write(c) < 0)
goto fail;
}
return;
fail:
if (c->sink_input) {
c->dead = 1;
pa_iochannel_free(c->io);
c->io = NULL;
pa_memblockq_prebuf_disable(c->input_memblockq);
pa_sink_notify(c->sink_input->sink);
} else
connection_free(c);
}
/*** sink_input callbacks ***/
static int sink_input_peek_cb(pa_sink_input *i, pa_memchunk *chunk) {
struct connection*c;
assert(i && i->userdata && chunk);
c = i->userdata;
if (pa_memblockq_peek(c->input_memblockq, chunk) < 0) {
if (c->dead)
connection_free(c);
return -1;
}
return 0;
}
static void sink_input_drop_cb(pa_sink_input *i, const pa_memchunk *chunk, size_t length) {
struct connection*c = i->userdata;
assert(i && c && length);
pa_memblockq_drop(c->input_memblockq, chunk, length);
/* do something */
assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable);
c->protocol->core->mainloop->defer_enable(c->defer_event, 1);
}
static void sink_input_kill_cb(pa_sink_input *i) {
assert(i && i->userdata);
connection_free((struct connection *) i->userdata);
}
static pa_usec_t sink_input_get_latency_cb(pa_sink_input *i) {
struct connection*c = i->userdata;
assert(i && c);
return pa_bytes_to_usec(pa_memblockq_get_length(c->input_memblockq), &c->sink_input->sample_spec);
}
/*** source_output callbacks ***/
static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) {
struct connection *c = o->userdata;
assert(o && c && chunk);
pa_memblockq_push(c->output_memblockq, chunk);
/* do something */
assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable);
c->protocol->core->mainloop->defer_enable(c->defer_event, 1);
}
static void source_output_kill_cb(pa_source_output *o) {
assert(o && o->userdata);
connection_free((struct connection *) o->userdata);
}
static pa_usec_t source_output_get_latency_cb(pa_source_output *o) {
struct connection*c = o->userdata;
assert(o && c);
return pa_bytes_to_usec(pa_memblockq_get_length(c->output_memblockq), &c->source_output->sample_spec);
}
/*** client callbacks ***/
static void client_kill_cb(pa_client *c) {
assert(c && c->userdata);
connection_free((struct connection *) c->userdata);
}
/*** pa_iochannel callbacks ***/
static void io_callback(pa_iochannel*io, void *userdata) {
struct connection *c = userdata;
assert(io && c && c->io == io);
do_work(c);
}
/*** fixed callback ***/
static void defer_callback(pa_mainloop_api*a, pa_defer_event *e, void *userdata) {
struct connection *c = userdata;
assert(a && c && c->defer_event == e);
do_work(c);
}
/*** socket_server callbacks ***/
static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata) {
pa_protocol_simple *p = userdata;
struct connection *c = NULL;
char cname[256];
assert(s && io && p);
if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) {
pa_log(__FILE__": Warning! Too many connections (%u), dropping incoming connection.", MAX_CONNECTIONS);
pa_iochannel_free(io);
return;
}
c = pa_xmalloc(sizeof(struct connection));
c->io = io;
c->sink_input = NULL;
c->source_output = NULL;
c->defer_event = NULL;
c->input_memblockq = c->output_memblockq = NULL;
c->protocol = p;
c->playback.current_memblock = NULL;
c->playback.memblock_index = 0;
c->playback.fragment_size = 0;
c->dead = 0;
pa_iochannel_socket_peer_to_string(io, cname, sizeof(cname));
c->client = pa_client_new(p->core, __FILE__, cname);
assert(c->client);
c->client->owner = p->module;
c->client->kill = client_kill_cb;
c->client->userdata = c;
if (p->mode & PLAYBACK) {
pa_sink *sink;
size_t l;
if (!(sink = pa_namereg_get(p->core, p->sink_name, PA_NAMEREG_SINK, 1))) {
pa_log(__FILE__": Failed to get sink.");
goto fail;
}
if (!(c->sink_input = pa_sink_input_new(sink, __FILE__, c->client->name, &p->sample_spec, NULL, NULL, 0, -1))) {
pa_log(__FILE__": Failed to create sink input.");
goto fail;
}
c->sink_input->owner = p->module;
c->sink_input->client = c->client;
c->sink_input->peek = sink_input_peek_cb;
c->sink_input->drop = sink_input_drop_cb;
c->sink_input->kill = sink_input_kill_cb;
c->sink_input->get_latency = sink_input_get_latency_cb;
c->sink_input->userdata = c;
l = (size_t) (pa_bytes_per_second(&p->sample_spec)*PLAYBACK_BUFFER_SECONDS);
c->input_memblockq = pa_memblockq_new(
0,
l,
0,
pa_frame_size(&p->sample_spec),
(size_t) -1,
l/PLAYBACK_BUFFER_FRAGMENTS,
NULL,
p->core->memblock_stat);
assert(c->input_memblockq);
pa_iochannel_socket_set_rcvbuf(io, l/PLAYBACK_BUFFER_FRAGMENTS*5);
c->playback.fragment_size = l/10;
}
if (p->mode & RECORD) {
pa_source *source;
size_t l;
if (!(source = pa_namereg_get(p->core, p->source_name, PA_NAMEREG_SOURCE, 1))) {
pa_log(__FILE__": Failed to get source.");
goto fail;
}
c->source_output = pa_source_output_new(source, __FILE__, c->client->name, &p->sample_spec, NULL, -1);
if (!c->source_output) {
pa_log(__FILE__": Failed to create source output.");
goto fail;
}
c->source_output->owner = p->module;
c->source_output->client = c->client;
c->source_output->push = source_output_push_cb;
c->source_output->kill = source_output_kill_cb;
c->source_output->get_latency = source_output_get_latency_cb;
c->source_output->userdata = c;
l = (size_t) (pa_bytes_per_second(&p->sample_spec)*RECORD_BUFFER_SECONDS);
c->output_memblockq = pa_memblockq_new(
0,
l,
0,
pa_frame_size(&p->sample_spec),
1,
0,
NULL,
p->core->memblock_stat);
pa_iochannel_socket_set_sndbuf(io, l/RECORD_BUFFER_FRAGMENTS*2);
}
pa_iochannel_set_callback(c->io, io_callback, c);
pa_idxset_put(p->connections, c, NULL);
c->defer_event = p->core->mainloop->defer_new(p->core->mainloop, defer_callback, c);
assert(c->defer_event);
p->core->mainloop->defer_enable(c->defer_event, 0);
return;
fail:
if (c)
connection_free(c);
}
pa_protocol_simple* pa_protocol_simple_new(pa_core *core, pa_socket_server *server, pa_module *m, pa_modargs *ma) {
pa_protocol_simple* p = NULL;
int enable;
assert(core && server && ma);
p = pa_xmalloc0(sizeof(pa_protocol_simple));
p->module = m;
p->core = core;
p->server = server;
p->connections = pa_idxset_new(NULL, NULL);
p->sample_spec = core->default_sample_spec;
if (pa_modargs_get_sample_spec(ma, &p->sample_spec) < 0) {
pa_log(__FILE__": Failed to parse sample type specification.");
goto fail;
}
p->source_name = pa_xstrdup(pa_modargs_get_value(ma, "source", NULL));
p->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));
enable = 0;
if (pa_modargs_get_value_boolean(ma, "record", &enable) < 0) {
pa_log(__FILE__": record= expects a numeric argument.");
goto fail;
}
p->mode = enable ? RECORD : 0;
enable = 1;
if (pa_modargs_get_value_boolean(ma, "playback", &enable) < 0) {
pa_log(__FILE__": playback= expects a numeric argument.");
goto fail;
}
p->mode |= enable ? PLAYBACK : 0;
if ((p->mode & (RECORD|PLAYBACK)) == 0) {
pa_log(__FILE__": neither playback nor recording enabled for protocol.");
goto fail;
}
pa_socket_server_set_callback(p->server, on_connection, p);
return p;
fail:
if (p)
pa_protocol_simple_free(p);
return NULL;
}
void pa_protocol_simple_free(pa_protocol_simple *p) {
struct connection *c;
assert(p);
if (p->connections) {
while((c = pa_idxset_first(p->connections, NULL)))
connection_free(c);
pa_idxset_free(p->connections, NULL, NULL);
}
if (p->server)
pa_socket_server_unref(p->server);
pa_xfree(p);
}

View file

@ -0,0 +1,35 @@
#ifndef fooprotocolsimplehfoo
#define fooprotocolsimplehfoo
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <pulsecore/socket-server.h>
#include <pulsecore/module.h>
#include <pulsecore/core.h>
#include <pulsecore/modargs.h>
typedef struct pa_protocol_simple pa_protocol_simple;
pa_protocol_simple* pa_protocol_simple_new(pa_core *core, pa_socket_server *server, pa_module *m, pa_modargs *ma);
void pa_protocol_simple_free(pa_protocol_simple *n);
#endif

View file

@ -0,0 +1,62 @@
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <assert.h>
#include <pulsecore/native-common.h>
#include "pstream-util.h"
void pa_pstream_send_tagstruct_with_creds(pa_pstream *p, pa_tagstruct *t, int creds) {
size_t length;
uint8_t *data;
pa_packet *packet;
assert(p);
assert(t);
data = pa_tagstruct_free_data(t, &length);
assert(data && length);
packet = pa_packet_new_dynamic(data, length);
assert(packet);
pa_pstream_send_packet(p, packet, creds);
pa_packet_unref(packet);
}
void pa_pstream_send_error(pa_pstream *p, uint32_t tag, uint32_t error) {
pa_tagstruct *t = pa_tagstruct_new(NULL, 0);
assert(t);
pa_tagstruct_putu32(t, PA_COMMAND_ERROR);
pa_tagstruct_putu32(t, tag);
pa_tagstruct_putu32(t, error);
pa_pstream_send_tagstruct(p, t);
}
void pa_pstream_send_simple_ack(pa_pstream *p, uint32_t tag) {
pa_tagstruct *t = pa_tagstruct_new(NULL, 0);
assert(t);
pa_tagstruct_putu32(t, PA_COMMAND_REPLY);
pa_tagstruct_putu32(t, tag);
pa_pstream_send_tagstruct(p, t);
}

View file

@ -0,0 +1,37 @@
#ifndef foopstreamutilhfoo
#define foopstreamutilhfoo
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <inttypes.h>
#include <pulsecore/pstream.h>
#include <pulsecore/tagstruct.h>
/* The tagstruct is freed!*/
void pa_pstream_send_tagstruct_with_creds(pa_pstream *p, pa_tagstruct *t, int creds);
#define pa_pstream_send_tagstruct(p, t) pa_pstream_send_tagstruct_with_creds((p), (t), 0)
void pa_pstream_send_error(pa_pstream *p, uint32_t tag, uint32_t error);
void pa_pstream_send_simple_ack(pa_pstream *p, uint32_t tag);
#endif

589
src/pulsecore/pstream.c Normal file
View file

@ -0,0 +1,589 @@
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#include "winsock.h"
#include <pulse/xmalloc.h>
#include <pulsecore/queue.h>
#include <pulsecore/log.h>
#include <pulsecore/core-scache.h>
#include "pstream.h"
enum {
PA_PSTREAM_DESCRIPTOR_LENGTH,
PA_PSTREAM_DESCRIPTOR_CHANNEL,
PA_PSTREAM_DESCRIPTOR_OFFSET_HI,
PA_PSTREAM_DESCRIPTOR_OFFSET_LO,
PA_PSTREAM_DESCRIPTOR_SEEK,
PA_PSTREAM_DESCRIPTOR_MAX
};
typedef uint32_t pa_pstream_descriptor[PA_PSTREAM_DESCRIPTOR_MAX];
#define PA_PSTREAM_DESCRIPTOR_SIZE (PA_PSTREAM_DESCRIPTOR_MAX*sizeof(uint32_t))
#define FRAME_SIZE_MAX PA_SCACHE_ENTRY_SIZE_MAX /* allow uploading a single sample in one frame at max */
struct item_info {
enum { PA_PSTREAM_ITEM_PACKET, PA_PSTREAM_ITEM_MEMBLOCK } type;
/* memblock info */
pa_memchunk chunk;
uint32_t channel;
int64_t offset;
pa_seek_mode_t seek_mode;
/* packet info */
pa_packet *packet;
#ifdef SCM_CREDENTIALS
int with_creds;
#endif
};
struct pa_pstream {
int ref;
pa_mainloop_api *mainloop;
pa_defer_event *defer_event;
pa_iochannel *io;
pa_queue *send_queue;
int dead;
struct {
struct item_info* current;
pa_pstream_descriptor descriptor;
void *data;
size_t index;
} write;
struct {
pa_memblock *memblock;
pa_packet *packet;
pa_pstream_descriptor descriptor;
void *data;
size_t index;
} read;
pa_pstream_packet_cb_t recieve_packet_callback;
void *recieve_packet_callback_userdata;
pa_pstream_memblock_cb_t recieve_memblock_callback;
void *recieve_memblock_callback_userdata;
pa_pstream_notify_cb_t drain_callback;
void *drain_callback_userdata;
pa_pstream_notify_cb_t die_callback;
void *die_callback_userdata;
pa_memblock_stat *memblock_stat;
#ifdef SCM_CREDENTIALS
int send_creds_now;
struct ucred ucred;
int creds_valid;
#endif
};
static int do_write(pa_pstream *p);
static int do_read(pa_pstream *p);
static void do_something(pa_pstream *p) {
assert(p);
p->mainloop->defer_enable(p->defer_event, 0);
pa_pstream_ref(p);
if (!p->dead && pa_iochannel_is_readable(p->io)) {
if (do_read(p) < 0)
goto fail;
} else if (!p->dead && pa_iochannel_is_hungup(p->io))
goto fail;
if (!p->dead && pa_iochannel_is_writable(p->io)) {
if (do_write(p) < 0)
goto fail;
}
pa_pstream_unref(p);
return;
fail:
p->dead = 1;
if (p->die_callback)
p->die_callback(p, p->die_callback_userdata);
pa_pstream_unref(p);
}
static void io_callback(pa_iochannel*io, void *userdata) {
pa_pstream *p = userdata;
assert(p);
assert(p->io == io);
do_something(p);
}
static void defer_callback(pa_mainloop_api *m, pa_defer_event *e, void*userdata) {
pa_pstream *p = userdata;
assert(p);
assert(p->defer_event == e);
assert(p->mainloop == m);
do_something(p);
}
pa_pstream *pa_pstream_new(pa_mainloop_api *m, pa_iochannel *io, pa_memblock_stat *s) {
pa_pstream *p;
assert(io);
p = pa_xnew(pa_pstream, 1);
p->ref = 1;
p->io = io;
pa_iochannel_set_callback(io, io_callback, p);
p->dead = 0;
p->mainloop = m;
p->defer_event = m->defer_new(m, defer_callback, p);
m->defer_enable(p->defer_event, 0);
p->send_queue = pa_queue_new();
assert(p->send_queue);
p->write.current = NULL;
p->write.index = 0;
p->read.memblock = NULL;
p->read.packet = NULL;
p->read.index = 0;
p->recieve_packet_callback = NULL;
p->recieve_packet_callback_userdata = NULL;
p->recieve_memblock_callback = NULL;
p->recieve_memblock_callback_userdata = NULL;
p->drain_callback = NULL;
p->drain_callback_userdata = NULL;
p->die_callback = NULL;
p->die_callback_userdata = NULL;
p->memblock_stat = s;
pa_iochannel_socket_set_rcvbuf(io, 1024*8);
pa_iochannel_socket_set_sndbuf(io, 1024*8);
#ifdef SCM_CREDENTIALS
p->send_creds_now = 0;
p->creds_valid = 0;
#endif
return p;
}
static void item_free(void *item, PA_GCC_UNUSED void *p) {
struct item_info *i = item;
assert(i);
if (i->type == PA_PSTREAM_ITEM_MEMBLOCK) {
assert(i->chunk.memblock);
pa_memblock_unref(i->chunk.memblock);
} else {
assert(i->type == PA_PSTREAM_ITEM_PACKET);
assert(i->packet);
pa_packet_unref(i->packet);
}
pa_xfree(i);
}
static void pstream_free(pa_pstream *p) {
assert(p);
pa_pstream_close(p);
pa_queue_free(p->send_queue, item_free, NULL);
if (p->write.current)
item_free(p->write.current, NULL);
if (p->read.memblock)
pa_memblock_unref(p->read.memblock);
if (p->read.packet)
pa_packet_unref(p->read.packet);
pa_xfree(p);
}
void pa_pstream_send_packet(pa_pstream*p, pa_packet *packet, int with_creds) {
struct item_info *i;
assert(p && packet && p->ref >= 1);
if (p->dead)
return;
/* pa_log(__FILE__": push-packet %p", packet); */
i = pa_xnew(struct item_info, 1);
i->type = PA_PSTREAM_ITEM_PACKET;
i->packet = pa_packet_ref(packet);
#ifdef SCM_CREDENTIALS
i->with_creds = with_creds;
#endif
pa_queue_push(p->send_queue, i);
p->mainloop->defer_enable(p->defer_event, 1);
}
void pa_pstream_send_memblock(pa_pstream*p, uint32_t channel, int64_t offset, pa_seek_mode_t seek_mode, const pa_memchunk *chunk) {
struct item_info *i;
assert(p && channel != (uint32_t) -1 && chunk && p->ref >= 1);
if (p->dead)
return;
/* pa_log(__FILE__": push-memblock %p", chunk); */
i = pa_xnew(struct item_info, 1);
i->type = PA_PSTREAM_ITEM_MEMBLOCK;
i->chunk = *chunk;
i->channel = channel;
i->offset = offset;
i->seek_mode = seek_mode;
pa_memblock_ref(i->chunk.memblock);
pa_queue_push(p->send_queue, i);
p->mainloop->defer_enable(p->defer_event, 1);
}
static void prepare_next_write_item(pa_pstream *p) {
assert(p);
if (!(p->write.current = pa_queue_pop(p->send_queue)))
return;
p->write.index = 0;
if (p->write.current->type == PA_PSTREAM_ITEM_PACKET) {
/*pa_log(__FILE__": pop-packet %p", p->write.current->packet);*/
assert(p->write.current->packet);
p->write.data = p->write.current->packet->data;
p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH] = htonl(p->write.current->packet->length);
p->write.descriptor[PA_PSTREAM_DESCRIPTOR_CHANNEL] = htonl((uint32_t) -1);
p->write.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_HI] = 0;
p->write.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_LO] = 0;
p->write.descriptor[PA_PSTREAM_DESCRIPTOR_SEEK] = 0;
#ifdef SCM_CREDENTIALS
p->send_creds_now = 1;
#endif
} else {
assert(p->write.current->type == PA_PSTREAM_ITEM_MEMBLOCK && p->write.current->chunk.memblock);
p->write.data = (uint8_t*) p->write.current->chunk.memblock->data + p->write.current->chunk.index;
p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH] = htonl(p->write.current->chunk.length);
p->write.descriptor[PA_PSTREAM_DESCRIPTOR_CHANNEL] = htonl(p->write.current->channel);
p->write.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_HI] = htonl((uint32_t) (((uint64_t) p->write.current->offset) >> 32));
p->write.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_LO] = htonl((uint32_t) ((uint64_t) p->write.current->offset));
p->write.descriptor[PA_PSTREAM_DESCRIPTOR_SEEK] = htonl(p->write.current->seek_mode);
#ifdef SCM_CREDENTIALS
p->send_creds_now = 1;
#endif
}
}
static int do_write(pa_pstream *p) {
void *d;
size_t l;
ssize_t r;
assert(p);
if (!p->write.current)
prepare_next_write_item(p);
if (!p->write.current)
return 0;
assert(p->write.data);
if (p->write.index < PA_PSTREAM_DESCRIPTOR_SIZE) {
d = (uint8_t*) p->write.descriptor + p->write.index;
l = PA_PSTREAM_DESCRIPTOR_SIZE - p->write.index;
} else {
d = (uint8_t*) p->write.data + p->write.index - PA_PSTREAM_DESCRIPTOR_SIZE;
l = ntohl(p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]) - (p->write.index - PA_PSTREAM_DESCRIPTOR_SIZE);
}
#ifdef SCM_CREDENTIALS
if (p->send_creds_now) {
if ((r = pa_iochannel_write_with_creds(p->io, d, l)) < 0)
return -1;
p->send_creds_now = 0;
} else
#endif
if ((r = pa_iochannel_write(p->io, d, l)) < 0)
return -1;
p->write.index += r;
if (p->write.index >= PA_PSTREAM_DESCRIPTOR_SIZE+ntohl(p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH])) {
assert(p->write.current);
item_free(p->write.current, (void *) 1);
p->write.current = NULL;
if (p->drain_callback && !pa_pstream_is_pending(p))
p->drain_callback(p, p->drain_callback_userdata);
}
return 0;
}
static int do_read(pa_pstream *p) {
void *d;
size_t l;
ssize_t r;
assert(p);
if (p->read.index < PA_PSTREAM_DESCRIPTOR_SIZE) {
d = (uint8_t*) p->read.descriptor + p->read.index;
l = PA_PSTREAM_DESCRIPTOR_SIZE - p->read.index;
} else {
assert(p->read.data);
d = (uint8_t*) p->read.data + p->read.index - PA_PSTREAM_DESCRIPTOR_SIZE;
l = ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]) - (p->read.index - PA_PSTREAM_DESCRIPTOR_SIZE);
}
#ifdef SCM_CREDENTIALS
{
int b;
if ((r = pa_iochannel_read_with_creds(p->io, d, l, &p->ucred, &b)) <= 0)
return -1;
p->creds_valid = p->creds_valid || b;
}
#else
if ((r = pa_iochannel_read(p->io, d, l)) <= 0)
return -1;
#endif
p->read.index += r;
if (p->read.index == PA_PSTREAM_DESCRIPTOR_SIZE) {
/* Reading of frame descriptor complete */
/* Frame size too large */
if (ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]) > FRAME_SIZE_MAX) {
pa_log_warn(__FILE__": Frame size too large: %lu > %lu", (unsigned long) ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]), (unsigned long) FRAME_SIZE_MAX);
return -1;
}
assert(!p->read.packet && !p->read.memblock);
if (ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_CHANNEL]) == (uint32_t) -1) {
/* Frame is a packet frame */
p->read.packet = pa_packet_new(ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]));
p->read.data = p->read.packet->data;
} else {
/* Frame is a memblock frame */
p->read.memblock = pa_memblock_new(ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]), p->memblock_stat);
p->read.data = p->read.memblock->data;
if (ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_SEEK]) > PA_SEEK_RELATIVE_END) {
pa_log_warn(__FILE__": Invalid seek mode");
return -1;
}
}
} else if (p->read.index > PA_PSTREAM_DESCRIPTOR_SIZE) {
/* Frame payload available */
if (p->read.memblock && p->recieve_memblock_callback) { /* Is this memblock data? Than pass it to the user */
l = (p->read.index - r) < PA_PSTREAM_DESCRIPTOR_SIZE ? p->read.index - PA_PSTREAM_DESCRIPTOR_SIZE : (size_t) r;
if (l > 0) {
pa_memchunk chunk;
chunk.memblock = p->read.memblock;
chunk.index = p->read.index - PA_PSTREAM_DESCRIPTOR_SIZE - l;
chunk.length = l;
if (p->recieve_memblock_callback) {
int64_t offset;
offset = (int64_t) (
(((uint64_t) ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_HI])) << 32) |
(((uint64_t) ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_LO]))));
p->recieve_memblock_callback(
p,
ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_CHANNEL]),
offset,
ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_SEEK]),
&chunk,
p->recieve_memblock_callback_userdata);
}
/* Drop seek info for following callbacks */
p->read.descriptor[PA_PSTREAM_DESCRIPTOR_SEEK] =
p->read.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_HI] =
p->read.descriptor[PA_PSTREAM_DESCRIPTOR_OFFSET_LO] = 0;
}
}
/* Frame complete */
if (p->read.index >= ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]) + PA_PSTREAM_DESCRIPTOR_SIZE) {
if (p->read.memblock) {
assert(!p->read.packet);
pa_memblock_unref(p->read.memblock);
p->read.memblock = NULL;
} else {
assert(p->read.packet);
if (p->recieve_packet_callback)
#ifdef SCM_CREDENTIALS
p->recieve_packet_callback(p, p->read.packet, p->creds_valid ? &p->ucred : NULL, p->recieve_packet_callback_userdata);
#else
p->recieve_packet_callback(p, p->read.packet, NULL, p->recieve_packet_callback_userdata);
#endif
pa_packet_unref(p->read.packet);
p->read.packet = NULL;
}
p->read.index = 0;
#ifdef SCM_CREDENTIALS
p->creds_valid = 0;
#endif
}
}
return 0;
}
void pa_pstream_set_die_callback(pa_pstream *p, pa_pstream_notify_cb_t cb, void *userdata) {
assert(p);
assert(p->ref >= 1);
p->die_callback = cb;
p->die_callback_userdata = userdata;
}
void pa_pstream_set_drain_callback(pa_pstream *p, pa_pstream_notify_cb_t cb, void *userdata) {
assert(p);
assert(p->ref >= 1);
p->drain_callback = cb;
p->drain_callback_userdata = userdata;
}
void pa_pstream_set_recieve_packet_callback(pa_pstream *p, pa_pstream_packet_cb_t cb, void *userdata) {
assert(p);
assert(p->ref >= 1);
p->recieve_packet_callback = cb;
p->recieve_packet_callback_userdata = userdata;
}
void pa_pstream_set_recieve_memblock_callback(pa_pstream *p, pa_pstream_memblock_cb_t cb, void *userdata) {
assert(p);
assert(p->ref >= 1);
p->recieve_memblock_callback = cb;
p->recieve_memblock_callback_userdata = userdata;
}
int pa_pstream_is_pending(pa_pstream *p) {
assert(p);
if (p->dead)
return 0;
return p->write.current || !pa_queue_is_empty(p->send_queue);
}
void pa_pstream_unref(pa_pstream*p) {
assert(p);
assert(p->ref >= 1);
if (--p->ref == 0)
pstream_free(p);
}
pa_pstream* pa_pstream_ref(pa_pstream*p) {
assert(p);
assert(p->ref >= 1);
p->ref++;
return p;
}
void pa_pstream_close(pa_pstream *p) {
assert(p);
p->dead = 1;
if (p->io) {
pa_iochannel_free(p->io);
p->io = NULL;
}
if (p->defer_event) {
p->mainloop->defer_free(p->defer_event);
p->defer_event = NULL;
}
p->die_callback = NULL;
p->drain_callback = NULL;
p->recieve_packet_callback = NULL;
p->recieve_memblock_callback = NULL;
}

57
src/pulsecore/pstream.h Normal file
View file

@ -0,0 +1,57 @@
#ifndef foopstreamhfoo
#define foopstreamhfoo
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#include <inttypes.h>
#include <pulse/mainloop-api.h>
#include <pulse/def.h>
#include <pulsecore/packet.h>
#include <pulsecore/memblock.h>
#include <pulsecore/iochannel.h>
#include <pulsecore/memchunk.h>
typedef struct pa_pstream pa_pstream;
typedef void (*pa_pstream_packet_cb_t)(pa_pstream *p, pa_packet *packet, const void *creds, void *userdata);
typedef void (*pa_pstream_memblock_cb_t)(pa_pstream *p, uint32_t channel, int64_t offset, pa_seek_mode_t seek, const pa_memchunk *chunk, void *userdata);
typedef void (*pa_pstream_notify_cb_t)(pa_pstream *p, void *userdata);
pa_pstream* pa_pstream_new(pa_mainloop_api *m, pa_iochannel *io, pa_memblock_stat *s);
void pa_pstream_unref(pa_pstream*p);
pa_pstream* pa_pstream_ref(pa_pstream*p);
void pa_pstream_send_packet(pa_pstream*p, pa_packet *packet, int with_creds);
void pa_pstream_send_memblock(pa_pstream*p, uint32_t channel, int64_t offset, pa_seek_mode_t seek, const pa_memchunk *chunk);
void pa_pstream_set_recieve_packet_callback(pa_pstream *p, pa_pstream_packet_cb_t cb, void *userdata);
void pa_pstream_set_recieve_memblock_callback(pa_pstream *p, pa_pstream_memblock_cb_t cb, void *userdata);
void pa_pstream_set_drain_callback(pa_pstream *p, pa_pstream_notify_cb_t cb, void *userdata);
void pa_pstream_set_die_callback(pa_pstream *p, pa_pstream_notify_cb_t cb, void *userdata);
int pa_pstream_is_pending(pa_pstream *p);
void pa_pstream_close(pa_pstream *p);
#endif

109
src/pulsecore/queue.c Normal file
View file

@ -0,0 +1,109 @@
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <assert.h>
#include <stdlib.h>
#include <pulse/xmalloc.h>
#include "queue.h"
struct queue_entry {
struct queue_entry *next;
void *data;
};
struct pa_queue {
struct queue_entry *front, *back;
unsigned length;
};
pa_queue* pa_queue_new(void) {
pa_queue *q = pa_xnew(pa_queue, 1);
q->front = q->back = NULL;
q->length = 0;
return q;
}
void pa_queue_free(pa_queue* q, void (*destroy)(void *p, void *userdata), void *userdata) {
struct queue_entry *e;
assert(q);
e = q->front;
while (e) {
struct queue_entry *n = e->next;
if (destroy)
destroy(e->data, userdata);
pa_xfree(e);
e = n;
}
pa_xfree(q);
}
void pa_queue_push(pa_queue *q, void *p) {
struct queue_entry *e;
e = pa_xnew(struct queue_entry, 1);
e->data = p;
e->next = NULL;
if (q->back)
q->back->next = e;
else {
assert(!q->front);
q->front = e;
}
q->back = e;
q->length++;
}
void* pa_queue_pop(pa_queue *q) {
void *p;
struct queue_entry *e;
assert(q);
if (!(e = q->front))
return NULL;
q->front = e->next;
if (q->back == e)
q->back = NULL;
p = e->data;
pa_xfree(e);
q->length--;
return p;
}
int pa_queue_is_empty(pa_queue *q) {
assert(q);
return q->length == 0;
}

40
src/pulsecore/queue.h Normal file
View file

@ -0,0 +1,40 @@
#ifndef fooqueuehfoo
#define fooqueuehfoo
/* $Id$ */
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
typedef struct pa_queue pa_queue;
/* A simple implementation of the abstract data type queue. Stores
* pointers as members. The memory has to be managed by the caller. */
pa_queue* pa_queue_new(void);
/* Free the queue and run the specified callback function for every remaining entry. The callback function may be NULL. */
void pa_queue_free(pa_queue* q, void (*destroy)(void *p, void *userdata), void *userdata);
void pa_queue_push(pa_queue *q, void *p);
void* pa_queue_pop(pa_queue *q);
int pa_queue_is_empty(pa_queue *q);
#endif

Some files were not shown because too many files have changed in this diff Show more