[pulseaudio-commits] r2329 - in /branches/glitch-free/src: daemon/ modules/ pulse/ pulsecore/ utils/

svnmailer-noreply at 0pointer.de svnmailer-noreply at 0pointer.de
Thu May 1 12:51:06 PDT 2008


Author: lennart
Date: Thu May  1 21:51:05 2008
New Revision: 2329

URL: http://0pointer.de/cgi-bin/viewcvs.cgi?rev=2329&root=pulseaudio&view=rev
Log:
Yes, yet another evil all-in-one commit of intervowen changes. I suck.
* Drop "state" directory, fold that into "runtime directory"
* No longer automatically rewind when a new stream connects
* Rework sound file stream, to cause a rewind on initialisation, shorten _pop() code a bit
* Fix reference counting of pa_socket_server in the protocol implementations
* Rework daemon initialization code to be compatible with non-SUID-root setups where RLIMIT_RTPRIO is non-zero
* Print warning if RT/HP is enabled in the config, but due to missing caps, rlimits, policy we cannot enable it.
* Fix potential memory leak in pa_open_config_file()
* Add pa_find_config_file() which works much like pa_open_config_file() but doesn't actually open the config file in question. Just searches for it.
* Add portable pa_is_path_absolute()
* Add pa_close_all() and use it on daemon startup to close leaking file descriptors (inspired from what I did for libdaemon)
* Add pa_unblock_sigs() and use it on daemon startup to unblock all signals (inspired from libdaemon, too)
* Add pa_reset_sigs() and use it on daemon startup to reset all signal handlers (inspired from libdaemon as well)
* Implement pa_set_env()
* Define RLIMIT_RTTIME and friends if not defined by glibc
* Add pa_strempty()
* rename state testing macros to include _IS_, to make clearer that they are no states, but testing macros
* Implement pa_source_output_set_requested_latency_within_thread() to be able to forward latency info to sources from within the IO thread
* Similar for sink inputs
* generelize since_underrun counter in sink inputs to "playing_for" and "underrun_for". Use only this for ignore potential rewind requests over underruns
* Add new native protocol message PLAYBACK_STREAM_MESSAGE_STARTED for notification about the end of an underrun
* Port native protocol to use underrun_for/playing_for which is maintained by the sink input anyway
* Pass underrun_for/playing_for in timing info to client
* Drop pa_sink_skip() since it breaks underrun detection code
* Move PID file and unix sockets to the runtime dir (i.e. ~/.pulse). This fixes a potention DoS attack from other users stealing dirs in /tmp from us so that we cannot take them anymore)
* Allow setting of more resource limits from the config file. Set RTTIME by default
* Streamline daemon startup code
* Rework algorithm to find default configuration files
* If run in system mode use "system.pa" instead of "default.pa" as default script file
* Change ladspa sink to use pa_clamp_samples() for clamping samples
* Teach module-null-sink how to deal with rewinding
* Try to support ALSA devices with no implicit channel map. Synthesize one by padding with PA_CHANNEL_POSITION_AUX channels. This is not tested since I lack hardware with these problems.
* Make use of time smoother in the client libraries.
* Add new pa_stream_is_corked() and pa_stream_set_started_callback() functions to public API
* Since our native socket moved, add some code for finding sockets created by old versions of PA. This should ease upgrades

Modified:
    branches/glitch-free/src/daemon/cmdline.c
    branches/glitch-free/src/daemon/daemon-conf.c
    branches/glitch-free/src/daemon/daemon-conf.h
    branches/glitch-free/src/daemon/daemon.conf.in
    branches/glitch-free/src/daemon/main.c
    branches/glitch-free/src/modules/alsa-util.c
    branches/glitch-free/src/modules/module-alsa-sink.c
    branches/glitch-free/src/modules/module-alsa-source.c
    branches/glitch-free/src/modules/module-combine.c
    branches/glitch-free/src/modules/module-default-device-restore.c
    branches/glitch-free/src/modules/module-device-restore.c
    branches/glitch-free/src/modules/module-esound-sink.c
    branches/glitch-free/src/modules/module-ladspa-sink.c
    branches/glitch-free/src/modules/module-match.c
    branches/glitch-free/src/modules/module-null-sink.c
    branches/glitch-free/src/modules/module-oss.c
    branches/glitch-free/src/modules/module-protocol-stub.c
    branches/glitch-free/src/modules/module-remap-sink.c
    branches/glitch-free/src/modules/module-suspend-on-idle.c
    branches/glitch-free/src/modules/module-tunnel.c
    branches/glitch-free/src/modules/module-volume-restore.c
    branches/glitch-free/src/pulse/client-conf.c
    branches/glitch-free/src/pulse/context.c
    branches/glitch-free/src/pulse/def.h
    branches/glitch-free/src/pulse/internal.h
    branches/glitch-free/src/pulse/introspect.c
    branches/glitch-free/src/pulse/scache.c
    branches/glitch-free/src/pulse/stream.c
    branches/glitch-free/src/pulse/stream.h
    branches/glitch-free/src/pulsecore/core-util.c
    branches/glitch-free/src/pulsecore/core-util.h
    branches/glitch-free/src/pulsecore/native-common.h
    branches/glitch-free/src/pulsecore/pid.c
    branches/glitch-free/src/pulsecore/protocol-cli.c
    branches/glitch-free/src/pulsecore/protocol-esound.c
    branches/glitch-free/src/pulsecore/protocol-http.c
    branches/glitch-free/src/pulsecore/protocol-native.c
    branches/glitch-free/src/pulsecore/protocol-simple.c
    branches/glitch-free/src/pulsecore/sink-input.c
    branches/glitch-free/src/pulsecore/sink-input.h
    branches/glitch-free/src/pulsecore/sink.c
    branches/glitch-free/src/pulsecore/sink.h
    branches/glitch-free/src/pulsecore/sound-file-stream.c
    branches/glitch-free/src/pulsecore/source-output.c
    branches/glitch-free/src/pulsecore/source-output.h
    branches/glitch-free/src/pulsecore/source.c
    branches/glitch-free/src/pulsecore/source.h
    branches/glitch-free/src/utils/pacmd.c

Modified: branches/glitch-free/src/daemon/cmdline.c
URL: http://0pointer.de/cgi-bin/viewcvs.cgi/branches/glitch-free/src/daemon/cmdline.c?rev=2329&root=pulseaudio&r1=2328&r2=2329&view=diff
==============================================================================
--- branches/glitch-free/src/daemon/cmdline.c (original)
+++ branches/glitch-free/src/daemon/cmdline.c Thu May  1 21:51:05 2008
@@ -293,8 +293,7 @@
                 break;
 
             case 'n':
-                pa_xfree(conf->default_script_file);
-                conf->default_script_file = NULL;
+                conf->load_default_script_file = FALSE;
                 break;
 
             case ARG_LOG_TARGET:

Modified: branches/glitch-free/src/daemon/daemon-conf.c
URL: http://0pointer.de/cgi-bin/viewcvs.cgi/branches/glitch-free/src/daemon/daemon-conf.c?rev=2329&root=pulseaudio&r1=2328&r2=2329&view=diff
==============================================================================
--- branches/glitch-free/src/daemon/daemon-conf.c (original)
+++ branches/glitch-free/src/daemon/daemon-conf.c Thu May  1 21:51:05 2008
@@ -33,6 +33,7 @@
 #include <sched.h>
 
 #include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
 
 #include <pulsecore/core-error.h>
 #include <pulsecore/core-util.h>
@@ -45,6 +46,8 @@
 
 #define DEFAULT_SCRIPT_FILE PA_DEFAULT_CONFIG_DIR PA_PATH_SEP "default.pa"
 #define DEFAULT_SCRIPT_FILE_USER PA_PATH_SEP "default.pa"
+#define DEFAULT_SYSTEM_SCRIPT_FILE PA_DEFAULT_CONFIG_DIR PA_PATH_SEP "system.pa"
+
 #define DEFAULT_CONFIG_FILE PA_DEFAULT_CONFIG_DIR PA_PATH_SEP "daemon.conf"
 #define DEFAULT_CONFIG_FILE_USER PA_PATH_SEP "daemon.conf"
 
@@ -67,6 +70,7 @@
     .auto_log_target = 1,
     .script_commands = NULL,
     .dl_search_path = NULL,
+    .load_default_script_file = TRUE,
     .default_script_file = NULL,
     .log_target = PA_LOG_SYSLOG,
     .log_level = PA_LOG_NOTICE,
@@ -81,33 +85,42 @@
     .default_fragment_size_msec = 25,
     .default_sample_spec = { .format = PA_SAMPLE_S16NE, .rate = 44100, .channels = 2 }
 #ifdef HAVE_SYS_RESOURCE_H
-    , .rlimit_as = { .value = 0, .is_set = FALSE },
+   ,.rlimit_fsize = { .value = 0, .is_set = FALSE },
+    .rlimit_data = { .value = 0, .is_set = FALSE },
+    .rlimit_stack = { .value = 0, .is_set = FALSE },
     .rlimit_core = { .value = 0, .is_set = FALSE },
-    .rlimit_data = { .value = 0, .is_set = FALSE },
-    .rlimit_fsize = { .value = 0, .is_set = FALSE },
-    .rlimit_nofile = { .value = 256, .is_set = TRUE },
-    .rlimit_stack = { .value = 0, .is_set = FALSE }
+    .rlimit_rss = { .value = 0, .is_set = FALSE }
 #ifdef RLIMIT_NPROC
-    , .rlimit_nproc = { .value = 0, .is_set = FALSE }
-#endif
+   ,.rlimit_nproc = { .value = 0, .is_set = FALSE }
+#endif
+   ,.rlimit_nofile = { .value = 256, .is_set = TRUE }
 #ifdef RLIMIT_MEMLOCK
-    , .rlimit_memlock = { .value = 0, .is_set = FALSE }
+   ,.rlimit_memlock = { .value = 0, .is_set = FALSE }
+#endif
+   ,.rlimit_as = { .value = 0, .is_set = FALSE }
+#ifdef RLIMIT_LOCKS
+   ,.rlimit_locks = { .value = 0, .is_set = FALSE }
+#endif
+#ifdef RLIMIT_SIGPENDING
+   ,.rlimit_sigpending = { .value = 0, .is_set = FALSE }
+#endif
+#ifdef RLIMIT_MSGQUEUE
+   ,.rlimit_msgqueue = { .value = 0, .is_set = FALSE }
 #endif
 #ifdef RLIMIT_NICE
-    , .rlimit_nice = { .value = 31, .is_set = TRUE }     /* nice level of -11 */
+   ,.rlimit_nice = { .value = 31, .is_set = TRUE }     /* nice level of -11 */
 #endif
 #ifdef RLIMIT_RTPRIO
-    , .rlimit_rtprio = { .value = 9, .is_set = TRUE }      /* One below JACK's default for the server */
+   ,.rlimit_rtprio = { .value = 9, .is_set = TRUE }    /* One below JACK's default for the server */
+#endif
+#ifdef RLIMIT_RTTIME
+   ,.rlimit_rttime = { .value = PA_USEC_PER_SEC, .is_set = TRUE }
 #endif
 #endif
 };
 
 pa_daemon_conf* pa_daemon_conf_new(void) {
-    FILE *f;
     pa_daemon_conf *c = pa_xnewdup(pa_daemon_conf, &default_conf, 1);
-
-    if ((f = pa_open_config_file(DEFAULT_SCRIPT_FILE, DEFAULT_SCRIPT_FILE_USER, ENV_SCRIPT_FILE, &c->default_script_file, "r")))
-        fclose(f);
 
     c->dl_search_path = pa_xstrdup(PA_DLSEARCHPATH);
     return c;
@@ -412,24 +425,38 @@
         { "default-fragment-size-msec", parse_fragment_size_msec, NULL },
         { "nice-level",                 parse_nice_level,         NULL },
         { "disable-remixing",           pa_config_parse_bool,     NULL },
+        { "load-default-script-file",   pa_config_parse_bool,     NULL },
 #ifdef HAVE_SYS_RESOURCE_H
+        { "rlimit-fsize",               parse_rlimit,             NULL },
+        { "rlimit-data",                parse_rlimit,             NULL },
+        { "rlimit-stack",               parse_rlimit,             NULL },
+        { "rlimit-core",                parse_rlimit,             NULL },
+        { "rlimit-rss",                 parse_rlimit,             NULL },
+        { "rlimit-nofile",              parse_rlimit,             NULL },
         { "rlimit-as",                  parse_rlimit,             NULL },
-        { "rlimit-core",                parse_rlimit,             NULL },
-        { "rlimit-data",                parse_rlimit,             NULL },
-        { "rlimit-fsize",               parse_rlimit,             NULL },
-        { "rlimit-nofile",              parse_rlimit,             NULL },
-        { "rlimit-stack",               parse_rlimit,             NULL },
 #ifdef RLIMIT_NPROC
         { "rlimit-nproc",               parse_rlimit,             NULL },
 #endif
 #ifdef RLIMIT_MEMLOCK
         { "rlimit-memlock",             parse_rlimit,             NULL },
 #endif
+#ifdef RLIMIT_LOCKS
+        { "rlimit-locks",               parse_rlimit,             NULL },
+#endif
+#ifdef RLIMIT_SIGPENDING
+        { "rlimit-sigpending",          parse_rlimit,             NULL },
+#endif
+#ifdef RLIMIT_MSGQUEUE
+        { "rlimit-msgqueue",            parse_rlimit,             NULL },
+#endif
 #ifdef RLIMIT_NICE
         { "rlimit-nice",                parse_rlimit,             NULL },
 #endif
 #ifdef RLIMIT_RTPRIO
         { "rlimit-rtprio",              parse_rlimit,             NULL },
+#endif
+#ifdef RLIMIT_RTTIME
+        { "rlimit-rttime",              parse_rlimit,             NULL },
 #endif
 #endif
         { NULL,                         NULL,                     NULL },
@@ -461,33 +488,66 @@
     table[23].data = c;
     table[24].data = c;
     table[25].data = &c->disable_remixing;
+    table[26].data = &c->load_default_script_file;
 #ifdef HAVE_SYS_RESOURCE_H
-    table[26].data = &c->rlimit_as;
-    table[27].data = &c->rlimit_core;
+    table[27].data = &c->rlimit_fsize;
     table[28].data = &c->rlimit_data;
-    table[29].data = &c->rlimit_fsize;
-    table[30].data = &c->rlimit_nofile;
-    table[31].data = &c->rlimit_stack;
+    table[29].data = &c->rlimit_stack;
+    table[30].data = &c->rlimit_as;
+    table[31].data = &c->rlimit_core;
+    table[32].data = &c->rlimit_nofile;
+    table[33].data = &c->rlimit_as;
 #ifdef RLIMIT_NPROC
-    table[32].data = &c->rlimit_nproc;
-#endif
+    table[34].data = &c->rlimit_nproc;
+#endif
+
 #ifdef RLIMIT_MEMLOCK
 #ifndef RLIMIT_NPROC
 #error "Houston, we have a numbering problem!"
 #endif
-    table[33].data = &c->rlimit_memlock;
-#endif
-#ifdef RLIMIT_NICE
+    table[35].data = &c->rlimit_memlock;
+#endif
+
+#ifdef RLIMIT_LOCKS
 #ifndef RLIMIT_MEMLOCK
 #error "Houston, we have a numbering problem!"
 #endif
-    table[34].data = &c->rlimit_nice;
-#endif
+    table[36].data = &c->rlimit_locks;
+#endif
+
+#ifdef RLIMIT_SIGPENDING
+#ifndef RLIMIT_LOCKS
+#error "Houston, we have a numbering problem!"
+#endif
+    table[37].data = &c->rlimit_sigpending;
+#endif
+
+#ifdef RLIMIT_MSGQUEUE
+#ifndef RLIMIT_SIGPENDING
+#error "Houston, we have a numbering problem!"
+#endif
+    table[38].data = &c->rlimit_msgqueue;
+#endif
+
+#ifdef RLIMIT_NICE
+#ifndef RLIMIT_MSGQUEUE
+#error "Houston, we have a numbering problem!"
+#endif
+    table[39].data = &c->rlimit_nice;
+#endif
+
 #ifdef RLIMIT_RTPRIO
 #ifndef RLIMIT_NICE
 #error "Houston, we have a numbering problem!"
 #endif
-    table[35].data = &c->rlimit_rtprio;
+    table[40].data = &c->rlimit_rtprio;
+#endif
+
+#ifdef RLIMIT_RTTIME
+#ifndef RLIMIT_RTTIME
+#error "Houston, we have a numbering problem!"
+#endif
+    table[41].data = &c->rlimit_rttime;
 #endif
 #endif
 
@@ -496,10 +556,10 @@
 
     f = filename ?
         fopen(c->config_file = pa_xstrdup(filename), "r") :
-        pa_open_config_file(DEFAULT_CONFIG_FILE, DEFAULT_CONFIG_FILE_USER, ENV_CONFIG_FILE, &c->config_file, "r");
+        pa_open_config_file(DEFAULT_CONFIG_FILE, DEFAULT_CONFIG_FILE_USER, ENV_CONFIG_FILE, &c->config_file);
 
     if (!f && errno != ENOENT) {
-        pa_log_warn("Failed to open configuration file '%s': %s", c->config_file, pa_cstrerror(errno));
+        pa_log_warn("Failed to open configuration file: %s", pa_cstrerror(errno));
         goto finish;
     }
 
@@ -514,6 +574,7 @@
 
 int pa_daemon_conf_env(pa_daemon_conf *c) {
     char *e;
+    pa_assert(c);
 
     if ((e = getenv(ENV_DL_SEARCH_PATH))) {
         pa_xfree(c->dl_search_path);
@@ -526,6 +587,35 @@
 
     return 0;
 }
+
+const char *pa_daemon_conf_get_default_script_file(pa_daemon_conf *c) {
+    pa_assert(c);
+
+    if (!c->default_script_file) {
+        if (c->system_instance)
+            c->default_script_file = pa_find_config_file(DEFAULT_SYSTEM_SCRIPT_FILE, NULL, ENV_SCRIPT_FILE);
+        else
+            c->default_script_file = pa_find_config_file(DEFAULT_SCRIPT_FILE, DEFAULT_SCRIPT_FILE_USER, ENV_SCRIPT_FILE);
+    }
+
+    return c->default_script_file;
+}
+
+FILE *pa_daemon_conf_open_default_script_file(pa_daemon_conf *c) {
+    FILE *f;
+    pa_assert(c);
+
+    if (!c->default_script_file) {
+        if (c->system_instance)
+            f = pa_open_config_file(DEFAULT_SYSTEM_SCRIPT_FILE, NULL, ENV_SCRIPT_FILE, &c->default_script_file);
+        else
+            f = pa_open_config_file(DEFAULT_SCRIPT_FILE, DEFAULT_SCRIPT_FILE_USER, ENV_SCRIPT_FILE, &c->default_script_file);
+    } else
+        f = fopen(c->default_script_file, "r");
+
+    return f;
+}
+
 
 static const char* const log_level_to_string[] = {
     [PA_LOG_DEBUG] = "debug",
@@ -561,8 +651,9 @@
     pa_strbuf_printf(s, "exit-idle-time = %i\n", c->exit_idle_time);
     pa_strbuf_printf(s, "module-idle-time = %i\n", c->module_idle_time);
     pa_strbuf_printf(s, "scache-idle-time = %i\n", c->scache_idle_time);
-    pa_strbuf_printf(s, "dl-search-path = %s\n", c->dl_search_path ? c->dl_search_path : "");
-    pa_strbuf_printf(s, "default-script-file = %s\n", c->default_script_file);
+    pa_strbuf_printf(s, "dl-search-path = %s\n", pa_strempty(c->dl_search_path));
+    pa_strbuf_printf(s, "default-script-file = %s\n", pa_strempty(pa_daemon_conf_get_default_script_file(c)));
+    pa_strbuf_printf(s, "load-default-script-file = %s\n", pa_yes_no(c->load_default_script_file));
     pa_strbuf_printf(s, "log-target = %s\n", c->auto_log_target ? "auto" : (c->log_target == PA_LOG_SYSLOG ? "syslog" : "stderr"));
     pa_strbuf_printf(s, "log-level = %s\n", log_level_to_string[c->log_level]);
     pa_strbuf_printf(s, "resample-method = %s\n", pa_resample_method_to_string(c->resample_method));
@@ -573,23 +664,36 @@
     pa_strbuf_printf(s, "default-fragments = %u\n", c->default_n_fragments);
     pa_strbuf_printf(s, "default-fragment-size-msec = %u\n", c->default_fragment_size_msec);
 #ifdef HAVE_SYS_RESOURCE_H
+    pa_strbuf_printf(s, "rlimit-fsize = %li\n", c->rlimit_fsize.is_set ? (long int) c->rlimit_fsize.value : -1);
+    pa_strbuf_printf(s, "rlimit-data = %li\n", c->rlimit_data.is_set ? (long int) c->rlimit_data.value : -1);
+    pa_strbuf_printf(s, "rlimit-stack = %li\n", c->rlimit_stack.is_set ? (long int) c->rlimit_stack.value : -1);
+    pa_strbuf_printf(s, "rlimit-core = %li\n", c->rlimit_core.is_set ? (long int) c->rlimit_core.value : -1);
     pa_strbuf_printf(s, "rlimit-as = %li\n", c->rlimit_as.is_set ? (long int) c->rlimit_as.value : -1);
-    pa_strbuf_printf(s, "rlimit-core = %li\n", c->rlimit_core.is_set ? (long int) c->rlimit_core.value : -1);
-    pa_strbuf_printf(s, "rlimit-data = %li\n", c->rlimit_data.is_set ? (long int) c->rlimit_data.value : -1);
-    pa_strbuf_printf(s, "rlimit-fsize = %li\n", c->rlimit_fsize.is_set ? (long int) c->rlimit_fsize.value : -1);
-    pa_strbuf_printf(s, "rlimit-nofile = %li\n", c->rlimit_nofile.is_set ? (long int) c->rlimit_nofile.value : -1);
-    pa_strbuf_printf(s, "rlimit-stack = %li\n", c->rlimit_stack.is_set ? (long int) c->rlimit_stack.value : -1);
+    pa_strbuf_printf(s, "rlimit-rss = %li\n", c->rlimit_rss.is_set ? (long int) c->rlimit_rss.value : -1);
 #ifdef RLIMIT_NPROC
     pa_strbuf_printf(s, "rlimit-nproc = %li\n", c->rlimit_nproc.is_set ? (long int) c->rlimit_nproc.value : -1);
 #endif
+    pa_strbuf_printf(s, "rlimit-nofile = %li\n", c->rlimit_nofile.is_set ? (long int) c->rlimit_nofile.value : -1);
 #ifdef RLIMIT_MEMLOCK
     pa_strbuf_printf(s, "rlimit-memlock = %li\n", c->rlimit_memlock.is_set ? (long int) c->rlimit_memlock.value : -1);
 #endif
+#ifdef RLIMIT_LOCKS
+    pa_strbuf_printf(s, "rlimit-locks = %li\n", c->rlimit_locks.is_set ? (long int) c->rlimit_locks.value : -1);
+#endif
+#ifdef RLIMIT_SIGPENDING
+    pa_strbuf_printf(s, "rlimit-sigpending = %li\n", c->rlimit_sigpending.is_set ? (long int) c->rlimit_sigpending.value : -1);
+#endif
+#ifdef RLIMIT_MSGQUEUE
+    pa_strbuf_printf(s, "rlimit-msgqueue = %li\n", c->rlimit_msgqueue.is_set ? (long int) c->rlimit_msgqueue.value : -1);
+#endif
 #ifdef RLIMIT_NICE
-    pa_strbuf_printf(s, "rlimit-nice = %li\n", c->rlimit_memlock.is_set ? (long int) c->rlimit_nice.value : -1);
+    pa_strbuf_printf(s, "rlimit-nice = %li\n", c->rlimit_nice.is_set ? (long int) c->rlimit_nice.value : -1);
 #endif
 #ifdef RLIMIT_RTPRIO
-    pa_strbuf_printf(s, "rlimit-rtprio = %li\n", c->rlimit_memlock.is_set ? (long int) c->rlimit_rtprio.value : -1);
+    pa_strbuf_printf(s, "rlimit-rtprio = %li\n", c->rlimit_rtprio.is_set ? (long int) c->rlimit_rtprio.value : -1);
+#endif
+#ifdef RLIMIT_RTTIME
+    pa_strbuf_printf(s, "rlimit-rttime = %li\n", c->rlimit_rttime.is_set ? (long int) c->rlimit_rttime.value : -1);
 #endif
 #endif
 

Modified: branches/glitch-free/src/daemon/daemon-conf.h
URL: http://0pointer.de/cgi-bin/viewcvs.cgi/branches/glitch-free/src/daemon/daemon-conf.h?rev=2329&root=pulseaudio&r1=2328&r2=2329&view=diff
==============================================================================
--- branches/glitch-free/src/daemon/daemon-conf.h (original)
+++ branches/glitch-free/src/daemon/daemon-conf.h Thu May  1 21:51:05 2008
@@ -27,6 +27,7 @@
 
 #include <pulsecore/log.h>
 #include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
 #include <pulse/sample.h>
 
 #ifdef HAVE_SYS_RESOURCE_H
@@ -65,7 +66,8 @@
         system_instance,
         no_cpu_limit,
         disable_shm,
-        disable_remixing;
+        disable_remixing,
+        load_default_script_file;
     int exit_idle_time,
         module_idle_time,
         scache_idle_time,
@@ -79,18 +81,30 @@
     char *config_file;
 
 #ifdef HAVE_SYS_RESOURCE_H
-    pa_rlimit rlimit_as, rlimit_core, rlimit_data, rlimit_fsize, rlimit_nofile, rlimit_stack;
+    pa_rlimit rlimit_fsize, rlimit_data, rlimit_stack, rlimit_core, rlimit_rss, rlimit_nofile, rlimit_as;
 #ifdef RLIMIT_NPROC
     pa_rlimit rlimit_nproc;
 #endif
 #ifdef RLIMIT_MEMLOCK
     pa_rlimit rlimit_memlock;
 #endif
+#ifdef RLIMIT_LOCKS
+    pa_rlimit rlimit_locks;
+#endif
+#ifdef RLIMIT_SIGPENDING
+    pa_rlimit rlimit_sigpending;
+#endif
+#ifdef RLIMIT_MSGQUEUE
+    pa_rlimit rlimit_msgqueue;
+#endif
 #ifdef RLIMIT_NICE
     pa_rlimit rlimit_nice;
 #endif
 #ifdef RLIMIT_RTPRIO
     pa_rlimit rlimit_rtprio;
+#endif
+#ifdef RLIMIT_RTTIME
+    pa_rlimit rlimit_rttime;
 #endif
 #endif
 
@@ -121,4 +135,7 @@
 int pa_daemon_conf_set_log_level(pa_daemon_conf *c, const char *string);
 int pa_daemon_conf_set_resample_method(pa_daemon_conf *c, const char *string);
 
+const char *pa_daemon_conf_get_default_script_file(pa_daemon_conf *c);
+FILE *pa_daemon_conf_open_default_script_file(pa_daemon_conf *c);
+
 #endif

Modified: branches/glitch-free/src/daemon/daemon.conf.in
URL: http://0pointer.de/cgi-bin/viewcvs.cgi/branches/glitch-free/src/daemon/daemon.conf.in?rev=2329&root=pulseaudio&r1=2328&r2=2329&view=diff
==============================================================================
--- branches/glitch-free/src/daemon/daemon.conf.in (original)
+++ branches/glitch-free/src/daemon/daemon.conf.in Thu May  1 21:51:05 2008
@@ -40,6 +40,7 @@
 
 ; dl-search-path = (depends on architecture)
 
+; load-defaul-script-file = yes
 ; default-script-file = @PA_DEFAULT_CONFIG_FILE@
 
 ; log-target = auto
@@ -50,16 +51,21 @@
 
 ; no-cpu-limit = no
 
+; rlimit-fsize = -1
+; rlimit-data = -1
+; rlimit-stack = -1
+; rlimit-core = -1
 ; rlimit-as = -1
-; rlimit-core = -1
-; rlimit-data = -1
-; rlimit-fsize = -1
+; rlimit-rss = -1
+; rlimit-nproc = -1
 ; rlimit-nofile = 256
-; rlimit-stack = -1
-; rlimit-nproc = -1
 ; rlimit-memlock = -1
+; rlimit-locks = -1
+; rlimit-sigpending = -1
+; rlimit-msgqueue = -1
 ; rlimit-nice = 31
 ; rlimit-rtprio = 9
+; rlimit-rtttime = 1000000
 
 ; default-sample-format = s16le
 ; default-sample-rate = 44100

Modified: branches/glitch-free/src/daemon/main.c
URL: http://0pointer.de/cgi-bin/viewcvs.cgi/branches/glitch-free/src/daemon/main.c?rev=2329&root=pulseaudio&r1=2328&r2=2329&view=diff
==============================================================================
--- branches/glitch-free/src/daemon/main.c (original)
+++ branches/glitch-free/src/daemon/main.c Thu May  1 21:51:05 2008
@@ -115,7 +115,7 @@
     MSG msg;
     struct timeval tvnext;
 
-    while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
+    while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
         if (msg.message == WM_QUIT)
             raise(SIGTERM);
         else {
@@ -163,8 +163,6 @@
             break;
     }
 }
-
-#define set_env(key, value) putenv(pa_sprintf_malloc("%s=%s", (key), (value)))
 
 #if defined(HAVE_PWD_H) && defined(HAVE_GRP_H)
 
@@ -241,14 +239,14 @@
         return -1;
     }
 
-    set_env("USER", PA_SYSTEM_USER);
-    set_env("USERNAME", PA_SYSTEM_USER);
-    set_env("LOGNAME", PA_SYSTEM_USER);
-    set_env("HOME", PA_SYSTEM_RUNTIME_PATH);
+    pa_set_env("USER", PA_SYSTEM_USER);
+    pa_set_env("USERNAME", PA_SYSTEM_USER);
+    pa_set_env("LOGNAME", PA_SYSTEM_USER);
+    pa_set_env("HOME", PA_SYSTEM_RUNTIME_PATH);
 
     /* Relevant for pa_runtime_path() */
-    set_env("PULSE_RUNTIME_PATH", PA_SYSTEM_RUNTIME_PATH);
-    set_env("PULSE_CONFIG_PATH", PA_SYSTEM_RUNTIME_PATH);
+    pa_set_env("PULSE_RUNTIME_PATH", PA_SYSTEM_RUNTIME_PATH);
+    pa_set_env("PULSE_CONFIG_PATH", PA_SYSTEM_RUNTIME_PATH);
 
     pa_log_info("Successfully dropped root privileges.");
 
@@ -264,23 +262,6 @@
 
 #endif /* HAVE_PWD_H && HAVE_GRP_H */
 
-static int create_runtime_dir(void) {
-    char fn[PATH_MAX];
-
-    pa_runtime_path(NULL, fn, sizeof(fn));
-
-    /* This function is called only when the daemon is started in
-     * per-user mode. We create the runtime directory somewhere in
-     * /tmp/ with the current UID/GID */
-
-    if (pa_make_secure_dir(fn, 0700, (uid_t)-1, (gid_t)-1) < 0) {
-        pa_log("Failed to create '%s': %s", fn, pa_cstrerror(errno));
-        return -1;
-    }
-
-    return 0;
-}
-
 #ifdef HAVE_SYS_RESOURCE_H
 
 static int set_one_rlimit(const pa_rlimit *r, int resource, const char *name) {
@@ -293,7 +274,7 @@
     rl.rlim_cur = rl.rlim_max = r->value;
 
     if (setrlimit(resource, &rl) < 0) {
-        pa_log_warn("setrlimit(%s, (%u, %u)) failed: %s", name, (unsigned) r->value, (unsigned) r->value, pa_cstrerror(errno));
+        pa_log_info("setrlimit(%s, (%u, %u)) failed: %s", name, (unsigned) r->value, (unsigned) r->value, pa_cstrerror(errno));
         return -1;
     }
 
@@ -301,23 +282,36 @@
 }
 
 static void set_all_rlimits(const pa_daemon_conf *conf) {
-    set_one_rlimit(&conf->rlimit_as, RLIMIT_AS, "RLIMIT_AS");
+    set_one_rlimit(&conf->rlimit_fsize, RLIMIT_FSIZE, "RLIMIT_FSIZE");
+    set_one_rlimit(&conf->rlimit_data, RLIMIT_DATA, "RLIMIT_DATA");
+    set_one_rlimit(&conf->rlimit_stack, RLIMIT_STACK, "RLIMIT_STACK");
     set_one_rlimit(&conf->rlimit_core, RLIMIT_CORE, "RLIMIT_CORE");
-    set_one_rlimit(&conf->rlimit_data, RLIMIT_DATA, "RLIMIT_DATA");
-    set_one_rlimit(&conf->rlimit_fsize, RLIMIT_FSIZE, "RLIMIT_FSIZE");
-    set_one_rlimit(&conf->rlimit_nofile, RLIMIT_NOFILE, "RLIMIT_NOFILE");
-    set_one_rlimit(&conf->rlimit_stack, RLIMIT_STACK, "RLIMIT_STACK");
+    set_one_rlimit(&conf->rlimit_rss, RLIMIT_RSS, "RLIMIT_RSS");
 #ifdef RLIMIT_NPROC
     set_one_rlimit(&conf->rlimit_nproc, RLIMIT_NPROC, "RLIMIT_NPROC");
 #endif
+    set_one_rlimit(&conf->rlimit_nofile, RLIMIT_NOFILE, "RLIMIT_NOFILE");
 #ifdef RLIMIT_MEMLOCK
     set_one_rlimit(&conf->rlimit_memlock, RLIMIT_MEMLOCK, "RLIMIT_MEMLOCK");
 #endif
+    set_one_rlimit(&conf->rlimit_as, RLIMIT_AS, "RLIMIT_AS");
+#ifdef RLIMIT_LOCKS
+    set_one_rlimit(&conf->rlimit_locks, RLIMIT_LOCKS, "RLIMIT_LOCKS");
+#endif
+#ifdef RLIMIT_SIGPENDING
+    set_one_rlimit(&conf->rlimit_sigpending, RLIMIT_SIGPENDING, "RLIMIT_SIGPENDING");
+#endif
+#ifdef RLIMIT_MSGQUEUE
+    set_one_rlimit(&conf->rlimit_msgqueue, RLIMIT_MSGQUEUE, "RLIMIT_MSGQUEUE");
+#endif
 #ifdef RLIMIT_NICE
     set_one_rlimit(&conf->rlimit_nice, RLIMIT_NICE, "RLIMIT_NICE");
 #endif
 #ifdef RLIMIT_RTPRIO
     set_one_rlimit(&conf->rlimit_rtprio, RLIMIT_RTPRIO, "RLIMIT_RTPRIO");
+#endif
+#ifdef RLIMIT_RTTIME
+    set_one_rlimit(&conf->rlimit_rttime, RLIMIT_RTTIME, "RLIMIT_RTTIME");
 #endif
 }
 #endif
@@ -329,18 +323,19 @@
     pa_mainloop *mainloop = NULL;
     char *s;
     int r = 0, retval = 1, d = 0;
+    pa_bool_t suid_root, real_root;
+    pa_bool_t valid_pid_file = FALSE;
+    gid_t gid = (gid_t) -1;
+    pa_bool_t ltdl_init = FALSE;
+    int passed_fd = -1;
+    const char *e;
+#ifdef HAVE_FORK
     int daemon_pipe[2] = { -1, -1 };
-    pa_bool_t suid_root, real_root;
-    int valid_pid_file = 0;
-    gid_t gid = (gid_t) -1;
-    pa_bool_t allow_realtime, allow_high_priority;
-    pa_bool_t ltdl_init = FALSE;
-
+#endif
 #ifdef OS_IS_WIN32
-    pa_time_event *timer;
-    struct timeval tv;
-#endif
-
+    pa_time_event *win32_timer;
+    struct timeval win32_tv;
+#endif
 
 #if defined(__linux__) && defined(__OPTIMIZE__)
     /*
@@ -355,7 +350,7 @@
         /* We have to execute ourselves, because the libc caches the
          * value of $LD_BIND_NOW on initialization. */
 
-        putenv(pa_xstrdup("LD_BIND_NOW=1"));
+        pa_set_env("LD_BIND_NOW", "1");
         pa_assert_se(rp = pa_readlink("/proc/self/exe"));
         pa_assert_se(execv(rp, argv) == 0);
     }
@@ -385,6 +380,18 @@
          * is just too risky tun let PA run as root all the time. */
     }
 
