mirror of
https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
synced 2025-10-29 05:40:23 -04:00
* some commenting work
* add new field "read_only" to memory blocks * add new API function pa_context_get_server() * filter capture data through mcalign on client * make module-tunnel use pa_socket_client_new_string() instead of using pa_resolve_server() directly. * remove pa_resolve_server() * remove debug.h and replace it by a macro definition on the gcc command line * some strbuf cleanups * small fixes in pa_stream for cleanup when server dies * new CLI command "load-sample-dir-lazy" * send FQDN as part of server info * rework mcalign, this time with memory block merging * fix iochannel cleanup when connection dies * check getaddrinfo() results git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@286 fefdeb5f-60dc-0310-8127-8f9354f1896f
This commit is contained in:
parent
f5f6605254
commit
0a2bbc528b
30 changed files with 636 additions and 273 deletions
15
doc/todo
15
doc/todo
|
|
@ -2,27 +2,22 @@
|
|||
|
||||
*** 0.7 ****
|
||||
- per-channel volume
|
||||
- add sample directory
|
||||
- make mcalign merge chunks
|
||||
- option to use default fragment size on alsa drivers
|
||||
- improve module-oss-mmap latency measurement
|
||||
- filter capture data in client through alignment
|
||||
- add radio module
|
||||
- add sync API
|
||||
- make most buffer sizes dependant on the sample type
|
||||
|
||||
- X11: support for the X11 synchronization extension
|
||||
- pass meta info for hearing impaired
|
||||
- limit all resources
|
||||
- check getaddrinfo results
|
||||
- commenting
|
||||
- non-fp mixing
|
||||
- non-fp resampling
|
||||
- make module-tunnel use pa_socket_client_new_string()
|
||||
|
||||
** later ***
|
||||
- pass meta info for hearing impaired
|
||||
- add sync API
|
||||
- X11: support for the X11 synchronization extension
|
||||
- xmlrpc/http
|
||||
- dbus
|
||||
- slp/rendezvous
|
||||
- rendezvous
|
||||
- make alsa modules use mmap
|
||||
|
||||
***********
|
||||
|
|
|
|||
|
|
@ -27,6 +27,9 @@ AM_CFLAGS+=-DDLSEARCHPATH=\"$(modlibdir)\"
|
|||
AM_CFLAGS+=-DDEFAULT_CONFIG_DIR=\"$(polypconfdir)\"
|
||||
AM_CFLAGS+=-DPOLYPAUDIO_BINARY=\"$(bindir)/polypaudio\"
|
||||
|
||||
# This cool debug trap works on i386/gcc only
|
||||
AM_CFLAGS+='-DDEBUG_TRAP=__asm__("int $$3")'
|
||||
|
||||
AM_LIBADD=$(PTHREAD_LIBS) -lm
|
||||
AM_LDADD=$(PTHREAD_LIBS) -lm
|
||||
|
||||
|
|
@ -40,7 +43,8 @@ noinst_PROGRAMS = \
|
|||
cpulimit-test \
|
||||
cpulimit-test2 \
|
||||
voltest \
|
||||
strlist-test
|
||||
strlist-test \
|
||||
mcalign-test
|
||||
|
||||
polypconf_DATA=default.pa daemon.conf client.conf
|
||||
|
||||
|
|
@ -196,7 +200,6 @@ polypaudio_SOURCES = idxset.c idxset.h \
|
|||
autoload.c autoload.h \
|
||||
xmalloc.c xmalloc.h \
|
||||
subscribe.h subscribe.c \
|
||||
debug.h \
|
||||
sound-file-stream.c sound-file-stream.h \
|
||||
cpulimit.c cpulimit.h \
|
||||
log.c log.h \
|
||||
|
|
@ -206,7 +209,8 @@ polypaudio_SOURCES = idxset.c idxset.h \
|
|||
dumpmodules.c dumpmodules.h \
|
||||
conf-parser.h conf-parser.c \
|
||||
caps.h caps.c \
|
||||
props.h props.c
|
||||
props.h props.c \
|
||||
mcalign.c mcalign.h
|
||||
|
||||
polypaudio_CFLAGS = $(AM_CFLAGS) $(LIBSAMPLERATE_CFLAGS) $(LIBSNDFILE_CFLAGS)
|
||||
polypaudio_INCLUDES = $(INCLTDL)
|
||||
|
|
@ -441,7 +445,8 @@ libpolyp_@PA_MAJORMINOR@_la_SOURCES = polyplib.h \
|
|||
client-conf.c client-conf.h \
|
||||
conf-parser.c conf-parser.h \
|
||||
strlist.c strlist.h \
|
||||
strbuf.c strbuf.h
|
||||
strbuf.c strbuf.h \
|
||||
mcalign.c mcalign.h
|
||||
|
||||
libpolyp_@PA_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS)
|
||||
libpolyp_@PA_MAJORMINOR@_la_LDFLAGS = -version-info 0:0:0
|
||||
|
|
@ -495,6 +500,10 @@ strlist_test_SOURCES = strlist-test.c strlist.c strlist.h strbuf.c strbuf.h util
|
|||
strlist_test_CFLAGS = $(AM_CFLAGS)
|
||||
strlist_test_LDADD = $(AM_LDADD)
|
||||
|
||||
mcalign_test_SOURCES = mcalign-test.c util.c util.h xmalloc.c xmalloc.h log.c log.h mcalign.c mcalign.h memchunk.c memchunk.h memblock.c memblock.h
|
||||
mcalign_test_CFLAGS = $(AM_CFLAGS)
|
||||
mcalign_test_LDADD = $(AM_LDADD)
|
||||
|
||||
cpulimit_test_SOURCES = cpulimit-test.c cpulimit.c util.c log.c cpulimit.h util.h log.h
|
||||
cpulimit_test_CFLAGS = $(AM_CFLAGS)
|
||||
cpulimit_test_LDADD = $(AM_LDADD) libpolyp-mainloop-@PA_MAJORMINOR@.la
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@
|
|||
#include "log.h"
|
||||
#include "caps.h"
|
||||
|
||||
/* Drop root rights when called SUID root */
|
||||
void pa_drop_root(void) {
|
||||
uid_t uid = getuid();
|
||||
|
||||
|
|
@ -50,6 +51,7 @@ void pa_drop_root(void) {
|
|||
|
||||
#ifdef HAVE_SYS_CAPABILITY_H
|
||||
|
||||
/* Limit capabilities set to CAPSYS_NICE */
|
||||
int pa_limit_caps(void) {
|
||||
int r = -1;
|
||||
cap_t caps;
|
||||
|
|
@ -76,6 +78,7 @@ fail:
|
|||
return r;
|
||||
}
|
||||
|
||||
/* Drop all capabilities, effectively becoming a normal user */
|
||||
int pa_drop_caps(void) {
|
||||
cap_t caps;
|
||||
int r = -1;
|
||||
|
|
@ -100,6 +103,7 @@ fail:
|
|||
|
||||
#else
|
||||
|
||||
/* NOOPs in case capabilities are not available. */
|
||||
int pa_limit_caps(void) {
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,6 +79,7 @@ static int pa_cli_command_scache_play(struct pa_core *c, struct pa_tokenizer *t,
|
|||
static int pa_cli_command_scache_remove(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose);
|
||||
static int pa_cli_command_scache_list(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose);
|
||||
static int pa_cli_command_scache_load(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose);
|
||||
static int pa_cli_command_scache_load_dir(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose);
|
||||
static int pa_cli_command_play_file(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose);
|
||||
static int pa_cli_command_autoload_list(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose);
|
||||
static int pa_cli_command_autoload_add(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose);
|
||||
|
|
@ -112,7 +113,8 @@ static const struct command commands[] = {
|
|||
{ "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, "Lazy 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},
|
||||
|
|
@ -545,6 +547,23 @@ static int pa_cli_command_scache_load(struct pa_core *c, struct pa_tokenizer *t,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int pa_cli_command_scache_load_dir(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) {
|
||||
const char *pname;
|
||||
assert(c && t && buf && fail && verbose);
|
||||
|
||||
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(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) {
|
||||
const char *fname, *sink_name;
|
||||
struct pa_sink *sink;
|
||||
|
|
|
|||
|
|
@ -32,30 +32,58 @@
|
|||
#include "util.h"
|
||||
#include "log.h"
|
||||
|
||||
|
||||
/* This module implements a watchdog that makes sure that the current
|
||||
* process doesn't consume more than 70% CPU time for 10 seconds. This
|
||||
* is very useful when using SCHED_FIFO scheduling which effectively
|
||||
* disables multitasking. */
|
||||
|
||||
/* Method of operation: Using SIGXCPU a signal handler is called every
|
||||
* 10s process CPU time. That function checks if less than 14s system
|
||||
* time have passed. In that case, it tries to contact the main event
|
||||
* loop through a pipe. After two additional seconds it is checked
|
||||
* whether the main event loop contact was successful. If not, the
|
||||
* program is terminated forcibly. */
|
||||
|
||||
/* Utilize this much CPU time at maximum */
|
||||
#define CPUTIME_PERCENT 70
|
||||
|
||||
/* Check every 10s */
|
||||
#define CPUTIME_INTERVAL_SOFT (10)
|
||||
|
||||
/* Recheck after 2s */
|
||||
#define CPUTIME_INTERVAL_HARD (2)
|
||||
|
||||
/* Time of the last CPU load check */
|
||||
static time_t last_time = 0;
|
||||
|
||||
/* Pipe for communicating with the main loop */
|
||||
static int the_pipe[2] = {-1, -1};
|
||||
|
||||
/* Main event loop and IO event for the FIFO */
|
||||
static struct pa_mainloop_api *api = NULL;
|
||||
static struct pa_io_event *io_event = NULL;
|
||||
static struct sigaction sigaction_prev;
|
||||
static int installed = 0;
|
||||
|
||||
/* Saved sigaction struct for SIGXCPU */
|
||||
static struct sigaction sigaction_prev;
|
||||
|
||||
/* Nonzero after pa_cpu_limit_init() */
|
||||
static int installed = 0;
|
||||
|
||||
/* The current state of operation */
|
||||
static enum {
|
||||
PHASE_IDLE,
|
||||
PHASE_SOFT
|
||||
PHASE_IDLE, /* Normal state */
|
||||
PHASE_SOFT /* After CPU overload has been detected */
|
||||
} phase = PHASE_IDLE;
|
||||
|
||||
/* Reset the SIGXCPU timer to the next t seconds */
|
||||
static void reset_cpu_time(int t) {
|
||||
int r;
|
||||
long n;
|
||||
struct rlimit rl;
|
||||
struct rusage ru;
|
||||
|
||||
/* Get the current CPU time of the current process */
|
||||
r = getrusage(RUSAGE_SELF, &ru);
|
||||
assert(r >= 0);
|
||||
|
||||
|
|
@ -69,10 +97,12 @@ static void reset_cpu_time(int t) {
|
|||
assert(r >= 0);
|
||||
}
|
||||
|
||||
/* A simple, thread-safe puts() work-alike */
|
||||
static void write_err(const char *p) {
|
||||
pa_loop_write(2, p, strlen(p));
|
||||
}
|
||||
|
||||
/* The signal handler, called on every SIGXCPU */
|
||||
static void signal_handler(int sig) {
|
||||
assert(sig == SIGXCPU);
|
||||
|
||||
|
|
@ -109,23 +139,26 @@ static void signal_handler(int sig) {
|
|||
|
||||
} else if (phase == PHASE_SOFT) {
|
||||
write_err("Hard CPU time limit exhausted, terminating forcibly.\n");
|
||||
_exit(1);
|
||||
_exit(1); /* Forced exit */
|
||||
}
|
||||
}
|
||||
|
||||
/* Callback for IO events on the FIFO */
|
||||
static void callback(struct pa_mainloop_api*m, struct pa_io_event*e, int fd, enum pa_io_event_flags f, void *userdata) {
|
||||
char c;
|
||||
assert(m && e && f == PA_IO_EVENT_INPUT && e == io_event && fd == the_pipe[0]);
|
||||
read(the_pipe[0], &c, sizeof(c));
|
||||
m->quit(m, 1);
|
||||
m->quit(m, 1); /* Quit the main loop */
|
||||
}
|
||||
|
||||
/* Initializes CPU load limiter */
|
||||
int pa_cpu_limit_init(struct pa_mainloop_api *m) {
|
||||
struct sigaction sa;
|
||||
assert(m && !api && !io_event && the_pipe[0] == -1 && the_pipe[1] == -1);
|
||||
assert(m && !api && !io_event && the_pipe[0] == -1 && the_pipe[1] == -1 && !installed);
|
||||
|
||||
time(&last_time);
|
||||
|
||||
/* Prepare the main loop pipe */
|
||||
if (pipe(the_pipe) < 0) {
|
||||
pa_log(__FILE__": pipe() failed: %s\n", strerror(errno));
|
||||
return -1;
|
||||
|
|
@ -141,6 +174,7 @@ int pa_cpu_limit_init(struct pa_mainloop_api *m) {
|
|||
|
||||
phase = PHASE_IDLE;
|
||||
|
||||
/* Install signal handler for SIGXCPU */
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.sa_handler = signal_handler;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
|
|
@ -158,6 +192,7 @@ int pa_cpu_limit_init(struct pa_mainloop_api *m) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Shutdown CPU load limiter */
|
||||
void pa_cpu_limit_done(void) {
|
||||
int r;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,29 +0,0 @@
|
|||
#ifndef foodebughfoo
|
||||
#define foodebughfoo
|
||||
|
||||
/* $Id$ */
|
||||
|
||||
/***
|
||||
This file is part of polypaudio.
|
||||
|
||||
polypaudio 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.
|
||||
|
||||
polypaudio 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 polypaudio; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
USA.
|
||||
***/
|
||||
|
||||
/* A nice trick for debuggers, working on x86 with GCC only */
|
||||
|
||||
#define DEBUG_TRAP __asm__("int $3")
|
||||
|
||||
#endif
|
||||
|
|
@ -82,6 +82,9 @@ static void callback(struct pa_mainloop_api* m, struct pa_io_event *e, int fd, e
|
|||
if (e == io->input_event) {
|
||||
io->mainloop->io_free(io->input_event);
|
||||
io->input_event = NULL;
|
||||
|
||||
if (io->output_event == e)
|
||||
io->output_event = NULL;
|
||||
}
|
||||
|
||||
if (e == io->output_event) {
|
||||
|
|
|
|||
68
polyp/mcalign-test.c
Normal file
68
polyp/mcalign-test.c
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "util.h"
|
||||
#include "mcalign.h"
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
struct pa_mcalign *a = pa_mcalign_new(11, NULL);
|
||||
struct pa_memchunk c;
|
||||
|
||||
pa_memchunk_reset(&c);
|
||||
|
||||
srand(time(NULL));
|
||||
|
||||
for (;;) {
|
||||
ssize_t r;
|
||||
size_t l;
|
||||
|
||||
if (!c.memblock) {
|
||||
c.memblock = pa_memblock_new(2048, NULL);
|
||||
c.index = c.length = 0;
|
||||
}
|
||||
|
||||
assert(c.index < c.memblock->length);
|
||||
|
||||
l = c.memblock->length - c.index;
|
||||
|
||||
l = l <= 1 ? l : rand() % (l-1) +1 ;
|
||||
|
||||
if ((r = read(STDIN_FILENO, (uint8_t*) c.memblock->data + c.index, l)) <= 0) {
|
||||
fprintf(stderr, "read() failed: %s\n", r < 0 ? strerror(errno) : "EOF");
|
||||
break;
|
||||
}
|
||||
|
||||
c.length = r;
|
||||
pa_mcalign_push(a, &c);
|
||||
fprintf(stderr, "Read %u bytes\n", r);
|
||||
|
||||
c.index += r;
|
||||
|
||||
if (c.index >= c.memblock->length) {
|
||||
pa_memblock_unref(c.memblock);
|
||||
pa_memchunk_reset(&c);
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
struct pa_memchunk t;
|
||||
|
||||
if (pa_mcalign_pop(a, &t) < 0)
|
||||
break;
|
||||
|
||||
pa_loop_write(STDOUT_FILENO, (uint8_t*) t.memblock->data + t.index, t.length);
|
||||
fprintf(stderr, "Wrote %u bytes.\n", t.length);
|
||||
|
||||
pa_memblock_unref(t.memblock);
|
||||
}
|
||||
}
|
||||
|
||||
pa_mcalign_free(a);
|
||||
|
||||
if (c.memblock)
|
||||
pa_memblock_unref(c.memblock);
|
||||
}
|
||||
188
polyp/mcalign.c
Normal file
188
polyp/mcalign.c
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
/* $Id$ */
|
||||
|
||||
/***
|
||||
This file is part of polypaudio.
|
||||
|
||||
polypaudio 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.
|
||||
|
||||
polypaudio 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 polypaudio; 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 "mcalign.h"
|
||||
#include "xmalloc.h"
|
||||
|
||||
struct pa_mcalign {
|
||||
size_t base;
|
||||
struct pa_memchunk leftover, current;
|
||||
struct pa_memblock_stat *memblock_stat;
|
||||
};
|
||||
|
||||
struct pa_mcalign *pa_mcalign_new(size_t base, struct pa_memblock_stat *s) {
|
||||
struct pa_mcalign *m;
|
||||
assert(base);
|
||||
|
||||
m = pa_xmalloc(sizeof(struct pa_mcalign));
|
||||
m->base = base;
|
||||
pa_memchunk_reset(&m->leftover);
|
||||
pa_memchunk_reset(&m->current);
|
||||
m->memblock_stat = s;
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
void pa_mcalign_free(struct 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(struct pa_mcalign *m, const struct pa_memchunk *c) {
|
||||
assert(m && c && c->memblock && c->length);
|
||||
|
||||
/* Append to the leftover memory block */
|
||||
if (m->leftover.memblock) {
|
||||
assert(!m->current.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 {
|
||||
assert(!m->leftover.memblock && !m->current.memblock);
|
||||
|
||||
/* 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(struct pa_mcalign *m, struct pa_memchunk *c) {
|
||||
assert(m && 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;
|
||||
|
||||
}
|
||||
77
polyp/mcalign.h
Normal file
77
polyp/mcalign.h
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
#ifndef foomcalignhfoo
|
||||
#define foomcalignhfoo
|
||||
|
||||
/* $Id$ */
|
||||
|
||||
/***
|
||||
This file is part of polypaudio.
|
||||
|
||||
polypaudio 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.
|
||||
|
||||
polypaudio 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 polypaudio; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
USA.
|
||||
***/
|
||||
|
||||
#include "memblock.h"
|
||||
#include "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:
|
||||
*
|
||||
* struct pa_mcalign *a = pa_mcalign_new(4, NULL);
|
||||
*
|
||||
* for (;;) {
|
||||
* struct pa_memchunk input;
|
||||
*
|
||||
* ... fill input ...
|
||||
*
|
||||
* pa_mcalign_push(m, &input);
|
||||
* pa_memblock_unref(input.memblock);
|
||||
*
|
||||
* for (;;) {
|
||||
* struct pa_memchunk output;
|
||||
*
|
||||
* if (pa_mcalign_pop(m, &output) < 0)
|
||||
* break;
|
||||
*
|
||||
* ... consume output ...
|
||||
*
|
||||
* pa_memblock_unref(output.memblock);
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* pa_memchunk_free(a);
|
||||
* */
|
||||
|
||||
struct pa_mcalign;
|
||||
|
||||
struct pa_mcalign *pa_mcalign_new(size_t base, struct pa_memblock_stat *s);
|
||||
void pa_mcalign_free(struct 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(struct pa_mcalign *m, const struct pa_memchunk *c);
|
||||
|
||||
/* Pop a new memchunk from the aligner. Returns 0 when sucessful,
|
||||
* nonzero otherwise. */
|
||||
int pa_mcalign_pop(struct pa_mcalign *m, struct pa_memchunk *c);
|
||||
|
||||
#endif
|
||||
|
|
@ -34,6 +34,11 @@
|
|||
static void stat_add(struct pa_memblock*m, struct pa_memblock_stat *s) {
|
||||
assert(m);
|
||||
|
||||
if (!s) {
|
||||
m->stat = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
m->stat = pa_memblock_stat_ref(s);
|
||||
s->total++;
|
||||
s->allocated++;
|
||||
|
|
@ -61,17 +66,7 @@ struct pa_memblock *pa_memblock_new(size_t length, struct pa_memblock_stat*s) {
|
|||
b->length = length;
|
||||
b->data = b+1;
|
||||
b->free_cb = NULL;
|
||||
stat_add(b, s);
|
||||
return b;
|
||||
}
|
||||
|
||||
struct pa_memblock *pa_memblock_new_fixed(void *d, size_t length, struct pa_memblock_stat*s) {
|
||||
struct pa_memblock *b = pa_xmalloc(sizeof(struct pa_memblock));
|
||||
b->type = PA_MEMBLOCK_FIXED;
|
||||
b->ref = 1;
|
||||
b->length = length;
|
||||
b->data = d;
|
||||
b->free_cb = NULL;
|
||||
b->read_only = 0;
|
||||
stat_add(b, s);
|
||||
return b;
|
||||
}
|
||||
|
|
@ -83,11 +78,24 @@ struct pa_memblock *pa_memblock_new_dynamic(void *d, size_t length, struct pa_me
|
|||
b->length = length;
|
||||
b->data = d;
|
||||
b->free_cb = NULL;
|
||||
b->read_only = 0;
|
||||
stat_add(b, s);
|
||||
return b;
|
||||
}
|
||||
|
||||
struct pa_memblock *pa_memblock_new_user(void *d, size_t length, void (*free_cb)(void *p), struct pa_memblock_stat*s) {
|
||||
struct pa_memblock *pa_memblock_new_fixed(void *d, size_t length, int read_only, struct pa_memblock_stat*s) {
|
||||
struct pa_memblock *b = pa_xmalloc(sizeof(struct 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;
|
||||
}
|
||||
|
||||
struct pa_memblock *pa_memblock_new_user(void *d, size_t length, void (*free_cb)(void *p), int read_only, struct pa_memblock_stat*s) {
|
||||
struct pa_memblock *b;
|
||||
assert(d && length && free_cb);
|
||||
b = pa_xmalloc(sizeof(struct pa_memblock));
|
||||
|
|
@ -96,6 +104,7 @@ struct pa_memblock *pa_memblock_new_user(void *d, size_t length, void (*free_cb)
|
|||
b->length = length;
|
||||
b->data = d;
|
||||
b->free_cb = free_cb;
|
||||
b->read_only = read_only;
|
||||
stat_add(b, s);
|
||||
return b;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ struct pa_memblock_stat;
|
|||
struct pa_memblock {
|
||||
enum pa_memblock_type type;
|
||||
unsigned ref;
|
||||
int read_only;
|
||||
size_t length;
|
||||
void *data;
|
||||
void (*free_cb)(void *p);
|
||||
|
|
@ -39,9 +40,9 @@ struct pa_memblock {
|
|||
};
|
||||
|
||||
struct pa_memblock *pa_memblock_new(size_t length, struct pa_memblock_stat*s);
|
||||
struct pa_memblock *pa_memblock_new_fixed(void *data, size_t length, struct pa_memblock_stat*s);
|
||||
struct pa_memblock *pa_memblock_new_dynamic(void *data, size_t length, struct pa_memblock_stat*s);
|
||||
struct pa_memblock *pa_memblock_new_user(void *data, size_t length, void (*free_cb)(void *p), struct pa_memblock_stat*s);
|
||||
struct pa_memblock *pa_memblock_new_fixed(void *data, size_t length, int read_only, struct pa_memblock_stat*s);
|
||||
struct pa_memblock *pa_memblock_new_user(void *data, size_t length, void (*free_cb)(void *p), int read_only, struct pa_memblock_stat*s);
|
||||
|
||||
void pa_memblock_unref(struct pa_memblock*b);
|
||||
struct pa_memblock* pa_memblock_ref(struct pa_memblock*b);
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@
|
|||
#include "memblockq.h"
|
||||
#include "xmalloc.h"
|
||||
#include "log.h"
|
||||
#include "mcalign.h"
|
||||
|
||||
struct memblock_list {
|
||||
struct memblock_list *next, *prev;
|
||||
|
|
|
|||
118
polyp/memchunk.c
118
polyp/memchunk.c
|
|
@ -31,120 +31,28 @@
|
|||
#include "memchunk.h"
|
||||
#include "xmalloc.h"
|
||||
|
||||
void pa_memchunk_make_writable(struct pa_memchunk *c, struct pa_memblock_stat *s) {
|
||||
void pa_memchunk_make_writable(struct pa_memchunk *c, struct pa_memblock_stat *s, size_t min) {
|
||||
struct pa_memblock *n;
|
||||
size_t l;
|
||||
assert(c && c->memblock && c->memblock->ref >= 1);
|
||||
|
||||
if (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(c->length, s);
|
||||
assert(n);
|
||||
memcpy(n->data, (uint8_t*) c->memblock->data+c->index, c->length);
|
||||
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(struct pa_memchunk *c) {
|
||||
assert(c);
|
||||
|
||||
struct pa_mcalign {
|
||||
size_t base;
|
||||
struct pa_memchunk chunk;
|
||||
uint8_t *buffer;
|
||||
size_t buffer_fill;
|
||||
struct pa_memblock_stat *memblock_stat;
|
||||
};
|
||||
|
||||
struct pa_mcalign *pa_mcalign_new(size_t base, struct pa_memblock_stat *s) {
|
||||
struct pa_mcalign *m;
|
||||
assert(base);
|
||||
|
||||
m = pa_xmalloc(sizeof(struct pa_mcalign));
|
||||
m->base = base;
|
||||
m->chunk.memblock = NULL;
|
||||
m->chunk.length = m->chunk.index = 0;
|
||||
m->buffer = NULL;
|
||||
m->buffer_fill = 0;
|
||||
m->memblock_stat = s;
|
||||
return m;
|
||||
}
|
||||
|
||||
void pa_mcalign_free(struct pa_mcalign *m) {
|
||||
assert(m);
|
||||
|
||||
pa_xfree(m->buffer);
|
||||
|
||||
if (m->chunk.memblock)
|
||||
pa_memblock_unref(m->chunk.memblock);
|
||||
|
||||
pa_xfree(m);
|
||||
}
|
||||
|
||||
void pa_mcalign_push(struct pa_mcalign *m, const struct pa_memchunk *c) {
|
||||
assert(m && c && !m->chunk.memblock && c->memblock && c->length);
|
||||
|
||||
m->chunk = *c;
|
||||
pa_memblock_ref(m->chunk.memblock);
|
||||
}
|
||||
|
||||
int pa_mcalign_pop(struct pa_mcalign *m, struct pa_memchunk *c) {
|
||||
int ret;
|
||||
assert(m && c && m->base > m->buffer_fill);
|
||||
|
||||
if (!m->chunk.memblock)
|
||||
return -1;
|
||||
|
||||
if (m->buffer_fill) {
|
||||
size_t l = m->base - m->buffer_fill;
|
||||
if (l > m->chunk.length)
|
||||
l = m->chunk.length;
|
||||
assert(m->buffer && l);
|
||||
|
||||
memcpy((uint8_t*) m->buffer + m->buffer_fill, (uint8_t*) m->chunk.memblock->data + m->chunk.index, l);
|
||||
m->buffer_fill += l;
|
||||
m->chunk.index += l;
|
||||
m->chunk.length -= l;
|
||||
|
||||
if (m->chunk.length == 0) {
|
||||
m->chunk.length = m->chunk.index = 0;
|
||||
pa_memblock_unref(m->chunk.memblock);
|
||||
m->chunk.memblock = NULL;
|
||||
}
|
||||
|
||||
assert(m->buffer_fill <= m->base);
|
||||
if (m->buffer_fill == m->base) {
|
||||
c->memblock = pa_memblock_new_dynamic(m->buffer, m->base, m->memblock_stat);
|
||||
assert(c->memblock);
|
||||
c->index = 0;
|
||||
c->length = m->base;
|
||||
m->buffer = NULL;
|
||||
m->buffer_fill = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
m->buffer_fill = m->chunk.length % m->base;
|
||||
|
||||
if (m->buffer_fill) {
|
||||
assert(!m->buffer);
|
||||
m->buffer = pa_xmalloc(m->base);
|
||||
m->chunk.length -= m->buffer_fill;
|
||||
memcpy(m->buffer, (uint8_t*) m->chunk.memblock->data + m->chunk.index + m->chunk.length, m->buffer_fill);
|
||||
}
|
||||
|
||||
if (m->chunk.length) {
|
||||
*c = m->chunk;
|
||||
pa_memblock_ref(c->memblock);
|
||||
ret = 0;
|
||||
} else
|
||||
ret = -1;
|
||||
|
||||
m->chunk.length = m->chunk.index = 0;
|
||||
pa_memblock_unref(m->chunk.memblock);
|
||||
m->chunk.memblock = NULL;
|
||||
|
||||
return ret;
|
||||
c->memblock = NULL;
|
||||
c->length = c->index = 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,18 +24,22 @@
|
|||
|
||||
#include "memblock.h"
|
||||
|
||||
/* A memchunk is 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 */
|
||||
|
||||
struct pa_memchunk {
|
||||
struct pa_memblock *memblock;
|
||||
size_t index, length;
|
||||
};
|
||||
|
||||
void pa_memchunk_make_writable(struct pa_memchunk *c, struct pa_memblock_stat *s);
|
||||
/* 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(struct pa_memchunk *c, struct pa_memblock_stat *s, size_t min);
|
||||
|
||||
struct pa_mcalign;
|
||||
|
||||
struct pa_mcalign *pa_mcalign_new(size_t base, struct pa_memblock_stat *s);
|
||||
void pa_mcalign_free(struct pa_mcalign *m);
|
||||
void pa_mcalign_push(struct pa_mcalign *m, const struct pa_memchunk *c);
|
||||
int pa_mcalign_pop(struct pa_mcalign *m, struct pa_memchunk *c);
|
||||
/* Invalidate a memchunk. This does not free the cotaining memblock,
|
||||
* but sets all members to zero. */
|
||||
void pa_memchunk_reset(struct pa_memchunk *c);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ static void out_fill_memblocks(struct userdata *u, unsigned n) {
|
|||
if (u->out_memblocks[u->out_current])
|
||||
pa_memblock_unref_fixed(u->out_memblocks[u->out_current]);
|
||||
|
||||
chunk.memblock = u->out_memblocks[u->out_current] = pa_memblock_new_fixed((uint8_t*)u->out_mmap+u->out_fragment_size*u->out_current, u->out_fragment_size, u->core->memblock_stat);
|
||||
chunk.memblock = u->out_memblocks[u->out_current] = pa_memblock_new_fixed((uint8_t*)u->out_mmap+u->out_fragment_size*u->out_current, u->out_fragment_size, 1, u->core->memblock_stat);
|
||||
assert(chunk.memblock);
|
||||
chunk.length = chunk.memblock->length;
|
||||
chunk.index = 0;
|
||||
|
|
@ -148,7 +148,7 @@ static void in_post_memblocks(struct userdata *u, unsigned n) {
|
|||
struct pa_memchunk chunk;
|
||||
|
||||
if (!u->in_memblocks[u->in_current]) {
|
||||
chunk.memblock = u->in_memblocks[u->in_current] = pa_memblock_new_fixed((uint8_t*) u->in_mmap+u->in_fragment_size*u->in_current, u->in_fragment_size, u->core->memblock_stat);
|
||||
chunk.memblock = u->in_memblocks[u->in_current] = pa_memblock_new_fixed((uint8_t*) u->in_mmap+u->in_fragment_size*u->in_current, u->in_fragment_size, 1, u->core->memblock_stat);
|
||||
chunk.length = chunk.memblock->length;
|
||||
chunk.index = 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -610,19 +610,9 @@ int pa__init(struct pa_core *c, struct pa_module*m) {
|
|||
goto fail;
|
||||
}
|
||||
|
||||
if (u->server_name[0] == '/')
|
||||
u->client = pa_socket_client_new_unix(c->mainloop, u->server_name);
|
||||
else {
|
||||
size_t len;
|
||||
struct sockaddr *sa;
|
||||
|
||||
if (!(sa = pa_resolve_server(u->server_name, &len, PA_NATIVE_DEFAULT_PORT))) {
|
||||
pa_log(__FILE__": failed to resolve server '%s'\n", u->server_name);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
u->client = pa_socket_client_new_sockaddr(c->mainloop, sa, len);
|
||||
pa_xfree(sa);
|
||||
if (!(u->client = pa_socket_client_new_string(c->mainloop, u->server_name, PA_NATIVE_DEFAULT_PORT))) {
|
||||
pa_log(__FILE__": failed to connect to server '%s'\n", u->server_name);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!u->client)
|
||||
|
|
|
|||
|
|
@ -56,8 +56,6 @@
|
|||
|
||||
#define AUTOSPAWN_LOCK "autospawn.lock"
|
||||
|
||||
|
||||
|
||||
static const struct pa_pdispatch_command command_table[PA_COMMAND_MAX] = {
|
||||
[PA_COMMAND_REQUEST] = { pa_command_request },
|
||||
[PA_COMMAND_PLAYBACK_STREAM_KILLED] = { pa_command_stream_killed },
|
||||
|
|
@ -72,7 +70,6 @@ static void unlock_autospawn_lock_file(struct pa_context *c) {
|
|||
pa_unlock_lockfile(c->autospawn_lock_fd);
|
||||
c->autospawn_lock_fd = -1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
struct pa_context *pa_context_new(struct pa_mainloop_api *mainloop, const char *name) {
|
||||
|
|
@ -106,6 +103,7 @@ struct pa_context *pa_context_new(struct pa_mainloop_api *mainloop, const char *
|
|||
c->memblock_stat = pa_memblock_stat_new();
|
||||
c->local = -1;
|
||||
c->server_list = NULL;
|
||||
c->server = NULL;
|
||||
c->autospawn_lock_fd = -1;
|
||||
memset(&c->spawn_api, 0, sizeof(c->spawn_api));
|
||||
c->do_autospawn = 0;
|
||||
|
|
@ -155,6 +153,7 @@ static void context_free(struct pa_context *c) {
|
|||
pa_strlist_free(c->server_list);
|
||||
|
||||
pa_xfree(c->name);
|
||||
pa_xfree(c->server);
|
||||
pa_xfree(c);
|
||||
}
|
||||
|
||||
|
|
@ -246,9 +245,20 @@ static void pstream_memblock_callback(struct pa_pstream *p, uint32_t channel, ui
|
|||
pa_context_ref(c);
|
||||
|
||||
if ((s = pa_dynarray_get(c->record_streams, channel))) {
|
||||
if (s->read_callback) {
|
||||
s->read_callback(s, (uint8_t*) chunk->memblock->data + chunk->index, chunk->length, s->read_userdata);
|
||||
s->counter += chunk->length;
|
||||
pa_mcalign_push(s->mcalign, chunk);
|
||||
|
||||
for (;;) {
|
||||
struct pa_memchunk t;
|
||||
|
||||
if (pa_mcalign_pop(s->mcalign, &t) < 0)
|
||||
break;
|
||||
|
||||
if (s->read_callback) {
|
||||
s->read_callback(s, (uint8_t*) t.memblock->data + t.index, t.length, s->read_userdata);
|
||||
s->counter += chunk->length;
|
||||
}
|
||||
|
||||
pa_memblock_unref(t.memblock);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -496,6 +506,9 @@ static int try_next_connection(struct pa_context *c) {
|
|||
}
|
||||
|
||||
/* pa_log(__FILE__": Trying to connect to %s...\n", u); */
|
||||
|
||||
pa_xfree(c->server);
|
||||
c->server = pa_xstrdup(u);
|
||||
|
||||
if (!(c->client = pa_socket_client_new_string(c->mainloop, u, PA_NATIVE_DEFAULT_PORT)))
|
||||
continue;
|
||||
|
|
@ -811,3 +824,15 @@ const char* pa_get_library_version(void) {
|
|||
return PACKAGE_VERSION;
|
||||
}
|
||||
|
||||
const char* pa_context_get_server(struct pa_context *c) {
|
||||
|
||||
if (!c->server)
|
||||
return NULL;
|
||||
|
||||
if (*c->server == '{') {
|
||||
char *e = strchr(c->server+1, '}');
|
||||
return e ? e+1 : c->server;
|
||||
}
|
||||
|
||||
return c->server;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -108,6 +108,9 @@ int pa_context_is_local(struct pa_context *c);
|
|||
/** Set a different application name for context on the server. \since 0.5 */
|
||||
struct pa_operation* pa_context_set_name(struct pa_context *c, const char *name, void(*cb)(struct pa_context*c, int success, void *userdata), void *userdata);
|
||||
|
||||
/** Return the server name this context is connected to. \since 0.7 */
|
||||
const char* pa_context_get_server(struct pa_context *c);
|
||||
|
||||
PA_C_DECL_END
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@
|
|||
#include "native-common.h"
|
||||
#include "client-conf.h"
|
||||
#include "strlist.h"
|
||||
#include "mcalign.h"
|
||||
|
||||
#define DEFAULT_TLENGTH (44100*2*2/2) //(10240*8)
|
||||
#define DEFAULT_MAXLENGTH ((DEFAULT_TLENGTH*3)/2)
|
||||
|
|
@ -77,6 +78,8 @@ struct pa_context {
|
|||
|
||||
struct pa_strlist *server_list;
|
||||
|
||||
char *server;
|
||||
|
||||
struct pa_client_conf *conf;
|
||||
};
|
||||
|
||||
|
|
@ -97,6 +100,7 @@ struct pa_stream {
|
|||
uint64_t counter;
|
||||
pa_usec_t previous_time;
|
||||
enum pa_stream_state state;
|
||||
struct pa_mcalign *mcalign;
|
||||
|
||||
int interpolate;
|
||||
int corked;
|
||||
|
|
|
|||
|
|
@ -61,6 +61,8 @@ struct pa_stream *pa_stream_new(struct pa_context *c, const char *name, const st
|
|||
s->state = PA_STREAM_DISCONNECTED;
|
||||
memset(&s->buffer_attr, 0, sizeof(s->buffer_attr));
|
||||
|
||||
s->mcalign = pa_mcalign_new(pa_frame_size(ss), c->memblock_stat);
|
||||
|
||||
s->counter = 0;
|
||||
s->previous_time = 0;
|
||||
|
||||
|
|
@ -83,6 +85,8 @@ static void stream_free(struct pa_stream *s) {
|
|||
assert(s->mainloop);
|
||||
s->mainloop->time_free(s->ipol_event);
|
||||
}
|
||||
|
||||
pa_mcalign_free(s->mcalign);
|
||||
|
||||
pa_xfree(s->name);
|
||||
pa_xfree(s);
|
||||
|
|
@ -203,11 +207,13 @@ static void ipol_callback(struct pa_mainloop_api *m, struct pa_time_event *e, co
|
|||
struct pa_stream *s = userdata;
|
||||
|
||||
pa_stream_ref(s);
|
||||
pa_operation_unref(pa_stream_get_latency_info(s, NULL, NULL));
|
||||
|
||||
if (s->state == PA_STREAM_READY)
|
||||
pa_operation_unref(pa_stream_get_latency_info(s, NULL, NULL));
|
||||
|
||||
gettimeofday(&tv2, NULL);
|
||||
tv2.tv_usec += LATENCY_IPOL_INTERVAL_USEC;
|
||||
|
||||
|
||||
m->time_restart(e, &tv2);
|
||||
|
||||
pa_stream_unref(s);
|
||||
|
|
@ -329,7 +335,7 @@ void pa_stream_write(struct pa_stream *s, const void *data, size_t length, void
|
|||
assert(s && s->context && data && length && s->state == PA_STREAM_READY && s->ref >= 1);
|
||||
|
||||
if (free_cb) {
|
||||
chunk.memblock = pa_memblock_new_user((void*) data, length, free_cb, s->context->memblock_stat);
|
||||
chunk.memblock = pa_memblock_new_user((void*) data, length, free_cb, 1, s->context->memblock_stat);
|
||||
assert(chunk.memblock && chunk.memblock->data);
|
||||
} else {
|
||||
chunk.memblock = pa_memblock_new(length, s->context->memblock_stat);
|
||||
|
|
|
|||
|
|
@ -42,7 +42,6 @@
|
|||
#include "scache.h"
|
||||
#include "sample-util.h"
|
||||
#include "authkey.h"
|
||||
#include "debug.h"
|
||||
#include "namereg.h"
|
||||
#include "xmalloc.h"
|
||||
#include "log.h"
|
||||
|
|
|
|||
|
|
@ -1343,7 +1343,7 @@ static void command_get_server_info(struct pa_pdispatch *pd, uint32_t command, u
|
|||
pa_tagstruct_puts(reply, PACKAGE_NAME);
|
||||
pa_tagstruct_puts(reply, PACKAGE_VERSION);
|
||||
pa_tagstruct_puts(reply, pa_get_user_name(txt, sizeof(txt)));
|
||||
pa_tagstruct_puts(reply, pa_get_host_name(txt, sizeof(txt)));
|
||||
pa_tagstruct_puts(reply, pa_get_fqdn(txt, sizeof(txt)));
|
||||
pa_tagstruct_put_sample_spec(reply, &c->protocol->core->default_sample_spec);
|
||||
|
||||
n = pa_namereg_get_default_sink_name(c->protocol->core);
|
||||
|
|
|
|||
|
|
@ -27,6 +27,12 @@
|
|||
#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>
|
||||
#include <glob.h>
|
||||
|
||||
#include "scache.h"
|
||||
#include "sink-input.h"
|
||||
|
|
@ -38,6 +44,7 @@
|
|||
#include "namereg.h"
|
||||
#include "sound-file.h"
|
||||
#include "util.h"
|
||||
#include "log.h"
|
||||
|
||||
#define UNLOAD_POLL_TIME 2
|
||||
|
||||
|
|
@ -291,3 +298,58 @@ void pa_scache_unload_unused(struct pa_core *c) {
|
|||
pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_CHANGE, e->index);
|
||||
}
|
||||
}
|
||||
|
||||
static void add_file(struct pa_core *c, const char *pathname) {
|
||||
struct stat st;
|
||||
const char *e;
|
||||
|
||||
if (!(e = strrchr(pathname, '/')))
|
||||
e = pathname;
|
||||
else
|
||||
e++;
|
||||
|
||||
if (stat(pathname, &st) < 0) {
|
||||
pa_log(__FILE__": stat('%s') failed: %s\n", pathname, strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode))
|
||||
pa_scache_add_file_lazy(c, e, pathname, NULL);
|
||||
}
|
||||
|
||||
int pa_scache_add_directory_lazy(struct pa_core *c, const char *pathname) {
|
||||
DIR *dir;
|
||||
assert(c && pathname);
|
||||
|
||||
/* First try to open this as directory */
|
||||
if (!(dir = opendir(pathname))) {
|
||||
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\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0; i < p.gl_pathc; i++)
|
||||
add_file(c, p.gl_pathv[i]);
|
||||
|
||||
globfree(&p);
|
||||
} 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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,6 +45,8 @@ int pa_scache_add_item(struct pa_core *c, const char *name, struct pa_sample_spe
|
|||
int pa_scache_add_file(struct pa_core *c, const char *name, const char *filename, uint32_t *index);
|
||||
int pa_scache_add_file_lazy(struct pa_core *c, const char *name, const char *filename, uint32_t *index);
|
||||
|
||||
int pa_scache_add_directory_lazy(struct pa_core *c, const char *pathname);
|
||||
|
||||
int pa_scache_remove_item(struct pa_core *c, const char *name);
|
||||
int pa_scache_play_item(struct pa_core *c, const char *name, struct pa_sink *sink, uint32_t volume);
|
||||
void pa_scache_free(struct pa_core *c);
|
||||
|
|
|
|||
|
|
@ -225,7 +225,7 @@ int pa_sink_render(struct pa_sink*s, size_t length, struct pa_memchunk *result)
|
|||
volume = pa_volume_multiply(s->volume, info[0].volume);
|
||||
|
||||
if (volume != PA_VOLUME_NORM) {
|
||||
pa_memchunk_make_writable(result, s->core->memblock_stat);
|
||||
pa_memchunk_make_writable(result, s->core->memblock_stat, 0);
|
||||
pa_volume_memchunk(result, &s->sample_spec, volume);
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -338,16 +338,24 @@ struct pa_socket_client* pa_socket_client_new_string(struct pa_mainloop_api *m,
|
|||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = kind == KIND_TCP4 ? AF_INET : (kind == KIND_TCP6 ? AF_INET6 : AF_UNSPEC);
|
||||
|
||||
if (getaddrinfo(h, NULL, &hints, &res) < 0 || !res)
|
||||
if (getaddrinfo(h, NULL, &hints, &res) < 0 || !res || !res->ai_addr)
|
||||
return NULL;
|
||||
|
||||
if (res->ai_addr->sa_family == AF_INET)
|
||||
if (res->ai_family == AF_INET) {
|
||||
if (res->ai_addrlen != sizeof(struct sockaddr_in))
|
||||
return NULL;
|
||||
assert(res->ai_addr->sa_family == res->ai_family);
|
||||
|
||||
((struct sockaddr_in*) res->ai_addr)->sin_port = htons(port);
|
||||
else if (res->ai_addr->sa_family == AF_INET6)
|
||||
} else if (res->ai_family == AF_INET6) {
|
||||
if (res->ai_addrlen != sizeof(struct sockaddr_in6))
|
||||
return NULL;
|
||||
assert(res->ai_addr->sa_family == res->ai_family);
|
||||
|
||||
((struct sockaddr_in6*) res->ai_addr)->sin6_port = htons(port);
|
||||
else
|
||||
} else
|
||||
return NULL;
|
||||
|
||||
|
||||
c = pa_socket_client_new_sockaddr(m, res->ai_addr, res->ai_addrlen);
|
||||
freeaddrinfo(res);
|
||||
return c;
|
||||
|
|
@ -360,6 +368,9 @@ struct pa_socket_client* pa_socket_client_new_string(struct pa_mainloop_api *m,
|
|||
|
||||
}
|
||||
|
||||
/* Return non-zero when the target sockaddr is considered
|
||||
local. "local" means UNIX socket or TCP socket on localhost. Other
|
||||
local IP addresses are not considered local. */
|
||||
int pa_socket_client_is_local(struct pa_socket_client *c) {
|
||||
assert(c);
|
||||
return c->local;
|
||||
|
|
|
|||
|
|
@ -202,36 +202,3 @@ int pa_unix_socket_remove_stale(const char *fn) {
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct sockaddr *pa_resolve_server(const char *server, size_t *len, uint16_t nport) {
|
||||
struct sockaddr *sa;
|
||||
struct addrinfo hints, *result = NULL;
|
||||
char *port, host[256], tmp[16];
|
||||
assert(server && len);
|
||||
|
||||
snprintf(host, sizeof(host), "%s", server);
|
||||
host[strcspn(host, ":")] = 0;
|
||||
|
||||
if ((port = strrchr(server, ':')))
|
||||
port++;
|
||||
|
||||
if (!port)
|
||||
snprintf(port = tmp, sizeof(tmp), "%u", nport);
|
||||
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = PF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_protocol = 0;
|
||||
|
||||
if (getaddrinfo(host, port, &hints, &result) != 0)
|
||||
return NULL;
|
||||
assert(result);
|
||||
|
||||
sa = pa_xmalloc(*len = result->ai_addrlen);
|
||||
memcpy(sa, result->ai_addr, *len);
|
||||
|
||||
freeaddrinfo(result);
|
||||
|
||||
return sa;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -35,6 +35,4 @@ int pa_socket_set_rcvbuf(int fd, size_t l);
|
|||
int pa_unix_socket_is_stale(const char *fn);
|
||||
int pa_unix_socket_remove_stale(const char *fn);
|
||||
|
||||
struct sockaddr *pa_resolve_server(const char *server, size_t *len, uint16_t port);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@
|
|||
|
||||
#include "strbuf.h"
|
||||
|
||||
/* Some magic for zero-length arrays */
|
||||
#ifdef __STDC_VERSION__
|
||||
#if __STDC_VERSION__ >= 199901L
|
||||
#ifndef STDC99
|
||||
|
|
@ -41,6 +42,7 @@
|
|||
#endif
|
||||
#endif
|
||||
|
||||
/* A chunk of the linked list that makes up the string */
|
||||
struct chunk {
|
||||
struct chunk *next;
|
||||
size_t length;
|
||||
|
|
@ -74,6 +76,8 @@ void pa_strbuf_free(struct pa_strbuf *sb) {
|
|||
pa_xfree(sb);
|
||||
}
|
||||
|
||||
/* Make a C string from the string buffer. The caller has to free
|
||||
* string with pa_xfree(). */
|
||||
char *pa_strbuf_tostring(struct pa_strbuf *sb) {
|
||||
char *t, *e;
|
||||
struct chunk *c;
|
||||
|
|
@ -83,15 +87,18 @@ char *pa_strbuf_tostring(struct pa_strbuf *sb) {
|
|||
|
||||
e = t;
|
||||
for (c = sb->head; c; c = c->next) {
|
||||
assert((size_t) (e-t) <= sb->length);
|
||||
memcpy(e, c->text, c->length);
|
||||
e += c->length;
|
||||
}
|
||||
|
||||
/* Trailing NUL */
|
||||
*e = 0;
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
/* Combination of pa_strbuf_free() and pa_strbuf_tostring() */
|
||||
char *pa_strbuf_tostring_free(struct pa_strbuf *sb) {
|
||||
char *t;
|
||||
assert(sb);
|
||||
|
|
@ -100,23 +107,15 @@ char *pa_strbuf_tostring_free(struct pa_strbuf *sb) {
|
|||
return t;
|
||||
}
|
||||
|
||||
/* Append a string to the string buffer */
|
||||
void pa_strbuf_puts(struct pa_strbuf *sb, const char *t) {
|
||||
assert(sb && t);
|
||||
pa_strbuf_putsn(sb, t, strlen(t));
|
||||
}
|
||||
}
|
||||
|
||||
void pa_strbuf_putsn(struct pa_strbuf *sb, const char *t, size_t l) {
|
||||
struct chunk *c;
|
||||
assert(sb && t);
|
||||
|
||||
if (!l)
|
||||
return;
|
||||
|
||||
c = pa_xmalloc(sizeof(struct chunk)+l);
|
||||
|
||||
c->next = NULL;
|
||||
c->length = l;
|
||||
memcpy(c->text, t, l);
|
||||
/* Append a new chunk to the linked list */
|
||||
static void append(struct pa_strbuf *sb, struct chunk *c) {
|
||||
assert(sb && c);
|
||||
|
||||
if (sb->tail) {
|
||||
assert(sb->head);
|
||||
|
|
@ -127,19 +126,36 @@ void pa_strbuf_putsn(struct pa_strbuf *sb, const char *t, size_t l) {
|
|||
}
|
||||
|
||||
sb->tail = c;
|
||||
sb->length += l;
|
||||
sb->length += c->length;
|
||||
c->next = NULL;
|
||||
}
|
||||
|
||||
/* The following is based on an example from the GNU libc documentation */
|
||||
/* Append up to l bytes of a string to the string buffer */
|
||||
void pa_strbuf_putsn(struct pa_strbuf *sb, const char *t, size_t l) {
|
||||
struct chunk *c;
|
||||
assert(sb && t);
|
||||
|
||||
if (!l)
|
||||
return;
|
||||
|
||||
c = pa_xmalloc(sizeof(struct chunk)+l);
|
||||
c->length = l;
|
||||
memcpy(c->text, t, l);
|
||||
|
||||
append(sb, c);
|
||||
}
|
||||
|
||||
/* Append a printf() style formatted string to the string buffer. */
|
||||
/* The following is based on an example from the GNU libc documentation */
|
||||
int pa_strbuf_printf(struct pa_strbuf *sb, const char *format, ...) {
|
||||
int r, size = 100;
|
||||
int size = 100;
|
||||
struct chunk *c = NULL;
|
||||
|
||||
assert(sb);
|
||||
|
||||
for(;;) {
|
||||
va_list ap;
|
||||
int r;
|
||||
|
||||
c = pa_xrealloc(c, sizeof(struct chunk)+size);
|
||||
|
||||
|
|
@ -149,19 +165,7 @@ int pa_strbuf_printf(struct pa_strbuf *sb, const char *format, ...) {
|
|||
|
||||
if (r > -1 && r < size) {
|
||||
c->length = r;
|
||||
c->next = NULL;
|
||||
|
||||
if (sb->tail) {
|
||||
assert(sb->head);
|
||||
sb->tail->next = c;
|
||||
} else {
|
||||
assert(!sb->head);
|
||||
sb->head = c;
|
||||
}
|
||||
|
||||
sb->tail = c;
|
||||
sb->length += r;
|
||||
|
||||
append(sb, c);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue