* some commenting work
authorLennart Poettering <lennart@poettering.net>
Wed, 17 Nov 2004 00:05:25 +0000 (00:05 +0000)
committerLennart Poettering <lennart@poettering.net>
Wed, 17 Nov 2004 00:05:25 +0000 (00:05 +0000)
* 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

30 files changed:
doc/todo
polyp/Makefile.am
polyp/caps.c
polyp/cli-command.c
polyp/cpulimit.c
polyp/debug.h [deleted file]
polyp/iochannel.c
polyp/mcalign-test.c [new file with mode: 0644]
polyp/mcalign.c [new file with mode: 0644]
polyp/mcalign.h [new file with mode: 0644]
polyp/memblock.c
polyp/memblock.h
polyp/memblockq.c
polyp/memchunk.c
polyp/memchunk.h
polyp/module-oss-mmap.c
polyp/module-tunnel.c
polyp/polyplib-context.c
polyp/polyplib-context.h
polyp/polyplib-internal.h
polyp/polyplib-stream.c
polyp/protocol-esound.c
polyp/protocol-native.c
polyp/scache.c
polyp/scache.h
polyp/sink.c
polyp/socket-client.c
polyp/socket-util.c
polyp/socket-util.h
polyp/strbuf.c

index 95cf0f4..4f760b7 100644 (file)
--- a/doc/todo
+++ b/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
 
 ***********
index 9ea6932..506cdc3 100644 (file)
@@ -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
index daf0b91..d371916 100644 (file)
@@ -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;
 }
index 0c71260..dec877f 100644 (file)
@@ -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;
index fcbbaf3..78dc5e1 100644 (file)
 #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;
+
+/* Saved sigaction struct for SIGXCPU */
 static struct sigaction sigaction_prev;
-static int installed = 0;
 
+/* 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;
 
diff --git a/polyp/debug.h b/polyp/debug.h
deleted file mode 100644 (file)
index fcfa714..0000000
+++ /dev/null
@@ -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
index b93860a..f174afd 100644 (file)
@@ -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) {
diff --git a/polyp/mcalign-test.c b/polyp/mcalign-test.c
new file mode 100644 (file)
index 0000000..ab1f9ae
--- /dev/null
@@ -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);
+}
diff --git a/polyp/mcalign.c b/polyp/mcalign.c
new file mode 100644 (file)
index 0000000..0b7f0db
--- /dev/null
@@ -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;
+    
+}
diff --git a/polyp/mcalign.h b/polyp/mcalign.h
new file mode 100644 (file)
index 0000000..925f438
--- /dev/null
@@ -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
index 8ddfb80..c070bee 100644 (file)
 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,33 +66,36 @@ 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;
+    b->read_only = 0;
     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 *pa_memblock_new_dynamic(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->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;
 }
 
-struct pa_memblock *pa_memblock_new_dynamic(void *d, size_t length, 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_DYNAMIC;
+    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), struct pa_memblock_stat*s) {
+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;
 }
index 69a85a8..91612ac 100644 (file)
@@ -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);
index 0fbeaad..ff16f62 100644 (file)
@@ -33,6 +33,7 @@
 #include "memblockq.h"
 #include "xmalloc.h"
 #include "log.h"
+#include "mcalign.h"
 
 struct memblock_list {
     struct memblock_list *next, *prev;
index a8aeb88..d1c923f 100644 (file)
 #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;
 }
index e73b6f6..a004c2e 100644 (file)
 
 #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);
-
-struct pa_mcalign;
+/* 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 *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
index 8aecd56..66daa77 100644 (file)
@@ -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;
             
index 368ae42..7497011 100644 (file)
@@ -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)
index b15dd8e..15ef60b 100644 (file)
@@ -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;
+}
index 565701b..db4b121 100644 (file)
@@ -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
index d1b5363..68ba76a 100644 (file)
@@ -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;
index a97096b..7d3d3a7 100644 (file)
@@ -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);
index 4da7e38..07c39e2 100644 (file)
@@ -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"
index 7d53901..02d81db 100644 (file)
@@ -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);
index 32a3628..ccdc718 100644 (file)
 #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;
+}
index afaba31..f7043f1 100644 (file)
@@ -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);
index c17af4a..29aef6f 100644 (file)
@@ -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 {
index b77d2ae..aea3858 100644 (file)
@@ -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;
index 1800710..495ee1b 100644 (file)
@@ -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;
-}
-
index 6c8ffe3..ae16fb1 100644 (file)
@@ -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
index 8876ba1..a652165 100644 (file)
@@ -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,11 +107,30 @@ 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));
-} 
+}
+
+/* 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);
+        sb->tail->next = c;
+    } else {
+        assert(!sb->head);
+        sb->head = c;
+    }
+
+    sb->tail = c;
+    sb->length += c->length;
+    c->next = NULL;
+}
 
+/* 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);
@@ -113,33 +139,23 @@ void pa_strbuf_putsn(struct pa_strbuf *sb, const char *t, size_t l) {
        return;
    
     c = pa_xmalloc(sizeof(struct chunk)+l);
-
-    c->next = NULL;
     c->length = l;
     memcpy(c->text, t, l);
 
-    if (sb->tail) {
-        assert(sb->head);
-        sb->tail->next = c;
-    } else {
-        assert(!sb->head);
-        sb->head = c;
-    }
-
-    sb->tail = c;
-    sb->length += 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;
         }