+    if ((e = getenv("PULSE_PASSED_FD"))) {
+        passed_fd = atoi(e);
+
+        if (passed_fd <= 2)
+            passed_fd = -1;
+    }
+
+    pa_close_all(passed_fd, -1);
+
+    pa_reset_sigs(-1);
+    pa_unblock_sigs(-1);
+
     /* At this point, we are a normal user, possibly with CAP_NICE if
      * we were started SUID. If we are started as normal root, than we
      * still are normal root. */
@@ -410,34 +417,37 @@
     pa_log_set_target(conf->auto_log_target ? PA_LOG_STDERR : conf->log_target, NULL);
 
     if (suid_root) {
+        pa_bool_t allow_realtime, allow_high_priority;
+
         /* Ok, we're suid root, so let's better not enable high prio
          * or RT by default */
 
         allow_high_priority = allow_realtime = FALSE;
 
+        if (conf->high_priority || conf->realtime_scheduling)
+            if (pa_own_uid_in_group(PA_REALTIME_GROUP, &gid) > 0) {
+                pa_log_info("We're in the group '"PA_REALTIME_GROUP"', allowing real-time and high-priority scheduling.");
+                allow_realtime = conf->realtime_scheduling;
+                allow_high_priority = conf->high_priority;
+            }
+
 #ifdef HAVE_POLKIT
-        if (conf->high_priority) {
+        if (conf->high_priority && !allow_high_priority) {
             if (pa_polkit_check("org.pulseaudio.acquire-high-priority") > 0) {
-                pa_log_info("PolicyKit grants us acquire-high-priority privilige.");
+                pa_log_info("PolicyKit grants us acquire-high-priority privilege.");
                 allow_high_priority = TRUE;
             } else
-                pa_log_info("PolicyKit refuses acquire-high-priority privilige.");
-        }
-
-        if (conf->realtime_scheduling) {
+                pa_log_info("PolicyKit refuses acquire-high-priority privilege.");
+        }
+
+        if (conf->realtime_scheduling && !allow_realtime) {
             if (pa_polkit_check("org.pulseaudio.acquire-real-time") > 0) {
-                pa_log_info("PolicyKit grants us acquire-real-time privilige.");
+                pa_log_info("PolicyKit grants us acquire-real-time privilege.");
                 allow_realtime = TRUE;
             } else
-                pa_log_info("PolicyKit refuses acquire-real-time privilige.");
-        }
-#endif
-
-        if ((conf->high_priority || conf->realtime_scheduling) && pa_own_uid_in_group(PA_REALTIME_GROUP, &gid) > 0) {
-            pa_log_info("We're in the group '"PA_REALTIME_GROUP"', allowing real-time and high-priority scheduling.");
-            allow_realtime = conf->realtime_scheduling;
-            allow_high_priority = conf->high_priority;
-        }
+                pa_log_info("PolicyKit refuses acquire-real-time privilege.");
+        }
+#endif
 
         if (!allow_high_priority && !allow_realtime) {
 
@@ -445,32 +455,21 @@
              * let's give it up early */
 
             pa_drop_caps();
-            pa_drop_root();
-            suid_root = real_root = FALSE;
+            suid_root = FALSE;
 
             if (conf->high_priority || conf->realtime_scheduling)
                 pa_log_notice("Called SUID root and real-time/high-priority scheduling was requested in the configuration. However, we lack the necessary priviliges:\n"
                               "We are not in group '"PA_REALTIME_GROUP"' and PolicyKit refuse to grant us priviliges. Dropping SUID again.\n"
                               "For enabling real-time scheduling please acquire the appropriate PolicyKit priviliges, or become a member of '"PA_REALTIME_GROUP"', or increase the RLIMIT_NICE/RLIMIT_RTPRIO resource limits for this user.");
         }
-
-    } else {
-
-        /* OK, we're a normal user, so let's allow the user evrything
-         * he asks for, it's now the kernel's job to enforce limits,
-         * not ours anymore */
-        allow_high_priority = allow_realtime = TRUE;
-    }
-
-    if (conf->high_priority && !allow_high_priority) {
-        pa_log_info("High-priority scheduling enabled in configuration but now allowed by policy. Disabling forcibly.");
-        conf->high_priority = FALSE;
-    }
-
-    if (conf->realtime_scheduling && !allow_realtime) {
-        pa_log_info("Real-time scheduling enabled in configuration but now allowed by policy. Disabling forcibly.");
-        conf->realtime_scheduling = FALSE;
-    }
+    }
+
+#ifdef HAVE_SYS_RESOURCE_H
+    set_all_rlimits(conf);
+#endif
+
+    if (conf->high_priority && !pa_can_high_priority())
+        pa_log_warn("High-priority scheduling enabled in configuration but now allowed by policy.");
 
     if (conf->high_priority && conf->cmd == PA_CMD_DAEMON)
         pa_raise_priority(conf->nice_level);
@@ -482,27 +481,37 @@
 
 #ifdef RLIMIT_RTPRIO
         if (!drop) {
-
+            struct rlimit rl;
             /* At this point we still have CAP_NICE if we were loaded
              * SUID root. If possible let's acquire RLIMIT_RTPRIO
              * instead and give CAP_NICE up. */
 
-            const pa_rlimit rl = { 9, TRUE };
-
-            if (set_one_rlimit(&rl, RLIMIT_RTPRIO, "RLIMIT_RTPRIO") >= 0) {
-                pa_log_info("Successfully increased RLIMIT_RTPRIO, giving up CAP_NICE.");
-                drop = TRUE;
-            } else
-                pa_log_warn("RLIMIT_RTPRIO failed: %s", pa_cstrerror(errno));
+            if (getrlimit(RLIMIT_RTPRIO, &rl) >= 0) {
+
+                if (rl.rlim_cur >= 9)
+                    drop = TRUE;
+                else {
+                    rl.rlim_max = rl.rlim_cur = 9;
+
+                    if (setrlimit(RLIMIT_RTPRIO, &rl) < 0) {
+                        pa_log_info("Successfully increased RLIMIT_RTPRIO");
+                        drop = TRUE;
+                    } else
+                        pa_log_warn("RLIMIT_RTPRIO failed: %s", pa_cstrerror(errno));
+                }
+            }
         }
 #endif
 
         if (drop)  {
+            pa_log_info("Giving up CAP_NICE");
             pa_drop_caps();
-            pa_drop_root();
-            suid_root = real_root = FALSE;
-        }
-    }
+            suid_root = FALSE;
+        }
+    }
+
+    if (conf->realtime_scheduling && !pa_can_realtime())
+        pa_log_warn("Real-time scheduling enabled in configuration but now allowed by policy.");
 
     LTDL_SET_PRELOADED_SYMBOLS();
     pa_ltdl_init();
@@ -605,7 +614,7 @@
 
 #ifdef HAVE_FORK
         if (pipe(daemon_pipe) < 0) {
-            pa_log("Failed to create pipe.");
+            pa_log("pipe failed: %s", pa_cstrerror(errno));
             goto finish;
         }
 
@@ -615,20 +624,24 @@
         }
 
         if (child != 0) {
+            ssize_t n;
             /* Father */
 
             pa_assert_se(pa_close(daemon_pipe[1]) == 0);
             daemon_pipe[1] = -1;
 
-            if (pa_loop_read(daemon_pipe[0], &retval, sizeof(retval), NULL) != sizeof(retval)) {
-                pa_log("read() failed: %s", pa_cstrerror(errno));
+            if ((n = pa_loop_read(daemon_pipe[0], &retval, sizeof(retval), NULL)) != sizeof(retval)) {
+
+                if (n < 0)
+                    pa_log("read() failed: %s", pa_cstrerror(errno));
+
                 retval = 1;
             }
 
             if (retval)
-                pa_log("daemon startup failed.");
+                pa_log("Daemon startup failed.");
             else
-                pa_log_info("daemon startup successful.");
+                pa_log_info("Daemon startup successful.");
 
             goto finish;
         }
@@ -652,9 +665,9 @@
         pa_close(1);
         pa_close(2);
 
-        open("/dev/null", O_RDONLY);
-        open("/dev/null", O_WRONLY);
-        open("/dev/null", O_WRONLY);
+        pa_assert_se(open("/dev/null", O_RDONLY) == 0);
+        pa_assert_se(open("/dev/null", O_WRONLY) == 1);
+        pa_assert_se(open("/dev/null", O_WRONLY) == 2);
 #else
         FreeConsole();
 #endif
@@ -677,38 +690,31 @@
 #endif
     }
 
+    pa_set_env("PULSE_INTERNAL", "1");
     pa_assert_se(chdir("/") == 0);
     umask(0022);
 
-    if (conf->system_instance) {
+    if (conf->system_instance)
         if (change_user() < 0)
             goto finish;
-    } else if (create_runtime_dir() < 0)
-        goto finish;
+
+    pa_log_info("This is PulseAudio " PACKAGE_VERSION);
+    pa_log_info("Page size is %lu bytes", (unsigned long) PA_PAGE_SIZE);
+    pa_log_info("Using runtime directory %s.", s = pa_get_runtime_dir());
+    pa_xfree(s);
 
     if (conf->use_pid_file) {
         if (pa_pid_file_create() < 0) {
             pa_log("pa_pid_file_create() failed.");
-#ifdef HAVE_FORK
-            if (conf->daemonize)
-                pa_loop_write(daemon_pipe[1], &retval, sizeof(retval), NULL);
-#endif
-            goto finish;
-        }
-
-        valid_pid_file = 1;
-    }
-
-#ifdef HAVE_SYS_RESOURCE_H
-    set_all_rlimits(conf);
-#endif
+            goto finish;
+        }
+
+        valid_pid_file = TRUE;
+    }
 
 #ifdef SIGPIPE
     signal(SIGPIPE, SIG_IGN);
 #endif
-
-    pa_log_info("This is PulseAudio " PACKAGE_VERSION);
-    pa_log_info("Page size is %lu bytes", (unsigned long) PA_PAGE_SIZE);
 
     if (pa_rtclock_hrtimer())
         pa_log_info("Fresh high-resolution timers available! Bon appetit!");
@@ -738,11 +744,11 @@
     c->realtime_priority = conf->realtime_priority;
     c->realtime_scheduling = !!conf->realtime_scheduling;
     c->disable_remixing = !!conf->disable_remixing;
+    c->running_as_daemon = !!conf->daemonize;
 
     pa_assert_se(pa_signal_init(pa_mainloop_get_api(mainloop)) == 0);
     pa_signal_new(SIGINT, signal_callback, c);
     pa_signal_new(SIGTERM, signal_callback, c);
-
 #ifdef SIGUSR1
     pa_signal_new(SIGUSR1, signal_callback, c);
 #endif
@@ -754,11 +760,8 @@
 #endif
 
 #ifdef OS_IS_WIN32
-    pa_assert_se(timer = pa_mainloop_get_api(mainloop)->time_new(pa_mainloop_get_api(mainloop), pa_gettimeofday(&tv), message_cb, NULL));
-#endif
-
-    if (conf->daemonize)
-        c->running_as_daemon = TRUE;
+    win32_timer = pa_mainloop_get_api(mainloop)->time_new(pa_mainloop_get_api(mainloop), pa_gettimeofday(&win32_tv), message_cb, NULL);
+#endif
 
     oil_init();
 
@@ -766,11 +769,18 @@
         pa_assert_se(pa_cpu_limit_init(pa_mainloop_get_api(mainloop)) == 0);
 
     buf = pa_strbuf_new();
-    if (conf->default_script_file)
-        r = pa_cli_command_execute_file(c, conf->default_script_file, buf, &conf->fail);
+    if (conf->load_default_script_file) {
+        FILE *f;
+
+        if ((f = pa_daemon_conf_open_default_script_file(conf))) {
+            r = pa_cli_command_execute_file_stream(c, f, buf, &conf->fail);
+            fclose(f);
+        }
+    }
 
     if (r >= 0)
         r = pa_cli_command_execute(c, conf->script_commands, buf, &conf->fail);
+
     pa_log_error("%s", s = pa_strbuf_tostring_free(buf));
     pa_xfree(s);
 
@@ -780,53 +790,55 @@
 
     if (r < 0 && conf->fail) {
         pa_log("Failed to initialize daemon.");
+        goto finish;
+    }
+
+    if (!c->modules || pa_idxset_size(c->modules) == 0) {
+        pa_log("Daemon startup without any loaded modules, refusing to work.");
+        goto finish;
+    }
+
+    if (c->default_sink_name && !pa_namereg_get(c, c->default_sink_name, PA_NAMEREG_SINK, TRUE) && conf->fail) {
+        pa_log_error("Default sink name (%s) does not exist in name register.", c->default_sink_name);
+        goto finish;
+    }
+
+
 #ifdef HAVE_FORK
-        if (conf->daemonize)
-            pa_loop_write(daemon_pipe[1], &retval, sizeof(retval), NULL);
-#endif
-    } else if (!c->modules || pa_idxset_size(c->modules) == 0) {
-        pa_log("daemon startup without any loaded modules, refusing to work.");
-#ifdef HAVE_FORK
-        if (conf->daemonize)
-            pa_loop_write(daemon_pipe[1], &retval, sizeof(retval), NULL);
-#endif
-    } else {
-
-        retval = 0;
-
-        if (c->default_sink_name &&
-            pa_namereg_get(c, c->default_sink_name, PA_NAMEREG_SINK, 1) == NULL) {
-            pa_log_error("%s : Default sink name (%s) does not exist in name register.", __FILE__, c->default_sink_name);
-            retval = !!conf->fail;
-        }
-
-#ifdef HAVE_FORK
-        if (conf->daemonize)
-            pa_loop_write(daemon_pipe[1], &retval, sizeof(retval), NULL);
-#endif
-
-        if (!retval) {
-            pa_log_info("Daemon startup complete.");
-            if (pa_mainloop_run(mainloop, &retval) < 0)
-                retval = 1;
-            pa_log_info("Daemon shutdown initiated.");
-        }
-    }
+    if (conf->daemonize) {
+        int ok = 0;
+        pa_loop_write(daemon_pipe[1], &ok, sizeof(ok), NULL);
+    }
+#endif
+
+    pa_log_info("Daemon startup complete.");
+
+    retval = 0;
+    if (pa_mainloop_run(mainloop, &retval) < 0)
+        goto finish;
+
+    pa_log_info("Daemon shutdown initiated.");
+
+finish:
 
 #ifdef OS_IS_WIN32
-    pa_mainloop_get_api(mainloop)->time_free(timer);
-#endif
-
-    pa_core_unref(c);
+    if (win32_timer)
+        pa_mainloop_get_api(mainloop)->time_free(win32_timer);
+#endif
+
+    if (c) {
+        pa_core_unref(c);
+        pa_log_info("Daemon terminated.");
+    }
 
     if (!conf->no_cpu_limit)
         pa_cpu_limit_done();
 
     pa_signal_done();
 
-    pa_log_info("Daemon terminated.");
-
-finish:
+#ifdef HAVE_FORK
+    pa_close_pipe(daemon_pipe);
+#endif
 
     if (mainloop)
         pa_mainloop_free(mainloop);
@@ -837,8 +849,6 @@
     if (valid_pid_file)
         pa_pid_file_remove();
 
-    pa_close_pipe(daemon_pipe);
-
 #ifdef OS_IS_WIN32
     WSACleanup();
 #endif

Modified: branches/glitch-free/src/modules/alsa-util.c
URL: http://0pointer.de/cgi-bin/viewcvs.cgi/branches/glitch-free/src/modules/alsa-util.c?rev=2329&root=pulseaudio&r1=2328&r2=2329&view=diff
==============================================================================
--- branches/glitch-free/src/modules/alsa-util.c (original)
+++ branches/glitch-free/src/modules/alsa-util.c Thu May  1 21:51:05 2008
@@ -671,8 +671,24 @@
         *dev = d;
 
         if (ss->channels != map->channels) {
-            pa_assert_se(pa_channel_map_init_auto(map, ss->channels, PA_CHANNEL_MAP_AUX));
-            pa_channel_map_init_auto(map, ss->channels, PA_CHANNEL_MAP_ALSA);
+            if (!pa_channel_map_init_auto(map, ss->channels, PA_CHANNEL_MAP_ALSA)) {
+                unsigned c;
+                pa_channel_position_t pos;
+
+                pa_log_warn("Device has an unknown channel mapping. This is a limitation of ALSA. Synthesizing channel map.");
+
+                for (c = ss->channels; c > 0; c--)
+                    if (pa_channel_map_init_auto(map, c, PA_CHANNEL_MAP_ALSA))
+                        break;
+
+                pa_assert(c > 0);
+
+                pos = PA_CHANNEL_POSITION_AUX0;
+                for (; c < map->channels; c ++)
+                    map->map[c] = pos++;
+
+                map->channels = ss->channels;
+            }
         }
 
         return pcm_handle;

Modified: branches/glitch-free/src/modules/module-alsa-sink.c
URL: http://0pointer.de/cgi-bin/viewcvs.cgi/branches/glitch-free/src/modules/module-alsa-sink.c?rev=2329&root=pulseaudio&r1=2328&r2=2329&view=diff
==============================================================================
--- branches/glitch-free/src/modules/module-alsa-sink.c (original)
+++ branches/glitch-free/src/modules/module-alsa-sink.c Thu May  1 21:51:05 2008
@@ -665,7 +665,7 @@
             switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) {
 
                 case PA_SINK_SUSPENDED:
-                    pa_assert(PA_SINK_OPENED(u->sink->thread_info.state));
+                    pa_assert(PA_SINK_IS_OPENED(u->sink->thread_info.state));
 
                     if (suspend(u) < 0)
                         return -1;
@@ -836,9 +836,20 @@
 
 static void sink_update_requested_latency_cb(pa_sink *s) {
     struct userdata *u = s->userdata;
-    pa_assert(u);
-
+    snd_pcm_sframes_t before;
+    pa_assert(u);
+
+    before = u->hwbuf_unused_frames;
     update_sw_params(u);
+
+    /* Let's check whether we now use only a smaller part of the
+    buffer then before. If so, we need to make sure that subsequent
+    rewinds are relative to the new maxium fill level and not to the
+    current fill level. Thus, let's do a full rewind once, to clear
+    things up. */
+
+    if (u->hwbuf_unused_frames > before)
+        pa_sink_request_rewind(s, 0);
 }
 
 static int process_rewind(struct userdata *u) {
@@ -846,6 +857,7 @@
     size_t rewind_nbytes, unused_nbytes, limit_nbytes;
     pa_assert(u);
 
+    /* Figure out how much we shall rewind and reset the counter */
     rewind_nbytes = u->sink->thread_info.rewind_nbytes;
     u->sink->thread_info.rewind_nbytes = 0;
 
@@ -917,7 +929,7 @@
 /*         pa_log_debug("loop"); */
 
         /* Render some data and write it to the dsp */
-        if (PA_SINK_OPENED(u->sink->thread_info.state)) {
+        if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
             int work_done = 0;
 
             if (u->sink->thread_info.rewind_nbytes > 0)
@@ -982,7 +994,7 @@
             goto finish;
 
         /* Tell ALSA about this and process its response */
-        if (PA_SINK_OPENED(u->sink->thread_info.state)) {
+        if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
             struct pollfd *pollfd;
             unsigned short revents = 0;
             int err;

Modified: branches/glitch-free/src/modules/module-alsa-source.c
URL: http://0pointer.de/cgi-bin/viewcvs.cgi/branches/glitch-free/src/modules/module-alsa-source.c?rev=2329&root=pulseaudio&r1=2328&r2=2329&view=diff
==============================================================================
--- branches/glitch-free/src/modules/module-alsa-source.c (original)
+++ branches/glitch-free/src/modules/module-alsa-source.c Thu May  1 21:51:05 2008
@@ -621,7 +621,7 @@
             switch ((pa_source_state_t) PA_PTR_TO_UINT(data)) {
 
                 case PA_SOURCE_SUSPENDED:
-                    pa_assert(PA_SOURCE_OPENED(u->source->thread_info.state));
+                    pa_assert(PA_SOURCE_IS_OPENED(u->source->thread_info.state));
 
                     if (suspend(u) < 0)
                         return -1;
@@ -819,7 +819,7 @@
         pa_log_debug("loop");
 
         /* Read some data and pass it to the sources */
-        if (PA_SOURCE_OPENED(u->source->thread_info.state)) {
+        if (PA_SOURCE_IS_OPENED(u->source->thread_info.state)) {
             int work_done = 0;
 
             if (u->use_mmap)
@@ -867,7 +867,7 @@
             goto finish;
 
         /* Tell ALSA about this and process its response */
-        if (PA_SOURCE_OPENED(u->source->thread_info.state)) {
+        if (PA_SOURCE_IS_OPENED(u->source->thread_info.state)) {
             struct pollfd *pollfd;
             unsigned short revents = 0;
             int err;

Modified: branches/glitch-free/src/modules/module-combine.c
URL: http://0pointer.de/cgi-bin/viewcvs.cgi/branches/glitch-free/src/modules/module-combine.c?rev=2329&root=pulseaudio&r1=2328&r2=2329&view=diff
==============================================================================
--- branches/glitch-free/src/modules/module-combine.c (original)
+++ branches/glitch-free/src/modules/module-combine.c Thu May  1 21:51:05 2008
@@ -162,13 +162,13 @@
     if (!u->master)
         return;
 
-    if (!PA_SINK_OPENED(pa_sink_get_state(u->sink)))
+    if (!PA_SINK_IS_OPENED(pa_sink_get_state(u->sink)))
         return;
 
     for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx)) {
         pa_usec_t sink_latency;
 
-        if (!o->sink_input || !PA_SINK_OPENED(pa_sink_get_state(o->sink)))
+        if (!o->sink_input || !PA_SINK_IS_OPENED(pa_sink_get_state(o->sink)))
             continue;
 
         sink_latency = pa_sink_get_latency(o->sink);
@@ -194,7 +194,7 @@
     for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx)) {
         uint32_t r = base_rate;
 
-        if (!o->sink_input || !PA_SINK_OPENED(pa_sink_get_state(o->sink)))
+        if (!o->sink_input || !PA_SINK_IS_OPENED(pa_sink_get_state(o->sink)))
             continue;
 
         if (o->total_latency < target_latency)
@@ -258,7 +258,10 @@
             pa_rtclock_get(&now);
 
             if (!u->thread_info.in_null_mode || pa_timeval_cmp(&u->thread_info.timestamp, &now) <= 0) {
-                pa_sink_skip(u->sink, u->block_size);
+                pa_memchunk chunk;
+
+                pa_sink_render_full(u->sink, u->block_size, &chunk);
+                pa_memblock_unref(chunk.memblock);
 
                 if (!u->thread_info.in_null_mode)
                     u->thread_info.timestamp = now;
@@ -432,7 +435,7 @@
 
         case SINK_INPUT_MESSAGE_POST:
 
-            if (PA_SINK_OPENED(o->sink_input->sink->thread_info.state))
+            if (PA_SINK_IS_OPENED(o->sink_input->sink->thread_info.state))
                 pa_memblockq_push_align(o->memblockq, chunk);
             else
                 pa_memblockq_flush(o->memblockq);
@@ -471,7 +474,7 @@
 
         pa_sink_input_put(o->sink_input);
 
-        if (o->userdata->sink && PA_SINK_LINKED(pa_sink_get_state(o->userdata->sink)))
+        if (o->userdata->sink && PA_SINK_IS_LINKED(pa_sink_get_state(o->userdata->sink)))
             pa_asyncmsgq_send(o->userdata->sink->asyncmsgq, PA_MSGOBJECT(o->userdata->sink), SINK_MESSAGE_ADD_OUTPUT, o, 0, NULL);
     }
 }
@@ -504,7 +507,7 @@
 
         pa_sink_suspend(o->sink, FALSE);
 
-        if (PA_SINK_OPENED(pa_sink_get_state(o->sink)))
+        if (PA_SINK_IS_OPENED(pa_sink_get_state(o->sink)))
             enable_output(o);
     }
 
@@ -525,7 +528,7 @@
 
     switch (state) {
         case PA_SINK_SUSPENDED:
-            pa_assert(PA_SINK_OPENED(pa_sink_get_state(u->sink)));
+            pa_assert(PA_SINK_IS_OPENED(pa_sink_get_state(u->sink)));
 
             suspend(u);
             break;
@@ -697,7 +700,7 @@
     if (u->master &&
         u->master != except &&
         u->master->sink_input &&
-        PA_SINK_OPENED(pa_sink_get_state(u->master->sink))) {
+        PA_SINK_IS_OPENED(pa_sink_get_state(u->master->sink))) {
         update_master(u, u->master);
         return;
     }
@@ -705,7 +708,7 @@
     for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx))
         if (o != except &&
             o->sink_input &&
-            PA_SINK_OPENED(pa_sink_get_state(o->sink))) {
+            PA_SINK_IS_OPENED(pa_sink_get_state(o->sink))) {
             update_master(u, o);
             return;
         }
@@ -780,7 +783,7 @@
 
     pa_assert_se(pa_idxset_put(u->outputs, o, NULL) == 0);
 
-    if (u->sink && PA_SINK_LINKED(pa_sink_get_state(u->sink)))
+    if (u->sink && PA_SINK_IS_LINKED(pa_sink_get_state(u->sink)))
         pa_asyncmsgq_send(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_ADD_OUTPUT, o, 0, NULL);
     else {
         /* If the sink is not yet started, we need to do the activation ourselves */
@@ -792,10 +795,10 @@
                 o->outq);
     }
 
-    if (PA_SINK_OPENED(pa_sink_get_state(u->sink)) || pa_sink_get_state(u->sink) == PA_SINK_INIT) {
+    if (PA_SINK_IS_OPENED(pa_sink_get_state(u->sink)) || pa_sink_get_state(u->sink) == PA_SINK_INIT) {
         pa_sink_suspend(sink, FALSE);
 
-        if (PA_SINK_OPENED(pa_sink_get_state(sink)))
+        if (PA_SINK_IS_OPENED(pa_sink_get_state(sink)))
             if (output_create_sink_input(o) < 0)
                 goto fail;
     }
@@ -898,7 +901,7 @@
 
     state = pa_sink_get_state(s);
 
-    if (PA_SINK_OPENED(state) && PA_SINK_OPENED(pa_sink_get_state(u->sink)) && !o->sink_input) {
+    if (PA_SINK_IS_OPENED(state) && PA_SINK_IS_OPENED(pa_sink_get_state(u->sink)) && !o->sink_input) {
         enable_output(o);
         pick_master(u, NULL);
     }

Modified: branches/glitch-free/src/modules/module-default-device-restore.c
URL: http://0pointer.de/cgi-bin/viewcvs.cgi/branches/glitch-free/src/modules/module-default-device-restore.c?rev=2329&root=pulseaudio&r1=2328&r2=2329&view=diff
==============================================================================
--- branches/glitch-free/src/modules/module-default-device-restore.c (original)
+++ branches/glitch-free/src/modules/module-default-device-restore.c Thu May  1 21:51:05 2008
@@ -3,7 +3,7 @@
 /***
   This file is part of PulseAudio.
 
-  Copyright 2006 Lennart Poettering
+  Copyright 2006-2008 Lennart Poettering
 
   PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
@@ -25,10 +25,16 @@
 #include <config.h>
 #endif
 
+#include <errno.h>
+#include <stdio.h>
+
+#include <pulse/timeval.h>
+
 #include <pulsecore/core-util.h>
 #include <pulsecore/module.h>
 #include <pulsecore/log.h>
 #include <pulsecore/namereg.h>
+#include <pulsecore/core-error.h>
 
 #include "module-default-device-restore-symdef.h"
 
@@ -39,15 +45,24 @@
 
 #define DEFAULT_SINK_FILE "default-sink"
 #define DEFAULT_SOURCE_FILE "default-source"
-
-int pa__init(pa_module *m) {
+#define DEFAULT_SAVE_INTERVAL 5
+
+struct userdata {
+    pa_core *core;
+    pa_subscription *subscription;
+    pa_time_event *time_event;
+    char *sink_filename, *source_filename;
+    pa_bool_t modified;
+};
+
+static void load(struct userdata *u) {
     FILE *f;
 
     /* We never overwrite manually configured settings */
 
-    if (m->core->default_sink_name)
+    if (u->core->default_sink_name)
         pa_log_info("Manually configured default sink, not overwriting.");
-    else if ((f = pa_open_config_file(NULL, DEFAULT_SINK_FILE, NULL, NULL, "r"))) {
+    else if ((f = fopen(u->sink_filename, "r"))) {
         char ln[256] = "";
 
         fgets(ln, sizeof(ln)-1, f);
@@ -55,17 +70,19 @@
         fclose(f);
 
         if (!ln[0])
-            pa_log_debug("No previous default sink setting, ignoring.");
-        else if (pa_namereg_get(m->core, ln, PA_NAMEREG_SINK, 1)) {
-            pa_namereg_set_default(m->core, ln, PA_NAMEREG_SINK);
-            pa_log_debug("Restored default sink '%s'.", ln);
+            pa_log_info("No previous default sink setting, ignoring.");
+        else if (pa_namereg_get(u->core, ln, PA_NAMEREG_SINK, TRUE)) {
+            pa_namereg_set_default(u->core, ln, PA_NAMEREG_SINK);
+            pa_log_info("Restored default sink '%s'.", ln);
         } else
             pa_log_info("Saved default sink '%s' not existant, not restoring default sink setting.", ln);
-    }
-
-    if (m->core->default_source_name)
+
+    } else if (errno != ENOENT)
+        pa_log("Failed to load default sink: %s", pa_cstrerror(errno));
+
+    if (u->core->default_source_name)
         pa_log_info("Manually configured default source, not overwriting.");
-    else if ((f = pa_open_config_file(NULL, DEFAULT_SOURCE_FILE, NULL, NULL, "r"))) {
+    else if ((f = fopen(u->source_filename, "r"))) {
         char ln[256] = "";
 
         fgets(ln, sizeof(ln)-1, f);
@@ -73,29 +90,114 @@
         fclose(f);
 
         if (!ln[0])
-            pa_log_debug("No previous default source setting, ignoring.");
-        else if (pa_namereg_get(m->core, ln, PA_NAMEREG_SOURCE, 1)) {
-            pa_namereg_set_default(m->core, ln, PA_NAMEREG_SOURCE);
-            pa_log_debug("Restored default source '%s'.", ln);
+            pa_log_info("No previous default source setting, ignoring.");
+        else if (pa_namereg_get(u->core, ln, PA_NAMEREG_SOURCE, TRUE)) {
+            pa_namereg_set_default(u->core, ln, PA_NAMEREG_SOURCE);
+            pa_log_info("Restored default source '%s'.", ln);
         } else
             pa_log_info("Saved default source '%s' not existant, not restoring default source setting.", ln);
-    }
+
+    } else if (errno != ENOENT)
+            pa_log("Failed to load default sink: %s", pa_cstrerror(errno));
+}
+
+static void save(struct userdata *u) {
+    FILE *f;
+
+    if (!u->modified)
+        return;
+
+    if (u->sink_filename) {
+        if ((f = fopen(u->sink_filename, "w"))) {
+            const char *n = pa_namereg_get_default_sink_name(u->core);
+            fprintf(f, "%s\n", pa_strempty(n));
+            fclose(f);
+        } else
+            pa_log("Failed to save default sink: %s", pa_cstrerror(errno));
+    }
+
+    if (u->source_filename) {
+        if ((f = fopen(u->source_filename, "w"))) {
+            const char *n = pa_namereg_get_default_source_name(u->core);
+            fprintf(f, "%s\n", pa_strempty(n));
+            fclose(f);
+        } else
+            pa_log("Failed to save default source: %s", pa_cstrerror(errno));
+    }
+
+    u->modified = FALSE;
+}
+
+static void time_cb(pa_mainloop_api *a, pa_time_event *e, const struct timeval *tv, void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(u);
+    save(u);
+
+    if (u->time_event) {
+        u->core->mainloop->time_free(u->time_event);
+        u->time_event = NULL;
+    }
+}
+
+static void subscribe_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(u);
+
+    u->modified = TRUE;
+
+    if (!u->time_event) {
+        struct timeval tv;
+        pa_gettimeofday(&tv);
+        pa_timeval_add(&tv, DEFAULT_SAVE_INTERVAL*PA_USEC_PER_SEC);
+        u->time_event = u->core->mainloop->time_new(u->core->mainloop, &tv, time_cb, u);
+    }
+}
+
+int pa__init(pa_module *m) {
+    struct userdata *u;
+
+    pa_assert(u);
+
+    u = pa_xnew0(struct userdata, 1);
+    u->core = m->core;
+
+    if (!(u->sink_filename = pa_runtime_path(DEFAULT_SINK_FILE)))
+        goto fail;
+
+    if (!(u->source_filename = pa_runtime_path(DEFAULT_SOURCE_FILE)))
+        goto fail;
+
+    load(u);
+
+    u->subscription = pa_subscription_new(u->core, PA_SUBSCRIPTION_MASK_SERVER, subscribe_cb, u);
 
     return 0;
+
+fail:
+    pa__done(m);
+
+    return -1;
 }
 
 void pa__done(pa_module*m) {
-    FILE *f;
-
-    if ((f = pa_open_config_file(NULL, DEFAULT_SINK_FILE, NULL, NULL, "w"))) {
-        const char *n = pa_namereg_get_default_sink_name(m->core);
-        fprintf(f, "%s\n", n ? n : "");
-        fclose(f);
-    }
-
-    if ((f = pa_open_config_file(NULL, DEFAULT_SOURCE_FILE, NULL, NULL, "w"))) {
-        const char *n = pa_namereg_get_default_source_name(m->core);
-        fprintf(f, "%s\n", n ? n : "");
-        fclose(f);
-    }
-}
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    save(u);
+
+    if (u->subscription)
+        pa_subscription_free(u->subscription);
+
+    if (u->time_event)
+        m->core->mainloop->time_free(u->time_event);
+
+    pa_xfree(u->sink_filename);
+    pa_xfree(u->source_filename);
+    pa_xfree(u);
+}

Modified: branches/glitch-free/src/modules/module-device-restore.c
URL: http://0pointer.de/cgi-bin/viewcvs.cgi/branches/glitch-free/src/modules/module-device-restore.c?rev=2329&root=pulseaudio&r1=2328&r2=2329&view=diff
==============================================================================
--- branches/glitch-free/src/modules/module-device-restore.c (original)
+++ branches/glitch-free/src/modules/module-device-restore.c Thu May  1 21:51:05 2008
@@ -263,7 +263,7 @@
 int pa__init(pa_module*m) {
     pa_modargs *ma = NULL;
     struct userdata *u;
-    char *fname, *state_dir;
+    char *fname, *runtime_dir;
     char hn[256];
     pa_sink *sink;
     pa_source *source;
@@ -290,11 +290,11 @@
     if (!pa_get_host_name(hn, sizeof(hn)))
         goto fail;
 
-    if (!(state_dir = pa_get_state_dir()))
-        goto fail;
-
-    fname = pa_sprintf_malloc("%s/device-volumes.%s.gdbm", state_dir, hn);
-    pa_xfree(state_dir);
+    if (!(runtime_dir = pa_get_runtime_dir()))
+        goto fail;
+
+    fname = pa_sprintf_malloc("%s/device-volumes.%s.gdbm", runtime_dir, hn);
+    pa_xfree(runtime_dir);
 
     if (!(u->gdbm_file = gdbm_open(fname, 0, GDBM_WRCREAT, 0600, NULL))) {
         pa_log("Failed to open volume database '%s': %s", fname, gdbm_strerror(gdbm_errno));
@@ -316,6 +316,7 @@
 
 fail:
     pa__done(m);
+
     if (ma)
         pa_modargs_free(ma);
 

Modified: branches/glitch-free/src/modules/module-esound-sink.c
URL: http://0pointer.de/cgi-bin/viewcvs.cgi/branches/glitch-free/src/modules/module-esound-sink.c?rev=2329&root=pulseaudio&r1=2328&r2=2329&view=diff
==============================================================================
--- branches/glitch-free/src/modules/module-esound-sink.c (original)
+++ branches/glitch-free/src/modules/module-esound-sink.c Thu May  1 21:51:05 2008
@@ -143,7 +143,7 @@
             switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) {
 
                 case PA_SINK_SUSPENDED:
-                    pa_assert(PA_SINK_OPENED(u->sink->thread_info.state));
+                    pa_assert(PA_SINK_IS_OPENED(u->sink->thread_info.state));
 
                     pa_smoother_pause(u->smoother, pa_rtclock_usec());
                     break;
@@ -211,7 +211,7 @@
             pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
 
             /* Render some data and write it to the fifo */
-            if (PA_SINK_OPENED(u->sink->thread_info.state) && pollfd->revents) {
+            if (PA_SINK_IS_OPENED(u->sink->thread_info.state) && pollfd->revents) {
                 pa_usec_t usec;
                 int64_t n;
 
@@ -294,7 +294,7 @@
             }
 
             /* Hmm, nothing to do. Let's sleep */
-            pollfd->events = PA_SINK_OPENED(u->sink->thread_info.state)  ? POLLOUT : 0;
+            pollfd->events = PA_SINK_IS_OPENED(u->sink->thread_info.state)  ? POLLOUT : 0;
         }
 
         if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)

Modified: branches/glitch-free/src/modules/module-ladspa-sink.c
URL: http://0pointer.de/cgi-bin/viewcvs.cgi/branches/glitch-free/src/modules/module-ladspa-sink.c?rev=2329&root=pulseaudio&r1=2328&r2=2329&view=diff
==============================================================================
--- branches/glitch-free/src/modules/module-ladspa-sink.c (original)
+++ branches/glitch-free/src/modules/module-ladspa-sink.c Thu May  1 21:51:05 2008
@@ -102,10 +102,14 @@
         case PA_SINK_MESSAGE_GET_LATENCY: {
             pa_usec_t usec = 0;
 
+            /* Get the latency of the master sink */
             if (PA_MSGOBJECT(u->master)->process_msg(PA_MSGOBJECT(u->master), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
                 usec = 0;
 
-            *((pa_usec_t*) data) = usec /* + pa_bytes_to_usec(u->memchunk.length, &u->sink->sample_spec) */;
+            /* Add the latency internal to our sink input on top */
+            usec += pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->master->sample_spec);
+
+            *((pa_usec_t*) data) = usec;
             return 0;
         }
     }
@@ -120,7 +124,10 @@
     pa_sink_assert_ref(s);
     pa_assert_se(u = s->userdata);
 
-    if (PA_SINK_LINKED(state) && u->sink_input && PA_SINK_INPUT_LINKED(pa_sink_input_get_state(u->sink_input)))
+    if (PA_SINK_IS_LINKED(state) &&
+        u->sink_input &&
+        PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input)))
+
         pa_sink_input_cork(u->sink_input, state == PA_SINK_SUSPENDED);
 
     return 0;
@@ -134,7 +141,7 @@
     pa_assert_se(u = s->userdata);
 
     /* Just hand this one over to the master sink */
-    pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes, FALSE);
+    pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes, FALSE, FALSE);
 }
 
 /* Called from I/O thread context */
