mirror of
https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
synced 2025-12-30 11:08:50 -05:00
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:
parent
dd21f11ded
commit
f44ba09265
303 changed files with 2724 additions and 2719 deletions
1
src/pulsecore/Makefile
Symbolic link
1
src/pulsecore/Makefile
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
../pulse/Makefile
|
||||
89
src/pulsecore/authkey-prop.c
Normal file
89
src/pulsecore/authkey-prop.c
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
43
src/pulsecore/authkey-prop.h
Normal file
43
src/pulsecore/authkey-prop.h
Normal 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
209
src/pulsecore/authkey.c
Normal 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
32
src/pulsecore/authkey.h
Normal 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
182
src/pulsecore/autoload.c
Normal 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
58
src/pulsecore/autoload.h
Normal 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
947
src/pulsecore/cli-command.c
Normal 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;
|
||||
}
|
||||
40
src/pulsecore/cli-command.h
Normal file
40
src/pulsecore/cli-command.h
Normal 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
387
src/pulsecore/cli-text.c
Normal 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
42
src/pulsecore/cli-text.h
Normal 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
149
src/pulsecore/cli.c
Normal 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
38
src/pulsecore/cli.h
Normal 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
96
src/pulsecore/client.c
Normal 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
57
src/pulsecore/client.h
Normal 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
181
src/pulsecore/conf-parser.c
Normal 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;
|
||||
}
|
||||
47
src/pulsecore/conf-parser.h
Normal file
47
src/pulsecore/conf-parser.h
Normal 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
30
src/pulsecore/core-def.h
Normal 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
205
src/pulsecore/core-error.c
Normal 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;
|
||||
}
|
||||
41
src/pulsecore/core-error.h
Normal file
41
src/pulsecore/core-error.h
Normal 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
412
src/pulsecore/core-scache.c
Normal 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;
|
||||
}
|
||||
64
src/pulsecore/core-scache.h
Normal file
64
src/pulsecore/core-scache.h
Normal 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
|
||||
233
src/pulsecore/core-subscribe.c
Normal file
233
src/pulsecore/core-subscribe.c
Normal 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);
|
||||
}
|
||||
|
||||
|
||||
37
src/pulsecore/core-subscribe.h
Normal file
37
src/pulsecore/core-subscribe.h
Normal 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
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
88
src/pulsecore/core-util.h
Normal 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
160
src/pulsecore/core.c
Normal 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
82
src/pulsecore/core.h
Normal 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
55
src/pulsecore/dllmain.c
Normal 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
103
src/pulsecore/dynarray.c
Normal 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
49
src/pulsecore/dynarray.h
Normal 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
|
||||
77
src/pulsecore/endianmacros.h
Normal file
77
src/pulsecore/endianmacros.h
Normal 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
209
src/pulsecore/esound.h
Normal 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
2531
src/pulsecore/g711.c
Normal file
File diff suppressed because it is too large
Load diff
40
src/pulsecore/g711.h
Normal file
40
src/pulsecore/g711.h
Normal 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
53
src/pulsecore/gccmacro.h
Normal 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
196
src/pulsecore/hashmap.c
Normal 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
53
src/pulsecore/hashmap.h
Normal 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
391
src/pulsecore/idxset.c
Normal 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
94
src/pulsecore/idxset.h
Normal 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
80
src/pulsecore/inet_ntop.c
Normal 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
12
src/pulsecore/inet_ntop.h
Normal 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
62
src/pulsecore/inet_pton.c
Normal 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
12
src/pulsecore/inet_pton.h
Normal 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
417
src/pulsecore/iochannel.c
Normal 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
81
src/pulsecore/iochannel.h
Normal 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
394
src/pulsecore/ioline.c
Normal 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
51
src/pulsecore/ioline.h
Normal 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
79
src/pulsecore/llist.h
Normal 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
211
src/pulsecore/log.c
Normal 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
69
src/pulsecore/log.h
Normal 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
206
src/pulsecore/mcalign.c
Normal 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
80
src/pulsecore/mcalign.h
Normal 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
173
src/pulsecore/memblock.c
Normal 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
86
src/pulsecore/memblock.h
Normal 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
636
src/pulsecore/memblockq.c
Normal 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
140
src/pulsecore/memblockq.h
Normal 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
59
src/pulsecore/memchunk.c
Normal 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
45
src/pulsecore/memchunk.h
Normal 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
310
src/pulsecore/modargs.c
Normal 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
60
src/pulsecore/modargs.h
Normal 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
93
src/pulsecore/modinfo.c
Normal 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
43
src/pulsecore/modinfo.h
Normal 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
319
src/pulsecore/module.c
Normal 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
70
src/pulsecore/module.h
Normal 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
214
src/pulsecore/namereg.c
Normal 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
43
src/pulsecore/namereg.h
Normal 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
|
||||
127
src/pulsecore/native-common.h
Normal file
127
src/pulsecore/native-common.h
Normal 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
79
src/pulsecore/packet.c
Normal 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
41
src/pulsecore/packet.h
Normal 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
115
src/pulsecore/parseaddr.c
Normal 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
42
src/pulsecore/parseaddr.h
Normal 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
318
src/pulsecore/pdispatch.c
Normal 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
53
src/pulsecore/pdispatch.h
Normal 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
304
src/pulsecore/pid.c
Normal 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
30
src/pulsecore/pid.h
Normal 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
160
src/pulsecore/pipe.c
Normal 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
31
src/pulsecore/pipe.h
Normal 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
|
||||
117
src/pulsecore/play-memchunk.c
Normal file
117
src/pulsecore/play-memchunk.c
Normal 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;
|
||||
}
|
||||
36
src/pulsecore/play-memchunk.h
Normal file
36
src/pulsecore/play-memchunk.h
Normal 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
191
src/pulsecore/poll.c
Normal 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
57
src/pulsecore/poll.h
Normal 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
121
src/pulsecore/props.c
Normal 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
58
src/pulsecore/props.h
Normal 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
|
||||
97
src/pulsecore/protocol-cli.c
Normal file
97
src/pulsecore/protocol-cli.c
Normal 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);
|
||||
}
|
||||
35
src/pulsecore/protocol-cli.h
Normal file
35
src/pulsecore/protocol-cli.h
Normal 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
|
||||
1253
src/pulsecore/protocol-esound.c
Normal file
1253
src/pulsecore/protocol-esound.c
Normal file
File diff suppressed because it is too large
Load diff
35
src/pulsecore/protocol-esound.h
Normal file
35
src/pulsecore/protocol-esound.h
Normal 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
|
||||
268
src/pulsecore/protocol-http.c
Normal file
268
src/pulsecore/protocol-http.c
Normal 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);
|
||||
}
|
||||
35
src/pulsecore/protocol-http.h
Normal file
35
src/pulsecore/protocol-http.h
Normal 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
|
||||
2398
src/pulsecore/protocol-native.c
Normal file
2398
src/pulsecore/protocol-native.c
Normal file
File diff suppressed because it is too large
Load diff
37
src/pulsecore/protocol-native.h
Normal file
37
src/pulsecore/protocol-native.h
Normal 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
|
||||
494
src/pulsecore/protocol-simple.c
Normal file
494
src/pulsecore/protocol-simple.c
Normal 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);
|
||||
}
|
||||
|
||||
35
src/pulsecore/protocol-simple.h
Normal file
35
src/pulsecore/protocol-simple.h
Normal 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
|
||||
62
src/pulsecore/pstream-util.c
Normal file
62
src/pulsecore/pstream-util.c
Normal 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);
|
||||
}
|
||||
37
src/pulsecore/pstream-util.h
Normal file
37
src/pulsecore/pstream-util.h
Normal 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
589
src/pulsecore/pstream.c
Normal 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
57
src/pulsecore/pstream.h
Normal 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
109
src/pulsecore/queue.c
Normal 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
40
src/pulsecore/queue.h
Normal 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
Loading…
Add table
Add a link
Reference in a new issue