@@ -145,24 +152,9 @@
     pa_assert_se(u = s->userdata);
 
     /* Just hand this one over to the master sink */
-    u->sink_input->thread_info.requested_sink_latency = pa_sink_get_requested_latency_within_thread(s);
-    pa_sink_invalidate_requested_latency(u->master);
-}
-
-/* Called from I/O thread context */
-static int sink_input_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
-    struct userdata *u = PA_SINK_INPUT(o)->userdata;
-
-    switch (code) {
-        case PA_SINK_INPUT_MESSAGE_GET_LATENCY:
-            *((pa_usec_t*) data) = 0 /*pa_bytes_to_usec(u->memchunk.length, &u->sink_input->sample_spec)*/;
-
-            /* Fall through, the default handler will add in the extra
-             * latency added by the resampler */
-            break;
-    }
-
-    return pa_sink_input_process_msg(o, code, data, offset, chunk);
+    pa_sink_input_set_requested_latency_within_thread(
+            u->sink_input,
+            pa_sink_get_requested_latency_within_thread(s));
 }
 
 /* Called from I/O thread context */
@@ -192,20 +184,9 @@
     dst = (float*) pa_memblock_acquire(chunk->memblock);
 
     for (c = 0; c < u->channels; c++) {
-        unsigned j;
-        float *p, *q;
-
-        p = src + c;
-        q = u->input;
-        for (j = 0; j < n; j++, p += u->channels, q++)
-            *q = PA_CLAMP_UNLIKELY(*p, -1.0, 1.0);
-
+        pa_sample_clamp(PA_SAMPLE_FLOAT32NE, u->input, sizeof(float), src+c, u->channels*sizeof(float), n);
         u->descriptor->run(u->handle[c], n);
-
-        q = u->output;
-        p = dst + c;
-        for (j = 0; j < n; j++, q++, p += u->channels)
-            *p = PA_CLAMP_UNLIKELY(*q, -1.0, 1.0);
+        pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst+c, u->channels*sizeof(float), u->output, sizeof(float), n);
     }
 
     pa_memblock_release(tchunk.memblock);
@@ -245,6 +226,9 @@
     pa_assert_se(u = i->userdata);
 
     pa_sink_detach_within_thread(u->sink);
+
+    pa_sink_set_asyncmsgq(u->sink, NULL);
+    pa_sink_set_rtpoll(u->sink, NULL);
 }
 
 /* Called from I/O thread context */
@@ -648,7 +632,6 @@
     if (!u->sink_input)
         goto fail;
 
-    u->sink_input->parent.process_msg = sink_input_process_msg;
     u->sink_input->pop = sink_input_pop_cb;
     u->sink_input->process_rewind = sink_input_process_rewind_cb;
     u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;

Modified: branches/glitch-free/src/modules/module-match.c
URL: http://0pointer.de/cgi-bin/viewcvs.cgi/branches/glitch-free/src/modules/module-match.c?rev=2329&root=pulseaudio&r1=2328&r2=2329&view=diff
==============================================================================
--- branches/glitch-free/src/modules/module-match.c (original)
+++ branches/glitch-free/src/modules/module-match.c Thu May  1 21:51:05 2008
@@ -82,12 +82,14 @@
 
     pa_assert(u);
 
-    f = filename ?
-        fopen(fn = pa_xstrdup(filename), "r") :
-        pa_open_config_file(DEFAULT_MATCH_TABLE_FILE, DEFAULT_MATCH_TABLE_FILE_USER, NULL, &fn, "r");
+    if (filename)
+        f = fopen(fn = pa_xstrdup(filename), "r");
+    else
+        f = pa_open_config_file(DEFAULT_MATCH_TABLE_FILE, DEFAULT_MATCH_TABLE_FILE_USER, NULL, &fn);
 
     if (!f) {
-        pa_log("failed to open file '%s': %s", fn, pa_cstrerror(errno));
+        pa_xfree(fn);
+        pa_log("Failed to open file config file: %s", pa_cstrerror(errno));
         goto finish;
     }
 

Modified: branches/glitch-free/src/modules/module-null-sink.c
URL: http://0pointer.de/cgi-bin/viewcvs.cgi/branches/glitch-free/src/modules/module-null-sink.c?rev=2329&root=pulseaudio&r1=2328&r2=2329&view=diff
==============================================================================
--- branches/glitch-free/src/modules/module-null-sink.c (original)
+++ branches/glitch-free/src/modules/module-null-sink.c Thu May  1 21:51:05 2008
@@ -3,7 +3,7 @@
 /***
   This file is part of PulseAudio.
 
-  Copyright 2004-2006 Lennart Poettering
+  Copyright 2004-2008 Lennart Poettering
 
   PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
@@ -64,6 +64,7 @@
         "description=<description for the sink>");
 
 #define DEFAULT_SINK_NAME "null"
+#define MAX_LATENCY_USEC (PA_USEC_PER_SEC * 2)
 
 struct userdata {
     pa_core *core;
@@ -76,7 +77,8 @@
 
     size_t block_size;
 
-    struct timeval timestamp;
+    pa_usec_t block_usec;
+    pa_usec_t timestamp;
 };
 
 static const char* const valid_modargs[] = {
@@ -96,24 +98,91 @@
         case PA_SINK_MESSAGE_SET_STATE:
 
             if (PA_PTR_TO_UINT(data) == PA_SINK_RUNNING)
-                pa_rtclock_get(&u->timestamp);
+                u->timestamp = pa_rtclock_usec();
 
             break;
 
         case PA_SINK_MESSAGE_GET_LATENCY: {
-            struct timeval now;
-
-            pa_rtclock_get(&now);
-
-            if (pa_timeval_cmp(&u->timestamp, &now) > 0)
-                *((pa_usec_t*) data) = 0;
-            else
-                *((pa_usec_t*) data) = pa_timeval_diff(&u->timestamp, &now);
+            pa_usec_t now;
+
+            now = pa_rtclock_usec();
+            *((pa_usec_t*) data) = u->timestamp > now ? u->timestamp - now : 0;
+
+            return 0;
+        }
+    }
+
+    return pa_sink_process_msg(o, code, data, offset, chunk);
+}
+
+static void sink_update_requested_latency_cb(pa_sink *s) {
+    struct userdata *u;
+
+    pa_sink_assert_ref(s);
+    u = s->userdata;
+    pa_assert(u);
+
+    u->block_usec = pa_sink_get_requested_latency_within_thread(s);
+}
+
+static void process_rewind(struct userdata *u, pa_usec_t now) {
+    size_t rewind_nbytes, in_buffer;
+    pa_usec_t delay;
+
+    pa_assert(u);
+
+    /* Figure out how much we shall rewind and reset the counter */
+    rewind_nbytes = u->sink->thread_info.rewind_nbytes;
+    u->sink->thread_info.rewind_nbytes = 0;
+
+    pa_assert(rewind_nbytes > 0);
+    pa_log_debug("Requested to rewind %lu bytes.", (unsigned long) rewind_nbytes);
+
+    if (u->timestamp <= now)
+        return;
+
+    delay = u->timestamp - now;
+    in_buffer = pa_usec_to_bytes(delay, &u->sink->sample_spec);
+
+    if (in_buffer <= 0)
+        return;
+
+    if (rewind_nbytes > in_buffer)
+        rewind_nbytes = in_buffer;
+
+    pa_sink_process_rewind(u->sink, rewind_nbytes);
+    u->timestamp -= pa_bytes_to_usec(rewind_nbytes, &u->sink->sample_spec);
+
+    pa_log_debug("Rewound %lu bytes.", (unsigned long) rewind_nbytes);
+}
+
+static void process_render(struct userdata *u, pa_usec_t now) {
+    size_t nbytes;
+    size_t ate = 0;
+
+    /* This is the configured latency. Sink inputs connected to us
+    might not have a single frame more than this value queued. Hence:
+    at maximum read this many bytes from the sink inputs. */
+
+    nbytes = pa_usec_to_bytes(u->block_usec, &u->sink->sample_spec);
+
+    /* Fill the buffer up the the latency size */
+    while (u->timestamp < now + u->block_usec) {
+        pa_memchunk chunk;
+
+        pa_sink_render(u->sink, nbytes, &chunk);
+        pa_memblock_unref(chunk.memblock);
+
+        pa_log_debug("Ate %lu bytes.", (unsigned long) chunk.length);
+        u->timestamp += pa_bytes_to_usec(chunk.length, &u->sink->sample_spec);
+
+        ate += chunk.length;
+
+        if (ate >= nbytes)
             break;
-        }
-    }
-
-    return pa_sink_process_msg(o, code, data, offset, chunk);
+    }
+
+    pa_log_debug("Ate in sum %lu bytes (of %lu)", (unsigned long) ate, (unsigned long) nbytes);
 }
 
 static void thread_func(void *userdata) {
@@ -126,23 +195,24 @@
     pa_thread_mq_install(&u->thread_mq);
     pa_rtpoll_install(u->rtpoll);
 
-    pa_rtclock_get(&u->timestamp);
+    u->timestamp = pa_rtclock_usec();
 
     for (;;) {
         int ret;
 
         /* Render some data and drop it immediately */
         if (u->sink->thread_info.state == PA_SINK_RUNNING) {
-            struct timeval now;
-
-            pa_rtclock_get(&now);
-
-            if (pa_timeval_cmp(&u->timestamp, &now) <= 0) {
-                pa_sink_skip(u->sink, u->block_size);
-                pa_timeval_add(&u->timestamp, pa_bytes_to_usec(u->block_size, &u->sink->sample_spec));
-            }
-
-            pa_rtpoll_set_timer_absolute(u->rtpoll, &u->timestamp);
+            pa_usec_t now;
+
+            now = pa_rtclock_usec();
+
+            if (u->sink->thread_info.rewind_nbytes > 0)
+                process_rewind(u, now);
+
+            if (u->timestamp <= now)
+                process_render(u, now);
+
+            pa_rtpoll_set_timer_absolute(u->rtpoll, u->timestamp);
         } else
             pa_rtpoll_set_timer_disabled(u->rtpoll);
 
@@ -197,26 +267,26 @@
     pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME));
     pa_sink_new_data_set_sample_spec(&data, &ss);
     pa_sink_new_data_set_channel_map(&data, &map);
-    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, pa_modargs_get_value(ma, "description", "NULL sink"));
-
-    u->sink = pa_sink_new(m->core, &data, 0);
+    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, pa_modargs_get_value(ma, "description", "Null Output"));
+
+    u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY);
     pa_sink_new_data_done(&data);
 
     if (!u->sink) {
-        pa_log("Failed to create sink.");
+        pa_log("Failed to create sink object.");
         goto fail;
     }
 
     u->sink->parent.process_msg = sink_process_msg;
+    u->sink->update_requested_latency = sink_update_requested_latency_cb;
     u->sink->userdata = u;
-    u->sink->flags = PA_SINK_LATENCY;
 
     pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
     pa_sink_set_rtpoll(u->sink, u->rtpoll);
 
-    u->block_size = pa_bytes_per_second(&ss) / 20; /* 50 ms */
-    if (u->block_size <= 0)
-        u->block_size = pa_frame_size(&ss);
+    u->block_usec = u->sink->max_latency = MAX_LATENCY_USEC;
+
+    u->sink->thread_info.max_rewind = pa_usec_to_bytes(u->block_usec, &u->sink->sample_spec);
 
     if (!(u->thread = pa_thread_new(thread_func, u))) {
         pa_log("Failed to create thread.");

Modified: branches/glitch-free/src/modules/module-oss.c
URL: http://0pointer.de/cgi-bin/viewcvs.cgi/branches/glitch-free/src/modules/module-oss.c?rev=2329&root=pulseaudio&r1=2328&r2=2329&view=diff
==============================================================================
--- branches/glitch-free/src/modules/module-oss.c (original)
+++ branches/glitch-free/src/modules/module-oss.c Thu May  1 21:51:05 2008
@@ -161,10 +161,10 @@
 
      pa_log_debug("trigger");
 
-    if (u->source && PA_SOURCE_OPENED(u->source->thread_info.state))
+    if (u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state))
         enable_bits |= PCM_ENABLE_INPUT;
 
-    if (u->sink && PA_SINK_OPENED(u->sink->thread_info.state))
+    if (u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state))
         enable_bits |= PCM_ENABLE_OUTPUT;
 
     pa_log_debug("trigger: %i", enable_bits);
@@ -202,7 +202,7 @@
              * register the fd as ready.
              */
 
-            if (u->source && PA_SOURCE_OPENED(u->source->thread_info.state)) {
+            if (u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state)) {
                 uint8_t *buf = pa_xnew(uint8_t, u->in_fragment_size);
                 pa_read(u->fd, buf, u->in_fragment_size, NULL);
                 pa_xfree(buf);
@@ -641,7 +641,7 @@
             switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) {
 
                 case PA_SINK_SUSPENDED:
-                    pa_assert(PA_SINK_OPENED(u->sink->thread_info.state));
+                    pa_assert(PA_SINK_IS_OPENED(u->sink->thread_info.state));
 
                     if (!u->source || u->source_suspended) {
                         if (suspend(u) < 0)
@@ -658,7 +658,7 @@
 
                     if (u->sink->thread_info.state == PA_SINK_INIT) {
                         do_trigger = TRUE;
-                        quick = u->source && PA_SOURCE_OPENED(u->source->thread_info.state);
+                        quick = u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state);
                     }
 
                     if (u->sink->thread_info.state == PA_SINK_SUSPENDED) {
@@ -721,7 +721,7 @@
 
             switch ((pa_source_state_t) PA_PTR_TO_UINT(data)) {
                 case PA_SOURCE_SUSPENDED:
-                    pa_assert(PA_SOURCE_OPENED(u->source->thread_info.state));
+                    pa_assert(PA_SOURCE_IS_OPENED(u->source->thread_info.state));
 
                     if (!u->sink || u->sink_suspended) {
                         if (suspend(u) < 0)
@@ -738,7 +738,7 @@
 
                     if (u->source->thread_info.state == PA_SOURCE_INIT) {
                         do_trigger = TRUE;
-                        quick = u->sink && PA_SINK_OPENED(u->sink->thread_info.state);
+                        quick = u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state);
                     }
 
                     if (u->source->thread_info.state == PA_SOURCE_SUSPENDED) {
@@ -877,7 +877,7 @@
 
         /* Render some data and write it to the dsp */
 
-        if (u->sink && PA_SINK_OPENED(u->sink->thread_info.state) && ((revents & POLLOUT) || u->use_mmap || u->use_getospace)) {
+        if (u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state) && ((revents & POLLOUT) || u->use_mmap || u->use_getospace)) {
 
             if (u->use_mmap) {
 
@@ -985,7 +985,7 @@
 
         /* Try to read some data and pass it on to the source driver. */
 
-        if (u->source && PA_SOURCE_OPENED(u->source->thread_info.state) && ((revents & POLLIN) || u->use_mmap || u->use_getispace)) {
+        if (u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state) && ((revents & POLLIN) || u->use_mmap || u->use_getispace)) {
 
             if (u->use_mmap) {
 
@@ -1095,8 +1095,8 @@
 
             pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
             pollfd->events =
-                ((u->source && PA_SOURCE_OPENED(u->source->thread_info.state)) ? POLLIN : 0) |
-                ((u->sink && PA_SINK_OPENED(u->sink->thread_info.state)) ? POLLOUT : 0);
+                ((u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state)) ? POLLIN : 0) |
+                ((u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state)) ? POLLOUT : 0);
         }
 
         /* Hmm, nothing to do. Let's sleep */

Modified: branches/glitch-free/src/modules/module-protocol-stub.c
URL: http://0pointer.de/cgi-bin/viewcvs.cgi/branches/glitch-free/src/modules/module-protocol-stub.c?rev=2329&root=pulseaudio&r1=2328&r2=2329&view=diff
==============================================================================
--- branches/glitch-free/src/modules/module-protocol-stub.c (original)
+++ branches/glitch-free/src/modules/module-protocol-stub.c Thu May  1 21:51:05 2008
@@ -215,15 +215,6 @@
 #else
     pa_socket_server *s;
     int r;
-    char tmp[PATH_MAX];
-
-#if defined(USE_PROTOCOL_ESOUND)
-#if defined(USE_PER_USER_ESOUND_SOCKET)
-    char esdsocketpath[PATH_MAX];
-#else
-    const char esdsocketpath[] = "/tmp/.esd/socket";
-#endif
-#endif
 #endif
 
     pa_assert(m);
@@ -255,26 +246,27 @@
         goto fail;
 
     if (s_ipv4)
-        if (!(u->protocol_ipv4 = protocol_new(m->core, s_ipv4, m, ma)))
-            pa_socket_server_unref(s_ipv4);
-
+        u->protocol_ipv4 = protocol_new(m->core, s_ipv4, m, ma);
     if (s_ipv6)
-        if (!(u->protocol_ipv6 = protocol_new(m->core, s_ipv6, m, ma)))
-            pa_socket_server_unref(s_ipv6);
+        u->protocol_ipv6 = protocol_new(m->core, s_ipv6, m, ma);
 
     if (!u->protocol_ipv4 && !u->protocol_ipv6)
         goto fail;
 
+    if (s_ipv6)
+        pa_socket_server_unref(s_ipv6);
+    if (s_ipv6)
+        pa_socket_server_unref(s_ipv4);
+
 #else
 
 #if defined(USE_PROTOCOL_ESOUND)
 
 #if defined(USE_PER_USER_ESOUND_SOCKET)
-    snprintf(esdsocketpath, sizeof(esdsocketpath), "/tmp/.esd-%lu/socket", (unsigned long) getuid());
-#endif
-
-    pa_runtime_path(pa_modargs_get_value(ma, "socket", esdsocketpath), tmp, sizeof(tmp));
-    u->socket_path = pa_xstrdup(tmp);
+    u->socket_path = pa_sprintf_malloc("/tmp/.esd-%lu/socket", (unsigned long) getuid());
+#else
+    u->socket_path = pa_xstrdup("/tmp/.esd/socket");
+#endif
 
     /* This socket doesn't reside in our own runtime dir but in
      * /tmp/.esd/, hence we have to create the dir first */
@@ -285,23 +277,25 @@
     }
 
 #else
-    pa_runtime_path(pa_modargs_get_value(ma, "socket", UNIX_SOCKET), tmp, sizeof(tmp));
-    u->socket_path = pa_xstrdup(tmp);
-#endif
-
-    if ((r = pa_unix_socket_remove_stale(tmp)) < 0) {
-        pa_log("Failed to remove stale UNIX socket '%s': %s", tmp, pa_cstrerror(errno));
-        goto fail;
-    }
-
-    if (r)
-        pa_log("Removed stale UNIX socket '%s'.", tmp);
-
-    if (!(s = pa_socket_server_new_unix(m->core->mainloop, tmp)))
+    if (!(u->socket_path = pa_runtime_path(pa_modargs_get_value(ma, "socket", UNIX_SOCKET)))) {
+        pa_log("Failed to generate socket path.");
+        goto fail;
+    }
+#endif
+
+    if ((r = pa_unix_socket_remove_stale(u->socket_path)) < 0) {
+        pa_log("Failed to remove stale UNIX socket '%s': %s", u->socket_path, pa_cstrerror(errno));
+        goto fail;
+    } else if (r > 0)
+        pa_log_info("Removed stale UNIX socket '%s'.", u->socket_path);
+
+    if (!(s = pa_socket_server_new_unix(m->core->mainloop, u->socket_path)))
         goto fail;
 
     if (!(u->protocol_unix = protocol_new(m->core, s, m, ma)))
         goto fail;
+
+    pa_socket_server_unref(s);
 
 #endif
 
@@ -325,23 +319,21 @@
 #else
         if (u->protocol_unix)
             protocol_free(u->protocol_unix);
-
-        if (u->socket_path)
-            pa_xfree(u->socket_path);
+        pa_xfree(u->socket_path);
 #endif
 
         pa_xfree(u);
-    } else {
-#if defined(USE_TCP_SOCKETS)
-        if (s_ipv4)
-            pa_socket_server_unref(s_ipv4);
-        if (s_ipv6)
-            pa_socket_server_unref(s_ipv6);
-#else
-        if (s)
-            pa_socket_server_unref(s);
-#endif
-    }
+    }
+
+#if defined(USE_TCP_SOCKETS)
+    if (s_ipv4)
+        pa_socket_server_unref(s_ipv4);
+    if (s_ipv6)
+        pa_socket_server_unref(s_ipv6);
+#else
+    if (s)
+        pa_socket_server_unref(s);
+#endif
 
     goto finish;
 }
@@ -362,7 +354,7 @@
     if (u->protocol_unix)
         protocol_free(u->protocol_unix);
 
-#if defined(USE_PROTOCOL_ESOUND)
+#if defined(USE_PROTOCOL_ESOUND) && !defined(USE_PER_USER_ESOUND_SOCKET)
     if (u->socket_path) {
         char *p = pa_parent_dir(u->socket_path);
         rmdir(p);

Modified: branches/glitch-free/src/modules/module-remap-sink.c
URL: http://0pointer.de/cgi-bin/viewcvs.cgi/branches/glitch-free/src/modules/module-remap-sink.c?rev=2329&root=pulseaudio&r1=2328&r2=2329&view=diff
==============================================================================
--- branches/glitch-free/src/modules/module-remap-sink.c (original)
+++ branches/glitch-free/src/modules/module-remap-sink.c Thu May  1 21:51:05 2008
@@ -81,10 +81,14 @@
         case PA_SINK_MESSAGE_GET_LATENCY: {
             pa_usec_t usec = 0;
 
+            /* Get the latency of the master sink */
             if (PA_MSGOBJECT(u->master)->process_msg(PA_MSGOBJECT(u->master), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
                 usec = 0;
 
-            *((pa_usec_t*) data) = usec/* + pa_bytes_to_usec(u->memchunk.length, &u->sink->sample_spec)*/;
+            /* Add the latency internal to our sink input on top */
+            usec += pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->master->sample_spec);
+
+            *((pa_usec_t*) data) = usec;
             return 0;
         }
     }
@@ -99,7 +103,10 @@
     pa_sink_assert_ref(s);
     pa_assert_se(u = s->userdata);
 
-    if (PA_SINK_LINKED(state) && u->sink_input && PA_SINK_INPUT_LINKED(pa_sink_input_get_state(u->sink_input)))
+    if (PA_SINK_IS_LINKED(state) &&
+        u->sink_input &&
+        PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input)))
+
         pa_sink_input_cork(u->sink_input, state == PA_SINK_SUSPENDED);
 
     return 0;
@@ -112,7 +119,7 @@
     pa_sink_assert_ref(s);
     pa_assert_se(u = s->userdata);
 
-    pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes, FALSE);
+    pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes, FALSE, FALSE);
 }
 
 /* Called from I/O thread context */
@@ -123,24 +130,9 @@
     pa_assert_se(u = s->userdata);
 
     /* Just hand this one over to the master sink */
-    u->sink_input->thread_info.requested_sink_latency = pa_sink_get_requested_latency_within_thread(s);
-    pa_sink_invalidate_requested_latency(u->master);
-}
-
-/* Called from I/O thread context */
-static int sink_input_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
-    struct userdata *u = PA_SINK_INPUT(o)->userdata;
-
-    switch (code) {
-        case PA_SINK_INPUT_MESSAGE_GET_LATENCY:
-            *((pa_usec_t*) data) = 0; /*pa_bytes_to_usec(u->memchunk.length, &u->sink_input->sample_spec);*/
-
-            /* Fall through, the default handler will add in the extra
-             * latency added by the resampler */
-            break;
-    }
-
-    return pa_sink_input_process_msg(o, code, data, offset, chunk);
+    pa_sink_input_set_requested_latency_within_thread(
+            u->sink_input,
+            pa_sink_get_requested_latency_within_thread(s));
 }
 
 /* Called from I/O thread context */
@@ -152,7 +144,6 @@
     pa_assert_se(u = i->userdata);
 
     pa_sink_render(u->sink, nbytes, chunk);
-
     return 0;
 }
 
@@ -185,6 +176,9 @@
     pa_assert_se(u = i->userdata);
 
     pa_sink_detach_within_thread(u->sink);
+
+    pa_sink_set_asyncmsgq(u->sink, NULL);
+    pa_sink_set_rtpoll(u->sink, NULL);
 }
 
 /* Called from I/O thread context */
@@ -317,7 +311,6 @@
     if (!u->sink_input)
         goto fail;
 
-    u->sink_input->parent.process_msg = sink_input_process_msg;
     u->sink_input->pop = sink_input_pop_cb;
     u->sink_input->process_rewind = sink_input_process_rewind_cb;
     u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;

Modified: branches/glitch-free/src/modules/module-suspend-on-idle.c
URL: http://0pointer.de/cgi-bin/viewcvs.cgi/branches/glitch-free/src/modules/module-suspend-on-idle.c?rev=2329&root=pulseaudio&r1=2328&r2=2329&view=diff
==============================================================================
--- branches/glitch-free/src/modules/module-suspend-on-idle.c (original)
+++ branches/glitch-free/src/modules/module-suspend-on-idle.c Thu May  1 21:51:05 2008
@@ -317,7 +317,7 @@
 
         if (pa_sink_used_by(s) <= 0) {
 
-            if (PA_SINK_OPENED(state))
+            if (PA_SINK_IS_OPENED(state))
                 restart(d);
 
         }
@@ -328,7 +328,7 @@
 
         if (pa_source_used_by(s) <= 0) {
 
-            if (PA_SOURCE_OPENED(state))
+            if (PA_SOURCE_IS_OPENED(state))
                 restart(d);
         }
     }

Modified: branches/glitch-free/src/modules/module-tunnel.c
URL: http://0pointer.de/cgi-bin/viewcvs.cgi/branches/glitch-free/src/modules/module-tunnel.c?rev=2329&root=pulseaudio&r1=2328&r2=2329&view=diff
==============================================================================
--- branches/glitch-free/src/modules/module-tunnel.c (original)
+++ branches/glitch-free/src/modules/module-tunnel.c Thu May  1 21:51:05 2008
@@ -303,7 +303,7 @@
 
             /* First, change the state, because otherwide pa_sink_render() would fail */
             if ((r = pa_sink_process_msg(o, code, data, offset, chunk)) >= 0)
-                if (PA_SINK_OPENED((pa_sink_state_t) PA_PTR_TO_UINT(data)))
+                if (PA_SINK_IS_OPENED((pa_sink_state_t) PA_PTR_TO_UINT(data)))
                     send_data(u);
 
             return r;
@@ -314,7 +314,7 @@
             pa_assert(offset > 0);
             u->requested_bytes += (size_t) offset;
 
-            if (PA_SINK_OPENED(u->sink->thread_info.state))
+            if (PA_SINK_IS_OPENED(u->sink->thread_info.state))
                 send_data(u);
 
             return 0;
@@ -343,7 +343,7 @@
     switch ((pa_sink_state_t) state) {
 
         case PA_SINK_SUSPENDED:
-            pa_assert(PA_SINK_OPENED(s->state));
+            pa_assert(PA_SINK_IS_OPENED(s->state));
             stream_cork(u, TRUE);
             break;
 
@@ -369,7 +369,7 @@
     switch (code) {
         case SOURCE_MESSAGE_POST:
 
-            if (PA_SOURCE_OPENED(u->source->thread_info.state))
+            if (PA_SOURCE_IS_OPENED(u->source->thread_info.state))
                 pa_source_post(u->source, chunk);
             return 0;
     }
@@ -385,7 +385,7 @@
     switch ((pa_source_state_t) state) {
 
         case PA_SOURCE_SUSPENDED:
-            pa_assert(PA_SOURCE_OPENED(s->state));
+            pa_assert(PA_SOURCE_IS_OPENED(s->state));
             stream_cork(u, TRUE);
             break;
 
@@ -1066,7 +1066,7 @@
     pa_tagstruct_putu32(reply, PA_INVALID_INDEX);
     pa_tagstruct_puts(reply, u->sink_name);
     pa_tagstruct_putu32(reply, u->maxlength);
-    pa_tagstruct_put_boolean(reply, !PA_SINK_OPENED(pa_sink_get_state(u->sink)));
+    pa_tagstruct_put_boolean(reply, !PA_SINK_IS_OPENED(pa_sink_get_state(u->sink)));
     pa_tagstruct_putu32(reply, u->tlength);
     pa_tagstruct_putu32(reply, u->prebuf);
     pa_tagstruct_putu32(reply, u->minreq);
@@ -1082,7 +1082,7 @@
     pa_tagstruct_putu32(reply, PA_INVALID_INDEX);
     pa_tagstruct_puts(reply, u->source_name);
     pa_tagstruct_putu32(reply, u->maxlength);
-    pa_tagstruct_put_boolean(reply, !PA_SOURCE_OPENED(pa_source_get_state(u->source)));
+    pa_tagstruct_put_boolean(reply, !PA_SOURCE_IS_OPENED(pa_source_get_state(u->source)));
     pa_tagstruct_putu32(reply, u->fragsize);
 #endif
 

Modified: branches/glitch-free/src/modules/module-volume-restore.c
URL: http://0pointer.de/cgi-bin/viewcvs.cgi/branches/glitch-free/src/modules/module-volume-restore.c?rev=2329&root=pulseaudio&r1=2328&r2=2329&view=diff
==============================================================================
--- branches/glitch-free/src/modules/module-volume-restore.c (original)
+++ branches/glitch-free/src/modules/module-volume-restore.c Thu May  1 21:51:05 2008
@@ -134,16 +134,12 @@
     char buf_name[256], buf_volume[256], buf_sink[256], buf_source[256];
     char *ln = buf_name;
 
-    f = u->table_file ?
-        fopen(u->table_file, "r") :
-        pa_open_config_file(NULL, DEFAULT_VOLUME_TABLE_FILE, NULL, &u->table_file, "r");
-
-    if (!f) {
+    if (!(f = fopen(u->table_file, "r"))) {
         if (errno == ENOENT) {
-            pa_log_info("starting with empty ruleset.");
+            pa_log_info("Starting with empty ruleset.");
             ret = 0;
         } else
-            pa_log("failed to open file '%s': %s", u->table_file, pa_cstrerror(errno));
+            pa_log("Failed to open file '%s': %s", u->table_file, pa_cstrerror(errno));
 
         goto finish;
     }
@@ -236,11 +232,7 @@
 
     pa_log_info("Saving rules...");
 
-    f = u->table_file ?
-        fopen(u->table_file, "w") :
-        pa_open_config_file(NULL, DEFAULT_VOLUME_TABLE_FILE, NULL, &u->table_file, "w");
-
-    if (!f) {
+    if (!(f = fopen(u->table_file, "w"))) {
         pa_log("Failed to open file '%s': %s", u->table_file, pa_cstrerror(errno));
         goto finish;
     }
@@ -496,7 +488,7 @@
     u = pa_xnew(struct userdata, 1);
     u->core = m->core;
     u->hashmap = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
-    u->table_file = pa_xstrdup(pa_modargs_get_value(ma, "table", NULL));
+    u->table_file = pa_runtime_path(pa_modargs_get_value(ma, "table", DEFAULT_VOLUME_TABLE_FILE));
     u->modified = FALSE;
     u->subscription = NULL;
     u->sink_input_new_hook_slot = u->sink_input_fixate_hook_slot = u->source_output_new_hook_slot = NULL;

Modified: branches/glitch-free/src/pulse/client-conf.c
URL: http://0pointer.de/cgi-bin/viewcvs.cgi/branches/glitch-free/src/pulse/client-conf.c?rev=2329&root=pulseaudio&r1=2328&r2=2329&view=diff
==============================================================================
--- branches/glitch-free/src/pulse/client-conf.c (original)
+++ branches/glitch-free/src/pulse/client-conf.c Thu May  1 21:51:05 2008
@@ -112,20 +112,26 @@
     table[6].data = &c->cookie_file;
     table[7].data = &c->disable_shm;
 
-    f = filename ?
-        fopen((fn = pa_xstrdup(filename)), "r") :
-        pa_open_config_file(DEFAULT_CLIENT_CONFIG_FILE, DEFAULT_CLIENT_CONFIG_FILE_USER, ENV_CLIENT_CONFIG_FILE, &fn, "r");
+    if (filename) {
 
-    if (!f && errno != EINTR) {
-        pa_log_warn("Failed to open configuration file '%s': %s", fn, pa_cstrerror(errno));
-        goto finish;
+        if (!(f = fopen(filename, "r"))) {
+            pa_log("Failed to open configuration file '%s': %s", fn, pa_cstrerror(errno));
+            goto finish;
+        }
+
+        fn = pa_xstrdup(fn);
+
+    } else {
+
+        if (!(f = pa_open_config_file(DEFAULT_CLIENT_CONFIG_FILE, DEFAULT_CLIENT_CONFIG_FILE_USER, ENV_CLIENT_CONFIG_FILE, &fn)))
+            if (errno != ENOENT)
+                goto finish;
     }
 
     r = f ? pa_config_parse(fn, f, table, NULL) : 0;
 
     if (!r)
         r = pa_client_conf_load_cookie(c);
-
 
 finish:
     pa_xfree(fn);

Modified: branches/glitch-free/src/pulse/context.c
URL: http://0pointer.de/cgi-bin/viewcvs.cgi/branches/glitch-free/src/pulse/context.c?rev=2329&root=pulseaudio&r1=2328&r2=2329&view=diff
==============================================================================
--- branches/glitch-free/src/pulse/context.c (original)
+++ branches/glitch-free/src/pulse/context.c Thu May  1 21:51:05 2008
@@ -3,7 +3,7 @@
 /***
   This file is part of PulseAudio.
 
-  Copyright 2004-2006 Lennart Poettering
+  Copyright 2004-2008 Lennart Poettering
   Copyright 2006 Pierre Ossman <ossman at cendio.se> for Cendio AB
 
   PulseAudio is free software; you can redistribute it and/or modify
@@ -93,6 +93,7 @@
     [PA_COMMAND_RECORD_STREAM_MOVED] = pa_command_stream_moved,
     [PA_COMMAND_PLAYBACK_STREAM_SUSPENDED] = pa_command_stream_suspended,
     [PA_COMMAND_RECORD_STREAM_SUSPENDED] = pa_command_stream_suspended,
+    [PA_COMMAND_STARTED] = pa_command_stream_started,
     [PA_COMMAND_SUBSCRIBE_EVENT] = pa_command_subscribe_event
 };
 
@@ -100,10 +101,12 @@
     pa_assert(c);
 
     if (c->autospawn_lock_fd >= 0) {
-        char lf[PATH_MAX];
-        pa_runtime_path(AUTOSPAWN_LOCK, lf, sizeof(lf));
-
+        char *lf;
+
+        lf = pa_runtime_path(AUTOSPAWN_LOCK);
         pa_unlock_lockfile(lf, c->autospawn_lock_fd);
+        pa_xfree(lf);
+
         c->autospawn_lock_fd = -1;
     }
 }
@@ -112,6 +115,16 @@
 
 pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name) {
     return pa_context_new_with_proplist(mainloop, name, NULL);
+}
+
+static void reset_callbacks(pa_context *c) {
+    pa_assert(c);
+
+    c->state_callback = NULL;
+    c->state_userdata = NULL;
+
+    c->subscribe_callback = NULL;
+    c->subscribe_userdata = NULL;
 }
 
 pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char *name, pa_proplist *p) {
@@ -146,18 +159,14 @@
     c->ctag = 0;
     c->csyncid = 0;
 
-    c->state_callback = NULL;
-    c->state_userdata = NULL;
-
-    c->subscribe_callback = NULL;
-    c->subscribe_userdata = NULL;
-
-    c->is_local = -1;
+    reset_callbacks(c);
+
+    c->is_local = FALSE;
     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;
+    c->do_autospawn = FALSE;
 
 #ifndef MSG_NOSIGNAL
 #ifdef SIGPIPE
@@ -186,25 +195,47 @@
     return c;
 }
 
-static void context_free(pa_context *c) {
-    pa_assert(c);
-
-    unlock_autospawn_lock_file(c);
+static void context_unlink(pa_context *c) {
+    pa_stream *s;
+
+    pa_assert(c);
+
+    s = c->streams ? pa_stream_ref(c->streams) : NULL;
+    while (s) {
+        pa_stream *n = s->next ? pa_stream_ref(s->next) : NULL;
+        pa_stream_set_state(s, c->state == PA_CONTEXT_FAILED ? PA_STREAM_FAILED : PA_STREAM_TERMINATED);
+        pa_stream_unref(s);
+        s = n;
+    }
 
     while (c->operations)
         pa_operation_cancel(c->operations);
 
-    while (c->streams)
-        pa_stream_set_state(c->streams, PA_STREAM_TERMINATED);
-
-    if (c->client)
-        pa_socket_client_unref(c->client);
-    if (c->pdispatch)
+    if (c->pdispatch) {
         pa_pdispatch_unref(c->pdispatch);
+        c->pdispatch = NULL;
+    }
+
     if (c->pstream) {
         pa_pstream_unlink(c->pstream);
         pa_pstream_unref(c->pstream);
-    }
+        c->pstream = NULL;
+    }
+
+    if (c->client) {
+        pa_socket_client_unref(c->client);
+        c->client = NULL;
+    }
+
+    reset_callbacks(c);
+}
+
+static void context_free(pa_context *c) {
+    pa_assert(c);
+
+    context_unlink(c);
+
+    unlock_autospawn_lock_file(c);
 
     if (c->record_streams)
         pa_dynarray_free(c->record_streams, NULL, NULL);
@@ -252,44 +283,14 @@
     pa_context_ref(c);
 
     c->state = st;
+
     if (c->state_callback)
         c->state_callback(c, c->state_userdata);
 
-    if (st == PA_CONTEXT_FAILED || st == PA_CONTEXT_TERMINATED) {
-        pa_stream *s;
-
-        s = c->streams ? pa_stream_ref(c->streams) : NULL;
-        while (s) {
-            pa_stream *n = s->next ? pa_stream_ref(s->next) : NULL;
-            pa_stream_set_state(s, st == PA_CONTEXT_FAILED ? PA_STREAM_FAILED : PA_STREAM_TERMINATED);
-            pa_stream_unref(s);
-            s = n;
-        }
-
-        if (c->pdispatch)
-            pa_pdispatch_unref(c->pdispatch);
-        c->pdispatch = NULL;
-
-        if (c->pstream) {
-            pa_pstream_unlink(c->pstream);
-            pa_pstream_unref(c->pstream);
-        }
-        c->pstream = NULL;
-
-        if (c->client)
-            pa_socket_client_unref(c->client);
-        c->client = NULL;
-    }
+    if (st == PA_CONTEXT_FAILED || st == PA_CONTEXT_TERMINATED)
+        context_unlink(c);
 
     pa_context_unref(c);
-}
-
-void pa_context_fail(pa_context *c, int error) {
-    pa_assert(c);
-    pa_assert(PA_REFCNT_VALUE(c) >= 1);
-
-    pa_context_set_error(c, error);
-    pa_context_set_state(c, PA_CONTEXT_FAILED);
 }
 
 int pa_context_set_error(pa_context *c, int error) {
@@ -300,6 +301,14 @@
         c->error = error;
 
     return error;
+}
+
+void pa_context_fail(pa_context *c, int error) {
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    pa_context_set_error(c, error);
+    pa_context_set_state(c, PA_CONTEXT_FAILED);
 }
 
 static void pstream_die_callback(pa_pstream *p, void *userdata) {
@@ -358,25 +367,41 @@
     pa_context_unref(c);
 }
 
-int pa_context_handle_error(pa_context *c, uint32_t command, pa_tagstruct *t) {
+int pa_context_handle_error(pa_context *c, uint32_t command, pa_tagstruct *t, pa_bool_t fail) {
+    uint32_t err;
     pa_assert(c);
     pa_assert(PA_REFCNT_VALUE(c) >= 1);
 
     if (command == PA_COMMAND_ERROR) {
         pa_assert(t);
 
-        if (pa_tagstruct_getu32(t, &c->error) < 0) {
+        if (pa_tagstruct_getu32(t, &err) < 0) {
             pa_context_fail(c, PA_ERR_PROTOCOL);
             return -1;
-
-        }
+        }
+
     } else if (command == PA_COMMAND_TIMEOUT)
-        c->error = PA_ERR_TIMEOUT;
+        err = PA_ERR_TIMEOUT;
     else {
         pa_context_fail(c, PA_ERR_PROTOCOL);
         return -1;
     }
 
+    if (err == PA_OK) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        return -1;
+    }
+
+    if (err >= PA_ERR_MAX)
+        err = PA_ERR_UNKNOWN;
+
+    if (fail) {
+        pa_context_fail(c, err);
+        return -1;
+    }
+
+    pa_context_set_error(c, err);
+
     return 0;
 }
 
@@ -390,11 +415,7 @@
     pa_context_ref(c);
 
     if (command != PA_COMMAND_REPLY) {
-
-        if (pa_context_handle_error(c, command, t) < 0)
-            pa_context_fail(c, PA_ERR_PROTOCOL);
-
-        pa_context_fail(c, c->error);
+        pa_context_handle_error(c, command, t, TRUE);
         goto finish;
     }
 
@@ -417,7 +438,7 @@
             /* Enable shared memory support if possible */
             if (c->version >= 10 &&
                 pa_mempool_is_shared(c->mempool) &&
-                c->is_local > 0) {
+                c->is_local) {
 
                 /* Only enable SHM if both sides are owned by the same
                  * user. This is a security measure because otherwise
@@ -486,7 +507,7 @@
     c->pdispatch = pa_pdispatch_new(c->mainloop, command_table, PA_COMMAND_MAX);
 
     if (!c->conf->cookie_valid)
-        pa_log_warn("No cookie loaded. Attempting to connect without.");
+        pa_log_info("No cookie loaded. Attempting to connect without.");
 
     t = pa_tagstruct_command(c, PA_COMMAND_AUTH, &tag);
     pa_tagstruct_putu32(t, PA_PROTOCOL_VERSION);
@@ -525,10 +546,13 @@
     int fds[2] = { -1, -1} ;
     pa_iochannel *io;
 
+    if (getuid() == 0)
+        return -1;
+
     pa_context_ref(c);
 
     if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) {
-        pa_log("socketpair(): %s", pa_cstrerror(errno));
+        pa_log_error("socketpair(): %s", pa_cstrerror(errno));
         pa_context_fail(c, PA_ERR_INTERNAL);
         goto fail;
     }
@@ -542,7 +566,7 @@
         c->spawn_api.prefork();
 
     if ((pid = fork()) < 0) {
-        pa_log("fork(): %s", pa_cstrerror(errno));
+        pa_log_error("fork(): %s", pa_cstrerror(errno));
         pa_context_fail(c, PA_ERR_INTERNAL);
 
         if (c->spawn_api.postfork)
@@ -557,9 +581,13 @@
 #define MAX_ARGS 64
         const char * argv[MAX_ARGS+1];
         int n;
-
-        /* Not required, since fds[0] has CLOEXEC enabled anyway */
-        pa_assert_se(pa_close(fds[0]) == 0);
+        char *f;
+
+        pa_close_all(fds[1], -1);
+
+        f = pa_sprintf_malloc("%i", fds[1]);
+        pa_set_env("PULSE_PASSED_FD", f);
+        pa_xfree(f);
 
         if (c->spawn_api.atfork)
             c->spawn_api.atfork();
@@ -591,6 +619,8 @@
     }
 
     /* Parent */
+
+    pa_assert_se(pa_close(fds[1]) == 0);
 
     r = waitpid(pid, &status, 0);
 
@@ -606,14 +636,12 @@
         goto fail;
     }
 
-    pa_assert_se(pa_close(fds[1]) == 0);
-
-    c->is_local = 1;
+    c->is_local = TRUE;
+
+    unlock_autospawn_lock_file(c);
 
     io = pa_iochannel_new(c->mainloop, fds[0], fds[0]);
-
     setup_context(c, io);
-    unlock_autospawn_lock_file(c);
 
     pa_context_unref(c);
 
@@ -665,7 +693,7 @@
         if (!(c->client = pa_socket_client_new_string(c->mainloop, u, PA_NATIVE_DEFAULT_PORT)))
             continue;
 
-        c->is_local = pa_socket_client_is_local(c->client);
+        c->is_local = !!pa_socket_client_is_local(c->client);
         pa_socket_client_set_callback(c->client, on_connection, c);
         break;
     }
@@ -680,6 +708,7 @@
 
 static void on_connection(pa_socket_client *client, pa_iochannel*io, void *userdata) {
     pa_context *c = userdata;
+    int saved_errno = errno;
 
     pa_assert(client);
     pa_assert(c);
@@ -692,7 +721,9 @@
 
     if (!io) {
         /* Try the item in the list */
-        if (errno == ECONNREFUSED || errno == ETIMEDOUT || errno == EHOSTUNREACH) {
+        if (saved_errno == ECONNREFUSED ||
+            saved_errno == ETIMEDOUT ||
+            saved_errno == EHOSTUNREACH) {
             try_next_connection(c);
             goto finish;
         }
@@ -706,6 +737,25 @@
 
 finish:
     pa_context_unref(c);
+}
+
+
+static char *get_legacy_runtime_dir(void) {
+    char *p, u[128];
+    struct stat st;
+
+    if (!pa_get_user_name(u, sizeof(u)))
+        return NULL;
+
+    p = pa_sprintf_malloc("/tmp/pulse-%s", u);
+
+    if (stat(p, &st) < 0)
+        return NULL;
+
+    if (st.st_uid != getuid())
+        return NULL;
+
+    return p;
 }
 
 int pa_context_connect(
@@ -736,8 +786,8 @@
             goto finish;
         }
     } else {
-        char *d;
-        char ufn[PATH_MAX];
+        char *d, *ufn;
+        static char *legacy_dir;
 
         /* Prepend in reverse order */
 
@@ -757,25 +807,34 @@
         c->server_list = pa_strlist_prepend(c->server_list, "tcp4:localhost");
 
         /* The system wide instance */
-        c->server_list = pa_strlist_prepend(c->server_list, PA_SYSTEM_RUNTIME_PATH "/" PA_NATIVE_DEFAULT_UNIX_SOCKET);
+        c->server_list = pa_strlist_prepend(c->server_list, PA_SYSTEM_RUNTIME_PATH PA_PATH_SEP PA_NATIVE_DEFAULT_UNIX_SOCKET);
+
+        /* The old per-user instance path. This is supported only to easy upgrades */
+        if ((legacy_dir = get_legacy_runtime_dir())) {
+            char *p = pa_sprintf_malloc("%s" PA_PATH_SEP PA_NATIVE_DEFAULT_UNIX_SOCKET, legacy_dir);
+            c->server_list = pa_strlist_prepend(c->server_list, p);
+            pa_xfree(p);
+            pa_xfree(legacy_dir);
+        }
 
         /* The per-user instance */
-        c->server_list = pa_strlist_prepend(c->server_list, pa_runtime_path(PA_NATIVE_DEFAULT_UNIX_SOCKET, ufn, sizeof(ufn)));
+        c->server_list = pa_strlist_prepend(c->server_list, ufn = pa_runtime_path(PA_NATIVE_DEFAULT_UNIX_SOCKET));
+        pa_xfree(ufn);
 
         /* Wrap the connection attempts in a single transaction for sane autospawn locking */
         if (!(flags & PA_CONTEXT_NOAUTOSPAWN) && c->conf->autospawn) {
-            char lf[PATH_MAX];
-
-            pa_runtime_path(AUTOSPAWN_LOCK, lf, sizeof(lf));
-            pa_make_secure_parent_dir(lf, 0700, (uid_t)-1, (gid_t)-1);
+            char *lf;
+
+            lf = pa_runtime_path(AUTOSPAWN_LOCK);
             pa_assert(c->autospawn_lock_fd <= 0);
             c->autospawn_lock_fd = pa_lock_lockfile(lf);
+            pa_xfree(lf);
 
             if (api)
                 c->spawn_api = *api;
-            c->do_autospawn = 1;
-        }
-
+
+            c->do_autospawn = TRUE;
+        }
     }
 
     pa_context_set_state(c, PA_CONTEXT_CONNECTING);
@@ -791,7 +850,8 @@
     pa_assert(c);
     pa_assert(PA_REFCNT_VALUE(c) >= 1);
 
-    pa_context_set_state(c, PA_CONTEXT_TERMINATED);
+    if (PA_CONTEXT_IS_GOOD(c->state))
+        pa_context_set_state(c, PA_CONTEXT_TERMINATED);
 }
 
 pa_context_state_t pa_context_get_state(pa_context *c) {
@@ -811,6 +871,9 @@
 void pa_context_set_state_callback(pa_context *c, pa_context_notify_cb_t cb, void *userdata) {
     pa_assert(c);
     pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    if (c->state == PA_CONTEXT_TERMINATED || c->state == PA_CONTEXT_FAILED)
+        return;
 
     c->state_callback = cb;
     c->state_userdata = userdata;
@@ -820,11 +883,7 @@
     pa_assert(c);
     pa_assert(PA_REFCNT_VALUE(c) >= 1);
 
-    PA_CHECK_VALIDITY(c,
-                      c->state == PA_CONTEXT_CONNECTING ||
-                      c->state == PA_CONTEXT_AUTHORIZING ||
-                      c->state == PA_CONTEXT_SETTING_NAME ||
-                      c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY(c, PA_CONTEXT_IS_GOOD(c->state), PA_ERR_BADSTATE);
 
     return (c->pstream && pa_pstream_is_pending(c->pstream)) ||
         (c->pdispatch && pa_pdispatch_is_pending(c->pdispatch)) ||
@@ -901,7 +960,7 @@
         goto finish;
 
     if (command != PA_COMMAND_REPLY) {
-        if (pa_context_handle_error(o->context, command, t) < 0)
+        if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
             goto finish;
 
         success = 0;
@@ -920,25 +979,6 @@
     pa_operation_unref(o);
 }
 
-pa_operation* pa_context_exit_daemon(pa_context *c, pa_context_success_cb_t cb, void *userdata) {
-    pa_tagstruct *t;
-    pa_operation *o;
-    uint32_t tag;
-
-    pa_assert(c);
-    pa_assert(PA_REFCNT_VALUE(c) >= 1);
-
-    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
-
-    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
-
-    t = pa_tagstruct_command(c, PA_COMMAND_EXIT, &tag);
-    pa_pstream_send_tagstruct(c->pstream, t);
-    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
-
-    return o;
-}
-
 pa_operation* pa_context_send_simple_command(pa_context *c, uint32_t command, pa_pdispatch_cb_t internal_cb, pa_operation_cb_t cb, void *userdata) {
     pa_tagstruct *t;
     pa_operation *o;
@@ -958,6 +998,13 @@
     return o;
 }
 
+pa_operation* pa_context_exit_daemon(pa_context *c, pa_context_success_cb_t cb, void *userdata) {
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    return pa_context_send_simple_command(c, PA_COMMAND_EXIT, pa_context_simple_ack_callback, (pa_operation_cb_t) cb, userdata);
+}
+
 pa_operation* pa_context_set_default_sink(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata) {
     pa_tagstruct *t;
     pa_operation *o;
@@ -969,7 +1016,6 @@
     PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
 
     o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
-
     t = pa_tagstruct_command(c, PA_COMMAND_SET_DEFAULT_SINK, &tag);
     pa_tagstruct_puts(t, name);
     pa_pstream_send_tagstruct(c->pstream, t);
@@ -989,7 +1035,6 @@
     PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
 
     o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
-
     t = pa_tagstruct_command(c, PA_COMMAND_SET_DEFAULT_SOURCE, &tag);
     pa_tagstruct_puts(t, name);
     pa_pstream_send_tagstruct(c->pstream, t);
@@ -1002,15 +1047,13 @@
     pa_assert(c);
     pa_assert(PA_REFCNT_VALUE(c) >= 1);
 
-    PA_CHECK_VALIDITY(c, c->is_local >= 0, PA_ERR_BADSTATE);
-
-    return c->is_local;
+    PA_CHECK_VALIDITY_RETURN_ANY(c, PA_CONTEXT_IS_GOOD(c->state), PA_ERR_BADSTATE, -1);
+
+    return !!c->is_local;
 }
 
 pa_operation* pa_context_set_name(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata) {
-    pa_tagstruct *t;
     pa_operation *o;
-    uint32_t tag;
 
     pa_assert(c);
     pa_assert(PA_REFCNT_VALUE(c) >= 1);
@@ -1020,11 +1063,14 @@
 
     if (c->version >= 13) {
         pa_proplist *p = pa_proplist_new();
+
         pa_proplist_sets(p, PA_PROP_APPLICATION_NAME, name);
         o = pa_context_proplist_update(c, PA_UPDATE_REPLACE, p, cb, userdata);
         pa_proplist_free(p);
-
     } else {
+        pa_tagstruct *t;
+        uint32_t tag;
+
         o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
         t = pa_tagstruct_command(c, PA_COMMAND_SET_CLIENT_NAME, &tag);
         pa_tagstruct_puts(t, name);
@@ -1062,7 +1108,7 @@
     pa_assert(c);
     pa_assert(PA_REFCNT_VALUE(c) >= 1);
 
-    PA_CHECK_VALIDITY_RETURN_ANY(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE, PA_INVALID_INDEX);
+    PA_CHECK_VALIDITY_RETURN_ANY(c, PA_CONTEXT_IS_GOOD(c->state), PA_ERR_BADSTATE, PA_INVALID_INDEX);
 
     return c->version;
 }

Modified: branches/glitch-free/src/pulse/def.h
URL: http://0pointer.de/cgi-bin/viewcvs.cgi/branches/glitch-free/src/pulse/def.h?rev=2329&root=pulseaudio&r1=2328&r2=2329&view=diff
==============================================================================
--- branches/glitch-free/src/pulse/def.h (original)
+++ branches/glitch-free/src/pulse/def.h Thu May  1 21:51:05 2008
@@ -48,6 +48,15 @@
     PA_CONTEXT_TERMINATED      /**< The connection was terminated cleanly */
 } pa_context_state_t;
 
+/** Return non-zero if the passed state is one of the connected states */
+static inline int PA_CONTEXT_IS_GOOD(pa_context_state_t x) {
+    return
+        x == PA_CONTEXT_CONNECTING ||
+        x == PA_CONTEXT_AUTHORIZING ||
+        x == PA_CONTEXT_SETTING_NAME ||
+        x == PA_CONTEXT_READY;
+}
+
 /** The state of a stream */
 typedef enum pa_stream_state {
     PA_STREAM_UNCONNECTED, /**< The stream is not yet connected to any sink or source */
@@ -56,6 +65,13 @@
     PA_STREAM_FAILED,       /**< An error occured that made the stream invalid */
     PA_STREAM_TERMINATED    /**< The stream has been terminated cleanly */
 } pa_stream_state_t;
+
+/** Return non-zero if the passed state is one of the connected states */
+static inline int PA_STREAM_IS_GOOD(pa_stream_state_t x) {
+    return
+        x == PA_STREAM_CREATING ||
+        x == PA_STREAM_READY;
+}
 
 /** The state of an operation */
 typedef enum pa_operation_state {
@@ -296,6 +312,7 @@
     PA_ERR_VERSION,                /**< Incompatible protocol version */
     PA_ERR_TOOLARGE,               /**< Data too large */
     PA_ERR_NOTSUPPORTED,           /**< Operation not supported \since 0.9.5 */
+    PA_ERR_UNKNOWN,                /**< The error code was unknown to the client */
     PA_ERR_MAX                     /**< Not really an error but the first invalid error code */
 };
 
@@ -368,7 +385,15 @@
     pa_usec_t source_usec;    /**< Time in usecs a sample takes from being recorded to being delivered to the application. Only for record streams. */
     pa_usec_t transport_usec; /**< Estimated time in usecs a sample takes to be transferred to/from the daemon. For both playback and record streams. */
 
-    int playing;              /**< Non-zero when the stream is currently playing. Only for playback streams. */
+    int playing;              /**< Non-zero when the stream is
+                               * currently not underrun and data is
+                               * being passed on to the device. Only
+                               * for playback streams. This field does
+                               * not say whether the data is actually
+                               * already being played. To determine
+                               * this check whether since_underrun
+                               * (converted to usec) is larger than
+                               * sink_usec.*/
 
     int write_index_corrupt;  /**< Non-zero if write_index is not
                                * up-to-date because a local write
@@ -403,6 +428,14 @@
                                 * the sink. \since 0.9.11 */
     pa_usec_t configured_source_usec; /**< The static configured latency for
                                 * the source. \since 0.9.11 */
+
+    int64_t since_underrun;    /**< Bytes that were handed to the sink
+                                  since the last underrun happened, or
+                                  since playback started again after
+                                  the last underrun. playing will tell
+                                  you which case it is. \since
+                                  0.9.11 */
+
 } pa_timing_info;
 
 /** A structure for the spawn api. This may be used to integrate auto

Modified: branches/glitch-free/src/pulse/internal.h
URL: http://0pointer.de/cgi-bin/viewcvs.cgi/branches/glitch-free/src/pulse/internal.h?rev=2329&root=pulseaudio&r1=2328&r2=2329&view=diff
==============================================================================
--- branches/glitch-free/src/pulse/internal.h (original)
+++ branches/glitch-free/src/pulse/internal.h Thu May  1 21:51:05 2008
@@ -42,6 +42,7 @@
 #include <pulsecore/memblockq.h>
 #include <pulsecore/hashmap.h>
 #include <pulsecore/refcnt.h>
+#include <pulsecore/time-smoother.h>
 
 #include "client-conf.h"
 
@@ -69,14 +70,13 @@
 
     pa_context_notify_cb_t state_callback;
     void *state_userdata;
-
     pa_context_subscribe_cb_t subscribe_callback;
     void *subscribe_userdata;
 
     pa_mempool *mempool;
 
-    int is_local;
-    int do_autospawn;
+    pa_bool_t is_local;
+    pa_bool_t do_autospawn;
     int autospawn_lock_fd;
     pa_spawn_api spawn_api;
 
@@ -89,35 +89,39 @@
     uint32_t client_index;
 };
 
-#define PA_MAX_WRITE_INDEX_CORRECTIONS 10
+#define PA_MAX_WRITE_INDEX_CORRECTIONS 32
 
 typedef struct pa_index_correction {
     uint32_t tag;
-    int valid;
     int64_t value;
-    int absolute, corrupt;
+    pa_bool_t valid:1;
+    pa_bool_t absolute:1;
+    pa_bool_t corrupt:1;
 } pa_index_correction;
 
 struct pa_stream {
     PA_REFCNT_DECLARE;
+    PA_LLIST_FIELDS(pa_stream);
+
     pa_context *context;
     pa_mainloop_api *mainloop;
-    PA_LLIST_FIELDS(pa_stream);
-
-    pa_proplist *proplist;
-    pa_bool_t manual_buffer_attr;
-    pa_buffer_attr buffer_attr;
+
+    pa_stream_direction_t direction;
+    pa_stream_state_t state;
+    pa_stream_flags_t flags;
+
     pa_sample_spec sample_spec;
     pa_channel_map channel_map;
-    pa_stream_flags_t flags;
+
+    pa_proplist *proplist;
+
     uint32_t channel;
+    pa_bool_t channel_valid;
     uint32_t syncid;
-    int channel_valid;
     uint32_t stream_index;
-    pa_stream_direction_t direction;
-    pa_stream_state_t state;
 
     uint32_t requested_bytes;
+    pa_buffer_attr buffer_attr;
 
     uint32_t device_index;
     char *device_name;
@@ -127,11 +131,11 @@
     void *peek_data;
     pa_memblockq *record_memblockq;
 
-    int corked;
+    pa_bool_t corked;
 
     /* Store latest latency info */
     pa_timing_info timing_info;
-    int timing_info_valid;
+    pa_bool_t timing_info_valid;
 
     /* Use to make sure that time advances monotonically */
     pa_usec_t previous_time;
@@ -146,10 +150,9 @@
 
     /* Latency interpolation stuff */
     pa_time_event *auto_timing_update_event;
-    int auto_timing_update_requested;
-
-    pa_usec_t cached_time;
-    int cached_time_valid;
+    pa_bool_t auto_timing_update_requested;
+
+    pa_smoother *smoother;
 
     /* Callbacks */
     pa_stream_notify_cb_t state_callback;
@@ -168,6 +171,8 @@
     void *moved_userdata;
     pa_stream_notify_cb_t suspended_callback;
     void *suspended_userdata;
+    pa_stream_notify_cb_t started_callback;
+    void *started_userdata;
 };
 
 typedef void (*pa_operation_cb_t)(void);
@@ -193,7 +198,7 @@
 void pa_command_overflow_or_underflow(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
 void pa_command_stream_suspended(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
 void pa_command_stream_moved(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
-
+void pa_command_stream_started(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
 pa_operation *pa_operation_new(pa_context *c, pa_stream *s, pa_operation_cb_t callback, void *userdata);
 void pa_operation_done(pa_operation *o);
 
@@ -205,7 +210,7 @@
 void pa_context_fail(pa_context *c, int error);
 int pa_context_set_error(pa_context *c, int error);
 void pa_context_set_state(pa_context *c, pa_context_state_t st);
-int pa_context_handle_error(pa_context *c, uint32_t command, pa_tagstruct *t);
+int pa_context_handle_error(pa_context *c, uint32_t command, pa_tagstruct *t, pa_bool_t fail);
 pa_operation* pa_context_send_simple_command(pa_context *c, uint32_t command, void (*internal_callback)(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata), void (*cb)(void), void *userdata);
 
 void pa_stream_set_state(pa_stream *s, pa_stream_state_t st);

Modified: branches/glitch-free/src/pulse/introspect.c
URL: http://0pointer.de/cgi-bin/viewcvs.cgi/branches/glitch-free/src/pulse/introspect.c?rev=2329&root=pulseaudio&r1=2328&r2=2329&view=diff
==============================================================================
--- branches/glitch-free/src/pulse/introspect.c (original)
+++ branches/glitch-free/src/pulse/introspect.c Thu May  1 21:51:05 2008
@@ -52,7 +52,7 @@
         goto finish;
 
     if (command != PA_COMMAND_REPLY) {
-        if (pa_context_handle_error(o->context, command, t) < 0)
+        if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
             goto finish;
 
         p = NULL;
@@ -95,7 +95,7 @@
         goto finish;
 
     if (command != PA_COMMAND_REPLY) {
-        if (pa_context_handle_error(o->context, command, t) < 0)
+        if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
             goto finish;
 
         p = NULL;
@@ -140,7 +140,7 @@
         goto finish;
 
     if (command != PA_COMMAND_REPLY) {
-        if (pa_context_handle_error(o->context, command, t) < 0)
+        if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
             goto finish;
 
         eol = -1;
@@ -261,7 +261,7 @@
         goto finish;
 
     if (command != PA_COMMAND_REPLY) {
-        if (pa_context_handle_error(o->context, command, t) < 0)
+        if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
             goto finish;
 
         eol = -1;
@@ -382,7 +382,7 @@
         goto finish;
 
     if (command != PA_COMMAND_REPLY) {
-        if (pa_context_handle_error(o->context, command, t) < 0)
+        if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
             goto finish;
 
         eol = -1;
@@ -464,7 +464,7 @@
         goto finish;
 
     if (command != PA_COMMAND_REPLY) {
-        if (pa_context_handle_error(o->context, command, t) < 0)
+        if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
             goto finish;
 
         eol = -1;
@@ -543,7 +543,7 @@
         goto finish;
 
     if (command != PA_COMMAND_REPLY) {
-        if (pa_context_handle_error(o->context, command, t) < 0)
+        if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
             goto finish;
 
         eol = -1;
@@ -637,7 +637,7 @@
         goto finish;
 
     if (command != PA_COMMAND_REPLY) {
-        if (pa_context_handle_error(o->context, command, t) < 0)
+        if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
             goto finish;
 
         eol = -1;
@@ -967,7 +967,7 @@
         goto finish;
 
     if (command != PA_COMMAND_REPLY) {
-        if (pa_context_handle_error(o->context, command, t) < 0)
+        if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
             goto finish;
 
         eol = -1;
@@ -1111,7 +1111,7 @@
         goto finish;
 
     if (command != PA_COMMAND_REPLY) {
-        if (pa_context_handle_error(o->context, command, t) < 0)
+        if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
             goto finish;
 
         idx = PA_INVALID_INDEX;
@@ -1172,7 +1172,7 @@
         goto finish;
 
     if (command != PA_COMMAND_REPLY) {
-        if (pa_context_handle_error(o->context, command, t) < 0)
+        if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
             goto finish;
 
         eol = -1;

Modified: branches/glitch-free/src/pulse/scache.c
URL: http://0pointer.de/cgi-bin/viewcvs.cgi/branches/glitch-free/src/pulse/scache.c?rev=2329&root=pulseaudio&r1=2328&r2=2329&view=diff
==============================================================================
--- branches/glitch-free/src/pulse/scache.c (original)
+++ branches/glitch-free/src/pulse/scache.c Thu May  1 21:51:05 2008
@@ -108,7 +108,7 @@
         goto finish;
 
     if (command != PA_COMMAND_REPLY) {
-        if (pa_context_handle_error(o->context, command, t) < 0)
+        if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
             goto finish;
 
         success = 0;
@@ -141,7 +141,7 @@
         goto finish;
 
     if (command != PA_COMMAND_REPLY) {
-        if (pa_context_handle_error(o->context, command, t) < 0)
+        if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
             goto finish;
 
         idx = PA_INVALID_INDEX;

Modified: branches/glitch-free/src/pulse/stream.c
URL: http://0pointer.de/cgi-bin/viewcvs.cgi/branches/glitch-free/src/pulse/stream.c?rev=2329&root=pulseaudio&r1=2328&r2=2329&view=diff
==============================================================================
--- branches/glitch-free/src/pulse/stream.c (original)
+++ branches/glitch-free/src/pulse/stream.c Thu May  1 21:51:05 2008
@@ -38,36 +38,20 @@
 #include <pulsecore/log.h>
 #include <pulsecore/hashmap.h>
 #include <pulsecore/macro.h>
+#include <pulsecore/rtclock.h>
 
 #include "internal.h"
 
-#define LATENCY_IPOL_INTERVAL_USEC (100000L)
+#define LATENCY_IPOL_INTERVAL_USEC (500*PA_USEC_PER_MSEC)
+
+#define SMOOTHER_ADJUST_TIME (1000*PA_USEC_PER_MSEC)
+#define SMOOTHER_HISTORY_TIME (5000*PA_USEC_PER_MSEC)
 
 pa_stream *pa_stream_new(pa_context *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map) {
     return pa_stream_new_with_proplist(c, name, ss, map, NULL);
 }
 
-pa_stream *pa_stream_new_with_proplist(pa_context *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map, pa_proplist *p) {
-    pa_stream *s;
-    int i;
-    pa_channel_map tmap;
-
-    pa_assert(c);
-    pa_assert(PA_REFCNT_VALUE(c) >= 1);
-
-    PA_CHECK_VALIDITY_RETURN_NULL(c, ss && pa_sample_spec_valid(ss), PA_ERR_INVALID);
-    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 12 || (ss->format != PA_SAMPLE_S32LE || ss->format != PA_SAMPLE_S32NE), PA_ERR_NOTSUPPORTED);
-    PA_CHECK_VALIDITY_RETURN_NULL(c, !map || (pa_channel_map_valid(map) && map->channels == ss->channels), PA_ERR_INVALID);
-    PA_CHECK_VALIDITY_RETURN_NULL(c, name || pa_proplist_contains(p, PA_PROP_MEDIA_NAME), PA_ERR_INVALID);
-
-    if (!map)
-        PA_CHECK_VALIDITY_RETURN_NULL(c, map = pa_channel_map_init_auto(&tmap, ss->channels, PA_CHANNEL_MAP_DEFAULT), PA_ERR_INVALID);
-
-    s = pa_xnew(pa_stream, 1);
-    PA_REFCNT_INIT(s);
-    s->context = c;
-    s->mainloop = c->mainloop;
-
+static void reset_callbacks(pa_stream *s) {
     s->read_callback = NULL;
     s->read_userdata = NULL;
     s->write_callback = NULL;
@@ -84,54 +68,84 @@
     s->moved_userdata = NULL;
     s->suspended_callback = NULL;
     s->suspended_userdata = NULL;
+    s->started_callback = NULL;
+    s->started_userdata = NULL;
+}
+
+pa_stream *pa_stream_new_with_proplist(
+        pa_context *c,
+        const char *name,
+        const pa_sample_spec *ss,
+        const pa_channel_map *map,
+        pa_proplist *p) {
+
+    pa_stream *s;
+    int i;
+    pa_channel_map tmap;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, ss && pa_sample_spec_valid(ss), PA_ERR_INVALID);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 12 || (ss->format != PA_SAMPLE_S32LE || ss->format != PA_SAMPLE_S32NE), PA_ERR_NOTSUPPORTED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !map || (pa_channel_map_valid(map) && map->channels == ss->channels), PA_ERR_INVALID);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, name || (p && pa_proplist_contains(p, PA_PROP_MEDIA_NAME)), PA_ERR_INVALID);
+
+    if (!map)
+        PA_CHECK_VALIDITY_RETURN_NULL(c, map = pa_channel_map_init_auto(&tmap, ss->channels, PA_CHANNEL_MAP_DEFAULT), PA_ERR_INVALID);
+
+    s = pa_xnew(pa_stream, 1);
+    PA_REFCNT_INIT(s);
+    s->context = c;
+    s->mainloop = c->mainloop;
 
     s->direction = PA_STREAM_NODIRECTION;
+    s->state = PA_STREAM_UNCONNECTED;
+    s->flags = 0;
+
     s->sample_spec = *ss;
     s->channel_map = *map;
-    s->flags = 0;
 
     s->proplist = p ? pa_proplist_copy(p) : pa_proplist_new();
-
     if (name)
         pa_proplist_sets(s->proplist, PA_PROP_MEDIA_NAME, name);
 
     s->channel = 0;
-    s->channel_valid = 0;
+    s->channel_valid = FALSE;
     s->syncid = c->csyncid++;
     s->stream_index = PA_INVALID_INDEX;
+
     s->requested_bytes = 0;
-    s->state = PA_STREAM_UNCONNECTED;
-
-    s->manual_buffer_attr = FALSE;
     memset(&s->buffer_attr, 0, sizeof(s->buffer_attr));
 
     s->device_index = PA_INVALID_INDEX;
     s->device_name = NULL;
     s->suspended = FALSE;
 
-    s->peek_memchunk.index = 0;
-    s->peek_memchunk.length = 0;
-    s->peek_memchunk.memblock = NULL;
+    pa_memchunk_reset(&s->peek_memchunk);
     s->peek_data = NULL;
 
     s->record_memblockq = NULL;
 
-    s->previous_time = 0;
+    s->corked = FALSE;
+
     memset(&s->timing_info, 0, sizeof(s->timing_info));
     s->timing_info_valid = FALSE;
+
+    s->previous_time = 0;
+
     s->read_index_not_before = 0;
     s->write_index_not_before = 0;
-
     for (i = 0; i < PA_MAX_WRITE_INDEX_CORRECTIONS; i++)
         s->write_index_corrections[i].valid = 0;
     s->current_write_index_correction = 0;
 
-    s->corked = 0;
-
-    s->cached_time_valid = 0;
-
     s->auto_timing_update_event = NULL;
-    s->auto_timing_update_requested = 0;
+    s->auto_timing_update_requested = FALSE;
+
+    reset_callbacks(s);
+
+    s->smoother = NULL;
 
     /* Refcounting is strictly one-way: from the "bigger" to the "smaller" object. */
     PA_LLIST_PREPEND(pa_stream, c->streams, s);
@@ -140,15 +154,50 @@
     return s;
 }
 
-static void stream_free(pa_stream *s) {
-    pa_assert(s);
-    pa_assert(!s->context);
-    pa_assert(!s->channel_valid);
+static void stream_unlink(pa_stream *s) {
+    pa_operation *o, *n;
+    pa_assert(s);
+
+    if (!s->context)
+        return;
+
+    /* Detach from context */
+
+    /* Unref all operatio object that point to us */
+    for (o = s->context->operations; o; o = n) {
+        n = o->next;
+
+        if (o->stream == s)
+            pa_operation_cancel(o);
+    }
+
+    /* Drop all outstanding replies for this stream */
+    if (s->context->pdispatch)
+        pa_pdispatch_unregister_reply(s->context->pdispatch, s);
+
+    if (s->channel_valid) {
+        pa_dynarray_put((s->direction == PA_STREAM_PLAYBACK) ? s->context->playback_streams : s->context->record_streams, s->channel, NULL);
+        s->channel = 0;
+        s->channel_valid = FALSE;
+    }
+
+    PA_LLIST_REMOVE(pa_stream, s->context->streams, s);
+    pa_stream_unref(s);
+
+    s->context = NULL;
 
     if (s->auto_timing_update_event) {
         pa_assert(s->mainloop);
         s->mainloop->time_free(s->auto_timing_update_event);
     }
+
+    reset_callbacks(s);
+}
+
+static void stream_free(pa_stream *s) {
+    pa_assert(s);
+
+    stream_unlink(s);
 
     if (s->peek_memchunk.memblock) {
         if (s->peek_data)
@@ -162,6 +211,9 @@
     if (s->proplist)
         pa_proplist_free(s->proplist);
 
+    if (s->smoother)
+        pa_smoother_free(s->smoother);
+
     pa_xfree(s->device_name);
     pa_xfree(s);
 }
@@ -215,46 +267,41 @@
     pa_stream_ref(s);
 
     s->state = st;
+
     if (s->state_callback)
         s->state_callback(s, s->state_userdata);
 
-    if ((st == PA_STREAM_FAILED || st == PA_STREAM_TERMINATED) && s->context) {
-
-        /* Detach from context */
-        pa_operation *o, *n;
-
-        /* Unref all operatio object that point to us */
-        for (o = s->context->operations; o; o = n) {
-            n = o->next;
-
-            if (o->stream == s)
-                pa_operation_cancel(o);
-        }
-
-        /* Drop all outstanding replies for this stream */
-        if (s->context->pdispatch)
-            pa_pdispatch_unregister_reply(s->context->pdispatch, s);
-
-        if (s->channel_valid)
-            pa_dynarray_put((s->direction == PA_STREAM_PLAYBACK) ? s->context->playback_streams : s->context->record_streams, s->channel, NULL);
-
-        PA_LLIST_REMOVE(pa_stream, s->context->streams, s);
-        pa_stream_unref(s);
-
-        s->channel = 0;
-        s->channel_valid = 0;
-
-        s->context = NULL;
-
-        s->read_callback = NULL;
-        s->write_callback = NULL;
-        s->state_callback = NULL;
-        s->overflow_callback = NULL;
-        s->underflow_callback = NULL;
-        s->latency_update_callback = NULL;
-    }
+    if ((st == PA_STREAM_FAILED || st == PA_STREAM_TERMINATED))
+        stream_unlink(s);
 
     pa_stream_unref(s);
+}
+
+static void request_auto_timing_update(pa_stream *s, pa_bool_t force) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    if (!(s->flags & PA_STREAM_AUTO_TIMING_UPDATE))
+        return;
+
+    if (s->state == PA_STREAM_READY &&
+        (force || !s->auto_timing_update_requested)) {
+        pa_operation *o;
+
+/*         pa_log("automatically requesting new timing data"); */
+
+        if ((o = pa_stream_update_timing_info(s, NULL, NULL))) {
+            pa_operation_unref(o);
+            s->auto_timing_update_requested = TRUE;
+        }
+    }
+
+    if (s->auto_timing_update_event) {
+        struct timeval next;
+        pa_gettimeofday(&next);
+        pa_timeval_add(&next, LATENCY_IPOL_INTERVAL_USEC);
+        s->mainloop->time_restart(s->auto_timing_update_event, &next);
+    }
 }
 
 void pa_command_stream_killed(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
@@ -277,6 +324,9 @@
     }
 
     if (!(s = pa_dynarray_get(command == PA_COMMAND_PLAYBACK_STREAM_KILLED ? c->playback_streams : c->record_streams, channel)))
+        goto finish;
+
+    if (s->state != PA_STREAM_READY)
         goto finish;
 
     pa_context_set_error(c, PA_ERR_KILLED);
@@ -293,6 +343,7 @@
     const char *dn;
     pa_bool_t suspended;
     uint32_t di;
+    pa_usec_t usec;
 
     pa_assert(pd);
     pa_assert(command == PA_COMMAND_PLAYBACK_STREAM_MOVED || command == PA_COMMAND_RECORD_STREAM_MOVED);
@@ -310,9 +361,20 @@
     if (pa_tagstruct_getu32(t, &channel) < 0 ||
         pa_tagstruct_getu32(t, &di) < 0 ||
         pa_tagstruct_gets(t, &dn) < 0 ||
-        pa_tagstruct_get_boolean(t, &suspended) < 0 ||
-        !pa_tagstruct_eof(t)) {
+        pa_tagstruct_get_boolean(t, &suspended) < 0) {
         pa_context_fail(c, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (c->version >= 13) {
+        if (pa_tagstruct_get_usec(t, &usec) < 0) {
+            pa_context_fail(s->context, PA_ERR_PROTOCOL);
+            goto finish;
+        }
+    }
+
+    if (!pa_tagstruct_eof(t)) {
+        pa_context_fail(s->context, PA_ERR_PROTOCOL);
         goto finish;
     }
 
@@ -323,12 +385,24 @@
 
     if (!(s = pa_dynarray_get(command == PA_COMMAND_PLAYBACK_STREAM_MOVED ? c->playback_streams : c->record_streams, channel)))
         goto finish;
+
+    if (s->state != PA_STREAM_READY)
+        goto finish;
+
+    if (c->version >= 13) {
+        if (s->direction == PA_STREAM_RECORD)
+            s->timing_info.configured_source_usec = usec;
+        else
+            s->timing_info.configured_sink_usec = usec;
+    }
 
     pa_xfree(s->device_name);
     s->device_name = pa_xstrdup(dn);
     s->device_index = di;
 
     s->suspended = suspended;
+
+    request_auto_timing_update(s, TRUE);
 
     if (s->moved_callback)
         s->moved_callback(s, s->moved_userdata);
@@ -366,10 +440,64 @@
     if (!(s = pa_dynarray_get(command == PA_COMMAND_PLAYBACK_STREAM_SUSPENDED ? c->playback_streams : c->record_streams, channel)))
         goto finish;
 
+    if (s->state != PA_STREAM_READY)
+        goto finish;
+
     s->suspended = suspended;
+
+    if (s->smoother) {
+        pa_usec_t x = pa_rtclock_usec();
+
+        if (s->timing_info_valid)
+            x -= s->timing_info.transport_usec;
+
+        if (s->suspended || s->corked)
+            pa_smoother_pause(s->smoother, x);
+    }
+
+    request_auto_timing_update(s, TRUE);
 
     if (s->suspended_callback)
         s->suspended_callback(s, s->suspended_userdata);
+
+finish:
+    pa_context_unref(c);
+}
+
+void pa_command_stream_started(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_context *c = userdata;
+    pa_stream *s;
+    uint32_t channel;
+
+    pa_assert(pd);
+    pa_assert(command == PA_COMMAND_STARTED);
+    pa_assert(t);
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    pa_context_ref(c);
+
+    if (c->version < 13) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (pa_tagstruct_getu32(t, &channel) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (!(s = pa_dynarray_get(c->playback_streams, channel)))
+        goto finish;
+
+    if (s->state != PA_STREAM_READY)
+        goto finish;
+
+    request_auto_timing_update(s, TRUE);
+
+    if (s->started_callback)
+        s->started_callback(s, s->suspended_userdata);
 
 finish:
     pa_context_unref(c);
@@ -398,12 +526,13 @@
     if (!(s = pa_dynarray_get(c->playback_streams, channel)))
         goto finish;
 
-    if (s->state == PA_STREAM_READY) {
-        s->requested_bytes += bytes;
-
-        if (s->requested_bytes > 0 && s->write_callback)
-            s->write_callback(s, s->requested_bytes, s->write_userdata);
-    }
+    if (s->state != PA_STREAM_READY)
+        goto finish;
+
+    s->requested_bytes += bytes;
+
+    if (s->requested_bytes > 0 && s->write_callback)
+        s->write_callback(s, s->requested_bytes, s->write_userdata);
 
 finish:
     pa_context_unref(c);
@@ -430,6 +559,21 @@
 
     if (!(s = pa_dynarray_get(c->playback_streams, channel)))
         goto finish;
+
+    if (s->state != PA_STREAM_READY)
+        goto finish;
+
+    if (s->smoother)
+        if (s->direction == PA_STREAM_PLAYBACK && s->buffer_attr.prebuf > 0) {
+            pa_usec_t x = pa_rtclock_usec();
+
+            if (s->timing_info_valid)
+                x -= s->timing_info.transport_usec;
+
+            pa_smoother_pause(s->smoother, x);
+        }
+
+    request_auto_timing_update(s, TRUE);
 
     if (s->state == PA_STREAM_READY) {
 
@@ -446,34 +590,7 @@
     pa_context_unref(c);
 }
 
-static void request_auto_timing_update(pa_stream *s, int force) {
-    pa_assert(s);
-    pa_assert(PA_REFCNT_VALUE(s) >= 1);
-
-    if (!(s->flags & PA_STREAM_AUTO_TIMING_UPDATE))
-        return;
-
-    if (s->state == PA_STREAM_READY &&
-        (force || !s->auto_timing_update_requested)) {
-        pa_operation *o;
-
-/*         pa_log("automatically requesting new timing data");   */
-
-        if ((o = pa_stream_update_timing_info(s, NULL, NULL))) {
-            pa_operation_unref(o);
-            s->auto_timing_update_requested = TRUE;
-        }
-    }
-
-    if (s->auto_timing_update_event) {
-        struct timeval next;
-        pa_gettimeofday(&next);
-        pa_timeval_add(&next, LATENCY_IPOL_INTERVAL_USEC);
-        s->mainloop->time_restart(s->auto_timing_update_event, &next);
-    }
-}
-
-static void invalidate_indexes(pa_stream *s, int r, int w) {
+static void invalidate_indexes(pa_stream *s, pa_bool_t r, pa_bool_t w) {
     pa_assert(s);
     pa_assert(PA_REFCNT_VALUE(s) >= 1);
 
@@ -500,11 +617,7 @@
 /*         pa_log("read_index invalidated"); */
     }
 
-    if ((s->direction == PA_STREAM_PLAYBACK && r) ||
-        (s->direction == PA_STREAM_RECORD && w))
-        s->cached_time_valid = 0;
-
-    request_auto_timing_update(s, 1);
+    request_auto_timing_update(s, TRUE);
 }
 
 static void auto_timing_update_callback(PA_GCC_UNUSED pa_mainloop_api *m, PA_GCC_UNUSED pa_time_event *e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) {
@@ -513,10 +626,8 @@
     pa_assert(s);
     pa_assert(PA_REFCNT_VALUE(s) >= 1);
 
-/*     pa_log("time event");    */
-
     pa_stream_ref(s);
-    request_auto_timing_update(s, 0);
+    request_auto_timing_update(s, FALSE);
     pa_stream_unref(s);
 }
 
@@ -536,6 +647,8 @@
         tv.tv_usec += LATENCY_IPOL_INTERVAL_USEC; /* every 100 ms */
         pa_assert(!s->auto_timing_update_event);
         s->auto_timing_update_event = s->mainloop->time_new(s->mainloop, &tv, &auto_timing_update_callback, s);
+
+        request_auto_timing_update(s, TRUE);
     }
 }
 
@@ -577,7 +690,7 @@
     pa_stream_ref(s);
 
     if (command != PA_COMMAND_REPLY) {
-        if (pa_context_handle_error(s->context, command, t) < 0)
+        if (pa_context_handle_error(s->context, command, t, FALSE) < 0)
             goto finish;
 
         pa_stream_set_state(s, PA_STREAM_FAILED);
@@ -585,7 +698,8 @@
     }
 
     if (pa_tagstruct_getu32(t, &s->channel) < 0 ||
-        ((s->direction != PA_STREAM_UPLOAD) && pa_tagstruct_getu32(t, &s->stream_index) < 0) ||
+        s->channel == PA_INVALID_INDEX ||
+        ((s->direction != PA_STREAM_UPLOAD) && (pa_tagstruct_getu32(t, &s->stream_index) < 0 ||  s->stream_index == PA_INVALID_INDEX)) ||
         ((s->direction != PA_STREAM_RECORD) && pa_tagstruct_getu32(t, &s->requested_bytes) < 0)) {
         pa_context_fail(s->context, PA_ERR_PROTOCOL);
         goto finish;
@@ -676,7 +790,7 @@
                 NULL);
     }
 
-    s->channel_valid = 1;
+    s->channel_valid = TRUE;
     pa_dynarray_put((s->direction == PA_STREAM_RECORD) ? s->context->record_streams : s->context->playback_streams, s->channel, s);
 
     create_stream_complete(s);
@@ -737,15 +851,22 @@
     if (sync_stream)
         s->syncid = sync_stream->syncid;
 
-    if (attr) {
+    if (attr)
         s->buffer_attr = *attr;
-        s->manual_buffer_attr = TRUE;
-    } else {
-        memset(&s->buffer_attr, 0, sizeof(s->buffer_attr));
-        s->manual_buffer_attr = FALSE;
-    }
-
     automatic_buffer_attr(s, &s->buffer_attr, &s->sample_spec);
+
+    if (flags & PA_STREAM_INTERPOLATE_TIMING) {
+        pa_usec_t x;
+
+        if (s->smoother)
+            pa_smoother_free(s->smoother);
+
+        s->smoother = pa_smoother_new(SMOOTHER_ADJUST_TIME, SMOOTHER_HISTORY_TIME, !(flags & PA_STREAM_NOT_MONOTONOUS));
+
+        x = pa_rtclock_usec();
+        pa_smoother_set_time_offset(s->smoother, x);
+        pa_smoother_pause(s->smoother, x);
+    }
 
     if (!dev)
         dev = s->direction == PA_STREAM_PLAYBACK ? s->context->conf->default_sink : s->context->conf->default_source;
@@ -922,31 +1043,31 @@
         if (s->write_index_corrections[s->current_write_index_correction].valid) {
 
             if (seek == PA_SEEK_ABSOLUTE) {
-                s->write_index_corrections[s->current_write_index_correction].corrupt = 0;
-                s->write_index_corrections[s->current_write_index_correction].absolute = 1;
+                s->write_index_corrections[s->current_write_index_correction].corrupt = FALSE;
+                s->write_index_corrections[s->current_write_index_correction].absolute = TRUE;
                 s->write_index_corrections[s->current_write_index_correction].value = offset + length;
             } else if (seek == PA_SEEK_RELATIVE) {
                 if (!s->write_index_corrections[s->current_write_index_correction].corrupt)
                     s->write_index_corrections[s->current_write_index_correction].value += offset + length;
             } else
-                s->write_index_corrections[s->current_write_index_correction].corrupt = 1;
+                s->write_index_corrections[s->current_write_index_correction].corrupt = TRUE;
         }
 
         /* Update the write index in the already available latency data */
         if (s->timing_info_valid) {
 
             if (seek == PA_SEEK_ABSOLUTE) {
-                s->timing_info.write_index_corrupt = 0;
+                s->timing_info.write_index_corrupt = FALSE;
                 s->timing_info.write_index = offset + length;
             } else if (seek == PA_SEEK_RELATIVE) {
                 if (!s->timing_info.write_index_corrupt)
                     s->timing_info.write_index += offset + length;
             } else
-                s->timing_info.write_index_corrupt = 1;
+                s->timing_info.write_index_corrupt = TRUE;
         }
 
         if (!s->timing_info_valid || s->timing_info.write_index_corrupt)
-            request_auto_timing_update(s, 1);
+            request_auto_timing_update(s, TRUE);
     }
 
     return 0;
@@ -995,9 +1116,7 @@
     pa_assert(s->peek_data);
     pa_memblock_release(s->peek_memchunk.memblock);
     pa_memblock_unref(s->peek_memchunk.memblock);
-    s->peek_memchunk.length = 0;
-    s->peek_memchunk.index = 0;
-    s->peek_memchunk.memblock = NULL;
+    pa_memchunk_reset(&s->peek_memchunk);
 
     return 0;
 }
@@ -1041,6 +1160,65 @@
     pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
 
     return o;
+}
+
+static pa_usec_t calc_time(pa_stream *s, pa_bool_t ignore_transport) {
+    pa_usec_t usec;
+
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+    pa_assert(s->state == PA_STREAM_READY);
+    pa_assert(s->direction != PA_STREAM_UPLOAD);
+    pa_assert(s->timing_info_valid);
+    pa_assert(s->direction != PA_STREAM_PLAYBACK || !s->timing_info.read_index_corrupt);
+    pa_assert(s->direction != PA_STREAM_RECORD || !s->timing_info.write_index_corrupt);
+
+    if (s->direction == PA_STREAM_PLAYBACK) {
+        /* The last byte that was written into the output device
+         * had this time value associated */
+        usec = pa_bytes_to_usec(s->timing_info.read_index < 0 ? 0 : (uint64_t) s->timing_info.read_index, &s->sample_spec);
+
+        if (!s->corked && !s->suspended) {
+
+            if (!ignore_transport)
+                /* Because the latency info took a little time to come
+                 * to us, we assume that the real output time is actually
+                 * a little ahead */
+                usec += s->timing_info.transport_usec;
+
+            /* However, the output device usually maintains a buffer
+               too, hence the real sample currently played is a little
+               back  */
+            if (s->timing_info.sink_usec >= usec)
+                usec = 0;
+            else
+                usec -= s->timing_info.sink_usec;
+        }
+
+    } else if (s->direction == PA_STREAM_RECORD) {
+        /* The last byte written into the server side queue had
+         * this time value associated */
+        usec = pa_bytes_to_usec(s->timing_info.write_index < 0 ? 0 : (uint64_t) s->timing_info.write_index, &s->sample_spec);
+
+        if (!s->corked && !s->suspended) {
+
+            if (!ignore_transport)
+                /* Add transport latency */
+                usec += s->timing_info.transport_usec;
+
+            /* Add latency of data in device buffer */
+            usec += s->timing_info.source_usec;
+
+            /* If this is a monitor source, we need to correct the
+             * time by the playback device buffer */
+            if (s->timing_info.sink_usec >= usec)
+                usec = 0;
+            else
+                usec -= s->timing_info.sink_usec;
+        }
+    }
+
+    return usec;
 }
 
 static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
@@ -1048,6 +1226,7 @@
     struct timeval local, remote, now;
     pa_timing_info *i;
     pa_bool_t playing = FALSE;
+    uint64_t underrun_for = 0, playing_for = 0;
 
     pa_assert(pd);
     pa_assert(o);
@@ -1061,29 +1240,46 @@
 /*     pa_log("pre corrupt w:%u r:%u\n", !o->stream->timing_info_valid || i->write_index_corrupt,!o->stream->timing_info_valid || i->read_index_corrupt); */
 
     o->stream->timing_info_valid = FALSE;
-    i->write_index_corrupt = 0;
-    i->read_index_corrupt = 0;
+    i->write_index_corrupt = FALSE;
+    i->read_index_corrupt = FALSE;
 
 /*     pa_log("timing update %u\n", tag); */
 
     if (command != PA_COMMAND_REPLY) {
-        if (pa_context_handle_error(o->context, command, t) < 0)
+        if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
             goto finish;
 
-    } else if (pa_tagstruct_get_usec(t, &i->sink_usec) < 0 ||
-               pa_tagstruct_get_usec(t, &i->source_usec) < 0 ||
-               pa_tagstruct_get_boolean(t, &playing) < 0 ||
-               pa_tagstruct_get_timeval(t, &local) < 0 ||
-               pa_tagstruct_get_timeval(t, &remote) < 0 ||
-               pa_tagstruct_gets64(t, &i->write_index) < 0 ||
-               pa_tagstruct_gets64(t, &i->read_index) < 0 ||
-               !pa_tagstruct_eof(t)) {
-        pa_context_fail(o->context, PA_ERR_PROTOCOL);
-        goto finish;
-
     } else {
-        o->stream->timing_info_valid = 1;
+
+        if (pa_tagstruct_get_usec(t, &i->sink_usec) < 0 ||
+            pa_tagstruct_get_usec(t, &i->source_usec) < 0 ||
+            pa_tagstruct_get_boolean(t, &playing) < 0 ||
+            pa_tagstruct_get_timeval(t, &local) < 0 ||
+            pa_tagstruct_get_timeval(t, &remote) < 0 ||
+            pa_tagstruct_gets64(t, &i->write_index) < 0 ||
+            pa_tagstruct_gets64(t, &i->read_index) < 0) {
+
+            pa_context_fail(o->context, PA_ERR_PROTOCOL);
+            goto finish;
+        }
+
+        if (o->context->version >= 13)
+            if (pa_tagstruct_getu64(t, &underrun_for) < 0 ||
+                pa_tagstruct_getu64(t, &playing_for) < 0) {
+
+                pa_context_fail(o->context, PA_ERR_PROTOCOL);
+                goto finish;
+            }
+
+
+        if (!pa_tagstruct_eof(t)) {
+            pa_context_fail(o->context, PA_ERR_PROTOCOL);
+            goto finish;
+        }
+
+        o->stream->timing_info_valid = TRUE;
         i->playing = (int) playing;
+        i->since_underrun = playing ? playing_for : underrun_for;
 
         pa_gettimeofday(&now);
 
@@ -1096,22 +1292,22 @@
             else
                 i->transport_usec = pa_timeval_diff(&now, &remote);
 
-            i->synchronized_clocks = 1;
+            i->synchronized_clocks = TRUE;
             i->timestamp = remote;
         } else {
             /* clocks are not synchronized, let's estimate latency then */
             i->transport_usec = pa_timeval_diff(&now, &local)/2;
-            i->synchronized_clocks = 0;
+            i->synchronized_clocks = FALSE;
             i->timestamp = local;
             pa_timeval_add(&i->timestamp, i->transport_usec);
         }
 
         /* Invalidate read and write indexes if necessary */
         if (tag < o->stream->read_index_not_before)
-            i->read_index_corrupt = 1;
+            i->read_index_corrupt = TRUE;
 
         if (tag < o->stream->write_index_not_before)
-            i->write_index_corrupt = 1;
+            i->write_index_corrupt = TRUE;
 
         if (o->stream->direction == PA_STREAM_PLAYBACK) {
             /* Write index correction */
@@ -1137,11 +1333,11 @@
                 if (o->stream->write_index_corrections[j].corrupt) {
                     /* A corrupting seek was made */
                     i->write_index = 0;
-                    i->write_index_corrupt = 1;
+                    i->write_index_corrupt = TRUE;
                 } else if (o->stream->write_index_corrections[j].absolute) {
                     /* An absolute seek was made */
                     i->write_index = o->stream->write_index_corrections[j].value;
-                    i->write_index_corrupt = 0;
+                    i->write_index_corrupt = FALSE;
                 } else if (!i->write_index_corrupt) {
                     /* A relative seek was made */
                     i->write_index += o->stream->write_index_corrections[j].value;
@@ -1156,24 +1352,56 @@
                 i->read_index -= pa_memblockq_get_length(o->stream->record_memblockq);
         }
 
-        o->stream->cached_time_valid = 0;
-    }
-
-    o->stream->auto_timing_update_requested = 0;
 /*     pa_log("post corrupt w:%u r:%u\n", i->write_index_corrupt || !o->stream->timing_info_valid, i->read_index_corrupt || !o->stream->timing_info_valid); */
 
-    /* Clear old correction entries */
-    if (o->stream->direction == PA_STREAM_PLAYBACK) {
-        int n;
-
-        for (n = 0; n < PA_MAX_WRITE_INDEX_CORRECTIONS; n++) {
-            if (!o->stream->write_index_corrections[n].valid)
-                continue;
-
-            if (o->stream->write_index_corrections[n].tag <= tag)
-                o->stream->write_index_corrections[n].valid = 0;
-        }
-    }
+        /* Clear old correction entries */
+        if (o->stream->direction == PA_STREAM_PLAYBACK) {
+            int n;
+
+            for (n = 0; n < PA_MAX_WRITE_INDEX_CORRECTIONS; n++) {
+                if (!o->stream->write_index_corrections[n].valid)
+                    continue;
+
+                if (o->stream->write_index_corrections[n].tag <= tag)
+                    o->stream->write_index_corrections[n].valid = FALSE;
+            }
+        }
+
+        /* Update smoother */
+        if (o->stream->smoother) {
+            pa_usec_t u, x;
+
+            u = x = pa_rtclock_usec() - i->transport_usec;
+
+            if (o->stream->direction == PA_STREAM_PLAYBACK &&
+                o->context->version >= 13) {
+                pa_usec_t su;
+
+                /* If we weren't playing then it will take some time
+                 * until the audio will actually come out through the
+                 * speakers. Since we follow that timing here, we need
+                 * to try to fix this up */
+
+                su = pa_bytes_to_usec(i->since_underrun, &o->stream->sample_spec);
+
+                if (su < i->sink_usec)
+                    x += i->sink_usec - su;
+            }
+
+            if (!i->playing)
+                pa_smoother_pause(o->stream->smoother, x);
+
+            /* Update the smoother */
+            if ((o->stream->direction == PA_STREAM_PLAYBACK && !i->read_index_corrupt) ||
+                (o->stream->direction == PA_STREAM_RECORD && !i->write_index_corrupt))
+                pa_smoother_put(o->stream->smoother, u, calc_time(o->stream, TRUE));
+
+            if (i->playing)
+                pa_smoother_resume(o->stream->smoother, x);
+        }
+    }
+
+    o->stream->auto_timing_update_requested = FALSE;
 
     if (o->stream->latency_update_callback)
         o->stream->latency_update_callback(o->stream, o->stream->latency_update_userdata);
@@ -1223,15 +1451,15 @@
 
     if (s->direction == PA_STREAM_PLAYBACK) {
         /* Fill in initial correction data */
-        o->stream->current_write_index_correction = cidx;
-        o->stream->write_index_corrections[cidx].valid = 1;
-        o->stream->write_index_corrections[cidx].tag = tag;
-        o->stream->write_index_corrections[cidx].absolute = 0;
-        o->stream->write_index_corrections[cidx].value = 0;
-        o->stream->write_index_corrections[cidx].corrupt = 0;
-    }
-
-/*     pa_log("requesting update %u\n", tag); */
+
+        s->current_write_index_correction = cidx;
+
+        s->write_index_corrections[cidx].valid = TRUE;
+        s->write_index_corrections[cidx].absolute = FALSE;
+        s->write_index_corrections[cidx].corrupt = FALSE;
+        s->write_index_corrections[cidx].tag = tag;
+        s->write_index_corrections[cidx].value = 0;
+    }
 
     return o;
 }
@@ -1246,7 +1474,7 @@
     pa_stream_ref(s);
 
     if (command != PA_COMMAND_REPLY) {
-        if (pa_context_handle_error(s->context, command, t) < 0)
+        if (pa_context_handle_error(s->context, command, t, FALSE) < 0)
             goto finish;
 
         pa_stream_set_state(s, PA_STREAM_FAILED);
@@ -1291,6 +1519,9 @@
     pa_assert(s);
     pa_assert(PA_REFCNT_VALUE(s) >= 1);
 
+    if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+        return;
+
     s->read_callback = cb;
     s->read_userdata = userdata;
 }
@@ -1299,6 +1530,9 @@
     pa_assert(s);
     pa_assert(PA_REFCNT_VALUE(s) >= 1);
 
+    if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+        return;
+
     s->write_callback = cb;
     s->write_userdata = userdata;
 }
@@ -1307,6 +1541,9 @@
     pa_assert(s);
     pa_assert(PA_REFCNT_VALUE(s) >= 1);
 
+    if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+        return;
+
     s->state_callback = cb;
     s->state_userdata = userdata;
 }
@@ -1315,6 +1552,9 @@
     pa_assert(s);
     pa_assert(PA_REFCNT_VALUE(s) >= 1);
 
+    if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+        return;
+
     s->overflow_callback = cb;
     s->overflow_userdata = userdata;
 }
@@ -1323,6 +1563,9 @@
     pa_assert(s);
     pa_assert(PA_REFCNT_VALUE(s) >= 1);
 
+    if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+        return;
+
     s->underflow_callback = cb;
     s->underflow_userdata = userdata;
 }
@@ -1331,6 +1574,9 @@
     pa_assert(s);
     pa_assert(PA_REFCNT_VALUE(s) >= 1);
 
+    if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+        return;
+
     s->latency_update_callback = cb;
     s->latency_update_userdata = userdata;
 }
@@ -1339,6 +1585,9 @@
     pa_assert(s);
     pa_assert(PA_REFCNT_VALUE(s) >= 1);
 
+    if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+        return;
+
     s->moved_callback = cb;
     s->moved_userdata = userdata;
 }
@@ -1347,8 +1596,22 @@
     pa_assert(s);
     pa_assert(PA_REFCNT_VALUE(s) >= 1);
 
+    if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+        return;
+
     s->suspended_callback = cb;
     s->suspended_userdata = userdata;
+}
+
+void pa_stream_set_started_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+        return;
+
+    s->started_callback = cb;
+    s->started_userdata = userdata;
 }
 
 void pa_stream_simple_ack_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
@@ -1363,7 +1626,7 @@
         goto finish;
 
     if (command != PA_COMMAND_REPLY) {
-        if (pa_context_handle_error(o->context, command, t) < 0)
+        if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
             goto finish;
 
         success = 0;
@@ -1406,8 +1669,18 @@
     pa_pstream_send_tagstruct(s->context->pstream, t);
     pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
 
+    if (s->smoother) {
+        pa_usec_t x = pa_rtclock_usec();
+
+        if (s->timing_info_valid)
+            x += s->timing_info.transport_usec;
+
+        if (s->suspended || s->corked)
+            pa_smoother_pause(s->smoother, x);
+    }
+
     if (s->direction == PA_STREAM_PLAYBACK)
-        invalidate_indexes(s, 1, 0);
+        invalidate_indexes(s, TRUE, FALSE);
 
     return o;
 }
@@ -1438,23 +1711,34 @@
     pa_assert(s);
     pa_assert(PA_REFCNT_VALUE(s) >= 1);
 
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
     PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
 
     if ((o = stream_send_simple_command(s, s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_FLUSH_PLAYBACK_STREAM : PA_COMMAND_FLUSH_RECORD_STREAM, cb, userdata))) {
 
         if (s->direction == PA_STREAM_PLAYBACK) {
             if (s->write_index_corrections[s->current_write_index_correction].valid)
-                s->write_index_corrections[s->current_write_index_correction].corrupt = 1;
+                s->write_index_corrections[s->current_write_index_correction].corrupt = TRUE;
 
             if (s->timing_info_valid)
-                s->timing_info.write_index_corrupt = 1;
+                s->timing_info.write_index_corrupt = TRUE;
 
             if (s->buffer_attr.prebuf > 0)
-                invalidate_indexes(s, 1, 0);
+                invalidate_indexes(s, TRUE, FALSE);
             else
-                request_auto_timing_update(s, 1);
+                request_auto_timing_update(s, TRUE);
+
+            if (s->smoother && s->buffer_attr.prebuf > 0) {
+                pa_usec_t x = pa_rtclock_usec();
+
+                if (s->timing_info_valid)
+                    x += s->timing_info.transport_usec;
+
+                pa_smoother_pause(s->smoother, x);
+            }
+
         } else
-            invalidate_indexes(s, 0, 1);
+            invalidate_indexes(s, FALSE, TRUE);
     }
 
     return o;
@@ -1466,11 +1750,12 @@
     pa_assert(s);
     pa_assert(PA_REFCNT_VALUE(s) >= 1);
 
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
     PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE);
     PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->buffer_attr.prebuf > 0, PA_ERR_BADSTATE);
 
     if ((o = stream_send_simple_command(s, PA_COMMAND_PREBUF_PLAYBACK_STREAM, cb, userdata)))
-        invalidate_indexes(s, 1, 0);
+        invalidate_indexes(s, TRUE, FALSE);
 
     return o;
 }
@@ -1481,19 +1766,18 @@
     pa_assert(s);
     pa_assert(PA_REFCNT_VALUE(s) >= 1);
 
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
     PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE);
     PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->buffer_attr.prebuf > 0, PA_ERR_BADSTATE);
 
     if ((o = stream_send_simple_command(s, PA_COMMAND_TRIGGER_PLAYBACK_STREAM, cb, userdata)))
-        invalidate_indexes(s, 1, 0);
+        invalidate_indexes(s, TRUE, FALSE);
 
     return o;
 }
 
 pa_operation* pa_stream_set_name(pa_stream *s, const char *name, pa_stream_success_cb_t cb, void *userdata) {
     pa_operation *o;
-    pa_tagstruct *t;
-    uint32_t tag;
 
     pa_assert(s);
     pa_assert(PA_REFCNT_VALUE(s) >= 1);
@@ -1502,22 +1786,32 @@
     PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
     PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
 
-    o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata);
-
-    t = pa_tagstruct_command(
-            s->context,
-            s->direction == PA_STREAM_RECORD ? PA_COMMAND_SET_RECORD_STREAM_NAME : PA_COMMAND_SET_PLAYBACK_STREAM_NAME,
-            &tag);
-    pa_tagstruct_putu32(t, s->channel);
-    pa_tagstruct_puts(t, name);
-    pa_pstream_send_tagstruct(s->context->pstream, t);
-    pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+    if (s->context->version >= 13) {
+        pa_proplist *p = pa_proplist_new();
+
+        pa_proplist_sets(p, PA_PROP_APPLICATION_NAME, name);
+        o = pa_stream_proplist_update(s, PA_UPDATE_REPLACE, p, cb, userdata);
+        pa_proplist_free(p);
+    } else {
+        pa_tagstruct *t;
+        uint32_t tag;
+
+        o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata);
+        t = pa_tagstruct_command(
+                s->context,
+                s->direction == PA_STREAM_RECORD ? PA_COMMAND_SET_RECORD_STREAM_NAME : PA_COMMAND_SET_PLAYBACK_STREAM_NAME,
+                &tag);
+        pa_tagstruct_putu32(t, s->channel);
+        pa_tagstruct_puts(t, name);
+        pa_pstream_send_tagstruct(s->context->pstream, t);
+        pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+    }
 
     return o;
 }
 
 int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec) {
-    pa_usec_t usec = 0;
+    pa_usec_t usec;
 
     pa_assert(s);
     pa_assert(PA_REFCNT_VALUE(s) >= 1);
@@ -1528,65 +1822,10 @@
     PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_PLAYBACK || !s->timing_info.read_index_corrupt, PA_ERR_NODATA);
     PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_RECORD || !s->timing_info.write_index_corrupt, PA_ERR_NODATA);
 
-    if (s->cached_time_valid)
-        /* We alredy calculated the time value for this timing info, so let's reuse it */
-        usec = s->cached_time;
-    else {
-        if (s->direction == PA_STREAM_PLAYBACK) {
-            /* The last byte that was written into the output device
-             * had this time value associated */
-            usec = pa_bytes_to_usec(s->timing_info.read_index < 0 ? 0 : (uint64_t) s->timing_info.read_index, &s->sample_spec);
-
-            if (!s->corked) {
-                /* Because the latency info took a little time to come
-                 * to us, we assume that the real output time is actually
-                 * a little ahead */
-                usec += s->timing_info.transport_usec;
-
-                /* However, the output device usually maintains a buffer
-                   too, hence the real sample currently played is a little
-                   back  */
-                if (s->timing_info.sink_usec >= usec)
-                    usec = 0;
-                else
-                    usec -= s->timing_info.sink_usec;
-            }
-
-        } else if (s->direction == PA_STREAM_RECORD) {
-            /* The last byte written into the server side queue had
-             * this time value associated */
-            usec = pa_bytes_to_usec(s->timing_info.write_index < 0 ? 0 : (uint64_t) s->timing_info.write_index, &s->sample_spec);
-
-            if (!s->corked) {
-                /* Add transport latency */
-                usec += s->timing_info.transport_usec;
-
-                /* Add latency of data in device buffer */
-                usec += s->timing_info.source_usec;
-
-                /* If this is a monitor source, we need to correct the
-                 * time by the playback device buffer */
-                if (s->timing_info.sink_usec >= usec)
-                    usec = 0;
-                else
-                    usec -= s->timing_info.sink_usec;
-            }
-        }
-
-        s->cached_time = usec;
-        s->cached_time_valid = 1;
-    }
-
-    /* Interpolate if requested */
-    if (s->flags & PA_STREAM_INTERPOLATE_TIMING) {
-
-        /* We just add the time that passed since the latency info was
-         * current */
-        if (!s->corked && s->timing_info.playing) {
-            struct timeval now;
-            usec += pa_timeval_diff(pa_gettimeofday(&now), &s->timing_info.timestamp);
-        }
-    }
+    if (s->smoother)
+        usec = pa_smoother_get(s->smoother, pa_rtclock_usec());
+    else
+        usec = calc_time(s, FALSE);
 
     /* Make sure the time runs monotonically */
     if (!(s->flags & PA_STREAM_NOT_MONOTONOUS)) {
@@ -1687,7 +1926,7 @@
 
     PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
     PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
-    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->context->version >= 9, PA_ERR_NODATA);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->context->version >= 9, PA_ERR_NOTSUPPORTED);
 
     return &s->buffer_attr;
 }
@@ -1704,7 +1943,7 @@
         goto finish;
 
     if (command != PA_COMMAND_REPLY) {
-        if (pa_context_handle_error(o->context, command, t) < 0)
+        if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
             goto finish;
 
         success = 0;
@@ -1730,8 +1969,6 @@
             pa_context_fail(o->context, PA_ERR_PROTOCOL);
             goto finish;
         }
-
-        o->stream->manual_buffer_attr = TRUE;
     }
 
     if (o->callback) {
@@ -1822,6 +2059,16 @@
     return s->suspended;
 }
 
+int pa_stream_is_corked(pa_stream *s) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
+
+    return s->corked;
+}
+
 static void stream_update_sample_rate_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
     pa_operation *o = userdata;
     int success = 1;
@@ -1834,7 +2081,7 @@
         goto finish;
 
     if (command != PA_COMMAND_REPLY) {
-        if (pa_context_handle_error(o->context, command, t) < 0)
+        if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
             goto finish;
 
         success = 0;

Modified: branches/glitch-free/src/pulse/stream.h
URL: http://0pointer.de/cgi-bin/viewcvs.cgi/branches/glitch-free/src/pulse/stream.h?rev=2329&root=pulseaudio&r1=2328&r2=2329&view=diff
==============================================================================
--- branches/glitch-free/src/pulse/stream.h (original)
+++ branches/glitch-free/src/pulse/stream.h Thu May  1 21:51:05 2008
@@ -339,6 +339,10 @@
  * server is older than 0.9.8. \since 0.9.8 */
 int pa_stream_is_suspended(pa_stream *s);
 
+/** Return 1 if the this stream has been corked. This will return 0 if
+ * not, and negative on error. \since 0.9.11 */
+int pa_stream_is_corked(pa_stream *s);
+
 /** Connect the stream to a sink */
 int pa_stream_connect_playback(
         pa_stream *s                  /**< The stream to connect to a sink */,
@@ -368,7 +372,7 @@
 int pa_stream_write(
         pa_stream *p             /**< The stream to use */,
         const void *data         /**< The data to write */,
-        size_t bytes             /**< The length of the data to write in bytes*/,
+        size_t nbytes            /**< The length of the data to write in bytes*/,
         pa_free_cb_t free_cb     /**< A cleanup routine for the data or NULL to request an internal copy */,
         int64_t offset,          /**< Offset for seeking, must be 0 for upload streams */
         pa_seek_mode_t seek      /**< Seek mode, must be PA_SEEK_RELATIVE for upload streams */);
@@ -381,7 +385,7 @@
 int pa_stream_peek(
         pa_stream *p                 /**< The stream to use */,
         const void **data            /**< Pointer to pointer that will point to data */,
-        size_t *bytes                /**< The length of the data read in bytes */);
+        size_t *nbytes               /**< The length of the data read in bytes */);
 
 /** Remove the current fragment on record streams. It is invalid to do this without first
  * calling pa_stream_peek(). */
@@ -418,6 +422,13 @@
 
 /** Set the callback function that is called when a buffer underflow happens. (Only for playback streams) */
 void pa_stream_set_underflow_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata);
+
+/** Set the callback function that is called when a the server starts
+ * playback after an underrun or on initial startup. This only informs
+ * that audio is flowing again, it is no indication that audio startet
+ * to reach the speakers already. (Only for playback streams). \since
+ * 0.9.11 */
+void pa_stream_set_started_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata);
 
 /** Set the callback function that is called whenever a latency
  * information update happens. Useful on PA_STREAM_AUTO_TIMING_UPDATE

Modified: branches/glitch-free/src/pulsecore/core-util.c
URL: http://0pointer.de/cgi-bin/viewcvs.cgi/branches/glitch-free/src/pulsecore/core-util.c?rev=2329&root=pulseaudio&r1=2328&r2=2329&view=diff
==============================================================================
--- branches/glitch-free/src/pulsecore/core-util.c (original)
+++ branches/glitch-free/src/pulsecore/core-util.c Thu May  1 21:51:05 2008
@@ -41,6 +41,7 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/time.h>
+#include <dirent.h>
 
 #ifdef HAVE_STRTOF_L
 #include <locale.h>
@@ -101,12 +102,6 @@
 /* Not all platforms have this */
 #ifndef MSG_NOSIGNAL
 #define MSG_NOSIGNAL 0
-#endif
-
-#ifndef OS_IS_WIN32
-#define PA_USER_RUNTIME_PATH_PREFIX "/tmp/pulse-"
-#else
-#define PA_USER_RUNTIME_PATH_PREFIX "%TEMP%\\pulse-"
 #endif
 
 #ifdef OS_IS_WIN32
@@ -221,7 +216,7 @@
         goto fail;
     }
 #else
-    pa_log_warn("secure directory creation not supported on Win32.");
+    pa_log_warn("Secure directory creation not supported on Win32.");
 #endif
 
     return 0;
@@ -557,6 +552,82 @@
 #endif
 }
 
+/* This is merely used for giving the user a hint. This is not correct
+ * for anything security related */
+pa_bool_t pa_can_realtime(void) {
+
+    if (geteuid() == 0)
+        return TRUE;
+
+#if defined(HAVE_SYS_RESOURCE_H) && defined(RLIMIT_RTPRIO)
+    {
+        struct rlimit rl;
+
+        if (getrlimit(RLIMIT_RTPRIO, &rl) >= 0)
+            if (rl.rlim_cur > 0 || rl.rlim_cur == RLIM_INFINITY)
+                return TRUE;
+    }
+#endif
+
+#if defined(HAVE_SYS_CAPABILITY_H) && defined(CAP_SYS_NICE)
+    {
+        cap_t cap;
+
+        if ((cap = cap_get_proc())) {
+            cap_flag_value_t flag = CAP_CLEAR;
+
+            if (cap_get_flag(cap, CAP_SYS_NICE, CAP_EFFECTIVE, &flag) >= 0)
+                if (flag == CAP_SET) {
+                    cap_free(cap);
+                    return TRUE;
+                }
+
+            cap_free(cap);
+        }
+    }
+#endif
+
+    return FALSE;
+}
+
+/* This is merely used for giving the user a hint. This is not correct
+ * for anything security related */
+pa_bool_t pa_can_high_priority(void) {
+
+    if (geteuid() == 0)
+        return TRUE;
+
+#if defined(HAVE_SYS_RESOURCE_H) && defined(RLIMIT_RTPRIO)
+    {
+        struct rlimit rl;
+
+        if (getrlimit(RLIMIT_NICE, &rl) >= 0)
+            if (rl.rlim_cur >= 21 || rl.rlim_cur == RLIM_INFINITY)
+                return TRUE;
+    }
+#endif
+
+#if defined(HAVE_SYS_CAPABILITY_H) && defined(CAP_SYS_NICE)
+    {
+        cap_t cap;
+
+        if ((cap = cap_get_proc())) {
+            cap_flag_value_t flag = CAP_CLEAR;
+
+            if (cap_get_flag(cap, CAP_SYS_NICE, CAP_EFFECTIVE, &flag) >= 0)
+                if (flag == CAP_SET) {
+                    cap_free(cap);
+                    return TRUE;
+                }
+
+            cap_free(cap);
+        }
+    }
+#endif
+
+    return FALSE;
+}
+
 /* Raise the priority of the current process as much as possible that
  * is <= the specified nice level..*/
 int pa_raise_priority(int nice_level) {
@@ -612,6 +683,7 @@
 
 /* Try to parse a boolean string value.*/
 int pa_parse_boolean(const char *v) {
+    pa_assert(v);
 
     if (!strcmp(v, "1") || v[0] == 'y' || v[0] == 'Y' || v[0] == 't' || v[0] == 'T' || !strcasecmp(v, "on"))
         return 1;
@@ -1093,11 +1165,11 @@
     return r;
 }
 
-char *pa_get_state_dir(void) {
+char *pa_get_runtime_dir(void) {
     const char *e;
     char *d;
 
-    if ((e = getenv("PULSE_STATE_PATH")))
+    if ((e = getenv("PULSE_RUNTIME_PATH")))
         d = pa_xstrdup(e);
     else {
         char h[PATH_MAX];
@@ -1107,19 +1179,15 @@
             return NULL;
         }
 
-        d = pa_sprintf_malloc("%s/.pulse", h);
-    }
-
-    mkdir(d, 0755);
-
-    if (access(d, W_OK) == 0)
-        return d;
-
-    pa_log_error("Failed to set up state directory %s", d);
-
-    pa_xfree(d);
-
-    return NULL;
+        d = pa_sprintf_malloc("%s" PA_PATH_SEP ".pulse", h);
+    }
+
+    if (pa_make_secure_dir(d, 0700, (pid_t) -1, (pid_t) -1) < 0)  {
+        pa_log_error("Failed to create secure directory: %s", pa_cstrerror(errno));
+        return NULL;
+    }
+
+    return d;
 }
 
 /* Try to open a configuration file. If "env" is specified, open the
@@ -1128,10 +1196,95 @@
  * file system. If "result" is non-NULL, a pointer to a newly
  * allocated buffer containing the used configuration file is
  * stored there.*/
-FILE *pa_open_config_file(const char *global, const char *local, const char *env, char **result, const char *mode) {
+FILE *pa_open_config_file(const char *global, const char *local, const char *env, char **result) {
     const char *fn;
-    char h[PATH_MAX];
-
+#ifdef OS_IS_WIN32
+    char buf[PATH_MAX];
+
+    if (!getenv(PULSE_ROOTENV))
+        pa_set_root(NULL);
+#endif
+
+    if (env && (fn = getenv(env))) {
+        FILE *f;
+
+#ifdef OS_IS_WIN32
+        if (!ExpandEnvironmentStrings(fn, buf, PATH_MAX))
+            return NULL;
+        fn = buf;
+#endif
+
+        if ((f = fopen(fn, "r"))) {
+            if (result)
+                *result = pa_xstrdup(fn);
+
+            return f;
+        }
+
+        pa_log_warn("Failed to open configuration file '%s': %s", fn, pa_cstrerror(errno));
+        return NULL;
+    }
+
+    if (local) {
+        const char *e;
+        char *lfn;
+        char h[PATH_MAX];
+        FILE *f;
+
+        if ((e = getenv("PULSE_CONFIG_PATH")))
+            fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", e, local);
+        else if (pa_get_home_dir(h, sizeof(h)))
+            fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP ".pulse" PA_PATH_SEP "%s", h, local);
+
+#ifdef OS_IS_WIN32
+        if (!ExpandEnvironmentStrings(lfn, buf, PATH_MAX)) {
+            pa_xfree(lfn);
+            return NULL;
+        }
+        fn = buf;
+#endif
+
+        if ((f = fopen(fn, "r"))) {
+            if (result)
+                *result = pa_xstrdup(fn);
+
+            pa_xfree(lfn);
+            return f;
+        }
+
+        if (errno != ENOENT) {
+            pa_log_warn("Failed to open configuration file '%s': %s", fn, pa_cstrerror(errno));
+            pa_xfree(lfn);
+            return NULL;
+        }
+
+        pa_xfree(lfn);
+    }
+
+    if (global) {
+        FILE *f;
+
+#ifdef OS_IS_WIN32
+        if (!ExpandEnvironmentStrings(global, buf, PATH_MAX))
+            return NULL;
+        global = buf;
+#endif
+
+        if ((f = fopen(global, "r"))) {
+
+            if (result)
+                *result = pa_xstrdup(global);
+
+            return f;
+        }
+    } else
+        errno = ENOENT;
+
+    return NULL;
+}
+
+char *pa_find_config_file(const char *global, const char *local, const char *env) {
+    const char *fn;
 #ifdef OS_IS_WIN32
     char buf[PATH_MAX];
 
@@ -1146,69 +1299,59 @@
         fn = buf;
 #endif
 
-        if (result)
-            *result = pa_xstrdup(fn);
-
-        return fopen(fn, mode);
+        if (access(fn, R_OK) == 0)
+            return pa_xstrdup(fn);
+
+        pa_log_warn("Failed to access configuration file '%s': %s", fn, pa_cstrerror(errno));
+        return NULL;
     }
 
     if (local) {
         const char *e;
-        char *lfn = NULL;
+        char *lfn;
+        char h[PATH_MAX];
 
         if ((e = getenv("PULSE_CONFIG_PATH")))
-            fn = lfn = pa_sprintf_malloc("%s/%s", e, local);
-        else if (pa_get_home_dir(h, sizeof(h))) {
-            char *d;
-
-            d = pa_sprintf_malloc("%s/.pulse", h);
-            mkdir(d, 0755);
-            pa_xfree(d);
-
-            fn = lfn = pa_sprintf_malloc("%s/.pulse/%s", h, local);
-        }
-
-        if (lfn) {
-            FILE *f;
+            fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", e, local);
+        else if (pa_get_home_dir(h, sizeof(h)))
+            fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP ".pulse" PA_PATH_SEP "%s", h, local);
 
 #ifdef OS_IS_WIN32
-            if (!ExpandEnvironmentStrings(lfn, buf, PATH_MAX))
-                return NULL;
-            fn = buf;
-#endif
-
-            f = fopen(fn, mode);
-            if (f != NULL) {
-                if (result)
-                    *result = pa_xstrdup(fn);
-                pa_xfree(lfn);
-                return f;
-            }
-
-            if (errno != ENOENT)
-                pa_log_warn("Failed to open configuration file '%s': %s", lfn, pa_cstrerror(errno));
-
+        if (!ExpandEnvironmentStrings(lfn, buf, PATH_MAX)) {
             pa_xfree(lfn);
-        }
-    }
-
-    if (!global) {
-        if (result)
-            *result = NULL;
+            return NULL;
+        }
+        fn = buf;
+#endif
+
+        if (access(fn, R_OK) == 0) {
+            char *r = pa_xstrdup(fn);
+            pa_xfree(lfn);
+            return r;
+        }
+
+        if (errno != ENOENT) {
+            pa_log_warn("Failed to access configuration file '%s': %s", fn, pa_cstrerror(errno));
+            pa_xfree(lfn);
+            return NULL;
+        }
+
+        pa_xfree(lfn);
+    }
+
+    if (global) {
+#ifdef OS_IS_WIN32
+        if (!ExpandEnvironmentStrings(global, buf, PATH_MAX))
+            return NULL;
+        global = buf;
+#endif
+
+        if (access(fn, R_OK) == 0)
+            return pa_xstrdup(global);
+    } else
         errno = ENOENT;
-        return NULL;
-    }
-
-#ifdef OS_IS_WIN32
-    if (!ExpandEnvironmentStrings(global, buf, PATH_MAX))
-        return NULL;
-    global = buf;
-#endif
-
-    if (result)
-        *result = pa_xstrdup(global);
-
-    return fopen(global, mode);
+
+    return NULL;
 }
 
 /* Format the specified data as a hexademical string */
@@ -1299,45 +1442,51 @@
     return l1 >= l2 && strcmp(s+l1-l2, sfx) == 0;
 }
 
-/* if fn is null return the PulseAudio run time path in s (/tmp/pulse)
- * if fn is non-null and starts with / return fn in s
- * otherwise append fn to the run time path and return it in s */
-char *pa_runtime_path(const char *fn, char *s, size_t l) {
-    const char *e;
+pa_bool_t pa_is_path_absolute(const char *fn) {
+    pa_assert(fn);
 
 #ifndef OS_IS_WIN32
-    if (fn && *fn == '/')
+    return *fn == '/';
 #else
-    if (fn && strlen(fn) >= 3 && isalpha(fn[0]) && fn[1] == ':' && fn[2] == '\\')
-#endif
-        return pa_strlcpy(s, fn, l);
-
-    if ((e = getenv("PULSE_RUNTIME_PATH"))) {
-
-        if (fn)
-            pa_snprintf(s, l, "%s%c%s", e, PA_PATH_SEP_CHAR, fn);
-        else
-            pa_snprintf(s, l, "%s", e);
-
-    } else {
-        char u[256];
-
-        if (fn)
-            pa_snprintf(s, l, "%s%s%c%s", PA_USER_RUNTIME_PATH_PREFIX, pa_get_user_name(u, sizeof(u)), PA_PATH_SEP_CHAR, fn);
-        else
-            pa_snprintf(s, l, "%s%s", PA_USER_RUNTIME_PATH_PREFIX, pa_get_user_name(u, sizeof(u)));
-    }
-
-
-#ifdef OS_IS_WIN32
-    {
-        char buf[l];
-        strcpy(buf, s);
-        ExpandEnvironmentStrings(buf, s, l);
-    }
-#endif
-
-    return s;
+    return strlen(fn) >= 3 && isalpha(fn[0]) && fn[1] == ':' && fn[2] == '\\';
+#endif
+}
+
+char *pa_make_path_absolute(const char *p) {
+    char *r;
+    char *cwd;
+
+    pa_assert(p);
+
+    if (pa_is_path_absolute(p))
+        return pa_xstrdup(p);
+
+    if (!(cwd = pa_getcwd()))
+        return pa_xstrdup(p);
+
+    r = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", cwd, p);
+    pa_xfree(cwd);
+    return r;
+}
+
+/* if fn is null return the PulseAudio run time path in s (~/.pulse)
+ * if fn is non-null and starts with / return fn
+ * otherwise append fn to the run time path and return it */
+char *pa_runtime_path(const char *fn) {
+    char *rtp;
+
+    if (pa_is_path_absolute(fn))
+        return pa_xstrdup(fn);
+
+    rtp = pa_get_runtime_dir();
+
+    if (fn) {
+        char *r;
+        r = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", rtp, fn);
+        pa_xfree(rtp);
+        return r;
+    } else
+        return rtp;
 }
 
 /* Convert the string s to a signed integer in *ret_i */
@@ -1484,23 +1633,6 @@
     }
 }
 
-char *pa_make_path_absolute(const char *p) {
-    char *r;
-    char *cwd;
-
-    pa_assert(p);
-
-    if (p[0] == '/')
-        return pa_xstrdup(p);
-
-    if (!(cwd = pa_getcwd()))
-        return pa_xstrdup(p);
-
-    r = pa_sprintf_malloc("%s/%s", cwd, p);
-    pa_xfree(cwd);
-    return r;
-}
-
 void *pa_will_need(const void *p, size_t l) {
 #ifdef RLIMIT_MEMLOCK
     struct rlimit rlim;
@@ -1606,3 +1738,249 @@
         l *= 2;
     }
 }
+
+int pa_close_all(int except_fd, ...) {
+    va_list ap;
+    int n = 0, i, r;
+    int *p;
+
+    va_start(ap, except_fd);
+
+    if (except_fd >= 0)
+        for (n = 1; va_arg(ap, int) >= 0; n++)
+            ;
+
+    va_end(ap);
+
+    p = pa_xnew(int, n+1);
+
+    va_start(ap, except_fd);
+
+    i = 0;
+    if (except_fd >= 0) {
+        p[i++] = except_fd;
+
+        while ((p[i++] = va_arg(ap, int)) >= 0)
+            ;
+    }
+    p[i] = -1;
+
+    va_end(ap);
+
+    r = pa_close_allv(p);
+    free(p);
+
+    return r;
+}
+
+int pa_close_allv(const int except_fds[]) {
+    struct rlimit rl;
+    int fd;
+    int saved_errno;
+
+#ifdef __linux__
+
+    DIR *d;
+
+    if ((d = opendir("/proc/self/fd"))) {
+
+        struct dirent *de;
+
+        while ((de = readdir(d))) {
+            long l;
+            char *e = NULL;
+            int i;
+
+            if (de->d_name[0] == '.')
+                continue;
+
+            errno = 0;
+            l = strtol(de->d_name, &e, 10);
+            if (errno != 0 || !e || *e) {
+                closedir(d);
+                errno = EINVAL;
+                return -1;
+            }
+
+            fd = (int) l;
+
+            if ((long) fd != l) {
+                closedir(d);
+                errno = EINVAL;
+                return -1;
+            }
+
+            if (fd <= 3)
+                continue;
+
+            if (fd == dirfd(d))
+                continue;
+
+            for (i = 0; except_fds[i] >= 0; i++)
+                if (except_fds[i] == fd)
+                    continue;
+
+            if (close(fd) < 0) {
+                saved_errno = errno;
+                closedir(d);
+                errno = saved_errno;
+
+                return -1;
+            }
+        }
+
+        closedir(d);
+        return 0;
+    }
+
+#endif
+
+    if (getrlimit(RLIMIT_NOFILE, &rl) < 0)
+        return -1;
+
+    for (fd = 0; fd < (int) rl.rlim_max; fd++) {
+        int i;
+
+        if (fd <= 3)
+            continue;
+
+        for (i = 0; except_fds[i] >= 0; i++)
+            if (except_fds[i] == fd)
+                continue;
+
+        if (close(fd) < 0 && errno != EBADF)
+            return -1;
+    }
+
+    return 0;
+}
+
+int pa_unblock_sigs(int except, ...) {
+    va_list ap;
+    int n = 0, i, r;
+    int *p;
+
+    va_start(ap, except);
+
+    if (except >= 1)
+        for (n = 1; va_arg(ap, int) >= 0; n++)
+            ;
+
+    va_end(ap);
+
+    p = pa_xnew(int, n+1);
+
+    va_start(ap, except);
+
+    i = 0;
+    if (except >= 1) {
+        p[i++] = except;
+
+        while ((p[i++] = va_arg(ap, int)) >= 0)
+            ;
+    }
+    p[i] = -1;
+
+    va_end(ap);
+
+    r = pa_unblock_sigsv(p);
+    pa_xfree(p);
+
+    return r;
+}
+
+int pa_unblock_sigsv(const int except[]) {
+    int i;
+    sigset_t ss;
+
+    if (sigemptyset(&ss) < 0)
+        return -1;
+
+    for (i = 0; except[i] > 0; i++)
+        if (sigaddset(&ss, except[i]) < 0)
+            return -1;
+
+    return sigprocmask(SIG_SETMASK, &ss, NULL);
+}
+
+int pa_reset_sigs(int except, ...) {
+    va_list ap;
+    int n = 0, i, r;
+    int *p;
+
+    va_start(ap, except);
+
+    if (except >= 1)
+        for (n = 1; va_arg(ap, int) >= 0; n++)
+            ;
+
+    va_end(ap);
+
+    p = pa_xnew(int, n+1);
+
+    va_start(ap, except);
+
+    i = 0;
+    if (except >= 1) {
+        p[i++] = except;
+
+        while ((p[i++] = va_arg(ap, int)) >= 0)
+            ;
+    }
+    p[i] = -1;
+
+    va_end(ap);
+
+    r = pa_reset_sigsv(p);
+    pa_xfree(p);
+
+    return r;
+}
+
+int pa_reset_sigsv(const int except[]) {
+    int sig;
+
+    for (sig = 1; sig < _NSIG; sig++) {
+        int reset = 1;
+
+        switch (sig) {
+            case SIGKILL:
+            case SIGSTOP:
+                reset = 0;
+                break;
+
+            default: {
+                int i;
+
+                for (i = 0; except[i] > 0; i++) {
+                    if (sig == except[i]) {
+                        reset = 0;
+                        break;
+                    }
+                }
+            }
+        }
+
+        if (reset) {
+            struct sigaction sa;
+
+            memset(&sa, 0, sizeof(sa));
+            sa.sa_handler = SIG_DFL;
+
+            /* On Linux the first two RT signals are reserved by
+             * glibc, and sigaction() will return EINVAL for them. */
+            if ((sigaction(sig, &sa, NULL) < 0))
+                if (errno != EINVAL)
+                    return -1;
+        }
+    }
+
+    return 0;
+}
+
+void pa_set_env(const char *key, const char *value) {
+    pa_assert(key);
+    pa_assert(value);
+
+    putenv(pa_sprintf_malloc("%s=%s", key, value));
+}

Modified: branches/glitch-free/src/pulsecore/core-util.h
URL: http://0pointer.de/cgi-bin/viewcvs.cgi/branches/glitch-free/src/pulsecore/core-util.h?rev=2329&root=pulseaudio&r1=2328&r2=2329&view=diff
==============================================================================
--- branches/glitch-free/src/pulsecore/core-util.h (original)
+++ branches/glitch-free/src/pulsecore/core-util.h Thu May  1 21:51:05 2008
@@ -30,10 +30,26 @@
 #include <stdarg.h>
 #include <stdio.h>
 
+#ifdef HAVE_SYS_RESOURCE_H
+#include <sys/resource.h>
+#endif
+
 #include <pulse/gccmacro.h>
 #include <pulsecore/macro.h>
 
 struct timeval;
+
+/* These resource limits are pretty new on Linux, let's define them
+ * here manually, in case the kernel is newer than the glibc */
+#if !defined(RLIMIT_NICE) && defined(__linux__)
+#define RLIMIT_NICE 13
+#endif
+#if !defined(RLIMIT_RTPRIO) && defined(__linux__)
+#define RLIMIT_RTPRIO 14
+#endif
+#if !defined(RLIMIT_RTTIME) && defined(__linux__)
+#define RLIMIT_RTTIME 15
+#endif
 
 void pa_make_fd_nonblock(int fd);
 void pa_make_fd_cloexec(int fd);
@@ -61,6 +77,9 @@
 int pa_raise_priority(int nice_level);
 void pa_reset_priority(void);
 
+pa_bool_t pa_can_realtime(void);
+pa_bool_t pa_can_high_priority(void);
+
 int pa_parse_boolean(const char *s) PA_GCC_PURE;
 
 static inline const char *pa_yes_no(pa_bool_t b) {
@@ -69,6 +88,10 @@
 
 static inline const char *pa_strnull(const char *x) {
     return x ? x : "(null)";
+}
+
+static inline const char *pa_strempty(const char *x) {
+    return x ? x : "";
 }
 
 char *pa_split(const char *c, const char*delimiters, const char **state);
@@ -88,15 +111,17 @@
 int pa_lock_lockfile(const char *fn);
 int pa_unlock_lockfile(const char *fn, int fd);
 
-FILE *pa_open_config_file(const char *global, const char *local, const char *env, char **result, const char *mode);
-
 char *pa_hexstr(const uint8_t* d, size_t dlength, char *s, size_t slength);
 size_t pa_parsehex(const char *p, uint8_t *d, size_t dlength);
 
 int pa_startswith(const char *s, const char *pfx) PA_GCC_PURE;
 int pa_endswith(const char *s, const char *sfx) PA_GCC_PURE;
 
-char *pa_runtime_path(const char *fn, char *s, size_t l);
+FILE *pa_open_config_file(const char *global, const char *local, const char *env, char **result);
+char* pa_find_config_file(const char *global, const char *local, const char *env);
+
+char *pa_get_runtime_dir(void);
+char *pa_runtime_path(const char *fn);
 
 int pa_atoi(const char *s, int32_t *ret_i);
 int pa_atou(const char *s, uint32_t *ret_u);
@@ -108,6 +133,7 @@
 
 char *pa_getcwd(void);
 char *pa_make_path_absolute(const char *p);
+pa_bool_t pa_is_path_absolute(const char *p);
 
 void *pa_will_need(const void *p, size_t l);
 
@@ -133,6 +159,13 @@
 
 char *pa_readlink(const char *p);
 
-char *pa_get_state_dir(void);
+int pa_close_all(int except_fd, ...);
+int pa_close_allv(const int except_fds[]);
+int pa_unblock_sigs(int except, ...);
+int pa_unblock_sigsv(const int except[]);
+int pa_reset_sigs(int except, ...);
+int pa_reset_sigsv(const int except[]);
+
+void pa_set_env(const char *key, const char *value);
 
 #endif

Modified: branches/glitch-free/src/pulsecore/native-common.h
URL: http://0pointer.de/cgi-bin/viewcvs.cgi/branches/glitch-free/src/pulsecore/native-common.h?rev=2329&root=pulseaudio&r1=2328&r2=2329&view=diff
==============================================================================
--- branches/glitch-free/src/pulsecore/native-common.h (original)
+++ branches/glitch-free/src/pulsecore/native-common.h Thu May  1 21:51:05 2008
@@ -147,6 +147,9 @@
     PA_COMMAND_REMOVE_PLAYBACK_STREAM_PROPLIST,
     PA_COMMAND_REMOVE_CLIENT_PROPLIST,
 
+    /* SERVER->CLIENT */
+    PA_COMMAND_STARTED,
+
     PA_COMMAND_MAX
 };
 

Modified: branches/glitch-free/src/pulsecore/pid.c
URL: http://0pointer.de/cgi-bin/viewcvs.cgi/branches/glitch-free/src/pulsecore/pid.c?rev=2329&root=pulseaudio&r1=2328&r2=2329&view=diff
==============================================================================
--- branches/glitch-free/src/pulsecore/pid.c (original)
+++ branches/glitch-free/src/pulsecore/pid.c Thu May  1 21:51:05 2008
@@ -144,16 +144,16 @@
 int pa_pid_file_create(void) {
     int fd = -1;
     int ret = -1;
-    char fn[PATH_MAX];
     char t[20];
     pid_t pid;
     size_t l;
+    char *fn;
 
 #ifdef OS_IS_WIN32
     HANDLE process;
 #endif
 
-    pa_runtime_path("pid", fn, sizeof(fn));
+    fn = pa_runtime_path("pid");
 
     if ((fd = open_pid_file(fn, O_CREAT|O_RDWR)) < 0)
         goto fail;
@@ -200,17 +200,19 @@
         }
     }
 
+    pa_xfree(fn);
+
     return ret;
 }
 
 /* Remove the PID file, if it is ours */
 int pa_pid_file_remove(void) {
     int fd = -1;
-    char fn[PATH_MAX];
+    char *fn;
     int ret = -1;
     pid_t pid;
 
-    pa_runtime_path("pid", fn, sizeof(fn));
+    fn = pa_runtime_path("pid");
 
     if ((fd = open_pid_file(fn, O_RDWR)) < 0) {
         pa_log_warn("Failed to open PID file '%s': %s", fn, pa_cstrerror(errno));
@@ -253,6 +255,8 @@
             ret = -1;
         }
     }
+
+    pa_xfree(fn);
 
     return ret;
 }
@@ -272,7 +276,7 @@
  * process. */
 int pa_pid_file_kill(int sig, pid_t *pid, const char *binary_name) {
     int fd = -1;
-    char fn[PATH_MAX];
+    char *fn;
     int ret = -1;
     pid_t _pid;
 #ifdef __linux__
@@ -281,7 +285,7 @@
     if (!pid)
         pid = &_pid;
 
-    pa_runtime_path("pid", fn, sizeof(fn));
+    fn = pa_runtime_path("pid");
 
     if ((fd = open_pid_file(fn, O_RDONLY)) < 0)
         goto fail;
@@ -296,7 +300,7 @@
         if ((e = pa_readlink(fn))) {
             char *f = pa_path_get_filename(e);
             if (strcmp(f, binary_name)
-#if defined(__OPTIMIZE__)
+#if !defined(__OPTIMIZE__)
                 /* libtool likes to rename our binary names ... */
                 && !(pa_startswith(f, "lt-") && strcmp(f+3, binary_name) == 0)
 #endif
@@ -319,6 +323,8 @@
     pa_xfree(e);
 #endif
 
+    pa_xfree(fn);
+
     return ret;
 
 }

Modified: branches/glitch-free/src/pulsecore/protocol-cli.c
URL: http://0pointer.de/cgi-bin/viewcvs.cgi/branches/glitch-free/src/pulsecore/protocol-cli.c?rev=2329&root=pulseaudio&r1=2328&r2=2329&view=diff
==============================================================================
--- branches/glitch-free/src/pulsecore/protocol-cli.c (original)
+++ branches/glitch-free/src/pulsecore/protocol-cli.c Thu May  1 21:51:05 2008
@@ -82,7 +82,7 @@
     p = pa_xnew(pa_protocol_cli, 1);
     p->module = m;
     p->core = core;
-    p->server = server;
+    p->server = pa_socket_server_ref(server);
     p->connections = pa_idxset_new(NULL, NULL);
 
     pa_socket_server_set_callback(p->server, on_connection, p);

Modified: branches/glitch-free/src/pulsecore/protocol-esound.c
URL: http://0pointer.de/cgi-bin/viewcvs.cgi/branches/glitch-free/src/pulsecore/protocol-esound.c?rev=2329&root=pulseaudio&r1=2328&r2=2329&view=diff
==============================================================================
--- branches/glitch-free/src/pulsecore/protocol-esound.c (original)
+++ branches/glitch-free/src/pulsecore/protocol-esound.c Thu May  1 21:51:05 2008
@@ -1433,7 +1433,7 @@
     p->core = core;
     p->module = m;
     p->public = public;
-    p->server = server;
+    p->server = pa_socket_server_ref(server);
     pa_socket_server_set_callback(p->server, on_connection, p);
     p->connections = pa_idxset_new(NULL, NULL);
 

Modified: branches/glitch-free/src/pulsecore/protocol-http.c
URL: http://0pointer.de/cgi-bin/viewcvs.cgi/branches/glitch-free/src/pulsecore/protocol-http.c?rev=2329&root=pulseaudio&r1=2328&r2=2329&view=diff
==============================================================================
--- branches/glitch-free/src/pulsecore/protocol-http.c (original)
+++ branches/glitch-free/src/pulsecore/protocol-http.c Thu May  1 21:51:05 2008
@@ -255,7 +255,7 @@
     p = pa_xnew(pa_protocol_http, 1);
     p->module = m;
     p->core = core;
-    p->server = server;
+    p->server = pa_socket_server_ref(server);
     p->connections = pa_idxset_new(NULL, NULL);
 
     pa_socket_server_set_callback(p->server, on_connection, p);

Modified: branches/glitch-free/src/pulsecore/protocol-native.c
URL: http://0pointer.de/cgi-bin/viewcvs.cgi/branches/glitch-free/src/pulsecore/protocol-native.c?rev=2329&root=pulseaudio&r1=2328&r2=2329&view=diff
==============================================================================
--- branches/glitch-free/src/pulsecore/protocol-native.c (original)
+++ branches/glitch-free/src/pulsecore/protocol-native.c Thu May  1 21:51:05 2008
@@ -105,7 +105,6 @@
     pa_bool_t drain_request;
     uint32_t drain_tag;
     uint32_t syncid;
-    uint64_t underrun; /* length of underrun */
 
     pa_atomic_t missing;
     size_t minreq;
@@ -193,7 +192,8 @@
     PLAYBACK_STREAM_MESSAGE_REQUEST_DATA,      /* data requested from sink input from the main loop */
     PLAYBACK_STREAM_MESSAGE_UNDERFLOW,
     PLAYBACK_STREAM_MESSAGE_OVERFLOW,
-    PLAYBACK_STREAM_MESSAGE_DRAIN_ACK
+    PLAYBACK_STREAM_MESSAGE_DRAIN_ACK,
+    PLAYBACK_STREAM_MESSAGE_STARTED
 };
 
 enum {
@@ -689,10 +689,24 @@
             break;
         }
 
+        case PLAYBACK_STREAM_MESSAGE_STARTED:
+
+            if (s->connection->version >= 13) {
+                pa_tagstruct *t;
+
+                /* Notify the user we're overflowed*/
+                t = pa_tagstruct_new(NULL, 0);
+                pa_tagstruct_putu32(t, PA_COMMAND_STARTED);
+                pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+                pa_tagstruct_putu32(t, s->index);
+                pa_pstream_send_tagstruct(s->connection->pstream, t);
+            }
+
+            break;
+
         case PLAYBACK_STREAM_MESSAGE_DRAIN_ACK:
             pa_pstream_send_simple_ack(s->connection->pstream, PA_PTR_TO_UINT(userdata));
             break;
-
     }
 
     return 0;
@@ -886,7 +900,6 @@
     s->connection = c;
     s->syncid = syncid;
     s->sink_input = sink_input;
-    s->underrun = (uint64_t) -1;
 
     s->sink_input->parent.process_msg = sink_input_process_msg;
     s->sink_input->pop = sink_input_pop_cb;
@@ -1091,7 +1104,7 @@
 
 /*     pa_log("handle_seek: %llu -- %i", (unsigned long long) s->underrun, pa_memblockq_is_readable(s->memblockq)); */
 
-    if (s->underrun != 0) {
+    if (s->sink_input->thread_info.underrun_for > 0) {
 
 /*         pa_log("%lu vs. %lu", (unsigned long) pa_memblockq_get_length(s->memblockq), (unsigned long) pa_memblockq_get_prebuf(s->memblockq)); */
 
@@ -1099,13 +1112,13 @@
 
             size_t u = pa_memblockq_get_length(s->memblockq);
 
-            if (u >= s->underrun)
-                u = s->underrun;
+            if (u >= s->sink_input->thread_info.underrun_for)
+                u = s->sink_input->thread_info.underrun_for;
 
             /* We just ended an underrun, let's ask the sink
              * to rewrite */
-            s->sink_input->thread_info.ignore_rewind = TRUE;
-            pa_sink_input_request_rewind(s->sink_input, u, TRUE);
+
+            pa_sink_input_request_rewind(s->sink_input, u, TRUE, TRUE);
         }
 
     } else {
@@ -1117,7 +1130,7 @@
             /* OK, the sink already asked for this data, so
              * let's have it usk us again */
 
-            pa_sink_input_request_rewind(s->sink_input, indexr - indexw, FALSE);
+            pa_sink_input_request_rewind(s->sink_input, indexr - indexw, FALSE, FALSE);
     }
 
     request_bytes(s);
@@ -1272,12 +1285,9 @@
         if (s->drain_request && pa_sink_input_safe_to_remove(i)) {
             s->drain_request = FALSE;
             pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_DRAIN_ACK, PA_UINT_TO_PTR(s->drain_tag), 0, NULL, NULL);
-        } else if (s->underrun == 0)
+        } else if (i->thread_info.playing_for > 0)
             pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_UNDERFLOW, NULL, 0, NULL, NULL);
 
-        if (s->underrun != (size_t) -1)
-            s->underrun += nbytes;
-
 /*         pa_log("added %llu bytes, total is %llu", (unsigned long long) nbytes, (unsigned long long) s->underrun); */
 
         request_bytes(s);
@@ -1287,7 +1297,8 @@
 
 /*     pa_log("NOTUNDERRUN"); */
 
-    s->underrun = 0;
+    if (i->thread_info.underrun_for > 0)
+        pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_STARTED, NULL, 0, NULL, NULL);
 
     pa_memblockq_drop(s->memblockq, chunk->length);
     request_bytes(s);
@@ -1303,7 +1314,7 @@
     playback_stream_assert_ref(s);
 
     /* If we are in an underrun, then we don't rewind */
-    if (s->underrun != 0)
+    if (i->thread_info.underrun_for > 0)
         return;
 
     pa_memblockq_rewind(s->memblockq, nbytes);
@@ -2120,11 +2131,17 @@
     pa_tagstruct_put_usec(reply, latency);
 
     pa_tagstruct_put_usec(reply, 0);
-    pa_tagstruct_put_boolean(reply, pa_sink_input_get_state(s->sink_input) == PA_SINK_INPUT_RUNNING);
+    pa_tagstruct_put_boolean(reply, s->sink_input->thread_info.playing_for > 0);
     pa_tagstruct_put_timeval(reply, &tv);
     pa_tagstruct_put_timeval(reply, pa_gettimeofday(&now));
     pa_tagstruct_puts64(reply, s->write_index);
     pa_tagstruct_puts64(reply, s->read_index);
+
+    if (c->version >= 13) {
+        pa_tagstruct_putu64(reply, s->sink_input->thread_info.underrun_for);
+        pa_tagstruct_putu64(reply, s->sink_input->thread_info.playing_for);
+    }
+
     pa_pstream_send_tagstruct(c->pstream, reply);
 }
 
@@ -2152,7 +2169,7 @@
     reply = reply_new(tag);
     pa_tagstruct_put_usec(reply, s->source_output->source->monitor_of ? pa_sink_get_latency(s->source_output->source->monitor_of) : 0);
     pa_tagstruct_put_usec(reply, pa_source_get_latency(s->source_output->source));
-    pa_tagstruct_put_boolean(reply, FALSE);
+    pa_tagstruct_put_boolean(reply, pa_source_get_state(s->source_output->source) == PA_SOURCE_RUNNING);
     pa_tagstruct_put_timeval(reply, &tv);
     pa_tagstruct_put_timeval(reply, pa_gettimeofday(&now));
     pa_tagstruct_puts64(reply, pa_memblockq_get_write_index(s->memblockq));
@@ -3937,7 +3954,7 @@
 
 #ifdef HAVE_CREDS
     {
-        pa_bool_t a = 1;
+        pa_bool_t a = TRUE;
         if (pa_modargs_get_value_boolean(ma, "auth-group-enabled", &a) < 0) {
             pa_log("auth-group-enabled= expects a boolean argument.");
             return NULL;
@@ -3982,7 +3999,7 @@
     if (!(p = protocol_new_internal(core, m, ma)))
         return NULL;
 
-    p->server = server;
+    p->server = pa_socket_server_ref(server);
     pa_socket_server_set_callback(p->server, on_connection, p);
 
     if (pa_socket_server_get_address(p->server, t, sizeof(t))) {

Modified: branches/glitch-free/src/pulsecore/protocol-simple.c
URL: http://0pointer.de/cgi-bin/viewcvs.cgi/branches/glitch-free/src/pulsecore/protocol-simple.c?rev=2329&root=pulseaudio&r1=2328&r2=2329&view=diff
==============================================================================
--- branches/glitch-free/src/pulsecore/protocol-simple.c (original)
+++ branches/glitch-free/src/pulsecore/protocol-simple.c Thu May  1 21:51:05 2008
@@ -587,7 +587,7 @@
     p = pa_xnew0(pa_protocol_simple, 1);
     p->module = m;
     p->core = core;
-    p->server = server;
+    p->server = pa_socket_server_ref(server);
     p->connections = pa_idxset_new(NULL, NULL);
 
     p->sample_spec = core->default_sample_spec;

Modified: branches/glitch-free/src/pulsecore/sink-input.c
URL: http://0pointer.de/cgi-bin/viewcvs.cgi/branches/glitch-free/src/pulsecore/sink-input.c?rev=2329&root=pulseaudio&r1=2328&r2=2329&view=diff
==============================================================================
--- branches/glitch-free/src/pulsecore/sink-input.c (original)
+++ branches/glitch-free/src/pulsecore/sink-input.c Thu May  1 21:51:05 2008
@@ -106,6 +106,7 @@
     i->moved = NULL;
     i->kill = NULL;
     i->get_latency = NULL;
+    i->state_change = NULL;
 }
 
 pa_sink_input* pa_sink_input_new(
@@ -249,8 +250,8 @@
     i->thread_info.muted = i->muted;
     i->thread_info.requested_sink_latency = (pa_usec_t) -1;
     i->thread_info.rewrite_nbytes = 0;
-    i->thread_info.since_underrun = 0;
-    i->thread_info.ignore_rewind = FALSE;
+    i->thread_info.underrun_for = (uint64_t) -1;
+    i->thread_info.playing_for = 0;
 
     i->thread_info.render_memblockq = pa_memblockq_new(
             0,
@@ -328,7 +329,7 @@
 
     pa_sink_input_ref(i);
 
-    linked = PA_SINK_INPUT_LINKED(i->state);
+    linked = PA_SINK_INPUT_IS_LINKED(i->state);
 
     if (linked)
         pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK], i);
@@ -344,12 +345,11 @@
     if (pa_idxset_remove_by_data(i->sink->inputs, i, NULL))
         pa_sink_input_unref(i);
 
-    if (linked) {
+    update_n_corked(i, PA_SINK_INPUT_UNLINKED);
+    i->state = PA_SINK_INPUT_UNLINKED;
+
+    if (linked)
         pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_REMOVE_INPUT, i, 0, NULL);
-        sink_input_set_state(i, PA_SINK_INPUT_UNLINKED);
-        pa_sink_update_status(i->sink);
-    } else
-        i->state = PA_SINK_INPUT_UNLINKED;
 
     reset_callbacks(i);
 
@@ -368,7 +368,7 @@
     pa_assert(i);
     pa_assert(pa_sink_input_refcnt(i) == 0);
 
-    if (PA_SINK_INPUT_LINKED(i->state))
+    if (PA_SINK_INPUT_IS_LINKED(i->state))
         pa_sink_input_unlink(i);
 
     pa_log_info("Freeing input %u \"%s\"", i->index, pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_MEDIA_NAME)));
@@ -402,7 +402,7 @@
     state = i->flags & PA_SINK_INPUT_START_CORKED ? PA_SINK_INPUT_CORKED : PA_SINK_INPUT_RUNNING;
 
     update_n_corked(i, state);
-    i->thread_info.state = i->state = state;
+    i->state = state;
 
     pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_ADD_INPUT, i, 0, NULL);
 
@@ -416,7 +416,7 @@
 
 void pa_sink_input_kill(pa_sink_input*i) {
     pa_sink_input_assert_ref(i);
-    pa_assert(PA_SINK_INPUT_LINKED(i->state));
+    pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
 
     if (i->kill)
         i->kill(i);
@@ -426,7 +426,7 @@
     pa_usec_t r = 0;
 
     pa_sink_input_assert_ref(i);
-    pa_assert(PA_SINK_INPUT_LINKED(i->state));
+    pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
 
     if (pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_GET_LATENCY, &r, 0, NULL) < 0)
         r = 0;
@@ -445,7 +445,7 @@
     size_t ilength;
 
     pa_sink_input_assert_ref(i);
-    pa_assert(PA_SINK_INPUT_LINKED(i->thread_info.state));
+    pa_assert(PA_SINK_INPUT_IS_LINKED(i->thread_info.state));
     pa_assert(pa_frame_aligned(slength, &i->sink->sample_spec));
     pa_assert(chunk);
     pa_assert(volume);
@@ -510,7 +510,9 @@
             pa_atomic_store(&i->thread_info.drained, 1);
 
             pa_memblockq_seek(i->thread_info.render_memblockq, slength, PA_SEEK_RELATIVE_ON_READ);
-            i->thread_info.since_underrun = 0;
+            i->thread_info.playing_for = 0;
+            if (i->thread_info.underrun_for != (uint64_t) -1)
+                i->thread_info.underrun_for += slength;
             break;
         }
 
@@ -519,7 +521,8 @@
         pa_assert(tchunk.length > 0);
         pa_assert(tchunk.memblock);
 
-        i->thread_info.since_underrun += tchunk.length;
+        i->thread_info.underrun_for = 0;
+        i->thread_info.playing_for += tchunk.length;
 
         while (tchunk.length > 0) {
             pa_memchunk wchunk;
@@ -590,7 +593,7 @@
 void pa_sink_input_drop(pa_sink_input *i, size_t nbytes /* in sink sample spec */) {
     pa_sink_input_assert_ref(i);
 
-    pa_assert(PA_SINK_INPUT_LINKED(i->thread_info.state));
+    pa_assert(PA_SINK_INPUT_IS_LINKED(i->thread_info.state));
     pa_assert(pa_frame_aligned(nbytes, &i->sink->sample_spec));
     pa_assert(nbytes > 0);
 
@@ -610,13 +613,13 @@
 void pa_sink_input_process_rewind(pa_sink_input *i, size_t nbytes /* in sink sample spec */) {
     pa_sink_input_assert_ref(i);
 
-    pa_assert(PA_SINK_INPUT_LINKED(i->thread_info.state));
+    pa_assert(PA_SINK_INPUT_IS_LINKED(i->thread_info.state));
     pa_assert(pa_frame_aligned(nbytes, &i->sink->sample_spec));
 
 /*     pa_log_debug("rewind(%lu, %lu)", (unsigned long) nbytes, (unsigned long) i->thread_info.rewrite_nbytes); */
 
-    if (i->thread_info.ignore_rewind) {
-        i->thread_info.ignore_rewind = FALSE;
+    if (i->thread_info.underrun_for > 0) {
+        /* We don't rewind when we are underrun */
         i->thread_info.rewrite_nbytes = 0;
         return;
     }
@@ -668,7 +671,7 @@
 /* Called from thread context */
 void pa_sink_input_update_max_rewind(pa_sink_input *i, size_t nbytes  /* in the sink's sample spec */) {
     pa_sink_input_assert_ref(i);
-    pa_assert(PA_SINK_INPUT_LINKED(i->thread_info.state));
+    pa_assert(PA_SINK_INPUT_IS_LINKED(i->thread_info.state));
     pa_assert(pa_frame_aligned(nbytes, &i->sink->sample_spec));
 
     pa_memblockq_set_maxrewind(i->thread_info.render_memblockq, nbytes);
@@ -677,21 +680,41 @@
         i->update_max_rewind(i, i->thread_info.resampler ? pa_resampler_request(i->thread_info.resampler, nbytes) : nbytes);
 }
 
+static pa_usec_t fixup_latency(pa_sink *s, pa_usec_t usec) {
+    pa_sink_assert_ref(s);
+
+    if (usec == (pa_usec_t) -1)
+        return usec;
+
+    if (s->max_latency > 0 && usec > s->max_latency)
+        usec = s->max_latency;
+
+    if (s->min_latency > 0 && usec < s->min_latency)
+        usec = s->min_latency;
+
+    return usec;
+}
+
+pa_usec_t pa_sink_input_set_requested_latency_within_thread(pa_sink_input *i, pa_usec_t usec) {
+
+    usec = fixup_latency(i->sink, usec);
+
+    i->thread_info.requested_sink_latency = usec;
+    pa_sink_invalidate_requested_latency(i->sink);
+
+    return usec;
+}
+
 pa_usec_t pa_sink_input_set_requested_latency(pa_sink_input *i, pa_usec_t usec) {
     pa_sink_input_assert_ref(i);
 
-    if (usec != (pa_usec_t) -1) {
-
-        if (i->sink->max_latency > 0 && usec > i->sink->max_latency)
-            usec = i->sink->max_latency;
-
-        if (i->sink->min_latency > 0 && usec < i->sink->min_latency)
-            usec = i->sink->min_latency;
-    }
-
-    if (PA_SINK_INPUT_LINKED(i->state))
+    usec = fixup_latency(i->sink, usec);
+
+    if (PA_SINK_INPUT_IS_LINKED(i->state))
         pa_asyncmsgq_post(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_REQUESTED_LATENCY, NULL, (int64_t) usec, NULL, NULL);
     else {
+        /* If this sink input is not realized yet, we have to touch
+         * the thread info data directly */
         i->thread_info.requested_sink_latency = usec;
         i->sink->thread_info.requested_latency_valid = FALSE;
     }
@@ -701,7 +724,7 @@
 
 void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume) {
     pa_sink_input_assert_ref(i);
-    pa_assert(PA_SINK_INPUT_LINKED(i->state));
+    pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
 
     if (pa_cvolume_equal(&i->volume, volume))
         return;
@@ -714,7 +737,7 @@
 
 const pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i) {
     pa_sink_input_assert_ref(i);
-    pa_assert(PA_SINK_INPUT_LINKED(i->state));
+    pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
 
     return &i->volume;
 }
@@ -722,7 +745,7 @@
 void pa_sink_input_set_mute(pa_sink_input *i, pa_bool_t mute) {
     pa_assert(i);
     pa_sink_input_assert_ref(i);
-    pa_assert(PA_SINK_INPUT_LINKED(i->state));
+    pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
 
     if (!i->muted == !mute)
         return;
@@ -735,21 +758,21 @@
 
 int pa_sink_input_get_mute(pa_sink_input *i) {
     pa_sink_input_assert_ref(i);
-    pa_assert(PA_SINK_INPUT_LINKED(i->state));
+    pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
 
     return !!i->muted;
 }
 
 void pa_sink_input_cork(pa_sink_input *i, pa_bool_t b) {
     pa_sink_input_assert_ref(i);
-    pa_assert(PA_SINK_INPUT_LINKED(i->state));
+    pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
 
     sink_input_set_state(i, b ? PA_SINK_INPUT_CORKED : PA_SINK_INPUT_RUNNING);
 }
 
 int pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate) {
     pa_sink_input_assert_ref(i);
-    pa_assert(PA_SINK_INPUT_LINKED(i->state));
+    pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
     pa_return_val_if_fail(i->thread_info.resampler, -1);
 
     if (i->sample_spec.rate == rate)
@@ -780,7 +803,7 @@
     else
         pa_proplist_unset(i->proplist, PA_PROP_MEDIA_NAME);
 
-    if (PA_SINK_INPUT_LINKED(i->state)) {
+    if (PA_SINK_INPUT_IS_LINKED(i->state)) {
         pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED], i);
         pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
     }
@@ -792,7 +815,7 @@
     return i->resample_method;
 }
 
-int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, int immediately) {
+int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, pa_bool_t immediately) {
     pa_resampler *new_resampler;
     pa_sink *origin;
     pa_usec_t silence_usec = 0;
@@ -800,7 +823,7 @@
     pa_sink_input_move_hook_data hook_data;
 
     pa_sink_input_assert_ref(i);
-    pa_assert(PA_SINK_INPUT_LINKED(i->state));
+    pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
     pa_sink_assert_ref(dest);
 
     origin = i->sink;
@@ -983,7 +1006,7 @@
     return 0;
 }
 
-static void set_state(pa_sink_input *i, pa_sink_input_state_t state) {
+void pa_sink_input_set_state_within_thread(pa_sink_input *i, pa_sink_input_state_t state) {
     pa_sink_input_assert_ref(i);
 
     if ((state == PA_SINK_INPUT_DRAINED || state == PA_SINK_INPUT_RUNNING) &&
@@ -998,16 +1021,17 @@
 
         /* This will tell the implementing sink input driver to rewind
          * so that the unplayed already mixed data is not lost */
-        pa_sink_input_request_rewind(i, 0, FALSE);
+        pa_sink_input_request_rewind(i, 0, FALSE, FALSE);
 
     } else if (i->thread_info.state == PA_SINK_INPUT_CORKED && state != PA_SINK_INPUT_CORKED) {
 
         /* OK, we're being uncorked. Make sure we're not rewound when
          * the hw buffer is remixed and request a remix. */
-        i->thread_info.ignore_rewind = TRUE;
-        i->thread_info.since_underrun = 0;
-        pa_sink_request_rewind(i->sink, 0);
-    }
+        pa_sink_input_request_rewind(i, 0, TRUE, TRUE);
+    }
+
+    if (i->state_change)
+        i->state_change(i, state);
 
     i->thread_info.state = state;
 }
@@ -1017,17 +1041,17 @@
     pa_sink_input *i = PA_SINK_INPUT(o);
 
     pa_sink_input_assert_ref(i);
-    pa_assert(PA_SINK_INPUT_LINKED(i->thread_info.state));
+    pa_assert(PA_SINK_INPUT_IS_LINKED(i->thread_info.state));
 
     switch (code) {
         case PA_SINK_INPUT_MESSAGE_SET_VOLUME:
             i->thread_info.volume = *((pa_cvolume*) userdata);
-            pa_sink_input_request_rewind(i, 0, FALSE);
+            pa_sink_input_request_rewind(i, 0, FALSE, FALSE);
             return 0;
 
         case PA_SINK_INPUT_MESSAGE_SET_MUTE:
             i->thread_info.muted = PA_PTR_TO_UINT(userdata);
-            pa_sink_input_request_rewind(i, 0, FALSE);
+            pa_sink_input_request_rewind(i, 0, FALSE, FALSE);
             return 0;
 
         case PA_SINK_INPUT_MESSAGE_GET_LATENCY: {
@@ -1048,22 +1072,20 @@
         case PA_SINK_INPUT_MESSAGE_SET_STATE: {
             pa_sink_input *ssync;
 
-            set_state(i, PA_PTR_TO_UINT(userdata));
+            pa_sink_input_set_state_within_thread(i, PA_PTR_TO_UINT(userdata));
 
             for (ssync = i->thread_info.sync_prev; ssync; ssync = ssync->thread_info.sync_prev)
-                set_state(ssync, PA_PTR_TO_UINT(userdata));
+                pa_sink_input_set_state_within_thread(ssync, PA_PTR_TO_UINT(userdata));
 
             for (ssync = i->thread_info.sync_next; ssync; ssync = ssync->thread_info.sync_next)
-                set_state(ssync, PA_PTR_TO_UINT(userdata));
+                pa_sink_input_set_state_within_thread(ssync, PA_PTR_TO_UINT(userdata));
 
             return 0;
         }
 
         case PA_SINK_INPUT_MESSAGE_SET_REQUESTED_LATENCY:
 
-            i->thread_info.requested_sink_latency = (pa_usec_t) offset;
-            pa_sink_invalidate_requested_latency(i->sink);
-
+            pa_sink_input_set_requested_latency_within_thread(i, (pa_usec_t) offset);
             return 0;
     }
 
@@ -1088,8 +1110,8 @@
     return TRUE;
 }
 
-void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes  /* in our sample spec */, pa_bool_t ignore_underruns) {
-    size_t l, lbq;
+void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes  /* in our sample spec */, pa_bool_t ignore_underruns, pa_bool_t not_here) {
+    size_t lbq;
 
     pa_sink_input_assert_ref(i);
 
@@ -1097,9 +1119,16 @@
     if (i->state == PA_SINK_INPUT_CORKED)
         return;
 
-    lbq = pa_memblockq_get_length(i->thread_info.render_memblockq);
-
+    /* Calculate how much we can rewind locally without having to
+     * touch the sink */
+    if (not_here)
+        lbq = 0;
+    else
+        lbq = pa_memblockq_get_length(i->thread_info.render_memblockq);
+
+    /* Check if rewinding for the maximum is requested, and if so, fix up */
     if (nbytes <= 0) {
+
         /* Calulate maximum number of bytes that could be rewound in theory */
         nbytes = i->sink->thread_info.max_rewind + lbq;
 
@@ -1110,26 +1139,33 @@
             nbytes;
     }
 
-    /* Increase the number of bytes to rewrite, never decrease */
-    if (nbytes > i->thread_info.rewrite_nbytes)
+    if (not_here) {
+        i->thread_info.playing_for = 0;
+        i->thread_info.underrun_for = (uint64_t) -1;
+    } else {
+        /* Increase the number of bytes to rewrite, never decrease */
+        if (nbytes < i->thread_info.rewrite_nbytes)
+            nbytes = i->thread_info.rewrite_nbytes;
+
+        /* Make sure to not overwrite over underruns */
+        if (!ignore_underruns)
+            if ((int64_t) nbytes > i->thread_info.playing_for)
+                nbytes = (size_t) i->thread_info.playing_for;
+
         i->thread_info.rewrite_nbytes = nbytes;
-
-    if (!ignore_underruns) {
-        /* Make sure to not overwrite over underruns */
-        if ((int64_t) i->thread_info.rewrite_nbytes > i->thread_info.since_underrun)
-            i->thread_info.rewrite_nbytes = (size_t) i->thread_info.since_underrun;
     }
 
     /* Transform to sink domain */
-    l = i->thread_info.resampler ?
-        pa_resampler_result(i->thread_info.resampler, i->thread_info.rewrite_nbytes) :
-        i->thread_info.rewrite_nbytes;
-
-    if (l <= 0)
+    nbytes =
+        i->thread_info.resampler ?
+        pa_resampler_result(i->thread_info.resampler, nbytes) :
+        nbytes;
+
+    if (nbytes <= 0)
         return;
 
-    if (l > lbq)
-        pa_sink_request_rewind(i->sink, l - lbq);
+    if (nbytes > lbq)
+        pa_sink_request_rewind(i->sink, nbytes - lbq);
 }
 
 pa_memchunk* pa_sink_input_get_silence(pa_sink_input *i, pa_memchunk *ret) {

Modified: branches/glitch-free/src/pulsecore/sink-input.h
URL: http://0pointer.de/cgi-bin/viewcvs.cgi/branches/glitch-free/src/pulsecore/sink-input.h?rev=2329&root=pulseaudio&r1=2328&r2=2329&view=diff
==============================================================================
--- branches/glitch-free/src/pulsecore/sink-input.h (original)
+++ branches/glitch-free/src/pulsecore/sink-input.h Thu May  1 21:51:05 2008
@@ -46,7 +46,7 @@
     PA_SINK_INPUT_UNLINKED      /*< The stream is dead */
 } pa_sink_input_state_t;
 
-static inline pa_bool_t PA_SINK_INPUT_LINKED(pa_sink_input_state_t x) {
+static inline pa_bool_t PA_SINK_INPUT_IS_LINKED(pa_sink_input_state_t x) {
     return x == PA_SINK_INPUT_DRAINED || x == PA_SINK_INPUT_RUNNING || x == PA_SINK_INPUT_CORKED;
 }
 
@@ -106,7 +106,7 @@
     void (*process_rewind) (pa_sink_input *i, size_t nbytes);     /* may NOT be NULL */
 
     /* Called whenever the maximum rewindable size of the sink
-     * changes. Called from RT context. */
+     * changes. Called from IO context. */
     void (*update_max_rewind) (pa_sink_input *i, size_t nbytes); /* may be NULL */
 
     /* If non-NULL this function is called when the input is first
@@ -138,6 +138,10 @@
     instead. */
     pa_usec_t (*get_latency) (pa_sink_input *i); /* may be NULL */
 
+    /* If non_NULL this function is called from thread context if the
+     * state changes. The old state is found in thread_info.state.  */
+    void (*state_change) (pa_sink_input *i, pa_sink_input_state_t state); /* may be NULL */
+
     struct {
         pa_sink_input_state_t state;
         pa_atomic_t drained, render_memblockq_is_empty;
@@ -152,7 +156,7 @@
         pa_memblockq *render_memblockq;
 
         size_t rewrite_nbytes;
-        int64_t since_underrun;
+        uint64_t underrun_for, playing_for;
         pa_bool_t ignore_rewind;
 
         pa_sink_input *sync_prev, *sync_next;
@@ -237,7 +241,7 @@
 sink driver will call ->rewind() and pass the number of bytes that
 could be rewound in the HW device. This functionality is required for
 implementing the "zero latency" write-through functionality. */
-void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes, pa_bool_t ignore_rewind);
+void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes, pa_bool_t ignore_rewind, pa_bool_t not_here);
 
 /* Callable by everyone from main thread*/
 
@@ -257,7 +261,7 @@
 
 pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i);
 
-int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, int immediately);
+int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, pa_bool_t immediately);
 
 pa_sink_input_state_t pa_sink_input_get_state(pa_sink_input *i);
 
@@ -269,7 +273,11 @@
 void pa_sink_input_process_rewind(pa_sink_input *i, size_t nbytes /* in the sink's sample spec */);
 void pa_sink_input_update_max_rewind(pa_sink_input *i, size_t nbytes  /* in the sink's sample spec */);
 
+void pa_sink_input_set_state_within_thread(pa_sink_input *i, pa_sink_input_state_t state);
+
 int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
+
+pa_usec_t pa_sink_input_set_requested_latency_within_thread(pa_sink_input *i, pa_usec_t usec);
 
 typedef struct pa_sink_input_move_info {
     pa_sink_input *sink_input;

Modified: branches/glitch-free/src/pulsecore/sink.c
URL: http://0pointer.de/cgi-bin/viewcvs.cgi/branches/glitch-free/src/pulsecore/sink.c?rev=2329&root=pulseaudio&r1=2328&r2=2329&view=diff
==============================================================================
--- branches/glitch-free/src/pulsecore/sink.c (original)
+++ branches/glitch-free/src/pulsecore/sink.c Thu May  1 21:51:05 2008
@@ -265,8 +265,8 @@
         return 0;
 
     suspend_change =
-        (s->state == PA_SINK_SUSPENDED && PA_SINK_OPENED(state)) ||
-        (PA_SINK_OPENED(s->state) && state == PA_SINK_SUSPENDED);
+        (s->state == PA_SINK_SUSPENDED && PA_SINK_IS_OPENED(state)) ||
+        (PA_SINK_IS_OPENED(s->state) && state == PA_SINK_SUSPENDED);
 
     if (s->set_state)
         if ((ret = s->set_state(s, state)) < 0)
@@ -328,7 +328,7 @@
      * may be called multiple times on the same sink without bad
      * effects. */
 
-    linked = PA_SINK_LINKED(s->state);
+    linked = PA_SINK_IS_LINKED(s->state);
 
     if (linked)
         pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_UNLINK], s);
@@ -366,7 +366,7 @@
     pa_assert(s);
     pa_assert(pa_sink_refcnt(s) == 0);
 
-    if (PA_SINK_LINKED(s->state))
+    if (PA_SINK_IS_LINKED(s->state))
         pa_sink_unlink(s);
 
     pa_log_info("Freeing sink %u \"%s\"", s->index, s->name);
@@ -397,7 +397,6 @@
 
 void pa_sink_set_asyncmsgq(pa_sink *s, pa_asyncmsgq *q) {
     pa_sink_assert_ref(s);
-    pa_assert(q);
 
     s->asyncmsgq = q;
 
@@ -407,7 +406,6 @@
 
 void pa_sink_set_rtpoll(pa_sink *s, pa_rtpoll *p) {
     pa_sink_assert_ref(s);
-    pa_assert(p);
 
     s->rtpoll = p;
     if (s->monitor_source)
@@ -416,7 +414,7 @@
 
 int pa_sink_update_status(pa_sink*s) {
     pa_sink_assert_ref(s);
-    pa_assert(PA_SINK_LINKED(s->state));
+    pa_assert(PA_SINK_IS_LINKED(s->state));
 
     if (s->state == PA_SINK_SUSPENDED)
         return 0;
@@ -426,7 +424,7 @@
 
 int pa_sink_suspend(pa_sink *s, pa_bool_t suspend) {
     pa_sink_assert_ref(s);
-    pa_assert(PA_SINK_LINKED(s->state));
+    pa_assert(PA_SINK_IS_LINKED(s->state));
 
     if (suspend)
         return sink_set_state(s, PA_SINK_SUSPENDED);
@@ -438,7 +436,10 @@
     pa_sink_input *i;
     void *state = NULL;
     pa_sink_assert_ref(s);
-    pa_assert(PA_SINK_LINKED(s->state));
+    pa_assert(PA_SINK_IS_LINKED(s->state));
+
+    /* Make sure the sink code already reset the counter! */
+    pa_assert(s->thread_info.rewind_nbytes <= 0);
 
     if (nbytes <= 0)
         return;
@@ -450,8 +451,9 @@
         pa_sink_input_process_rewind(i, nbytes);
     }
 
-    if (s->monitor_source && PA_SOURCE_OPENED(pa_source_get_state(s->monitor_source)))
+    if (s->monitor_source && PA_SOURCE_IS_OPENED(pa_source_get_state(s->monitor_source)))
         pa_source_process_rewind(s->monitor_source, nbytes);
+
 }
 
 static unsigned fill_mix_info(pa_sink *s, size_t *length, pa_mix_info *info, unsigned maxinfo) {
@@ -557,7 +559,7 @@
     size_t block_size_max;
 
     pa_sink_assert_ref(s);
-    pa_assert(PA_SINK_OPENED(s->thread_info.state));
+    pa_assert(PA_SINK_IS_OPENED(s->thread_info.state));
     pa_assert(pa_frame_aligned(length, &s->sample_spec));
     pa_assert(result);
 
@@ -621,7 +623,7 @@
     if (s->thread_info.state == PA_SINK_RUNNING)
         inputs_drop(s, info, n, result->length);
 
-    if (s->monitor_source && PA_SOURCE_OPENED(pa_source_get_state(s->monitor_source)))
+    if (s->monitor_source && PA_SOURCE_IS_OPENED(pa_source_get_state(s->monitor_source)))
         pa_source_post(s->monitor_source, result);
 
     pa_sink_unref(s);
@@ -633,7 +635,7 @@
     size_t length, block_size_max;
 
     pa_sink_assert_ref(s);
-    pa_assert(PA_SINK_OPENED(s->thread_info.state));
+    pa_assert(PA_SINK_IS_OPENED(s->thread_info.state));
     pa_assert(target);
     pa_assert(target->memblock);
     pa_assert(target->length > 0);
@@ -700,7 +702,7 @@
     if (s->thread_info.state == PA_SINK_RUNNING)
         inputs_drop(s, info, n, target->length);
 
-    if (s->monitor_source && PA_SOURCE_OPENED(pa_source_get_state(s->monitor_source)))
+    if (s->monitor_source && PA_SOURCE_IS_OPENED(pa_source_get_state(s->monitor_source)))
         pa_source_post(s->monitor_source, target);
 
     pa_sink_unref(s);
@@ -711,7 +713,7 @@
     size_t l, d;
 
     pa_sink_assert_ref(s);
-    pa_assert(PA_SINK_OPENED(s->thread_info.state));
+    pa_assert(PA_SINK_IS_OPENED(s->thread_info.state));
     pa_assert(target);
     pa_assert(target->memblock);
     pa_assert(target->length > 0);
@@ -739,7 +741,7 @@
 
 void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result) {
     pa_sink_assert_ref(s);
-    pa_assert(PA_SINK_OPENED(s->thread_info.state));
+    pa_assert(PA_SINK_IS_OPENED(s->thread_info.state));
     pa_assert(length > 0);
     pa_assert(pa_frame_aligned(length, &s->sample_spec));
     pa_assert(result);
@@ -755,50 +757,15 @@
     pa_sink_render_into_full(s, result);
 }
 
-void pa_sink_skip(pa_sink *s, size_t length) {
-    pa_sink_input *i;
-    void *state = NULL;
-
-    pa_sink_assert_ref(s);
-    pa_assert(PA_SINK_OPENED(s->thread_info.state));
-    pa_assert(length > 0);
-    pa_assert(pa_frame_aligned(length, &s->sample_spec));
-
-    s->thread_info.rewind_nbytes = 0;
-
-    if (pa_source_used_by(s->monitor_source)) {
-        pa_memchunk chunk;
-
-        /* If something is connected to our monitor source, we have to
-         * pass valid data to it */
-
-        while (length > 0) {
-            pa_sink_render(s, length, &chunk);
-            pa_memblock_unref(chunk.memblock);
-
-            pa_assert(chunk.length <= length);
-            length -= chunk.length;
-        }
-
-    } else {
-        /* Ok, noone cares about the rendered data, so let's not even render it */
-
-        while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL))) {
-            pa_sink_input_assert_ref(i);
-            pa_sink_input_drop(i, length);
-        }
-    }
-}
-
 pa_usec_t pa_sink_get_latency(pa_sink *s) {
     pa_usec_t usec = 0;
 
     pa_sink_assert_ref(s);
-    pa_assert(PA_SINK_LINKED(s->state));
+    pa_assert(PA_SINK_IS_LINKED(s->state));
 
     /* The returned value is supposed to be in the time domain of the sound card! */
 
-    if (!PA_SINK_OPENED(s->state))
+    if (!PA_SINK_IS_OPENED(s->state))
         return 0;
 
     if (s->get_latency)
@@ -814,7 +781,7 @@
     int changed;
 
     pa_sink_assert_ref(s);
-    pa_assert(PA_SINK_LINKED(s->state));
+    pa_assert(PA_SINK_IS_LINKED(s->state));
     pa_assert(volume);
 
     changed = !pa_cvolume_equal(volume, &s->volume);
@@ -834,7 +801,7 @@
     struct pa_cvolume old_volume;
 
     pa_sink_assert_ref(s);
-    pa_assert(PA_SINK_LINKED(s->state));
+    pa_assert(PA_SINK_IS_LINKED(s->state));
 
     old_volume = s->volume;
 
@@ -854,7 +821,7 @@
     int changed;
 
     pa_sink_assert_ref(s);
-    pa_assert(PA_SINK_LINKED(s->state));
+    pa_assert(PA_SINK_IS_LINKED(s->state));
 
     changed = s->muted != mute;
     s->muted = mute;
@@ -873,7 +840,7 @@
     pa_bool_t old_muted;
 
     pa_sink_assert_ref(s);
-    pa_assert(PA_SINK_LINKED(s->state));
+    pa_assert(PA_SINK_IS_LINKED(s->state));
 
     old_muted = s->muted;
 
@@ -914,7 +881,7 @@
         pa_xfree(n);
     }
 
-    if (PA_SINK_LINKED(s->state)) {
+    if (PA_SINK_IS_LINKED(s->state)) {
         pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
         pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_PROPLIST_CHANGED], s);
     }
@@ -924,7 +891,7 @@
     unsigned ret;
 
     pa_sink_assert_ref(s);
-    pa_assert(PA_SINK_LINKED(s->state));
+    pa_assert(PA_SINK_IS_LINKED(s->state));
 
     ret = pa_idxset_size(s->inputs);
 
@@ -941,7 +908,7 @@
     unsigned ret;
 
     pa_sink_assert_ref(s);
-    pa_assert(PA_SINK_LINKED(s->state));
+    pa_assert(PA_SINK_IS_LINKED(s->state));
 
     ret = pa_idxset_size(s->inputs);
     pa_assert(ret >= s->n_corked);
@@ -980,25 +947,27 @@
                 i->thread_info.sync_next->thread_info.sync_prev = i;
             }
 
-            pa_sink_input_update_max_rewind(i, s->thread_info.max_rewind);
-
             pa_assert(!i->thread_info.attached);
             i->thread_info.attached = TRUE;
 
             if (i->attach)
                 i->attach(i);
+
+            pa_sink_input_set_state_within_thread(i, i->state);
+
+            pa_sink_input_update_max_rewind(i, s->thread_info.max_rewind);
+
+            pa_sink_invalidate_requested_latency(s);
+
+            /* We don't rewind here automatically. This is left to the
+             * sink input implementor because some sink inputs need a
+             * slow start, i.e. need some time to buffer client
+             * samples before beginning streaming. */
 
             /* If you change anything here, make sure to change the
              * ghost sink input handling a few lines down at
              * PA_SINK_MESSAGE_REMOVE_INPUT_AND_BUFFER, too. */
 
-            pa_sink_invalidate_requested_latency(s);
-
-            /* Make sure we're not rewound when the hw buffer is remixed and request a remix*/
-            i->thread_info.ignore_rewind = TRUE;
-            i->thread_info.since_underrun = 0;
-            pa_sink_request_rewind(s, 0);
-
             return 0;
         }
 
@@ -1008,6 +977,8 @@
             /* If you change anything here, make sure to change the
              * sink input handling a few lines down at
              * PA_SINK_MESSAGE_REMOVE_INPUT_AND_BUFFER, too. */
+
+            pa_sink_input_set_state_within_thread(i, i->state);
 
             if (i->detach)
                 i->detach(i);
@@ -1036,7 +1007,6 @@
                 pa_sink_input_unref(i);
 
             pa_sink_invalidate_requested_latency(s);
-
             pa_sink_request_rewind(s, 0);
 
             return 0;
@@ -1117,11 +1087,9 @@
 
                 if (info->ghost_sink_input->attach)
                     info->ghost_sink_input->attach(info->ghost_sink_input);
-
             }
 
             pa_sink_invalidate_requested_latency(s);
-
             pa_sink_request_rewind(s, 0);
 
             return 0;
@@ -1196,14 +1164,14 @@
 
 void pa_sink_detach(pa_sink *s) {
     pa_sink_assert_ref(s);
-    pa_assert(PA_SINK_LINKED(s->state));
+    pa_assert(PA_SINK_IS_LINKED(s->state));
 
     pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_DETACH, NULL, 0, NULL);
 }
 
 void pa_sink_attach(pa_sink *s) {
     pa_sink_assert_ref(s);
-    pa_assert(PA_SINK_LINKED(s->state));
+    pa_assert(PA_SINK_IS_LINKED(s->state));
 
     pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_ATTACH, NULL, 0, NULL);
 }
@@ -1213,7 +1181,7 @@
     void *state = NULL;
 
     pa_sink_assert_ref(s);
-    pa_assert(PA_SINK_LINKED(s->thread_info.state));
+    pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
 
     while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
         if (i->detach)
@@ -1228,7 +1196,7 @@
     void *state = NULL;
 
     pa_sink_assert_ref(s);
-    pa_assert(PA_SINK_LINKED(s->thread_info.state));
+    pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
 
     while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
         if (i->attach)
@@ -1240,7 +1208,7 @@
 
 void pa_sink_request_rewind(pa_sink*s, size_t nbytes) {
     pa_sink_assert_ref(s);
-    pa_assert(PA_SINK_LINKED(s->thread_info.state));
+    pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
 
     if (nbytes <= 0)
         nbytes = s->thread_info.max_rewind;
@@ -1290,9 +1258,9 @@
     pa_usec_t usec = 0;
 
     pa_sink_assert_ref(s);
-    pa_assert(PA_SINK_LINKED(s->state));
-
-    if (!PA_SINK_OPENED(s->state))
+    pa_assert(PA_SINK_IS_LINKED(s->state));
+
+    if (!PA_SINK_IS_OPENED(s->state))
         return 0;
 
     if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) < 0)
@@ -1325,7 +1293,7 @@
 void pa_sink_invalidate_requested_latency(pa_sink *s) {
 
     pa_sink_assert_ref(s);
-    pa_assert(PA_SINK_LINKED(s->thread_info.state));
+    pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
 
     if (!s->thread_info.requested_latency_valid)
         return;

Modified: branches/glitch-free/src/pulsecore/sink.h
URL: http://0pointer.de/cgi-bin/viewcvs.cgi/branches/glitch-free/src/pulsecore/sink.h?rev=2329&root=pulseaudio&r1=2328&r2=2329&view=diff
==============================================================================
--- branches/glitch-free/src/pulsecore/sink.h (original)
+++ branches/glitch-free/src/pulsecore/sink.h Thu May  1 21:51:05 2008
@@ -33,7 +33,6 @@
 #include <pulse/channelmap.h>
 #include <pulse/volume.h>
 
-#include <pulsecore/core-def.h>
 #include <pulsecore/core.h>
 #include <pulsecore/idxset.h>
 #include <pulsecore/source.h>
@@ -52,11 +51,11 @@
     PA_SINK_UNLINKED
 } pa_sink_state_t;
 
-static inline pa_bool_t PA_SINK_OPENED(pa_sink_state_t x) {
+static inline pa_bool_t PA_SINK_IS_OPENED(pa_sink_state_t x) {
     return x == PA_SINK_RUNNING || x == PA_SINK_IDLE;
 }
 
-static inline pa_bool_t PA_SINK_LINKED(pa_sink_state_t x) {
+static inline pa_bool_t PA_SINK_IS_LINKED(pa_sink_state_t x) {
     return x == PA_SINK_RUNNING || x == PA_SINK_IDLE || x == PA_SINK_SUSPENDED;
 }
 
@@ -94,13 +93,42 @@
     pa_usec_t min_latency; /* we won't go below this latency */
     pa_usec_t max_latency; /* An upper limit for the latencies */
 
+    /* Called when the main loop requests a state change. Called from
+     * main loop context. If returns -1 the state change will be
+     * inhibited */
     int (*set_state)(pa_sink *s, pa_sink_state_t state); /* may be NULL */
-    int (*get_volume)(pa_sink *s);             /* dito */
+
+    /* Callled when the volume is queried. Called from main loop
+     * context. If this is NULL a PA_SINK_MESSAGE_GET_VOLUME message
+     * will be sent to the IO thread instead. */
+    int (*get_volume)(pa_sink *s);             /* may be null */
+
+    /* Called when the volume shall be changed. Called from main loop
+     * context. If this is NULL a PA_SINK_MESSAGE_SET_VOLUME message
+     * will be sent to the IO thread instead. */
     int (*set_volume)(pa_sink *s);             /* dito */
+
+    /* Called when the mute setting is queried. Called from main loop
+     * context. If this is NULL a PA_SINK_MESSAGE_GET_MUTE message
+     * will be sent to the IO thread instead. */
     int (*get_mute)(pa_sink *s);               /* dito */
+
+    /* Called when the mute setting shall be changed. Called from main
+     * loop context. If this is NULL a PA_SINK_MESSAGE_SET_MUTE
+     * message will be sent to the IO thread instead. */
     int (*set_mute)(pa_sink *s);               /* dito */
-    pa_usec_t (*get_latency)(pa_sink *s);      /* dito */
+
+    /* Called when the latency is queried. Called from main loop
+    context. If this is NULL a PA_SINK_MESSAGE_GET_LATENCY message
+    will be sent to the IO thread instead. */
+    pa_usec_t (*get_latency)(pa_sink *s); /* dito */
+
+    /* Called when a rewind request is issued. Called from IO thread
+     * context. */
     void (*request_rewind)(pa_sink *s);        /* dito */
+
+    /* Called when a the requested latency is changed. Called from IO
+     * thread context. */
     void (*update_requested_latency)(pa_sink *s); /* dito */
 
     /* Contains copies of the above data so that the real-time worker
@@ -213,7 +241,6 @@
 void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result);
 void pa_sink_render_into(pa_sink*s, pa_memchunk *target);
 void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target);
-void pa_sink_skip(pa_sink *s, size_t length);
 
 void pa_sink_process_rewind(pa_sink *s, size_t nbytes);
 

Modified: branches/glitch-free/src/pulsecore/sound-file-stream.c
URL: http://0pointer.de/cgi-bin/viewcvs.cgi/branches/glitch-free/src/pulsecore/sound-file-stream.c?rev=2329&root=pulseaudio&r1=2328&r2=2329&view=diff
==============================================================================
--- branches/glitch-free/src/pulsecore/sound-file-stream.c (original)
+++ branches/glitch-free/src/pulsecore/sound-file-stream.c Thu May  1 21:51:05 2008
@@ -55,6 +55,8 @@
     SNDFILE *sndfile;
     sf_count_t (*readf_function)(SNDFILE *sndfile, void *ptr, sf_count_t frames);
 
+    /* We need this memblockq here to easily fulfill rewind requests
+     * (even beyond the file start!) */
     pa_memblockq *memblockq;
 } file_stream;
 
@@ -66,6 +68,7 @@
 #define FILE_STREAM(o) (file_stream_cast(o))
 static PA_DEFINE_CHECK_TYPE(file_stream, pa_msgobject);
 
+/* Called from main context */
 static void file_stream_unlink(file_stream *u) {
     pa_assert(u);
 
@@ -80,6 +83,7 @@
     file_stream_unref(u);
 }
 
+/* Called from main context */
 static void file_stream_free(pa_object *o) {
     file_stream *u = FILE_STREAM(o);
     pa_assert(u);
@@ -93,6 +97,7 @@
     pa_xfree(u);
 }
 
+/* Called from main context */
 static int file_stream_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) {
     file_stream *u = FILE_STREAM(o);
     file_stream_assert_ref(u);
@@ -106,6 +111,7 @@
     return 0;
 }
 
+/* Called from main context */
 static void sink_input_kill_cb(pa_sink_input *i) {
     file_stream *u;
 
@@ -116,6 +122,22 @@
     file_stream_unlink(u);
 }
 
+/* Called from IO thread context */
+static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t state) {
+    file_stream *u;
+
+    pa_sink_input_assert_ref(i);
+    u = FILE_STREAM(i->userdata);
+    file_stream_assert_ref(u);
+
+    /* If we are added for the first time, ask for a rewinding so that
+     * we are heard right-away. */
+    if (PA_SINK_INPUT_IS_LINKED(state) &&
+        i->thread_info.state == PA_SINK_INPUT_INIT)
+        pa_sink_input_request_rewind(i, 0, FALSE, TRUE);
+}
+
+/* Called from IO thread context */
 static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
     file_stream *u;
 
@@ -131,6 +153,9 @@
 
     for (;;) {
         pa_memchunk tchunk;
+        size_t fs;
+        void *p;
+        sf_count_t n;
 
         if (pa_memblockq_peek(u->memblockq, chunk) >= 0) {
             pa_memblockq_drop(u->memblockq, chunk->length);
@@ -143,36 +168,19 @@
         tchunk.memblock = pa_memblock_new(i->sink->core->mempool, length);
         tchunk.index = 0;
 
+        p = pa_memblock_acquire(tchunk.memblock);
+
         if (u->readf_function) {
-            sf_count_t n;
-            void *p;
-            size_t fs = pa_frame_size(&i->sample_spec);
-
-            p = pa_memblock_acquire(tchunk.memblock);
+            fs = pa_frame_size(&i->sample_spec);
             n = u->readf_function(u->sndfile, p, length/fs);
-            pa_memblock_release(tchunk.memblock);
-
-            if (n <= 0)
-                n = 0;
-
-            tchunk.length = n * fs;
-
         } else {
-            sf_count_t n;
-            void *p;
-
-            p = pa_memblock_acquire(tchunk.memblock);
+            fs = 1;
             n = sf_read_raw(u->sndfile, p, length);
-            pa_memblock_release(tchunk.memblock);
-
-            if (n <= 0)
-                n = 0;
-
-            tchunk.length = n;
         }
 
-        if (tchunk.length <= 0) {
-
+        pa_memblock_release(tchunk.memblock);
+
+        if (n <= 0) {
             pa_memblock_unref(tchunk.memblock);
 
             sf_close(u->sndfile);
@@ -180,6 +188,8 @@
             break;
         }
 
+        tchunk.length = n * fs;
+
         pa_memblockq_push(u->memblockq, &tchunk);
         pa_memblock_unref(tchunk.memblock);
     }
@@ -196,7 +206,7 @@
     }
 
     return -1;
-}
+ }
 
 static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
     file_stream *u;
@@ -334,6 +344,7 @@
     u->sink_input->process_rewind = sink_input_process_rewind_cb;
     u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
     u->sink_input->kill = sink_input_kill_cb;
+    u->sink_input->state_change = sink_input_state_change_cb;
     u->sink_input->userdata = u;
 
     pa_sink_input_get_silence(u->sink_input, &silence);

Modified: branches/glitch-free/src/pulsecore/source-output.c
URL: http://0pointer.de/cgi-bin/viewcvs.cgi/branches/glitch-free/src/pulsecore/source-output.c?rev=2329&root=pulseaudio&r1=2328&r2=2329&view=diff
==============================================================================
--- branches/glitch-free/src/pulsecore/source-output.c (original)
+++ branches/glitch-free/src/pulsecore/source-output.c Thu May  1 21:51:05 2008
@@ -88,6 +88,7 @@
     o->moved = NULL;
     o->kill = NULL;
     o->get_latency = NULL;
+    o->state_change = NULL;
 }
 
 pa_source_output* pa_source_output_new(
@@ -263,7 +264,7 @@
 
     pa_source_output_ref(o);
 
-    linked = PA_SOURCE_OUTPUT_LINKED(o->state);
+    linked = PA_SOURCE_OUTPUT_IS_LINKED(o->state);
 
     if (linked)
         pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK], o);
@@ -295,7 +296,7 @@
 
     pa_assert(pa_source_output_refcnt(o) == 0);
 
-    if (PA_SOURCE_OUTPUT_LINKED(o->state))
+    if (PA_SOURCE_OUTPUT_IS_LINKED(o->state))
         pa_source_output_unlink(o);
 
     pa_log_info("Freeing output %u \"%s\"", o->index, pa_strnull(pa_proplist_gets(o->proplist, PA_PROP_MEDIA_NAME)));
@@ -335,7 +336,7 @@
 
 void pa_source_output_kill(pa_source_output*o) {
     pa_source_output_assert_ref(o);
-    pa_assert(PA_SOURCE_OUTPUT_LINKED(o->state));
+    pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
 
     if (o->kill)
         o->kill(o);
@@ -345,7 +346,7 @@
     pa_usec_t r = 0;
 
     pa_source_output_assert_ref(o);
-    pa_assert(PA_SOURCE_OUTPUT_LINKED(o->state));
+    pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
 
     if (pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_GET_LATENCY, &r, 0, NULL) < 0)
         r = 0;
@@ -362,7 +363,7 @@
     size_t limit, mbs = 0;
 
     pa_source_output_assert_ref(o);
-    pa_assert(PA_SOURCE_OUTPUT_LINKED(o->thread_info.state));
+    pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->thread_info.state));
     pa_assert(chunk);
     pa_assert(pa_frame_aligned(chunk->length, &o->source->sample_spec));
 
@@ -419,7 +420,7 @@
 void pa_source_output_process_rewind(pa_source_output *o, size_t nbytes /* in sink sample spec */) {
     pa_source_output_assert_ref(o);
 
-    pa_assert(PA_SOURCE_OUTPUT_LINKED(o->state));
+    pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
     pa_assert(pa_frame_aligned(nbytes, &o->source->sample_spec));
 
     if (nbytes <= 0)
@@ -446,28 +447,48 @@
 /* Called from thread context */
 void pa_source_output_update_max_rewind(pa_source_output *o, size_t nbytes  /* in the source's sample spec */) {
     pa_source_output_assert_ref(o);
-    pa_assert(PA_SOURCE_OUTPUT_LINKED(o->thread_info.state));
+    pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->thread_info.state));
     pa_assert(pa_frame_aligned(nbytes, &o->source->sample_spec));
 
     if (o->update_max_rewind)
         o->update_max_rewind(o, o->thread_info.resampler ? pa_resampler_result(o->thread_info.resampler, nbytes) : nbytes);
 }
 
+static pa_usec_t fixup_latency(pa_source *s, pa_usec_t usec) {
+    pa_source_assert_ref(s);
+
+    if (usec == (pa_usec_t) -1)
+        return usec;
+
+    if (s->max_latency > 0 && usec > s->max_latency)
+        usec = s->max_latency;
+
+    if (s->min_latency > 0 && usec < s->min_latency)
+        usec = s->min_latency;
+
+    return usec;
+}
+
+pa_usec_t pa_source_output_set_requested_latency_within_thread(pa_source_output *o, pa_usec_t usec) {
+
+    usec = fixup_latency(o->source, usec);
+
+    o->thread_info.requested_source_latency = usec;
+    pa_source_invalidate_requested_latency(o->source);
+
+    return usec;
+}
+
 pa_usec_t pa_source_output_set_requested_latency(pa_source_output *o, pa_usec_t usec) {
     pa_source_output_assert_ref(o);
 
-    if (usec != (pa_usec_t) -1) {
-
-        if (o->source->max_latency > 0 && usec > o->source->max_latency)
-            usec = o->source->max_latency;
-
-        if (o->source->min_latency > 0 && usec < o->source->min_latency)
-            usec = o->source->min_latency;
-    }
-
-    if (PA_SOURCE_OUTPUT_LINKED(o->state))
+    usec = fixup_latency(o->source, usec);
+
+    if (PA_SOURCE_OUTPUT_IS_LINKED(o->state))
         pa_asyncmsgq_post(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_REQUESTED_LATENCY, NULL, (int64_t) usec, NULL, NULL);
     else {
+        /* If this sink input is not realized yet, we have to touch
+         * the thread info data directly */
         o->thread_info.requested_source_latency = usec;
         o->source->thread_info.requested_latency_valid = FALSE;
     }
@@ -477,14 +498,14 @@
 
 void pa_source_output_cork(pa_source_output *o, pa_bool_t b) {
     pa_source_output_assert_ref(o);
-    pa_assert(PA_SOURCE_OUTPUT_LINKED(o->state));
+    pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
 
     source_output_set_state(o, b ? PA_SOURCE_OUTPUT_CORKED : PA_SOURCE_OUTPUT_RUNNING);
 }
 
 int pa_source_output_set_rate(pa_source_output *o, uint32_t rate) {
     pa_source_output_assert_ref(o);
-    pa_assert(PA_SOURCE_OUTPUT_LINKED(o->state));
+    pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
     pa_return_val_if_fail(o->thread_info.resampler, -1);
 
     if (o->sample_spec.rate == rate)
@@ -515,7 +536,7 @@
     else
         pa_proplist_unset(o->proplist, PA_PROP_MEDIA_NAME);
 
-    if (PA_SOURCE_OUTPUT_LINKED(o->state)) {
+    if (PA_SOURCE_OUTPUT_IS_LINKED(o->state)) {
         pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED], o);
         pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
     }
@@ -533,7 +554,7 @@
     pa_source_output_move_hook_data hook_data;
 
     pa_source_output_assert_ref(o);
-    pa_assert(PA_SOURCE_OUTPUT_LINKED(o->state));
+    pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
     pa_source_assert_ref(dest);
 
     origin = o->source;
@@ -616,12 +637,21 @@
     return 0;
 }
 
+void pa_source_output_set_state_within_thread(pa_source_output *o, pa_source_output_state_t state) {
+    pa_source_output_assert_ref(o);
+
+    if (o->state_change)
+        o->state_change(o, state);
+
+    o->thread_info.state = state;
+}
+
 /* Called from thread context */
 int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, int64_t offset, pa_memchunk* chunk) {
     pa_source_output *o = PA_SOURCE_OUTPUT(mo);
 
     pa_source_output_assert_ref(o);
-    pa_assert(PA_SOURCE_OUTPUT_LINKED(o->thread_info.state));
+    pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->thread_info.state));
 
     switch (code) {
 
@@ -633,25 +663,20 @@
             return 0;
         }
 
-        case PA_SOURCE_OUTPUT_MESSAGE_SET_RATE: {
+        case PA_SOURCE_OUTPUT_MESSAGE_SET_RATE:
 
             o->thread_info.sample_spec.rate = PA_PTR_TO_UINT(userdata);
             pa_resampler_set_output_rate(o->thread_info.resampler, PA_PTR_TO_UINT(userdata));
-
             return 0;
-        }
-
-        case PA_SOURCE_OUTPUT_MESSAGE_SET_STATE: {
-            o->thread_info.state = PA_PTR_TO_UINT(userdata);
-
+
+        case PA_SOURCE_OUTPUT_MESSAGE_SET_STATE:
+
+            pa_source_output_set_state_within_thread(o, PA_PTR_TO_UINT(userdata));
             return 0;
-        }
 
         case PA_SOURCE_OUTPUT_MESSAGE_SET_REQUESTED_LATENCY:
 
-            o->thread_info.requested_source_latency = (pa_usec_t) offset;
-            pa_source_invalidate_requested_latency(o->source);
-
+            pa_source_output_set_requested_latency_within_thread(o, (pa_usec_t) offset);
             return 0;
     }
 

Modified: branches/glitch-free/src/pulsecore/source-output.h
URL: http://0pointer.de/cgi-bin/viewcvs.cgi/branches/glitch-free/src/pulsecore/source-output.h?rev=2329&root=pulseaudio&r1=2328&r2=2329&view=diff
==============================================================================
--- branches/glitch-free/src/pulsecore/source-output.h (original)
+++ branches/glitch-free/src/pulsecore/source-output.h Thu May  1 21:51:05 2008
@@ -42,7 +42,7 @@
     PA_SOURCE_OUTPUT_UNLINKED
 } pa_source_output_state_t;
 
-static inline pa_bool_t PA_SOURCE_OUTPUT_LINKED(pa_source_output_state_t x) {
+static inline pa_bool_t PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_state_t x) {
     return x == PA_SOURCE_OUTPUT_RUNNING || x == PA_SOURCE_OUTPUT_CORKED;
 }
 
@@ -83,11 +83,11 @@
     void (*push)(pa_source_output *o, const pa_memchunk *chunk);
 
     /* Only relevant for monitor sources right now: called when the
-     * recorded stream is rewound. */
+     * recorded stream is rewound. Called from IO context*/
     void (*process_rewind)(pa_source_output *o, size_t nbytes);
 
     /* Called whenever the maximum rewindable size of the source
-     * changes. Called from RT context. */
+     * changes. Called from IO thread context. */
     void (*update_max_rewind) (pa_source_output *o, size_t nbytes); /* may be NULL */
 
     /* If non-NULL this function is called when the output is first
@@ -115,6 +115,10 @@
     PA_SOURCE_OUTPUT_MESSAGE_GET_LATENCY message is sent to the IO
     thread instead. */
     pa_usec_t (*get_latency) (pa_source_output *o); /* may be NULL */
+
+    /* If non_NULL this function is called from thread context if the
+     * state changes. The old state is found in thread_info.state.  */
+    void (*state_change) (pa_source_output *o, pa_source_output_state_t state); /* may be NULL */
 
     struct {
         pa_source_output_state_t state;
@@ -213,4 +217,8 @@
 
 int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
 
+void pa_source_output_set_state_within_thread(pa_source_output *o, pa_source_output_state_t state);
+
+pa_usec_t pa_source_output_set_requested_latency_within_thread(pa_source_output *o, pa_usec_t usec);
+
 #endif

Modified: branches/glitch-free/src/pulsecore/source.c
URL: http://0pointer.de/cgi-bin/viewcvs.cgi/branches/glitch-free/src/pulsecore/source.c?rev=2329&root=pulseaudio&r1=2328&r2=2329&view=diff
==============================================================================
--- branches/glitch-free/src/pulsecore/source.c (original)
+++ branches/glitch-free/src/pulsecore/source.c Thu May  1 21:51:05 2008
@@ -228,8 +228,8 @@
         return 0;
 
     suspend_change =
-        (s->state == PA_SOURCE_SUSPENDED && PA_SOURCE_OPENED(state)) ||
-        (PA_SOURCE_OPENED(s->state) && state == PA_SOURCE_SUSPENDED);
+        (s->state == PA_SOURCE_SUSPENDED && PA_SOURCE_IS_OPENED(state)) ||
+        (PA_SOURCE_IS_OPENED(s->state) && state == PA_SOURCE_SUSPENDED);
 
     if (s->set_state)
         if ((ret = s->set_state(s, state)) < 0)
@@ -284,7 +284,7 @@
     /* See pa_sink_unlink() for a couple of comments how this function
      * works. */
 
-    linked = PA_SOURCE_LINKED(s->state);
+    linked = PA_SOURCE_IS_LINKED(s->state);
 
     if (linked)
         pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], s);
@@ -319,7 +319,7 @@
     pa_assert(s);
     pa_assert(pa_source_refcnt(s) == 0);
 
-    if (PA_SOURCE_LINKED(s->state))
+    if (PA_SOURCE_IS_LINKED(s->state))
         pa_source_unlink(s);
 
     pa_log_info("Freeing source %u \"%s\"", s->index, s->name);
@@ -345,21 +345,19 @@
 
 void pa_source_set_asyncmsgq(pa_source *s, pa_asyncmsgq *q) {
     pa_source_assert_ref(s);
-    pa_assert(q);
 
     s->asyncmsgq = q;
 }
 
 void pa_source_set_rtpoll(pa_source *s, pa_rtpoll *p) {
     pa_source_assert_ref(s);
-    pa_assert(p);
 
     s->rtpoll = p;
 }
 
 int pa_source_update_status(pa_source*s) {
     pa_source_assert_ref(s);
-    pa_assert(PA_SOURCE_LINKED(s->state));
+    pa_assert(PA_SOURCE_IS_LINKED(s->state));
 
     if (s->state == PA_SOURCE_SUSPENDED)
         return 0;
@@ -369,7 +367,7 @@
 
 int pa_source_suspend(pa_source *s, pa_bool_t suspend) {
     pa_source_assert_ref(s);
-    pa_assert(PA_SOURCE_LINKED(s->state));
+    pa_assert(PA_SOURCE_IS_LINKED(s->state));
 
     if (suspend)
         return source_set_state(s, PA_SOURCE_SUSPENDED);
@@ -382,7 +380,7 @@
     void *state = NULL;
 
     pa_source_assert_ref(s);
-    pa_assert(PA_SOURCE_OPENED(s->thread_info.state));
+    pa_assert(PA_SOURCE_IS_OPENED(s->thread_info.state));
 
     if (nbytes <= 0)
         return;
@@ -400,7 +398,7 @@
     void *state = NULL;
 
     pa_source_assert_ref(s);
-    pa_assert(PA_SOURCE_OPENED(s->thread_info.state));
+    pa_assert(PA_SOURCE_IS_OPENED(s->thread_info.state));
     pa_assert(chunk);
 
     if (s->thread_info.state != PA_SOURCE_RUNNING)
@@ -436,9 +434,9 @@
     pa_usec_t usec;
 
     pa_source_assert_ref(s);
-    pa_assert(PA_SOURCE_LINKED(s->state));
-
-    if (!PA_SOURCE_OPENED(s->state))
+    pa_assert(PA_SOURCE_IS_LINKED(s->state));
+
+    if (!PA_SOURCE_IS_OPENED(s->state))
         return 0;
 
     if (s->get_latency)
@@ -454,7 +452,7 @@
     int changed;
 
     pa_source_assert_ref(s);
-    pa_assert(PA_SOURCE_LINKED(s->state));
+    pa_assert(PA_SOURCE_IS_LINKED(s->state));
     pa_assert(volume);
 
     changed = !pa_cvolume_equal(volume, &s->volume);
@@ -474,7 +472,7 @@
     pa_cvolume old_volume;
 
     pa_source_assert_ref(s);
-    pa_assert(PA_SOURCE_LINKED(s->state));
+    pa_assert(PA_SOURCE_IS_LINKED(s->state));
 
     old_volume = s->volume;
 
@@ -494,7 +492,7 @@
     int changed;
 
     pa_source_assert_ref(s);
-    pa_assert(PA_SOURCE_LINKED(s->state));
+    pa_assert(PA_SOURCE_IS_LINKED(s->state));
 
     changed = s->muted != mute;
     s->muted = mute;
@@ -513,7 +511,7 @@
     pa_bool_t old_muted;
 
     pa_source_assert_ref(s);
-    pa_assert(PA_SOURCE_LINKED(s->state));
+    pa_assert(PA_SOURCE_IS_LINKED(s->state));
 
     old_muted = s->muted;
 
@@ -546,7 +544,7 @@
     else
         pa_proplist_unset(s->proplist, PA_PROP_DEVICE_DESCRIPTION);
 
-    if (PA_SOURCE_LINKED(s->state)) {
+    if (PA_SOURCE_IS_LINKED(s->state)) {
         pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
         pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_PROPLIST_CHANGED], s);
     }
@@ -554,7 +552,7 @@
 
 unsigned pa_source_linked_by(pa_source *s) {
     pa_source_assert_ref(s);
-    pa_assert(PA_SOURCE_LINKED(s->state));
+    pa_assert(PA_SOURCE_IS_LINKED(s->state));
 
     return pa_idxset_size(s->outputs);
 }
@@ -563,7 +561,7 @@
     unsigned ret;
 
     pa_source_assert_ref(s);
-    pa_assert(PA_SOURCE_LINKED(s->state));
+    pa_assert(PA_SOURCE_IS_LINKED(s->state));
 
     ret = pa_idxset_size(s->outputs);
     pa_assert(ret >= s->n_corked);
@@ -590,6 +588,8 @@
             if (o->attach)
                 o->attach(o);
 
+            pa_source_output_set_state_within_thread(o, o->state);
+
             pa_source_invalidate_requested_latency(s);
 
             return 0;
@@ -597,6 +597,8 @@
 
         case PA_SOURCE_MESSAGE_REMOVE_OUTPUT: {
             pa_source_output *o = PA_SOURCE_OUTPUT(userdata);
+
+            pa_source_output_set_state_within_thread(o, o->state);
 
             if (o->detach)
                 o->detach(o);
@@ -676,14 +678,14 @@
 
 void pa_source_detach(pa_source *s) {
     pa_source_assert_ref(s);
-    pa_assert(PA_SOURCE_LINKED(s->state));
+    pa_assert(PA_SOURCE_IS_LINKED(s->state));
 
     pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_DETACH, NULL, 0, NULL);
 }
 
 void pa_source_attach(pa_source *s) {
     pa_source_assert_ref(s);
-    pa_assert(PA_SOURCE_LINKED(s->state));
+    pa_assert(PA_SOURCE_IS_LINKED(s->state));
 
     pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_ATTACH, NULL, 0, NULL);
 }
@@ -693,7 +695,7 @@
     void *state = NULL;
 
     pa_source_assert_ref(s);
-    pa_assert(PA_SOURCE_LINKED(s->thread_info.state));
+    pa_assert(PA_SOURCE_IS_LINKED(s->thread_info.state));
 
     while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
         if (o->detach)
@@ -705,7 +707,7 @@
     void *state = NULL;
 
     pa_source_assert_ref(s);
-    pa_assert(PA_SOURCE_LINKED(s->thread_info.state));
+    pa_assert(PA_SOURCE_IS_LINKED(s->thread_info.state));
 
     while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
         if (o->attach)
@@ -746,9 +748,9 @@
     pa_usec_t usec;
 
     pa_source_assert_ref(s);
-    pa_assert(PA_SOURCE_LINKED(s->state));
-
-    if (!PA_SOURCE_OPENED(s->state))
+    pa_assert(PA_SOURCE_IS_LINKED(s->state));
+
+    if (!PA_SOURCE_IS_OPENED(s->state))
         return 0;
 
     if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) < 0)
@@ -778,7 +780,7 @@
 void pa_source_invalidate_requested_latency(pa_source *s) {
 
     pa_source_assert_ref(s);
-    pa_assert(PA_SOURCE_LINKED(s->thread_info.state));
+    pa_assert(PA_SOURCE_IS_LINKED(s->thread_info.state));
 
     if (!s->thread_info.requested_latency_valid)
         return;

Modified: branches/glitch-free/src/pulsecore/source.h
URL: http://0pointer.de/cgi-bin/viewcvs.cgi/branches/glitch-free/src/pulsecore/source.h?rev=2329&root=pulseaudio&r1=2328&r2=2329&view=diff
==============================================================================
--- branches/glitch-free/src/pulsecore/source.h (original)
+++ branches/glitch-free/src/pulsecore/source.h Thu May  1 21:51:05 2008
@@ -33,7 +33,6 @@
 #include <pulse/channelmap.h>
 #include <pulse/volume.h>
 
-#include <pulsecore/core-def.h>
 #include <pulsecore/core.h>
 #include <pulsecore/idxset.h>
 #include <pulsecore/memblock.h>
@@ -54,11 +53,11 @@
     PA_SOURCE_UNLINKED
 } pa_source_state_t;
 
-static inline pa_bool_t PA_SOURCE_OPENED(pa_source_state_t x) {
+static inline pa_bool_t PA_SOURCE_IS_OPENED(pa_source_state_t x) {
     return x == PA_SOURCE_RUNNING || x == PA_SOURCE_IDLE;
 }
 
-static inline pa_bool_t PA_SOURCE_LINKED(pa_source_state_t x) {
+static inline pa_bool_t PA_SOURCE_IS_LINKED(pa_source_state_t x) {
     return x == PA_SOURCE_RUNNING || x == PA_SOURCE_IDLE || x == PA_SOURCE_SUSPENDED;
 }
 

Modified: branches/glitch-free/src/utils/pacmd.c
URL: http://0pointer.de/cgi-bin/viewcvs.cgi/branches/glitch-free/src/utils/pacmd.c?rev=2329&root=pulseaudio&r1=2328&r2=2329&view=diff
==============================================================================
--- branches/glitch-free/src/utils/pacmd.c (original)
+++ branches/glitch-free/src/utils/pacmd.c Thu May  1 21:51:05 2008
@@ -36,6 +36,7 @@
 
 #include <pulse/error.h>
 #include <pulse/util.h>
+#include <pulse/xmalloc.h>
 
 #include <pulsecore/core-util.h>
 #include <pulsecore/log.h>
@@ -49,6 +50,7 @@
     char ibuf[256], obuf[256];
     size_t ibuf_index, ibuf_length, obuf_index, obuf_length;
     fd_set ifds, ofds;
+    char *cli;
 
     if (pa_pid_file_check_running(&pid, "pulseaudio") < 0) {
         pa_log("no PulseAudio daemon running");
@@ -62,7 +64,10 @@
 
     memset(&sa, 0, sizeof(sa));
     sa.sun_family = AF_UNIX;
-    pa_runtime_path("cli", sa.sun_path, sizeof(sa.sun_path));
+
+    cli = pa_runtime_path("cli");
+    pa_strlcpy(sa.sun_path, cli, sizeof(sa.sun_path));
+    pa_xfree(cli);
 
     for (i = 0; i < 5; i++) {
         int r;




More information about the pulseaudio-commits mailing list