mirror of
				https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
				synced 2025-11-03 09:01:50 -05:00 
			
		
		
		
	- Check process name when dealing with PID files
- Add new PA_STREAM_FIX_CHANNELS, FIX_RATE, FIX_FORMAT, DONT_MOVE, VARIABLE_RATES to pa_sream_flags_t adn implement it - Expose those flags in pacat - Add notifications about device suspend/resume to the protocol and expose them in libpulse - Allow changing of buffer_attr during playback - allow disabling for remixing globally - hookup polkit support git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@2067 fefdeb5f-60dc-0310-8127-8f9354f1896f
This commit is contained in:
		
							parent
							
								
									4ac6b53478
								
							
						
					
					
						commit
						14a9b80afb
					
				
					 27 changed files with 1498 additions and 231 deletions
				
			
		| 
						 | 
				
			
			@ -71,6 +71,7 @@ static const pa_daemon_conf default_conf = {
 | 
			
		|||
    .log_target = PA_LOG_SYSLOG,
 | 
			
		||||
    .log_level = PA_LOG_NOTICE,
 | 
			
		||||
    .resample_method = PA_RESAMPLER_AUTO,
 | 
			
		||||
    .disable_remixing = FALSE,
 | 
			
		||||
    .config_file = NULL,
 | 
			
		||||
    .use_pid_file = TRUE,
 | 
			
		||||
    .system_instance = FALSE,
 | 
			
		||||
| 
						 | 
				
			
			@ -410,6 +411,7 @@ int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) {
 | 
			
		|||
        { "default-fragments",          parse_fragments,          NULL },
 | 
			
		||||
        { "default-fragment-size-msec", parse_fragment_size_msec, NULL },
 | 
			
		||||
        { "nice-level",                 parse_nice_level,         NULL },
 | 
			
		||||
        { "disable-remixing",           pa_config_parse_bool,     NULL },
 | 
			
		||||
#ifdef HAVE_SYS_RESOURCE_H
 | 
			
		||||
        { "rlimit-as",                  parse_rlimit,             NULL },
 | 
			
		||||
        { "rlimit-core",                parse_rlimit,             NULL },
 | 
			
		||||
| 
						 | 
				
			
			@ -458,33 +460,34 @@ int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) {
 | 
			
		|||
    table[22].data = c;
 | 
			
		||||
    table[23].data = c;
 | 
			
		||||
    table[24].data = c;
 | 
			
		||||
    table[25].data = &c->disable_remixing;
 | 
			
		||||
#ifdef HAVE_SYS_RESOURCE_H
 | 
			
		||||
    table[25].data = &c->rlimit_as;
 | 
			
		||||
    table[26].data = &c->rlimit_core;
 | 
			
		||||
    table[27].data = &c->rlimit_data;
 | 
			
		||||
    table[28].data = &c->rlimit_fsize;
 | 
			
		||||
    table[29].data = &c->rlimit_nofile;
 | 
			
		||||
    table[30].data = &c->rlimit_stack;
 | 
			
		||||
    table[26].data = &c->rlimit_as;
 | 
			
		||||
    table[27].data = &c->rlimit_core;
 | 
			
		||||
    table[28].data = &c->rlimit_data;
 | 
			
		||||
    table[29].data = &c->rlimit_fsize;
 | 
			
		||||
    table[30].data = &c->rlimit_nofile;
 | 
			
		||||
    table[31].data = &c->rlimit_stack;
 | 
			
		||||
#ifdef RLIMIT_NPROC
 | 
			
		||||
    table[31].data = &c->rlimit_nproc;
 | 
			
		||||
    table[32].data = &c->rlimit_nproc;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef RLIMIT_MEMLOCK
 | 
			
		||||
#ifndef RLIMIT_NPROC
 | 
			
		||||
#error "Houston, we have a numbering problem!"
 | 
			
		||||
#endif
 | 
			
		||||
    table[32].data = &c->rlimit_memlock;
 | 
			
		||||
    table[33].data = &c->rlimit_memlock;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef RLIMIT_NICE
 | 
			
		||||
#ifndef RLIMIT_MEMLOCK
 | 
			
		||||
#error "Houston, we have a numbering problem!"
 | 
			
		||||
#endif
 | 
			
		||||
    table[33].data = &c->rlimit_nice;
 | 
			
		||||
    table[34].data = &c->rlimit_nice;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef RLIMIT_RTPRIO
 | 
			
		||||
#ifndef RLIMIT_NICE
 | 
			
		||||
#error "Houston, we have a numbering problem!"
 | 
			
		||||
#endif
 | 
			
		||||
    table[34].data = &c->rlimit_rtprio;
 | 
			
		||||
    table[35].data = &c->rlimit_rtprio;
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -563,6 +566,7 @@ char *pa_daemon_conf_dump(pa_daemon_conf *c) {
 | 
			
		|||
    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));
 | 
			
		||||
    pa_strbuf_printf(s, "disable-remixing = %s\n", pa_yes_no(c->disable_remixing));
 | 
			
		||||
    pa_strbuf_printf(s, "default-sample-format = %s\n", pa_sample_format_to_string(c->default_sample_spec.format));
 | 
			
		||||
    pa_strbuf_printf(s, "default-sample-rate = %u\n", c->default_sample_spec.rate);
 | 
			
		||||
    pa_strbuf_printf(s, "default-sample-channels = %u\n", c->default_sample_spec.channels);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -64,7 +64,8 @@ typedef struct pa_daemon_conf {
 | 
			
		|||
        use_pid_file,
 | 
			
		||||
        system_instance,
 | 
			
		||||
        no_cpu_limit,
 | 
			
		||||
        disable_shm;
 | 
			
		||||
        disable_shm,
 | 
			
		||||
        disable_remixing;
 | 
			
		||||
    int exit_idle_time,
 | 
			
		||||
        module_idle_time,
 | 
			
		||||
        scache_idle_time,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -46,6 +46,7 @@
 | 
			
		|||
; log-level = notice
 | 
			
		||||
 | 
			
		||||
; resample-method = speex-float-3
 | 
			
		||||
; disable-remixing = no
 | 
			
		||||
 | 
			
		||||
; no-cpu-limit = no
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -94,6 +94,7 @@
 | 
			
		|||
#include "dumpmodules.h"
 | 
			
		||||
#include "caps.h"
 | 
			
		||||
#include "ltdl-bind-now.h"
 | 
			
		||||
#include "polkit.h"
 | 
			
		||||
 | 
			
		||||
#ifdef HAVE_LIBWRAP
 | 
			
		||||
/* Only one instance of these variables */
 | 
			
		||||
| 
						 | 
				
			
			@ -281,17 +282,21 @@ static int create_runtime_dir(void) {
 | 
			
		|||
 | 
			
		||||
#ifdef HAVE_SYS_RESOURCE_H
 | 
			
		||||
 | 
			
		||||
static void set_one_rlimit(const pa_rlimit *r, int resource, const char *name) {
 | 
			
		||||
static int set_one_rlimit(const pa_rlimit *r, int resource, const char *name) {
 | 
			
		||||
    struct rlimit rl;
 | 
			
		||||
    pa_assert(r);
 | 
			
		||||
 | 
			
		||||
    if (!r->is_set)
 | 
			
		||||
        return;
 | 
			
		||||
        return 0;
 | 
			
		||||
 | 
			
		||||
    rl.rlim_cur = rl.rlim_max = r->value;
 | 
			
		||||
 | 
			
		||||
    if (setrlimit(resource, &rl) < 0)
 | 
			
		||||
    if (setrlimit(resource, &rl) < 0) {
 | 
			
		||||
        pa_log_warn("setrlimit(%s, (%u, %u)) failed: %s", name, (unsigned) r->value, (unsigned) r->value, pa_cstrerror(errno));
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void set_all_rlimits(const pa_daemon_conf *conf) {
 | 
			
		||||
| 
						 | 
				
			
			@ -324,9 +329,10 @@ int main(int argc, char *argv[]) {
 | 
			
		|||
    char *s;
 | 
			
		||||
    int r = 0, retval = 1, d = 0;
 | 
			
		||||
    int daemon_pipe[2] = { -1, -1 };
 | 
			
		||||
    int suid_root, real_root;
 | 
			
		||||
    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;
 | 
			
		||||
 | 
			
		||||
#ifdef OS_IS_WIN32
 | 
			
		||||
    pa_time_event *timer;
 | 
			
		||||
| 
						 | 
				
			
			@ -357,8 +363,8 @@ int main(int argc, char *argv[]) {
 | 
			
		|||
    real_root = getuid() == 0;
 | 
			
		||||
    suid_root = !real_root && geteuid() == 0;
 | 
			
		||||
#else
 | 
			
		||||
    real_root = 0;
 | 
			
		||||
    suid_root = 0;
 | 
			
		||||
    real_root = FALSE;
 | 
			
		||||
    suid_root = FALSE;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    if (suid_root) {
 | 
			
		||||
| 
						 | 
				
			
			@ -377,29 +383,12 @@ int main(int argc, char *argv[]) {
 | 
			
		|||
         * is just too risky tun let PA run as root all the time. */
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* 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. */
 | 
			
		||||
 | 
			
		||||
    setlocale(LC_ALL, "");
 | 
			
		||||
 | 
			
		||||
    if (suid_root && (pa_own_uid_in_group(PA_REALTIME_GROUP, &gid) <= 0)) {
 | 
			
		||||
        pa_log_info("Warning: Called SUID root, but not in group '"PA_REALTIME_GROUP"'. "
 | 
			
		||||
                    "For enabling real-time scheduling please become a member of '"PA_REALTIME_GROUP"' , or increase the RLIMIT_RTPRIO user limit.");
 | 
			
		||||
        pa_drop_caps();
 | 
			
		||||
        pa_drop_root();
 | 
			
		||||
        suid_root = real_root = 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    LTDL_SET_PRELOADED_SYMBOLS();
 | 
			
		||||
 | 
			
		||||
    pa_ltdl_init();
 | 
			
		||||
 | 
			
		||||
#ifdef OS_IS_WIN32
 | 
			
		||||
    {
 | 
			
		||||
        WSADATA data;
 | 
			
		||||
        WSAStartup(MAKEWORD(2, 0), &data);
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    pa_random_seed();
 | 
			
		||||
 | 
			
		||||
    pa_log_set_maximal_level(PA_LOG_INFO);
 | 
			
		||||
    pa_log_set_ident("pulseaudio");
 | 
			
		||||
 | 
			
		||||
    conf = pa_daemon_conf_new();
 | 
			
		||||
| 
						 | 
				
			
			@ -411,24 +400,123 @@ int main(int argc, char *argv[]) {
 | 
			
		|||
        goto finish;
 | 
			
		||||
 | 
			
		||||
    if (pa_cmdline_parse(conf, argc, argv, &d) < 0) {
 | 
			
		||||
        pa_log("failed to parse command line.");
 | 
			
		||||
        pa_log("Failed to parse command line.");
 | 
			
		||||
        goto finish;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pa_log_set_maximal_level(conf->log_level);
 | 
			
		||||
    pa_log_set_target(conf->auto_log_target ? PA_LOG_STDERR : conf->log_target, NULL);
 | 
			
		||||
 | 
			
		||||
    if (suid_root) {
 | 
			
		||||
        /* Ok, we're suid root, so let's better not enable high prio
 | 
			
		||||
         * or RT by default */
 | 
			
		||||
 | 
			
		||||
        allow_high_priority = allow_realtime = FALSE;
 | 
			
		||||
 | 
			
		||||
#ifdef HAVE_POLKIT
 | 
			
		||||
        if (conf->high_priority) {
 | 
			
		||||
            if (pa_polkit_check("org.pulseaudio.acquire-high-priority") > 0) {
 | 
			
		||||
                pa_log_info("PolicyKit grants us acquire-high-priority privilige.");
 | 
			
		||||
                allow_high_priority = TRUE;
 | 
			
		||||
            } else
 | 
			
		||||
                pa_log_info("PolicyKit refuses acquire-high-priority privilige.");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (conf->realtime_scheduling) {
 | 
			
		||||
            if (pa_polkit_check("org.pulseaudio.acquire-real-time") > 0) {
 | 
			
		||||
                pa_log_info("PolicyKit grants us acquire-real-time privilige.");
 | 
			
		||||
                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;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!allow_high_priority && !allow_realtime) {
 | 
			
		||||
 | 
			
		||||
            /* OK, there's no further need to keep CAP_NICE. Hence
 | 
			
		||||
             * let's give it up early */
 | 
			
		||||
 | 
			
		||||
            pa_drop_caps();
 | 
			
		||||
            pa_drop_root();
 | 
			
		||||
            suid_root = real_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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (conf->high_priority && conf->cmd == PA_CMD_DAEMON)
 | 
			
		||||
        pa_raise_priority(conf->nice_level);
 | 
			
		||||
 | 
			
		||||
    if (suid_root && (conf->cmd != PA_CMD_DAEMON || !conf->realtime_scheduling)) {
 | 
			
		||||
        pa_drop_caps();
 | 
			
		||||
        pa_drop_root();
 | 
			
		||||
    if (suid_root) {
 | 
			
		||||
        pa_bool_t drop;
 | 
			
		||||
 | 
			
		||||
        drop = conf->cmd != PA_CMD_DAEMON || !conf->realtime_scheduling;
 | 
			
		||||
 | 
			
		||||
#ifdef RLIMIT_RTPRIO
 | 
			
		||||
        if (!drop) {
 | 
			
		||||
 | 
			
		||||
            /* 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));
 | 
			
		||||
        }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
        if (drop)  {
 | 
			
		||||
            pa_drop_caps();
 | 
			
		||||
            pa_drop_root();
 | 
			
		||||
            suid_root = real_root = FALSE;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    LTDL_SET_PRELOADED_SYMBOLS();
 | 
			
		||||
    pa_ltdl_init();
 | 
			
		||||
 | 
			
		||||
    if (conf->dl_search_path)
 | 
			
		||||
        lt_dlsetsearchpath(conf->dl_search_path);
 | 
			
		||||
 | 
			
		||||
#ifdef OS_IS_WIN32
 | 
			
		||||
    {
 | 
			
		||||
        WSADATA data;
 | 
			
		||||
        WSAStartup(MAKEWORD(2, 0), &data);
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    pa_random_seed();
 | 
			
		||||
 | 
			
		||||
    switch (conf->cmd) {
 | 
			
		||||
        case PA_CMD_DUMP_MODULES:
 | 
			
		||||
            pa_dump_modules(conf, argc-d, argv+d);
 | 
			
		||||
| 
						 | 
				
			
			@ -466,10 +554,10 @@ int main(int argc, char *argv[]) {
 | 
			
		|||
        case PA_CMD_CHECK: {
 | 
			
		||||
            pid_t pid;
 | 
			
		||||
 | 
			
		||||
            if (pa_pid_file_check_running(&pid) < 0) {
 | 
			
		||||
                pa_log_info("daemon not running");
 | 
			
		||||
            } else {
 | 
			
		||||
                pa_log_info("daemon running as PID %u", pid);
 | 
			
		||||
            if (pa_pid_file_check_running(&pid, "pulseaudio") < 0)
 | 
			
		||||
                pa_log_info("Daemon not running");
 | 
			
		||||
            else {
 | 
			
		||||
                pa_log_info("Daemon running as PID %u", pid);
 | 
			
		||||
                retval = 0;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -478,8 +566,8 @@ int main(int argc, char *argv[]) {
 | 
			
		|||
        }
 | 
			
		||||
        case PA_CMD_KILL:
 | 
			
		||||
 | 
			
		||||
            if (pa_pid_file_kill(SIGINT, NULL) < 0)
 | 
			
		||||
                pa_log("failed to kill daemon.");
 | 
			
		||||
            if (pa_pid_file_kill(SIGINT, NULL, "pulseaudio") < 0)
 | 
			
		||||
                pa_log("Failed to kill daemon.");
 | 
			
		||||
            else
 | 
			
		||||
                retval = 0;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -496,9 +584,9 @@ int main(int argc, char *argv[]) {
 | 
			
		|||
            pa_assert(conf->cmd == PA_CMD_DAEMON);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (real_root && !conf->system_instance) {
 | 
			
		||||
    if (real_root && !conf->system_instance)
 | 
			
		||||
        pa_log_warn("This program is not intended to be run as root (unless --system is specified).");
 | 
			
		||||
    } else if (!real_root && conf->system_instance) {
 | 
			
		||||
    else if (!real_root && conf->system_instance) {
 | 
			
		||||
        pa_log("Root priviliges required.");
 | 
			
		||||
        goto finish;
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -645,6 +733,7 @@ int main(int argc, char *argv[]) {
 | 
			
		|||
    c->resample_method = conf->resample_method;
 | 
			
		||||
    c->realtime_priority = conf->realtime_priority;
 | 
			
		||||
    c->realtime_scheduling = !!conf->realtime_scheduling;
 | 
			
		||||
    c->disable_remixing = !!conf->disable_remixing;
 | 
			
		||||
 | 
			
		||||
    pa_assert_se(pa_signal_init(pa_mainloop_get_api(mainloop)) == 0);
 | 
			
		||||
    pa_signal_new(SIGINT, signal_callback, c);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -156,26 +156,33 @@ pa_stream_flush;
 | 
			
		|||
pa_stream_get_buffer_attr;
 | 
			
		||||
pa_stream_get_channel_map;
 | 
			
		||||
pa_stream_get_context;
 | 
			
		||||
pa_stream_get_device_index;
 | 
			
		||||
pa_stream_get_device_name;
 | 
			
		||||
pa_stream_get_index;
 | 
			
		||||
pa_stream_get_latency;
 | 
			
		||||
pa_stream_get_sample_spec;
 | 
			
		||||
pa_stream_get_state;
 | 
			
		||||
pa_stream_get_time;
 | 
			
		||||
pa_stream_get_timing_info;
 | 
			
		||||
pa_stream_is_suspended;
 | 
			
		||||
pa_stream_new;
 | 
			
		||||
pa_stream_peek;
 | 
			
		||||
pa_stream_prebuf;
 | 
			
		||||
pa_stream_readable_size;
 | 
			
		||||
pa_stream_ref;
 | 
			
		||||
pa_stream_set_buffer_attr;
 | 
			
		||||
pa_stream_set_latency_update_callback;
 | 
			
		||||
pa_stream_set_moved_callback;
 | 
			
		||||
pa_stream_set_name;
 | 
			
		||||
pa_stream_set_overflow_callback;
 | 
			
		||||
pa_stream_set_read_callback;
 | 
			
		||||
pa_stream_set_state_callback;
 | 
			
		||||
pa_stream_set_suspended_callback;
 | 
			
		||||
pa_stream_set_underflow_callback;
 | 
			
		||||
pa_stream_set_write_callback;
 | 
			
		||||
pa_stream_trigger;
 | 
			
		||||
pa_stream_unref;
 | 
			
		||||
pa_stream_update_sample_rate;
 | 
			
		||||
pa_stream_update_timing_info;
 | 
			
		||||
pa_stream_writable_size;
 | 
			
		||||
pa_stream_write;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -66,8 +66,6 @@ struct userdata {
 | 
			
		|||
        *source_output_unlink_slot,
 | 
			
		||||
        *sink_input_move_slot,
 | 
			
		||||
        *source_output_move_slot,
 | 
			
		||||
        *sink_input_move_post_slot,
 | 
			
		||||
        *source_output_move_post_slot,
 | 
			
		||||
        *sink_input_state_changed_slot,
 | 
			
		||||
        *source_output_state_changed_slot;
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -131,27 +129,27 @@ static void resume(struct device_info *d) {
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static pa_hook_result_t sink_input_new_hook_cb(pa_core *c, pa_sink_input *s, struct userdata *u) {
 | 
			
		||||
static pa_hook_result_t sink_input_fixate_hook_cb(pa_core *c, pa_sink_input_new_data *data, struct userdata *u) {
 | 
			
		||||
    struct device_info *d;
 | 
			
		||||
 | 
			
		||||
    pa_assert(c);
 | 
			
		||||
    pa_sink_input_assert_ref(s);
 | 
			
		||||
    pa_assert(data);
 | 
			
		||||
    pa_assert(u);
 | 
			
		||||
 | 
			
		||||
    if ((d = pa_hashmap_get(u->device_infos, s->sink)))
 | 
			
		||||
    if ((d = pa_hashmap_get(u->device_infos, data->sink)))
 | 
			
		||||
        resume(d);
 | 
			
		||||
 | 
			
		||||
    return PA_HOOK_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static pa_hook_result_t source_output_new_hook_cb(pa_core *c, pa_source_output *s, struct userdata *u) {
 | 
			
		||||
static pa_hook_result_t source_output_fixate_hook_cb(pa_core *c, pa_source_output_new_data *data, struct userdata *u) {
 | 
			
		||||
    struct device_info *d;
 | 
			
		||||
 | 
			
		||||
    pa_assert(c);
 | 
			
		||||
    pa_source_output_assert_ref(s);
 | 
			
		||||
    pa_assert(data);
 | 
			
		||||
    pa_assert(u);
 | 
			
		||||
 | 
			
		||||
    if ((d = pa_hashmap_get(u->device_infos, s->source)))
 | 
			
		||||
    if ((d = pa_hashmap_get(u->device_infos, data->source)))
 | 
			
		||||
        resume(d);
 | 
			
		||||
 | 
			
		||||
    return PA_HOOK_OK;
 | 
			
		||||
| 
						 | 
				
			
			@ -185,56 +183,37 @@ static pa_hook_result_t source_output_unlink_hook_cb(pa_core *c, pa_source_outpu
 | 
			
		|||
    return PA_HOOK_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static pa_hook_result_t sink_input_move_hook_cb(pa_core *c, pa_sink_input *s, struct userdata *u) {
 | 
			
		||||
    pa_assert(c);
 | 
			
		||||
    pa_sink_input_assert_ref(s);
 | 
			
		||||
    pa_assert(u);
 | 
			
		||||
 | 
			
		||||
    if (pa_sink_used_by(s->sink) <= 1) {
 | 
			
		||||
        struct device_info *d;
 | 
			
		||||
        if ((d = pa_hashmap_get(u->device_infos, s->sink)))
 | 
			
		||||
            restart(d);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return PA_HOOK_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static pa_hook_result_t sink_input_move_post_hook_cb(pa_core *c, pa_sink_input *s, struct userdata *u) {
 | 
			
		||||
static pa_hook_result_t sink_input_move_hook_cb(pa_core *c, pa_sink_input_move_hook_data *data, struct userdata *u) {
 | 
			
		||||
    struct device_info *d;
 | 
			
		||||
 | 
			
		||||
    pa_assert(c);
 | 
			
		||||
    pa_sink_input_assert_ref(s);
 | 
			
		||||
    pa_assert(data);
 | 
			
		||||
    pa_assert(u);
 | 
			
		||||
 | 
			
		||||
    if ((d = pa_hashmap_get(u->device_infos, s->sink)))
 | 
			
		||||
    if ((d = pa_hashmap_get(u->device_infos, data->destination)))
 | 
			
		||||
        resume(d);
 | 
			
		||||
 | 
			
		||||
    return PA_HOOK_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static pa_hook_result_t source_output_move_hook_cb(pa_core *c, pa_source_output *s, struct userdata *u) {
 | 
			
		||||
    pa_assert(c);
 | 
			
		||||
    pa_source_output_assert_ref(s);
 | 
			
		||||
    pa_assert(u);
 | 
			
		||||
 | 
			
		||||
    if (pa_source_used_by(s->source) <= 1) {
 | 
			
		||||
        struct device_info *d;
 | 
			
		||||
 | 
			
		||||
        if ((d = pa_hashmap_get(u->device_infos, s->source)))
 | 
			
		||||
    if (pa_sink_used_by(data->sink_input->sink) <= 1)
 | 
			
		||||
        if ((d = pa_hashmap_get(u->device_infos, data->sink_input->sink)))
 | 
			
		||||
            restart(d);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return PA_HOOK_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static pa_hook_result_t source_output_move_post_hook_cb(pa_core *c, pa_source_output *s, struct userdata *u) {
 | 
			
		||||
static pa_hook_result_t source_output_move_hook_cb(pa_core *c, pa_source_output_move_hook_data *data, struct userdata *u) {
 | 
			
		||||
    struct device_info *d;
 | 
			
		||||
 | 
			
		||||
    pa_assert(c);
 | 
			
		||||
    pa_source_output_assert_ref(s);
 | 
			
		||||
    pa_assert(data);
 | 
			
		||||
    pa_assert(u);
 | 
			
		||||
 | 
			
		||||
    if ((d = pa_hashmap_get(u->device_infos, s->source)))
 | 
			
		||||
    if ((d = pa_hashmap_get(u->device_infos, data->destination)))
 | 
			
		||||
        resume(d);
 | 
			
		||||
 | 
			
		||||
    if (pa_source_used_by(data->source_output->source) <= 1)
 | 
			
		||||
        if ((d = pa_hashmap_get(u->device_infos, data->source_output->source)))
 | 
			
		||||
            restart(d);
 | 
			
		||||
 | 
			
		||||
    return PA_HOOK_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -395,18 +374,15 @@ int pa__init(pa_module*m) {
 | 
			
		|||
    u->sink_state_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_STATE_CHANGED], (pa_hook_cb_t) device_state_changed_hook_cb, u);
 | 
			
		||||
    u->source_state_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_STATE_CHANGED], (pa_hook_cb_t) device_state_changed_hook_cb, u);
 | 
			
		||||
 | 
			
		||||
    u->sink_input_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_PUT], (pa_hook_cb_t) sink_input_new_hook_cb, u);
 | 
			
		||||
    u->source_output_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PUT], (pa_hook_cb_t) source_output_new_hook_cb, u);
 | 
			
		||||
    u->sink_input_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], (pa_hook_cb_t) sink_input_fixate_hook_cb, u);
 | 
			
		||||
    u->source_output_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE], (pa_hook_cb_t) source_output_fixate_hook_cb, u);
 | 
			
		||||
    u->sink_input_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK_POST], (pa_hook_cb_t) sink_input_unlink_hook_cb, u);
 | 
			
		||||
    u->source_output_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK_POST], (pa_hook_cb_t) source_output_unlink_hook_cb, u);
 | 
			
		||||
    u->sink_input_move_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE], (pa_hook_cb_t) sink_input_move_hook_cb, u);
 | 
			
		||||
    u->source_output_move_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE], (pa_hook_cb_t) source_output_move_hook_cb, u);
 | 
			
		||||
    u->sink_input_move_post_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_POST], (pa_hook_cb_t) sink_input_move_post_hook_cb, u);
 | 
			
		||||
    u->source_output_move_post_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_POST], (pa_hook_cb_t) source_output_move_post_hook_cb, u);
 | 
			
		||||
    u->sink_input_state_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], (pa_hook_cb_t) sink_input_state_changed_hook_cb, u);
 | 
			
		||||
    u->source_output_state_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED], (pa_hook_cb_t) source_output_state_changed_hook_cb, u);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    pa_modargs_free(ma);
 | 
			
		||||
    return 0;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -449,8 +425,6 @@ void pa__done(pa_module*m) {
 | 
			
		|||
        pa_hook_slot_free(u->sink_input_unlink_slot);
 | 
			
		||||
    if (u->sink_input_move_slot)
 | 
			
		||||
        pa_hook_slot_free(u->sink_input_move_slot);
 | 
			
		||||
    if (u->sink_input_move_post_slot)
 | 
			
		||||
        pa_hook_slot_free(u->sink_input_move_post_slot);
 | 
			
		||||
    if (u->sink_input_state_changed_slot)
 | 
			
		||||
        pa_hook_slot_free(u->sink_input_state_changed_slot);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -460,8 +434,6 @@ void pa__done(pa_module*m) {
 | 
			
		|||
        pa_hook_slot_free(u->source_output_unlink_slot);
 | 
			
		||||
    if (u->source_output_move_slot)
 | 
			
		||||
        pa_hook_slot_free(u->source_output_move_slot);
 | 
			
		||||
    if (u->source_output_move_post_slot)
 | 
			
		||||
        pa_hook_slot_free(u->source_output_move_post_slot);
 | 
			
		||||
    if (u->source_output_state_changed_slot)
 | 
			
		||||
        pa_hook_slot_free(u->source_output_state_changed_slot);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -35,6 +35,7 @@
 | 
			
		|||
 | 
			
		||||
#include <pulse/xmalloc.h>
 | 
			
		||||
#include <pulse/volume.h>
 | 
			
		||||
#include <pulse/timeval.h>
 | 
			
		||||
 | 
			
		||||
#include <pulsecore/core-error.h>
 | 
			
		||||
#include <pulsecore/module.h>
 | 
			
		||||
| 
						 | 
				
			
			@ -52,14 +53,20 @@ PA_MODULE_AUTHOR("Lennart Poettering");
 | 
			
		|||
PA_MODULE_DESCRIPTION("Automatically restore the volume and the devices of streams");
 | 
			
		||||
PA_MODULE_VERSION(PACKAGE_VERSION);
 | 
			
		||||
PA_MODULE_LOAD_ONCE(TRUE);
 | 
			
		||||
PA_MODULE_USAGE("table=<filename>");
 | 
			
		||||
PA_MODULE_USAGE(
 | 
			
		||||
        "table=<filename> "
 | 
			
		||||
        "restore_device=<Restore the device for each stream?> "
 | 
			
		||||
        "restore_volume=<Restore the volume for each stream?>"
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
#define WHITESPACE "\n\r \t"
 | 
			
		||||
 | 
			
		||||
#define DEFAULT_VOLUME_TABLE_FILE "volume-restore.table"
 | 
			
		||||
#define SAVE_INTERVAL 10
 | 
			
		||||
 | 
			
		||||
static const char* const valid_modargs[] = {
 | 
			
		||||
    "table",
 | 
			
		||||
    "restore_device",
 | 
			
		||||
    "restore_volume",
 | 
			
		||||
    NULL,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -67,16 +74,20 @@ struct rule {
 | 
			
		|||
    char* name;
 | 
			
		||||
    pa_bool_t volume_is_set;
 | 
			
		||||
    pa_cvolume volume;
 | 
			
		||||
    char *sink;
 | 
			
		||||
    char *source;
 | 
			
		||||
    char *sink, *source;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct userdata {
 | 
			
		||||
    pa_core *core;
 | 
			
		||||
    pa_hashmap *hashmap;
 | 
			
		||||
    pa_subscription *subscription;
 | 
			
		||||
    pa_hook_slot *sink_input_hook_slot, *source_output_hook_slot;
 | 
			
		||||
    pa_hook_slot
 | 
			
		||||
        *sink_input_new_hook_slot,
 | 
			
		||||
        *sink_input_fixate_hook_slot,
 | 
			
		||||
        *source_output_new_hook_slot;
 | 
			
		||||
    pa_bool_t modified;
 | 
			
		||||
    char *table_file;
 | 
			
		||||
    pa_time_event *save_time_event;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static pa_cvolume* parse_volume(const char *s, pa_cvolume *v) {
 | 
			
		||||
| 
						 | 
				
			
			@ -220,12 +231,17 @@ static int save_rules(struct userdata *u) {
 | 
			
		|||
    void *state = NULL;
 | 
			
		||||
    struct rule *rule;
 | 
			
		||||
 | 
			
		||||
    if (!u->modified)
 | 
			
		||||
        return 0;
 | 
			
		||||
 | 
			
		||||
    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) {
 | 
			
		||||
        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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -249,6 +265,8 @@ static int save_rules(struct userdata *u) {
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    ret = 0;
 | 
			
		||||
    u->modified = FALSE;
 | 
			
		||||
    pa_log_debug("Successfully saved rules...");
 | 
			
		||||
 | 
			
		||||
finish:
 | 
			
		||||
    if (f) {
 | 
			
		||||
| 
						 | 
				
			
			@ -289,6 +307,21 @@ static char* client_name(pa_client *c) {
 | 
			
		|||
    return t;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *tv, void *userdata) {
 | 
			
		||||
    struct userdata *u = userdata;
 | 
			
		||||
 | 
			
		||||
    pa_assert(a);
 | 
			
		||||
    pa_assert(e);
 | 
			
		||||
    pa_assert(tv);
 | 
			
		||||
    pa_assert(u);
 | 
			
		||||
 | 
			
		||||
    pa_assert(e == u->save_time_event);
 | 
			
		||||
    u->core->mainloop->time_free(u->save_time_event);
 | 
			
		||||
    u->save_time_event = NULL;
 | 
			
		||||
 | 
			
		||||
    save_rules(u);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
 | 
			
		||||
    struct userdata *u =  userdata;
 | 
			
		||||
    pa_sink_input *si = NULL;
 | 
			
		||||
| 
						 | 
				
			
			@ -371,24 +404,28 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
 | 
			
		|||
        pa_hashmap_put(u->hashmap, r->name, r);
 | 
			
		||||
        u->modified = TRUE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (u->modified && !u->save_time_event) {
 | 
			
		||||
        struct timeval tv;
 | 
			
		||||
        pa_gettimeofday(&tv);
 | 
			
		||||
        tv.tv_sec += SAVE_INTERVAL;
 | 
			
		||||
        u->save_time_event = u->core->mainloop->time_new(u->core->mainloop, &tv, save_time_callback, u);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static pa_hook_result_t sink_input_hook_callback(pa_core *c, pa_sink_input_new_data *data, struct userdata *u) {
 | 
			
		||||
static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *data, struct userdata *u) {
 | 
			
		||||
    struct rule *r;
 | 
			
		||||
    char *name;
 | 
			
		||||
 | 
			
		||||
    pa_assert(data);
 | 
			
		||||
 | 
			
		||||
    /* In the NEW hook we only adjust the device. Adjusting the volume
 | 
			
		||||
     * is left for the FIXATE hook */
 | 
			
		||||
 | 
			
		||||
    if (!data->client || !(name = client_name(data->client)))
 | 
			
		||||
        return PA_HOOK_OK;
 | 
			
		||||
 | 
			
		||||
    if ((r = pa_hashmap_get(u->hashmap, name))) {
 | 
			
		||||
 | 
			
		||||
        if (r->volume_is_set && data->sample_spec.channels == r->volume.channels) {
 | 
			
		||||
            pa_log_info("Restoring volume for <%s>", r->name);
 | 
			
		||||
            pa_sink_input_new_data_set_volume(data, &r->volume);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!data->sink && r->sink) {
 | 
			
		||||
            if ((data->sink = pa_namereg_get(c, r->sink, PA_NAMEREG_SINK, 1)))
 | 
			
		||||
                pa_log_info("Restoring sink for <%s>", r->name);
 | 
			
		||||
| 
						 | 
				
			
			@ -400,7 +437,32 @@ static pa_hook_result_t sink_input_hook_callback(pa_core *c, pa_sink_input_new_d
 | 
			
		|||
    return PA_HOOK_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static pa_hook_result_t source_output_hook_callback(pa_core *c, pa_source_output_new_data *data, struct userdata *u) {
 | 
			
		||||
static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *c, pa_sink_input_new_data *data, struct userdata *u) {
 | 
			
		||||
    struct rule *r;
 | 
			
		||||
    char *name;
 | 
			
		||||
 | 
			
		||||
    pa_assert(data);
 | 
			
		||||
 | 
			
		||||
    /* In the FIXATE hook we only adjust the volum. Adjusting the device
 | 
			
		||||
     * is left for the NEW hook */
 | 
			
		||||
 | 
			
		||||
    if (!data->client || !(name = client_name(data->client)))
 | 
			
		||||
        return PA_HOOK_OK;
 | 
			
		||||
 | 
			
		||||
    if ((r = pa_hashmap_get(u->hashmap, name))) {
 | 
			
		||||
 | 
			
		||||
        if (r->volume_is_set && data->sample_spec.channels == r->volume.channels) {
 | 
			
		||||
            pa_log_info("Restoring volume for <%s>", r->name);
 | 
			
		||||
            pa_sink_input_new_data_set_volume(data, &r->volume);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pa_xfree(name);
 | 
			
		||||
 | 
			
		||||
    return PA_HOOK_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_output_new_data *data, struct userdata *u) {
 | 
			
		||||
    struct rule *r;
 | 
			
		||||
    char *name;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -422,6 +484,7 @@ static pa_hook_result_t source_output_hook_callback(pa_core *c, pa_source_output
 | 
			
		|||
int pa__init(pa_module*m) {
 | 
			
		||||
    pa_modargs *ma = NULL;
 | 
			
		||||
    struct userdata *u;
 | 
			
		||||
    pa_bool_t restore_device = TRUE, restore_volume = TRUE;
 | 
			
		||||
 | 
			
		||||
    pa_assert(m);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -431,20 +494,39 @@ int pa__init(pa_module*m) {
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    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->subscription = NULL;
 | 
			
		||||
    u->table_file = pa_xstrdup(pa_modargs_get_value(ma, "table", NULL));
 | 
			
		||||
    u->modified = FALSE;
 | 
			
		||||
    u->sink_input_hook_slot = u->source_output_hook_slot = NULL;
 | 
			
		||||
    u->subscription = NULL;
 | 
			
		||||
    u->sink_input_new_hook_slot = u->sink_input_fixate_hook_slot = u->source_output_new_hook_slot = NULL;
 | 
			
		||||
    u->save_time_event = NULL;
 | 
			
		||||
 | 
			
		||||
    m->userdata = u;
 | 
			
		||||
 | 
			
		||||
    if (pa_modargs_get_value_boolean(ma, "restore_device", &restore_device) < 0 ||
 | 
			
		||||
        pa_modargs_get_value_boolean(ma, "restore_volume", &restore_volume) < 0) {
 | 
			
		||||
        pa_log("restore_volume= and restore_device= expect boolean arguments");
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!(restore_device || restore_volume)) {
 | 
			
		||||
        pa_log("Both restrong the volume and restoring the device are disabled. There's no point in using this module at all then, failing.");
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (load_rules(u) < 0)
 | 
			
		||||
        goto fail;
 | 
			
		||||
 | 
			
		||||
    u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK_INPUT|PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT, subscribe_callback, u);
 | 
			
		||||
    u->sink_input_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], (pa_hook_cb_t) sink_input_hook_callback, u);
 | 
			
		||||
    u->source_output_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], (pa_hook_cb_t) source_output_hook_callback, u);
 | 
			
		||||
 | 
			
		||||
    if (restore_device) {
 | 
			
		||||
        u->sink_input_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], (pa_hook_cb_t) sink_input_new_hook_callback, u);
 | 
			
		||||
        u->source_output_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], (pa_hook_cb_t) source_output_new_hook_callback, u);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (restore_volume)
 | 
			
		||||
        u->sink_input_fixate_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], (pa_hook_cb_t) sink_input_fixate_hook_callback, u);
 | 
			
		||||
 | 
			
		||||
    pa_modargs_free(ma);
 | 
			
		||||
    return 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -478,19 +560,21 @@ void pa__done(pa_module*m) {
 | 
			
		|||
    if (u->subscription)
 | 
			
		||||
        pa_subscription_free(u->subscription);
 | 
			
		||||
 | 
			
		||||
    if (u->sink_input_hook_slot)
 | 
			
		||||
        pa_hook_slot_free(u->sink_input_hook_slot);
 | 
			
		||||
    if (u->source_output_hook_slot)
 | 
			
		||||
        pa_hook_slot_free(u->source_output_hook_slot);
 | 
			
		||||
    if (u->sink_input_new_hook_slot)
 | 
			
		||||
        pa_hook_slot_free(u->sink_input_new_hook_slot);
 | 
			
		||||
    if (u->sink_input_fixate_hook_slot)
 | 
			
		||||
        pa_hook_slot_free(u->sink_input_fixate_hook_slot);
 | 
			
		||||
    if (u->source_output_new_hook_slot)
 | 
			
		||||
        pa_hook_slot_free(u->source_output_new_hook_slot);
 | 
			
		||||
 | 
			
		||||
    if (u->hashmap) {
 | 
			
		||||
 | 
			
		||||
        if (u->modified)
 | 
			
		||||
            save_rules(u);
 | 
			
		||||
 | 
			
		||||
        save_rules(u);
 | 
			
		||||
        pa_hashmap_free(u->hashmap, free_func, NULL);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (u->save_time_event)
 | 
			
		||||
        u->core->mainloop->time_free(u->save_time_event);
 | 
			
		||||
 | 
			
		||||
    pa_xfree(u->table_file);
 | 
			
		||||
    pa_xfree(u);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -86,6 +86,10 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
 | 
			
		|||
    [PA_COMMAND_UNDERFLOW] = pa_command_overflow_or_underflow,
 | 
			
		||||
    [PA_COMMAND_PLAYBACK_STREAM_KILLED] = pa_command_stream_killed,
 | 
			
		||||
    [PA_COMMAND_RECORD_STREAM_KILLED] = pa_command_stream_killed,
 | 
			
		||||
    [PA_COMMAND_PLAYBACK_STREAM_MOVED] = pa_command_stream_moved,
 | 
			
		||||
    [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_SUBSCRIBE_EVENT] = pa_command_subscribe_event
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -396,7 +400,7 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t
 | 
			
		|||
            /* Enable shared memory support if possible */
 | 
			
		||||
            if (c->version >= 10 &&
 | 
			
		||||
                pa_mempool_is_shared(c->mempool) &&
 | 
			
		||||
                c->is_local) {
 | 
			
		||||
                c->is_local > 0) {
 | 
			
		||||
 | 
			
		||||
                /* Only enable SHM if both sides are owned by the same
 | 
			
		||||
                 * user. This is a security measure because otherwise
 | 
			
		||||
| 
						 | 
				
			
			@ -965,6 +969,9 @@ pa_operation* pa_context_set_default_source(pa_context *c, const char *name, pa_
 | 
			
		|||
 | 
			
		||||
int pa_context_is_local(pa_context *c) {
 | 
			
		||||
    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;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -122,7 +122,7 @@ typedef enum pa_stream_flags {
 | 
			
		|||
                                      * ahead can be corrected
 | 
			
		||||
                                      * quickly, without the need to
 | 
			
		||||
                                      * wait. */
 | 
			
		||||
    PA_STREAM_AUTO_TIMING_UPDATE = 8 /**< If set timing update requests
 | 
			
		||||
    PA_STREAM_AUTO_TIMING_UPDATE = 8, /**< If set timing update requests
 | 
			
		||||
                                       * are issued periodically
 | 
			
		||||
                                       * automatically. Combined with
 | 
			
		||||
                                       * PA_STREAM_INTERPOLATE_TIMING
 | 
			
		||||
| 
						 | 
				
			
			@ -132,6 +132,83 @@ typedef enum pa_stream_flags {
 | 
			
		|||
                                       * pa_stream_get_latency() at
 | 
			
		||||
                                       * all times without a packet
 | 
			
		||||
                                       * round trip.*/
 | 
			
		||||
    PA_STREAM_NO_REMAP_CHANNELS = 16, /**< Don't remap channels by
 | 
			
		||||
                                       * their name, instead map them
 | 
			
		||||
                                       * simply by their
 | 
			
		||||
                                       * index. Implies
 | 
			
		||||
                                       * PA_STREAM_NO_REMIX_CHANNELS. Only
 | 
			
		||||
                                       * supported when the server is
 | 
			
		||||
                                       * at least PA 0.9.8. It is
 | 
			
		||||
                                       * ignored on older
 | 
			
		||||
                                       * servers.\since 0.9.8 */
 | 
			
		||||
    PA_STREAM_NO_REMIX_CHANNELS = 32, /**< When remapping channels by
 | 
			
		||||
                                       * name, don't upmix or downmix
 | 
			
		||||
                                       * them to related
 | 
			
		||||
                                       * channels. Copy them into
 | 
			
		||||
                                       * matching channels of the
 | 
			
		||||
                                       * device 1:1. Only supported
 | 
			
		||||
                                       * when the server is at least
 | 
			
		||||
                                       * PA 0.9.8. It is ignored on
 | 
			
		||||
                                       * older servers. \since
 | 
			
		||||
                                       * 0.9.8 */
 | 
			
		||||
    PA_STREAM_FIX_FORMAT = 64, /**< Use the sample format of the
 | 
			
		||||
                                * sink/device this stream is being
 | 
			
		||||
                                * connected to, and possibly ignore
 | 
			
		||||
                                * the format the sample spec contains
 | 
			
		||||
                                * -- but you still have to pass a
 | 
			
		||||
                                * valid value in it as a hint to
 | 
			
		||||
                                * PulseAudio what would suit your
 | 
			
		||||
                                * stream best. If this is used you
 | 
			
		||||
                                * should query the used sample format
 | 
			
		||||
                                * after creating the stream by using
 | 
			
		||||
                                * pa_stream_get_sample_spec(). Also,
 | 
			
		||||
                                * if you specified manual buffer
 | 
			
		||||
                                * metrics it is recommended to update
 | 
			
		||||
                                * them with
 | 
			
		||||
                                * pa_stream_set_buffer_attr() to
 | 
			
		||||
                                * compensate for the changed frame
 | 
			
		||||
                                * sizes. Only supported when the
 | 
			
		||||
                                * server is at least PA 0.9.8. It is
 | 
			
		||||
                                * ignored on older servers. \since
 | 
			
		||||
                                * 0.9.8 */
 | 
			
		||||
 | 
			
		||||
    PA_STREAM_FIX_RATE = 128, /**< Use the sample rate of the sink,
 | 
			
		||||
                               * and possibly ignore the rate the
 | 
			
		||||
                               * sample spec contains. Usage similar
 | 
			
		||||
                               * to PA_STREAM_FIX_FORMAT.Only
 | 
			
		||||
                               * supported when the server is at least
 | 
			
		||||
                               * PA 0.9.8. It is ignored on older
 | 
			
		||||
                               * servers. \since 0.9.8 */
 | 
			
		||||
 | 
			
		||||
    PA_STREAM_FIX_CHANNELS = 256, /**< Use the number of channels and
 | 
			
		||||
                               * the channel map of the sink, and
 | 
			
		||||
                               * possibly ignore the number of
 | 
			
		||||
                               * channels and the map the sample spec
 | 
			
		||||
                               * and the passed channel map
 | 
			
		||||
                               * contains. Usage similar to
 | 
			
		||||
                               * PA_STREAM_FIX_FORMAT. Only supported
 | 
			
		||||
                               * when the server is at least PA
 | 
			
		||||
                               * 0.9.8. It is ignored on older
 | 
			
		||||
                               * servers. \since 0.9.8 */
 | 
			
		||||
    PA_STREAM_DONT_MOVE = 512, /**< Don't allow moving of this stream to
 | 
			
		||||
                              * another sink/device. Useful if you use
 | 
			
		||||
                              * any of the PA_STREAM_FIX_ flags and
 | 
			
		||||
                              * want to make sure that resampling
 | 
			
		||||
                              * never takes place -- which might
 | 
			
		||||
                              * happen if the stream is moved to
 | 
			
		||||
                              * another sink/source whith a different
 | 
			
		||||
                              * sample spec/channel map. Only
 | 
			
		||||
                              * supported when the server is at least
 | 
			
		||||
                              * PA 0.9.8. It is ignored on older
 | 
			
		||||
                              * servers. \since 0.9.8 */
 | 
			
		||||
    PA_STREAM_VARIABLE_RATE = 1024, /**< Allow dynamic changing of the
 | 
			
		||||
                                     * sampling rate during playback
 | 
			
		||||
                                     * with
 | 
			
		||||
                                     * pa_stream_update_sample_rate(). Only
 | 
			
		||||
                                     * supported when the server is at
 | 
			
		||||
                                     * least PA 0.9.8. It is ignored
 | 
			
		||||
                                     * on older servers. \since
 | 
			
		||||
                                     * 0.9.8 */
 | 
			
		||||
} pa_stream_flags_t;
 | 
			
		||||
 | 
			
		||||
/** Playback and record buffer metrics */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -103,6 +103,7 @@ struct pa_stream {
 | 
			
		|||
    PA_LLIST_FIELDS(pa_stream);
 | 
			
		||||
 | 
			
		||||
    char *name;
 | 
			
		||||
    pa_bool_t manual_buffer_attr;
 | 
			
		||||
    pa_buffer_attr buffer_attr;
 | 
			
		||||
    pa_sample_spec sample_spec;
 | 
			
		||||
    pa_channel_map channel_map;
 | 
			
		||||
| 
						 | 
				
			
			@ -110,12 +111,17 @@ struct pa_stream {
 | 
			
		|||
    uint32_t channel;
 | 
			
		||||
    uint32_t syncid;
 | 
			
		||||
    int channel_valid;
 | 
			
		||||
    uint32_t device_index;
 | 
			
		||||
    uint32_t stream_index;
 | 
			
		||||
    pa_stream_direction_t direction;
 | 
			
		||||
    pa_stream_state_t state;
 | 
			
		||||
    pa_bool_t buffer_attr_not_ready, timing_info_not_ready;
 | 
			
		||||
 | 
			
		||||
    uint32_t requested_bytes;
 | 
			
		||||
 | 
			
		||||
    uint32_t device_index;
 | 
			
		||||
    char *device_name;
 | 
			
		||||
    pa_bool_t suspended;
 | 
			
		||||
 | 
			
		||||
    pa_memchunk peek_memchunk;
 | 
			
		||||
    void *peek_data;
 | 
			
		||||
    pa_memblockq *record_memblockq;
 | 
			
		||||
| 
						 | 
				
			
			@ -157,6 +163,10 @@ struct pa_stream {
 | 
			
		|||
    void *underflow_userdata;
 | 
			
		||||
    pa_stream_notify_cb_t latency_update_callback;
 | 
			
		||||
    void *latency_update_userdata;
 | 
			
		||||
    pa_stream_notify_cb_t moved_callback;
 | 
			
		||||
    void *moved_userdata;
 | 
			
		||||
    pa_stream_notify_cb_t suspended_callback;
 | 
			
		||||
    void *suspended_userdata;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef void (*pa_operation_cb_t)(void);
 | 
			
		||||
| 
						 | 
				
			
			@ -172,12 +182,16 @@ struct pa_operation {
 | 
			
		|||
    pa_operation_state_t state;
 | 
			
		||||
    void *userdata;
 | 
			
		||||
    pa_operation_cb_t callback;
 | 
			
		||||
 | 
			
		||||
    void *private; /* some operations might need this */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void pa_command_request(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
 | 
			
		||||
void pa_command_stream_killed(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
 | 
			
		||||
void pa_command_subscribe_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
 | 
			
		||||
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);
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -44,6 +44,7 @@ pa_operation *pa_operation_new(pa_context *c, pa_stream *s, pa_operation_cb_t cb
 | 
			
		|||
    PA_REFCNT_INIT(o);
 | 
			
		||||
    o->context = c;
 | 
			
		||||
    o->stream = s;
 | 
			
		||||
    o->private = NULL;
 | 
			
		||||
 | 
			
		||||
    o->state = PA_OPERATION_RUNNING;
 | 
			
		||||
    o->callback = cb;
 | 
			
		||||
| 
						 | 
				
			
			@ -63,7 +64,6 @@ pa_operation *pa_operation_ref(pa_operation *o) {
 | 
			
		|||
    PA_REFCNT_INC(o);
 | 
			
		||||
    return o;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void pa_operation_unref(pa_operation *o) {
 | 
			
		||||
    pa_assert(o);
 | 
			
		||||
    pa_assert(PA_REFCNT_VALUE(o) >= 1);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -51,6 +51,7 @@ pa_stream *pa_stream_new(pa_context *c, const char *name, const pa_sample_spec *
 | 
			
		|||
    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);
 | 
			
		||||
 | 
			
		||||
    s = pa_xnew(pa_stream, 1);
 | 
			
		||||
| 
						 | 
				
			
			@ -58,6 +59,8 @@ pa_stream *pa_stream_new(pa_context *c, const char *name, const pa_sample_spec *
 | 
			
		|||
    s->context = c;
 | 
			
		||||
    s->mainloop = c->mainloop;
 | 
			
		||||
 | 
			
		||||
    s->buffer_attr_not_ready = s->timing_info_not_ready = FALSE;
 | 
			
		||||
 | 
			
		||||
    s->read_callback = NULL;
 | 
			
		||||
    s->read_userdata = NULL;
 | 
			
		||||
    s->write_callback = NULL;
 | 
			
		||||
| 
						 | 
				
			
			@ -70,6 +73,10 @@ pa_stream *pa_stream_new(pa_context *c, const char *name, const pa_sample_spec *
 | 
			
		|||
    s->underflow_userdata = NULL;
 | 
			
		||||
    s->latency_update_callback = NULL;
 | 
			
		||||
    s->latency_update_userdata = NULL;
 | 
			
		||||
    s->moved_callback = NULL;
 | 
			
		||||
    s->moved_userdata = NULL;
 | 
			
		||||
    s->suspended_callback = NULL;
 | 
			
		||||
    s->suspended_userdata = NULL;
 | 
			
		||||
 | 
			
		||||
    s->direction = PA_STREAM_NODIRECTION;
 | 
			
		||||
    s->name = pa_xstrdup(name);
 | 
			
		||||
| 
						 | 
				
			
			@ -84,11 +91,17 @@ pa_stream *pa_stream_new(pa_context *c, const char *name, const pa_sample_spec *
 | 
			
		|||
    s->channel = 0;
 | 
			
		||||
    s->channel_valid = 0;
 | 
			
		||||
    s->syncid = c->csyncid++;
 | 
			
		||||
    s->device_index = PA_INVALID_INDEX;
 | 
			
		||||
    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;
 | 
			
		||||
| 
						 | 
				
			
			@ -139,6 +152,7 @@ static void stream_free(pa_stream *s) {
 | 
			
		|||
        pa_memblockq_free(s->record_memblockq);
 | 
			
		||||
 | 
			
		||||
    pa_xfree(s->name);
 | 
			
		||||
    pa_xfree(s->device_name);
 | 
			
		||||
    pa_xfree(s);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -178,7 +192,7 @@ uint32_t pa_stream_get_index(pa_stream *s) {
 | 
			
		|||
 | 
			
		||||
    PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE, PA_INVALID_INDEX);
 | 
			
		||||
 | 
			
		||||
    return s->device_index;
 | 
			
		||||
    return s->stream_index;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void pa_stream_set_state(pa_stream *s, pa_stream_state_t st) {
 | 
			
		||||
| 
						 | 
				
			
			@ -262,6 +276,95 @@ finish:
 | 
			
		|||
    pa_context_unref(c);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void pa_command_stream_moved(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
 | 
			
		||||
    pa_context *c = userdata;
 | 
			
		||||
    pa_stream *s;
 | 
			
		||||
    uint32_t channel;
 | 
			
		||||
    const char *dn;
 | 
			
		||||
    int suspended;
 | 
			
		||||
    uint32_t di;
 | 
			
		||||
 | 
			
		||||
    pa_assert(pd);
 | 
			
		||||
    pa_assert(command == PA_COMMAND_PLAYBACK_STREAM_MOVED || command == PA_COMMAND_RECORD_STREAM_MOVED);
 | 
			
		||||
    pa_assert(t);
 | 
			
		||||
    pa_assert(c);
 | 
			
		||||
    pa_assert(PA_REFCNT_VALUE(c) >= 1);
 | 
			
		||||
 | 
			
		||||
    pa_context_ref(c);
 | 
			
		||||
 | 
			
		||||
    if (c->version < 12) {
 | 
			
		||||
        pa_context_fail(c, PA_ERR_PROTOCOL);
 | 
			
		||||
        goto finish;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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_context_fail(c, PA_ERR_PROTOCOL);
 | 
			
		||||
        goto finish;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!dn || di == PA_INVALID_INDEX) {
 | 
			
		||||
        pa_context_fail(c, PA_ERR_PROTOCOL);
 | 
			
		||||
        goto finish;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!(s = pa_dynarray_get(command == PA_COMMAND_PLAYBACK_STREAM_MOVED ? c->playback_streams : c->record_streams, channel)))
 | 
			
		||||
        goto finish;
 | 
			
		||||
 | 
			
		||||
    pa_xfree(s->device_name);
 | 
			
		||||
    s->device_name = pa_xstrdup(dn);
 | 
			
		||||
    s->device_index = di;
 | 
			
		||||
 | 
			
		||||
    s->suspended = suspended;
 | 
			
		||||
 | 
			
		||||
    if (s->moved_callback)
 | 
			
		||||
        s->moved_callback(s, s->moved_userdata);
 | 
			
		||||
 | 
			
		||||
finish:
 | 
			
		||||
    pa_context_unref(c);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void pa_command_stream_suspended(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
 | 
			
		||||
    pa_context *c = userdata;
 | 
			
		||||
    pa_stream *s;
 | 
			
		||||
    uint32_t channel;
 | 
			
		||||
    int suspended;
 | 
			
		||||
 | 
			
		||||
    pa_assert(pd);
 | 
			
		||||
    pa_assert(command == PA_COMMAND_PLAYBACK_STREAM_SUSPENDED || command == PA_COMMAND_RECORD_STREAM_SUSPENDED);
 | 
			
		||||
    pa_assert(t);
 | 
			
		||||
    pa_assert(c);
 | 
			
		||||
    pa_assert(PA_REFCNT_VALUE(c) >= 1);
 | 
			
		||||
 | 
			
		||||
    pa_context_ref(c);
 | 
			
		||||
 | 
			
		||||
    if (c->version < 12) {
 | 
			
		||||
        pa_context_fail(c, PA_ERR_PROTOCOL);
 | 
			
		||||
        goto finish;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (pa_tagstruct_getu32(t, &channel) < 0 ||
 | 
			
		||||
        pa_tagstruct_get_boolean(t, &suspended) < 0 ||
 | 
			
		||||
        !pa_tagstruct_eof(t)) {
 | 
			
		||||
        pa_context_fail(c, PA_ERR_PROTOCOL);
 | 
			
		||||
        goto finish;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!(s = pa_dynarray_get(command == PA_COMMAND_PLAYBACK_STREAM_SUSPENDED ? c->playback_streams : c->record_streams, channel)))
 | 
			
		||||
        goto finish;
 | 
			
		||||
 | 
			
		||||
    s->suspended = suspended;
 | 
			
		||||
 | 
			
		||||
    if (s->suspended_callback)
 | 
			
		||||
        s->suspended_callback(s, s->suspended_userdata);
 | 
			
		||||
 | 
			
		||||
finish:
 | 
			
		||||
    pa_context_unref(c);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void pa_command_request(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
 | 
			
		||||
    pa_stream *s;
 | 
			
		||||
    pa_context *c = userdata;
 | 
			
		||||
| 
						 | 
				
			
			@ -412,6 +515,9 @@ static void create_stream_complete(pa_stream *s) {
 | 
			
		|||
    pa_assert(PA_REFCNT_VALUE(s) >= 1);
 | 
			
		||||
    pa_assert(s->state == PA_STREAM_CREATING);
 | 
			
		||||
 | 
			
		||||
    if (s->buffer_attr_not_ready || s->timing_info_not_ready)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    pa_stream_set_state(s, PA_STREAM_READY);
 | 
			
		||||
 | 
			
		||||
    if (s->requested_bytes > 0 && s->write_callback)
 | 
			
		||||
| 
						 | 
				
			
			@ -426,6 +532,17 @@ static void create_stream_complete(pa_stream *s) {
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void automatic_buffer_attr(pa_buffer_attr *attr, pa_sample_spec *ss) {
 | 
			
		||||
    pa_assert(attr);
 | 
			
		||||
    pa_assert(ss);
 | 
			
		||||
 | 
			
		||||
    attr->tlength = pa_bytes_per_second(ss)/2;
 | 
			
		||||
    attr->maxlength = (attr->tlength*3)/2;
 | 
			
		||||
    attr->minreq = attr->tlength/50;
 | 
			
		||||
    attr->prebuf = attr->tlength - attr->minreq;
 | 
			
		||||
    attr->fragsize = attr->tlength/50;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
 | 
			
		||||
    pa_stream *s = userdata;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -445,13 +562,13 @@ void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    if (pa_tagstruct_getu32(t, &s->channel) < 0 ||
 | 
			
		||||
        ((s->direction != PA_STREAM_UPLOAD) && pa_tagstruct_getu32(t, &s->device_index) < 0) ||
 | 
			
		||||
        ((s->direction != PA_STREAM_UPLOAD) && pa_tagstruct_getu32(t, &s->stream_index) < 0) ||
 | 
			
		||||
        ((s->direction != PA_STREAM_RECORD) && pa_tagstruct_getu32(t, &s->requested_bytes) < 0)) {
 | 
			
		||||
        pa_context_fail(s->context, PA_ERR_PROTOCOL);
 | 
			
		||||
        goto finish;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (pa_context_get_server_protocol_version(s->context) >= 9) {
 | 
			
		||||
    if (s->context->version >= 9) {
 | 
			
		||||
        if (s->direction == PA_STREAM_PLAYBACK) {
 | 
			
		||||
            if (pa_tagstruct_getu32(t, &s->buffer_attr.maxlength) < 0 ||
 | 
			
		||||
                pa_tagstruct_getu32(t, &s->buffer_attr.tlength) < 0 ||
 | 
			
		||||
| 
						 | 
				
			
			@ -469,6 +586,58 @@ void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (s->context->version >= 12) {
 | 
			
		||||
        pa_sample_spec ss;
 | 
			
		||||
        pa_channel_map cm;
 | 
			
		||||
        const char *dn = NULL;
 | 
			
		||||
        int suspended;
 | 
			
		||||
 | 
			
		||||
        if (pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
 | 
			
		||||
            pa_tagstruct_get_channel_map(t, &cm) < 0 ||
 | 
			
		||||
            pa_tagstruct_getu32(t, &s->device_index) < 0 ||
 | 
			
		||||
            pa_tagstruct_gets(t, &dn) < 0 ||
 | 
			
		||||
            pa_tagstruct_get_boolean(t, &suspended) < 0) {
 | 
			
		||||
            pa_context_fail(s->context, PA_ERR_PROTOCOL);
 | 
			
		||||
            goto finish;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!dn || s->device_index == PA_INVALID_INDEX ||
 | 
			
		||||
            ss.channels != cm.channels ||
 | 
			
		||||
            !pa_channel_map_valid(&cm) ||
 | 
			
		||||
            !pa_sample_spec_valid(&ss) ||
 | 
			
		||||
            (!(s->flags & PA_STREAM_FIX_FORMAT) && ss.format != s->sample_spec.format) ||
 | 
			
		||||
            (!(s->flags & PA_STREAM_FIX_RATE) && ss.rate != s->sample_spec.rate) ||
 | 
			
		||||
            (!(s->flags & PA_STREAM_FIX_CHANNELS) && !pa_channel_map_equal(&cm, &s->channel_map))) {
 | 
			
		||||
            pa_context_fail(s->context, PA_ERR_PROTOCOL);
 | 
			
		||||
            goto finish;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        pa_xfree(s->device_name);
 | 
			
		||||
        s->device_name = pa_xstrdup(dn);
 | 
			
		||||
        s->suspended = suspended;
 | 
			
		||||
 | 
			
		||||
        if (!s->manual_buffer_attr && pa_bytes_per_second(&ss) != pa_bytes_per_second(&s->sample_spec)) {
 | 
			
		||||
            pa_buffer_attr attr;
 | 
			
		||||
            pa_operation *o;
 | 
			
		||||
 | 
			
		||||
            automatic_buffer_attr(&attr, &ss);
 | 
			
		||||
 | 
			
		||||
            /* If we need to update the buffer metrics, we wait for
 | 
			
		||||
             * the the OK for that call before we go to
 | 
			
		||||
             * PA_STREAM_READY */
 | 
			
		||||
 | 
			
		||||
            s->state = PA_STREAM_READY;
 | 
			
		||||
            pa_assert_se(o = pa_stream_set_buffer_attr(s, &attr, NULL, NULL));
 | 
			
		||||
            pa_operation_unref(o);
 | 
			
		||||
            s->state = PA_STREAM_CREATING;
 | 
			
		||||
 | 
			
		||||
            s->buffer_attr_not_ready = TRUE;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        s->channel_map = cm;
 | 
			
		||||
        s->sample_spec = ss;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!pa_tagstruct_eof(t)) {
 | 
			
		||||
        pa_context_fail(s->context, PA_ERR_PROTOCOL);
 | 
			
		||||
        goto finish;
 | 
			
		||||
| 
						 | 
				
			
			@ -491,15 +660,19 @@ void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED
 | 
			
		|||
    pa_dynarray_put((s->direction == PA_STREAM_RECORD) ? s->context->record_streams : s->context->playback_streams, s->channel, s);
 | 
			
		||||
 | 
			
		||||
    if (s->direction != PA_STREAM_UPLOAD && s->flags & PA_STREAM_AUTO_TIMING_UPDATE) {
 | 
			
		||||
 | 
			
		||||
        /* If automatic timing updates are active, we wait for the
 | 
			
		||||
         * first timing update before going to PA_STREAM_READY
 | 
			
		||||
         * state */
 | 
			
		||||
 | 
			
		||||
        s->state = PA_STREAM_READY;
 | 
			
		||||
        request_auto_timing_update(s, 1);
 | 
			
		||||
        s->state = PA_STREAM_CREATING;
 | 
			
		||||
 | 
			
		||||
    } else
 | 
			
		||||
        create_stream_complete(s);
 | 
			
		||||
        s->timing_info_not_ready = TRUE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    create_stream_complete(s);
 | 
			
		||||
 | 
			
		||||
finish:
 | 
			
		||||
    pa_stream_unref(s);
 | 
			
		||||
| 
						 | 
				
			
			@ -525,7 +698,13 @@ static int create_stream(
 | 
			
		|||
                                               PA_STREAM_START_CORKED|
 | 
			
		||||
                                               PA_STREAM_INTERPOLATE_TIMING|
 | 
			
		||||
                                               PA_STREAM_NOT_MONOTONOUS|
 | 
			
		||||
                                               PA_STREAM_AUTO_TIMING_UPDATE : 0))), PA_ERR_INVALID);
 | 
			
		||||
                                               PA_STREAM_AUTO_TIMING_UPDATE|
 | 
			
		||||
                                               PA_STREAM_NO_REMAP_CHANNELS|
 | 
			
		||||
                                               PA_STREAM_NO_REMIX_CHANNELS|
 | 
			
		||||
                                               PA_STREAM_FIX_FORMAT|
 | 
			
		||||
                                               PA_STREAM_FIX_RATE|
 | 
			
		||||
                                               PA_STREAM_FIX_CHANNELS|
 | 
			
		||||
                                               PA_STREAM_DONT_MOVE : 0))), PA_ERR_INVALID);
 | 
			
		||||
    PA_CHECK_VALIDITY(s->context, !volume || volume->channels == s->sample_spec.channels, PA_ERR_INVALID);
 | 
			
		||||
    PA_CHECK_VALIDITY(s->context, !sync_stream || (direction == PA_STREAM_PLAYBACK && sync_stream->direction == PA_STREAM_PLAYBACK), PA_ERR_INVALID);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -537,15 +716,17 @@ static int create_stream(
 | 
			
		|||
    if (sync_stream)
 | 
			
		||||
        s->syncid = sync_stream->syncid;
 | 
			
		||||
 | 
			
		||||
    if (attr)
 | 
			
		||||
    if (attr) {
 | 
			
		||||
        s->buffer_attr = *attr;
 | 
			
		||||
    else {
 | 
			
		||||
        s->manual_buffer_attr = TRUE;
 | 
			
		||||
    } else {
 | 
			
		||||
        /* half a second, with minimum request of 10 ms */
 | 
			
		||||
        s->buffer_attr.tlength = pa_bytes_per_second(&s->sample_spec)/2;
 | 
			
		||||
        s->buffer_attr.maxlength = (s->buffer_attr.tlength*3)/2;
 | 
			
		||||
        s->buffer_attr.minreq = s->buffer_attr.tlength/50;
 | 
			
		||||
        s->buffer_attr.prebuf = s->buffer_attr.tlength - s->buffer_attr.minreq;
 | 
			
		||||
        s->buffer_attr.fragsize = s->buffer_attr.tlength/50;
 | 
			
		||||
        s->manual_buffer_attr = FALSE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!dev)
 | 
			
		||||
| 
						 | 
				
			
			@ -585,6 +766,19 @@ static int create_stream(
 | 
			
		|||
    } else
 | 
			
		||||
        pa_tagstruct_putu32(t, s->buffer_attr.fragsize);
 | 
			
		||||
 | 
			
		||||
    if (s->context->version >= 12 && s->direction != PA_STREAM_UPLOAD) {
 | 
			
		||||
        pa_tagstruct_put(
 | 
			
		||||
                t,
 | 
			
		||||
                PA_TAG_BOOLEAN, flags & PA_STREAM_NO_REMAP_CHANNELS,
 | 
			
		||||
                PA_TAG_BOOLEAN, flags & PA_STREAM_NO_REMIX_CHANNELS,
 | 
			
		||||
                PA_TAG_BOOLEAN, flags & PA_STREAM_FIX_FORMAT,
 | 
			
		||||
                PA_TAG_BOOLEAN, flags & PA_STREAM_FIX_RATE,
 | 
			
		||||
                PA_TAG_BOOLEAN, flags & PA_STREAM_FIX_CHANNELS,
 | 
			
		||||
                PA_TAG_BOOLEAN, flags & PA_STREAM_DONT_MOVE,
 | 
			
		||||
                PA_TAG_BOOLEAN, flags & PA_STREAM_VARIABLE_RATE,
 | 
			
		||||
                PA_TAG_INVALID);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pa_pstream_send_tagstruct(s->context->pstream, t);
 | 
			
		||||
    pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_create_stream_callback, s, NULL);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -921,8 +1115,10 @@ static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command,
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    /* First, let's complete the initialization, if necessary. */
 | 
			
		||||
    if (o->stream->state == PA_STREAM_CREATING)
 | 
			
		||||
    if (o->stream->state == PA_STREAM_CREATING) {
 | 
			
		||||
        o->stream->timing_info_not_ready = FALSE;
 | 
			
		||||
        create_stream_complete(o->stream);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (o->stream->latency_update_callback)
 | 
			
		||||
        o->stream->latency_update_callback(o->stream, o->stream->latency_update_userdata);
 | 
			
		||||
| 
						 | 
				
			
			@ -1084,6 +1280,22 @@ void pa_stream_set_latency_update_callback(pa_stream *s, pa_stream_notify_cb_t c
 | 
			
		|||
    s->latency_update_userdata = userdata;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void pa_stream_set_moved_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) {
 | 
			
		||||
    pa_assert(s);
 | 
			
		||||
    pa_assert(PA_REFCNT_VALUE(s) >= 1);
 | 
			
		||||
 | 
			
		||||
    s->moved_callback = cb;
 | 
			
		||||
    s->moved_userdata = userdata;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void pa_stream_set_suspended_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) {
 | 
			
		||||
    pa_assert(s);
 | 
			
		||||
    pa_assert(PA_REFCNT_VALUE(s) >= 1);
 | 
			
		||||
 | 
			
		||||
    s->suspended_callback = cb;
 | 
			
		||||
    s->suspended_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) {
 | 
			
		||||
    pa_operation *o = userdata;
 | 
			
		||||
    int success = 1;
 | 
			
		||||
| 
						 | 
				
			
			@ -1420,8 +1632,208 @@ const pa_buffer_attr* pa_stream_get_buffer_attr(pa_stream *s) {
 | 
			
		|||
 | 
			
		||||
    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,
 | 
			
		||||
        pa_context_get_server_protocol_version(s->context) >= 9, PA_ERR_NODATA);
 | 
			
		||||
    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->context->version >= 9, PA_ERR_NODATA);
 | 
			
		||||
 | 
			
		||||
    return &s->buffer_attr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void stream_set_buffer_attr_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;
 | 
			
		||||
 | 
			
		||||
    pa_assert(pd);
 | 
			
		||||
    pa_assert(o);
 | 
			
		||||
    pa_assert(PA_REFCNT_VALUE(o) >= 1);
 | 
			
		||||
 | 
			
		||||
    if (!o->context)
 | 
			
		||||
        goto finish;
 | 
			
		||||
 | 
			
		||||
    if (command != PA_COMMAND_REPLY) {
 | 
			
		||||
        if (pa_context_handle_error(o->context, command, t) < 0)
 | 
			
		||||
            goto finish;
 | 
			
		||||
 | 
			
		||||
        success = 0;
 | 
			
		||||
    } else {
 | 
			
		||||
 | 
			
		||||
        if (o->stream->direction == PA_STREAM_PLAYBACK) {
 | 
			
		||||
            if (pa_tagstruct_getu32(t, &o->stream->buffer_attr.maxlength) < 0 ||
 | 
			
		||||
                pa_tagstruct_getu32(t, &o->stream->buffer_attr.tlength) < 0 ||
 | 
			
		||||
                pa_tagstruct_getu32(t, &o->stream->buffer_attr.prebuf) < 0 ||
 | 
			
		||||
                pa_tagstruct_getu32(t, &o->stream->buffer_attr.minreq) < 0) {
 | 
			
		||||
                pa_context_fail(o->context, PA_ERR_PROTOCOL);
 | 
			
		||||
                goto finish;
 | 
			
		||||
            }
 | 
			
		||||
        } else if (o->stream->direction == PA_STREAM_RECORD) {
 | 
			
		||||
            if (pa_tagstruct_getu32(t, &o->stream->buffer_attr.maxlength) < 0 ||
 | 
			
		||||
                pa_tagstruct_getu32(t, &o->stream->buffer_attr.fragsize) < 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->manual_buffer_attr = TRUE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (o->stream->state == PA_STREAM_CREATING) {
 | 
			
		||||
        o->stream->buffer_attr_not_ready = FALSE;
 | 
			
		||||
        create_stream_complete(o->stream);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (o->callback) {
 | 
			
		||||
        pa_stream_success_cb_t cb = (pa_stream_success_cb_t) o->callback;
 | 
			
		||||
        cb(o->stream, success, o->userdata);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
finish:
 | 
			
		||||
    pa_operation_done(o);
 | 
			
		||||
    pa_operation_unref(o);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
pa_operation* pa_stream_set_buffer_attr(pa_stream *s, const pa_buffer_attr *attr, 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);
 | 
			
		||||
    pa_assert(attr);
 | 
			
		||||
 | 
			
		||||
    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 >= 12, PA_ERR_NOTSUPPORTED);
 | 
			
		||||
 | 
			
		||||
    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_BUFFER_ATTR : PA_COMMAND_SET_PLAYBACK_STREAM_BUFFER_ATTR,
 | 
			
		||||
            &tag);
 | 
			
		||||
    pa_tagstruct_putu32(t, s->channel);
 | 
			
		||||
 | 
			
		||||
    pa_tagstruct_putu32(t, attr->maxlength);
 | 
			
		||||
 | 
			
		||||
    if (s->direction == PA_STREAM_PLAYBACK)
 | 
			
		||||
        pa_tagstruct_put(
 | 
			
		||||
                t,
 | 
			
		||||
                PA_TAG_U32, attr->tlength,
 | 
			
		||||
                PA_TAG_U32, attr->prebuf,
 | 
			
		||||
                PA_TAG_U32, attr->minreq,
 | 
			
		||||
                PA_TAG_INVALID);
 | 
			
		||||
    else
 | 
			
		||||
        pa_tagstruct_putu32(t, attr->fragsize);
 | 
			
		||||
 | 
			
		||||
    pa_pstream_send_tagstruct(s->context->pstream, t);
 | 
			
		||||
    pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, stream_set_buffer_attr_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
 | 
			
		||||
 | 
			
		||||
    return o;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint32_t pa_stream_get_device_index(pa_stream *s) {
 | 
			
		||||
    pa_assert(s);
 | 
			
		||||
    pa_assert(PA_REFCNT_VALUE(s) >= 1);
 | 
			
		||||
 | 
			
		||||
    PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE, PA_INVALID_INDEX);
 | 
			
		||||
    PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE, PA_INVALID_INDEX);
 | 
			
		||||
    PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->context->version >= 12, PA_ERR_NOTSUPPORTED, PA_INVALID_INDEX);
 | 
			
		||||
    PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->device_index != PA_INVALID_INDEX, PA_ERR_BADSTATE, PA_INVALID_INDEX);
 | 
			
		||||
 | 
			
		||||
    return s->device_index;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *pa_stream_get_device_name(pa_stream *s) {
 | 
			
		||||
    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);
 | 
			
		||||
    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->context->version >= 12, PA_ERR_NOTSUPPORTED);
 | 
			
		||||
    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->device_name, PA_ERR_BADSTATE);
 | 
			
		||||
 | 
			
		||||
    return s->device_name;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int pa_stream_is_suspended(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);
 | 
			
		||||
    PA_CHECK_VALIDITY(s->context, s->context->version >= 12, PA_ERR_NOTSUPPORTED);
 | 
			
		||||
 | 
			
		||||
    return s->suspended;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
 | 
			
		||||
    pa_assert(pd);
 | 
			
		||||
    pa_assert(o);
 | 
			
		||||
    pa_assert(PA_REFCNT_VALUE(o) >= 1);
 | 
			
		||||
 | 
			
		||||
    if (!o->context)
 | 
			
		||||
        goto finish;
 | 
			
		||||
 | 
			
		||||
    if (command != PA_COMMAND_REPLY) {
 | 
			
		||||
        if (pa_context_handle_error(o->context, command, t) < 0)
 | 
			
		||||
            goto finish;
 | 
			
		||||
 | 
			
		||||
        success = 0;
 | 
			
		||||
    } else {
 | 
			
		||||
 | 
			
		||||
        if (!pa_tagstruct_eof(t)) {
 | 
			
		||||
            pa_context_fail(o->context, PA_ERR_PROTOCOL);
 | 
			
		||||
            goto finish;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    o->stream->sample_spec.rate = PA_PTR_TO_UINT(o->private);
 | 
			
		||||
    pa_assert(pa_sample_spec_valid(&o->stream->sample_spec));
 | 
			
		||||
 | 
			
		||||
    if (o->callback) {
 | 
			
		||||
        pa_stream_success_cb_t cb = (pa_stream_success_cb_t) o->callback;
 | 
			
		||||
        cb(o->stream, success, o->userdata);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
finish:
 | 
			
		||||
    pa_operation_done(o);
 | 
			
		||||
    pa_operation_unref(o);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
pa_operation *pa_stream_update_sample_rate(pa_stream *s, uint32_t rate, 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);
 | 
			
		||||
 | 
			
		||||
    PA_CHECK_VALIDITY_RETURN_NULL(s->context, rate > 0 && rate <= PA_RATE_MAX, PA_ERR_INVALID);
 | 
			
		||||
    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->flags & PA_STREAM_VARIABLE_RATE, PA_ERR_BADSTATE);
 | 
			
		||||
    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->context->version >= 12, PA_ERR_NOTSUPPORTED);
 | 
			
		||||
 | 
			
		||||
    o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata);
 | 
			
		||||
    o->private = PA_UINT_TO_PTR(rate);
 | 
			
		||||
 | 
			
		||||
    t = pa_tagstruct_command(
 | 
			
		||||
            s->context,
 | 
			
		||||
            s->direction == PA_STREAM_RECORD ? PA_COMMAND_UPDATE_RECORD_STREAM_SAMPLE_RATE : PA_COMMAND_UPDATE_PLAYBACK_STREAM_SAMPLE_RATE,
 | 
			
		||||
            &tag);
 | 
			
		||||
    pa_tagstruct_putu32(t, s->channel);
 | 
			
		||||
    pa_tagstruct_putu32(t, rate);
 | 
			
		||||
 | 
			
		||||
    pa_pstream_send_tagstruct(s->context->pstream, t);
 | 
			
		||||
    pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, stream_update_sample_rate_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
 | 
			
		||||
 | 
			
		||||
    return o;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -295,9 +295,38 @@ pa_stream_state_t pa_stream_get_state(pa_stream *p);
 | 
			
		|||
/** Return the context this stream is attached to */
 | 
			
		||||
pa_context* pa_stream_get_context(pa_stream *p);
 | 
			
		||||
 | 
			
		||||
/** Return the device (sink input or source output) index this stream is connected to */
 | 
			
		||||
/** Return the sink input resp. source output index this stream is
 | 
			
		||||
 * identified in the server with. This is useful for usage with the
 | 
			
		||||
 * introspection functions, such as pa_context_get_sink_input_info()
 | 
			
		||||
 * resp. pa_context_get_source_output_info(). */
 | 
			
		||||
uint32_t pa_stream_get_index(pa_stream *s);
 | 
			
		||||
 | 
			
		||||
/** Return the index of the sink or source this stream is connected to
 | 
			
		||||
 * in the server. This is useful for usage with the introspection
 | 
			
		||||
 * functions, such as pa_context_get_sink_info_by_index()
 | 
			
		||||
 * resp. pa_context_get_source_info_by_index(). Please note that
 | 
			
		||||
 * streams may be moved between sinks/sources and thus it is
 | 
			
		||||
 * recommended to use pa_stream_set_moved_callback() to be notified
 | 
			
		||||
 * about this. This function will return with PA_ERR_NOTSUPPORTED when the
 | 
			
		||||
 * server is older than 0.9.8. \since 0.9.8 */
 | 
			
		||||
uint32_t pa_stream_get_device_index(pa_stream *s);
 | 
			
		||||
 | 
			
		||||
/** Return the name of the sink or source this stream is connected to
 | 
			
		||||
 * in the server. This is useful for usage with the introspection
 | 
			
		||||
 * functions, such as pa_context_get_sink_info_by_name()
 | 
			
		||||
 * resp. pa_context_get_source_info_by_name(). Please note that
 | 
			
		||||
 * streams may be moved between sinks/sources and thus it is
 | 
			
		||||
 * recommended to use pa_stream_set_moved_callback() to be notified
 | 
			
		||||
 * about this. This function will return with PA_ERR_NOTSUPPORTED when the
 | 
			
		||||
 * server is older than 0.9.8. \since 0.9.8 */
 | 
			
		||||
const char *pa_stream_get_device_name(pa_stream *s);
 | 
			
		||||
 | 
			
		||||
/** Return 1 if the sink or source this stream is connected to has
 | 
			
		||||
 * been suspended. This will return 0 if not, and negative on
 | 
			
		||||
 * error. This function will return with PA_ERR_NOTSUPPORTED when the
 | 
			
		||||
 * server is older than 0.9.8. \since 0.9.8 */
 | 
			
		||||
int pa_stream_is_suspended(pa_stream *s);
 | 
			
		||||
 | 
			
		||||
/** Connect the stream to a sink */
 | 
			
		||||
int pa_stream_connect_playback(
 | 
			
		||||
        pa_stream *s                  /**< The stream to connect to a sink */,
 | 
			
		||||
| 
						 | 
				
			
			@ -346,10 +375,10 @@ int pa_stream_peek(
 | 
			
		|||
 * calling pa_stream_peek(). \since 0.8 */
 | 
			
		||||
int pa_stream_drop(pa_stream *p);
 | 
			
		||||
 | 
			
		||||
/** Return the nember of bytes that may be written using pa_stream_write(), in bytes */
 | 
			
		||||
/** Return the number of bytes that may be written using pa_stream_write() */
 | 
			
		||||
size_t pa_stream_writable_size(pa_stream *p);
 | 
			
		||||
 | 
			
		||||
/** Return the number of bytes that may be read using pa_stream_read(), in bytes \since 0.8 */
 | 
			
		||||
/** Return the number of bytes that may be read using pa_stream_read() \since 0.8 */
 | 
			
		||||
size_t pa_stream_readable_size(pa_stream *p);
 | 
			
		||||
 | 
			
		||||
/** Drain a playback stream. Use this for notification when the buffer is empty */
 | 
			
		||||
| 
						 | 
				
			
			@ -378,9 +407,28 @@ void pa_stream_set_overflow_callback(pa_stream *p, pa_stream_notify_cb_t cb, voi
 | 
			
		|||
/** Set the callback function that is called when a buffer underflow happens. (Only for playback streams) \since 0.8 */
 | 
			
		||||
void pa_stream_set_underflow_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 streams only. (Only for playback streams) \since 0.8.2 */
 | 
			
		||||
/** Set the callback function that is called whenever a latency
 | 
			
		||||
 * information update happens. Useful on PA_STREAM_AUTO_TIMING_UPDATE
 | 
			
		||||
 * streams only. (Only for playback streams) \since 0.8.2 */
 | 
			
		||||
void pa_stream_set_latency_update_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata);
 | 
			
		||||
 | 
			
		||||
/** Set the callback function that is called whenever the stream is
 | 
			
		||||
 * moved to a different sink/source. Use pa_stream_get_device_name()or
 | 
			
		||||
 * pa_stream_get_device_index() to query the new sink/source. This
 | 
			
		||||
 * notification is only generated when the server is at least
 | 
			
		||||
 * 0.9.8. \since 0.9.8 */
 | 
			
		||||
void pa_stream_set_moved_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata);
 | 
			
		||||
 | 
			
		||||
/** Set the callback function that is called whenever the sink/source
 | 
			
		||||
 * this stream is connected to is suspended or resumed. Use
 | 
			
		||||
 * pa_stream_is_suspended() to query the new suspend status. Please
 | 
			
		||||
 * note that the suspend status might also change when the stream is
 | 
			
		||||
 * moved between devices. Thus if you call this function you very
 | 
			
		||||
 * likely want to call pa_stream_set_moved_callback, too. This
 | 
			
		||||
 * notification is only generated when the server is at least
 | 
			
		||||
 * 0.9.8. \since 0.9.8 */
 | 
			
		||||
void pa_stream_set_suspended_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata);
 | 
			
		||||
 | 
			
		||||
/** Pause (or resume) playback of this stream temporarily. Available on both playback and recording streams. \since 0.3 */
 | 
			
		||||
pa_operation* pa_stream_cork(pa_stream *s, int b, pa_stream_success_cb_t cb, void *userdata);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -447,6 +495,21 @@ const pa_channel_map* pa_stream_get_channel_map(pa_stream *s);
 | 
			
		|||
 * PulseAudio 0.9. \since 0.9.0 */
 | 
			
		||||
const pa_buffer_attr* pa_stream_get_buffer_attr(pa_stream *s);
 | 
			
		||||
 | 
			
		||||
/** Change the buffer metrics of the stream during playback. The
 | 
			
		||||
 * server might have chosen different buffer metrics then
 | 
			
		||||
 * requested. The selected metrics may be queried with
 | 
			
		||||
 * pa_stream_get_buffer_attr() as soon as the callback is called. Only
 | 
			
		||||
 * valid after the stream has been connected successfully and if the
 | 
			
		||||
 * server is at least PulseAudio 0.9.8. \since 0.9.8 */
 | 
			
		||||
pa_operation *pa_stream_set_buffer_attr(pa_stream *s, const pa_buffer_attr *attr, pa_stream_success_cb_t cb, void *userdata);
 | 
			
		||||
 | 
			
		||||
/* Change the stream sampling rate during playback. You need to pass
 | 
			
		||||
 * PA_STREAM_VARIABLE_RATE in the flags parameter of
 | 
			
		||||
 * pa_stream_connect() if you plan to use this function. Only valid
 | 
			
		||||
 * after the stream has been connected successfully and if the server
 | 
			
		||||
 * is at least PulseAudio 0.9.8. \since 0.9.8 */
 | 
			
		||||
pa_operation *pa_stream_update_sample_rate(pa_stream *s, uint32_t rate, pa_stream_success_cb_t cb, void *userdata);
 | 
			
		||||
 | 
			
		||||
PA_C_DECL_END
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -236,7 +236,7 @@ char *pa_source_output_list_to_string(pa_core *c) {
 | 
			
		|||
            "    index: %u\n"
 | 
			
		||||
            "\tname: '%s'\n"
 | 
			
		||||
            "\tdriver: <%s>\n"
 | 
			
		||||
            "\tflags: %s%s\n"
 | 
			
		||||
            "\tflags: %s%s%s%s%s%s%s\n"
 | 
			
		||||
            "\tstate: %s\n"
 | 
			
		||||
            "\tsource: <%u> '%s'\n"
 | 
			
		||||
            "\tlatency: <%0.0f usec>\n"
 | 
			
		||||
| 
						 | 
				
			
			@ -248,6 +248,11 @@ char *pa_source_output_list_to_string(pa_core *c) {
 | 
			
		|||
            o->driver,
 | 
			
		||||
            o->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE ? "VARIABLE_RATE " : "",
 | 
			
		||||
            o->flags & PA_SOURCE_OUTPUT_DONT_MOVE ? "DONT_MOVE " : "",
 | 
			
		||||
            o->flags & PA_SOURCE_OUTPUT_NO_REMAP ? "NO_REMAP " : "",
 | 
			
		||||
            o->flags & PA_SOURCE_OUTPUT_NO_REMIX ? "NO_REMIX " : "",
 | 
			
		||||
            o->flags & PA_SOURCE_OUTPUT_FIX_FORMAT ? "FIX_FORMAT " : "",
 | 
			
		||||
            o->flags & PA_SOURCE_OUTPUT_FIX_RATE ? "FIX_RATE " : "",
 | 
			
		||||
            o->flags & PA_SOURCE_OUTPUT_FIX_CHANNELS ? "FIX_CHANNELS " : "",
 | 
			
		||||
            state_table[pa_source_output_get_state(o)],
 | 
			
		||||
            o->source->index, o->source->name,
 | 
			
		||||
            (double) pa_source_output_get_latency(o),
 | 
			
		||||
| 
						 | 
				
			
			@ -289,7 +294,7 @@ char *pa_sink_input_list_to_string(pa_core *c) {
 | 
			
		|||
            "    index: %u\n"
 | 
			
		||||
            "\tname: <%s>\n"
 | 
			
		||||
            "\tdriver: <%s>\n"
 | 
			
		||||
            "\tflags: %s%s\n"
 | 
			
		||||
            "\tflags: %s%s%s%s%s%s%s\n"
 | 
			
		||||
            "\tstate: %s\n"
 | 
			
		||||
            "\tsink: <%u> '%s'\n"
 | 
			
		||||
            "\tvolume: <%s>\n"
 | 
			
		||||
| 
						 | 
				
			
			@ -303,6 +308,11 @@ char *pa_sink_input_list_to_string(pa_core *c) {
 | 
			
		|||
            i->driver,
 | 
			
		||||
            i->flags & PA_SINK_INPUT_VARIABLE_RATE ? "VARIABLE_RATE " : "",
 | 
			
		||||
            i->flags & PA_SINK_INPUT_DONT_MOVE ? "DONT_MOVE " : "",
 | 
			
		||||
            i->flags & PA_SINK_INPUT_NO_REMAP ? "NO_REMAP " : "",
 | 
			
		||||
            i->flags & PA_SINK_INPUT_NO_REMIX ? "NO_REMIX " : "",
 | 
			
		||||
            i->flags & PA_SINK_INPUT_FIX_FORMAT ? "FIX_FORMAT " : "",
 | 
			
		||||
            i->flags & PA_SINK_INPUT_FIX_RATE ? "FIX_RATE " : "",
 | 
			
		||||
            i->flags & PA_SINK_INPUT_FIX_CHANNELS ? "FIX_CHANNELS " : "",
 | 
			
		||||
            state_table[pa_sink_input_get_state(i)],
 | 
			
		||||
            i->sink->index, i->sink->name,
 | 
			
		||||
            pa_cvolume_snprint(cv, sizeof(cv), pa_sink_input_get_volume(i)),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -138,6 +138,7 @@ pa_core* pa_core_new(pa_mainloop_api *m, int shared) {
 | 
			
		|||
    c->disallow_module_loading = FALSE;
 | 
			
		||||
    c->realtime_scheduling = FALSE;
 | 
			
		||||
    c->realtime_priority = 5;
 | 
			
		||||
    c->disable_remixing = FALSE;
 | 
			
		||||
 | 
			
		||||
    for (j = 0; j < PA_CORE_HOOK_MAX; j++)
 | 
			
		||||
        pa_hook_init(&c->hooks[j], c);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -54,6 +54,7 @@ typedef enum pa_core_hook {
 | 
			
		|||
    PA_CORE_HOOK_SOURCE_STATE_CHANGED,
 | 
			
		||||
    PA_CORE_HOOK_SOURCE_DESCRIPTION_CHANGED,
 | 
			
		||||
    PA_CORE_HOOK_SINK_INPUT_NEW,
 | 
			
		||||
    PA_CORE_HOOK_SINK_INPUT_FIXATE,
 | 
			
		||||
    PA_CORE_HOOK_SINK_INPUT_PUT,
 | 
			
		||||
    PA_CORE_HOOK_SINK_INPUT_UNLINK,
 | 
			
		||||
    PA_CORE_HOOK_SINK_INPUT_UNLINK_POST,
 | 
			
		||||
| 
						 | 
				
			
			@ -62,6 +63,7 @@ typedef enum pa_core_hook {
 | 
			
		|||
    PA_CORE_HOOK_SINK_INPUT_NAME_CHANGED,
 | 
			
		||||
    PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED,
 | 
			
		||||
    PA_CORE_HOOK_SOURCE_OUTPUT_NEW,
 | 
			
		||||
    PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE,
 | 
			
		||||
    PA_CORE_HOOK_SOURCE_OUTPUT_PUT,
 | 
			
		||||
    PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK,
 | 
			
		||||
    PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK_POST,
 | 
			
		||||
| 
						 | 
				
			
			@ -118,6 +120,7 @@ struct pa_core {
 | 
			
		|||
    pa_bool_t is_system_instance;
 | 
			
		||||
    pa_bool_t realtime_scheduling;
 | 
			
		||||
    int realtime_priority;
 | 
			
		||||
    pa_bool_t disable_remixing;
 | 
			
		||||
 | 
			
		||||
    /* hooks */
 | 
			
		||||
    pa_hook hooks[PA_CORE_HOOK_MAX];
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -42,6 +42,7 @@
 | 
			
		|||
#endif
 | 
			
		||||
 | 
			
		||||
#include <pulse/xmalloc.h>
 | 
			
		||||
#include <pulse/util.h>
 | 
			
		||||
 | 
			
		||||
#include <pulsecore/core-error.h>
 | 
			
		||||
#include <pulsecore/core-util.h>
 | 
			
		||||
| 
						 | 
				
			
			@ -260,8 +261,8 @@ fail:
 | 
			
		|||
 * exists and the PID therein too. Returns 0 on succcess, -1
 | 
			
		||||
 * otherwise. If pid is non-NULL and a running daemon was found,
 | 
			
		||||
 * return its PID therein */
 | 
			
		||||
int pa_pid_file_check_running(pid_t *pid) {
 | 
			
		||||
    return pa_pid_file_kill(0, pid);
 | 
			
		||||
int pa_pid_file_check_running(pid_t *pid, const char *binary_name) {
 | 
			
		||||
    return pa_pid_file_kill(0, pid, binary_name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifndef OS_IS_WIN32
 | 
			
		||||
| 
						 | 
				
			
			@ -269,12 +270,14 @@ int pa_pid_file_check_running(pid_t *pid) {
 | 
			
		|||
/* Kill a current running daemon. Return non-zero on success, -1
 | 
			
		||||
 * otherwise. If successful *pid contains the PID of the daemon
 | 
			
		||||
 * process. */
 | 
			
		||||
int pa_pid_file_kill(int sig, pid_t *pid) {
 | 
			
		||||
int pa_pid_file_kill(int sig, pid_t *pid, const char *binary_name) {
 | 
			
		||||
    int fd = -1;
 | 
			
		||||
    char fn[PATH_MAX];
 | 
			
		||||
    int ret = -1;
 | 
			
		||||
    pid_t _pid;
 | 
			
		||||
 | 
			
		||||
#ifdef __linux__
 | 
			
		||||
    char *e = NULL;
 | 
			
		||||
#endif
 | 
			
		||||
    if (!pid)
 | 
			
		||||
        pid = &_pid;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -286,6 +289,23 @@ int pa_pid_file_kill(int sig, pid_t *pid) {
 | 
			
		|||
    if ((*pid = read_pid(fn, fd)) == (pid_t) -1)
 | 
			
		||||
        goto fail;
 | 
			
		||||
 | 
			
		||||
#ifdef __linux__
 | 
			
		||||
    if (binary_name) {
 | 
			
		||||
        pa_snprintf(fn, sizeof(fn), "/proc/%lu/exe", (unsigned long) pid);
 | 
			
		||||
 | 
			
		||||
        if ((e = pa_readlink(fn))) {
 | 
			
		||||
            char *f = pa_path_get_filename(e);
 | 
			
		||||
            if (strcmp(f, binary_name)
 | 
			
		||||
#if defined(__OPTIMIZE__)
 | 
			
		||||
                /* libtool likes to rename our binary names ... */
 | 
			
		||||
                && !(pa_startswith(f, "lt-") && strcmp(f+3, binary_name) == 0)
 | 
			
		||||
#endif
 | 
			
		||||
            )
 | 
			
		||||
                goto fail;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    ret = kill(*pid, sig);
 | 
			
		||||
 | 
			
		||||
fail:
 | 
			
		||||
| 
						 | 
				
			
			@ -295,13 +315,17 @@ fail:
 | 
			
		|||
        pa_close(fd);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#ifdef __linux__
 | 
			
		||||
    pa_xfree(e);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    return ret;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#else /* OS_IS_WIN32 */
 | 
			
		||||
 | 
			
		||||
int pa_pid_file_kill(int sig, pid_t *pid) {
 | 
			
		||||
int pa_pid_file_kill(int sig, pid_t *pid, const char *exe_name) {
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,7 +26,7 @@
 | 
			
		|||
 | 
			
		||||
int pa_pid_file_create(void);
 | 
			
		||||
int pa_pid_file_remove(void);
 | 
			
		||||
int pa_pid_file_check_running(pid_t *pid);
 | 
			
		||||
int pa_pid_file_kill(int sig, pid_t *pid);
 | 
			
		||||
int pa_pid_file_check_running(pid_t *pid, const char *binary_name);
 | 
			
		||||
int pa_pid_file_kill(int sig, pid_t *pid, const char *binary_name);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -202,12 +202,16 @@ enum {
 | 
			
		|||
static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk);
 | 
			
		||||
static void sink_input_drop_cb(pa_sink_input *i, size_t length);
 | 
			
		||||
static void sink_input_kill_cb(pa_sink_input *i);
 | 
			
		||||
static void sink_input_suspend_cb(pa_sink_input *i, pa_bool_t suspend);
 | 
			
		||||
static void sink_input_moved_cb(pa_sink_input *i);
 | 
			
		||||
 | 
			
		||||
static void send_memblock(connection *c);
 | 
			
		||||
static void request_bytes(struct playback_stream*s);
 | 
			
		||||
 | 
			
		||||
static void source_output_kill_cb(pa_source_output *o);
 | 
			
		||||
static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk);
 | 
			
		||||
static void source_output_suspend_cb(pa_source_output *o, pa_bool_t suspend);
 | 
			
		||||
static void source_output_moved_cb(pa_source_output *o);
 | 
			
		||||
static pa_usec_t source_output_get_latency_cb(pa_source_output *o);
 | 
			
		||||
 | 
			
		||||
static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
 | 
			
		||||
| 
						 | 
				
			
			@ -248,6 +252,8 @@ static void command_cork_record_stream(pa_pdispatch *pd, uint32_t command, uint3
 | 
			
		|||
static void command_flush_record_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
 | 
			
		||||
static void command_move_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
 | 
			
		||||
static void command_suspend(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
 | 
			
		||||
static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
 | 
			
		||||
static void command_update_stream_sample_rate(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
 | 
			
		||||
 | 
			
		||||
static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
 | 
			
		||||
    [PA_COMMAND_ERROR] = NULL,
 | 
			
		||||
| 
						 | 
				
			
			@ -323,7 +329,13 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
 | 
			
		|||
    [PA_COMMAND_REMOVE_AUTOLOAD] = command_remove_autoload,
 | 
			
		||||
 | 
			
		||||
    [PA_COMMAND_MOVE_SINK_INPUT] = command_move_stream,
 | 
			
		||||
    [PA_COMMAND_MOVE_SOURCE_OUTPUT] = command_move_stream
 | 
			
		||||
    [PA_COMMAND_MOVE_SOURCE_OUTPUT] = command_move_stream,
 | 
			
		||||
 | 
			
		||||
    [PA_COMMAND_SET_PLAYBACK_STREAM_BUFFER_ATTR] = command_set_stream_buffer_attr,
 | 
			
		||||
    [PA_COMMAND_SET_RECORD_STREAM_BUFFER_ATTR] = command_set_stream_buffer_attr,
 | 
			
		||||
 | 
			
		||||
    [PA_COMMAND_UPDATE_PLAYBACK_STREAM_SAMPLE_RATE] = command_update_stream_sample_rate,
 | 
			
		||||
    [PA_COMMAND_UPDATE_RECORD_STREAM_SAMPLE_RATE] = command_update_stream_sample_rate
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* structure management */
 | 
			
		||||
| 
						 | 
				
			
			@ -435,12 +447,12 @@ static int record_stream_process_msg(pa_msgobject *o, int code, void*userdata, i
 | 
			
		|||
static record_stream* record_stream_new(
 | 
			
		||||
        connection *c,
 | 
			
		||||
        pa_source *source,
 | 
			
		||||
        const pa_sample_spec *ss,
 | 
			
		||||
        const pa_channel_map *map,
 | 
			
		||||
        pa_sample_spec *ss,
 | 
			
		||||
        pa_channel_map *map,
 | 
			
		||||
        const char *name,
 | 
			
		||||
        uint32_t *maxlength,
 | 
			
		||||
        uint32_t fragment_size,
 | 
			
		||||
        int corked) {
 | 
			
		||||
        pa_source_output_flags_t flags) {
 | 
			
		||||
 | 
			
		||||
    record_stream *s;
 | 
			
		||||
    pa_source_output *source_output;
 | 
			
		||||
| 
						 | 
				
			
			@ -462,7 +474,7 @@ static record_stream* record_stream_new(
 | 
			
		|||
    pa_source_output_new_data_set_sample_spec(&data, ss);
 | 
			
		||||
    pa_source_output_new_data_set_channel_map(&data, map);
 | 
			
		||||
 | 
			
		||||
    if (!(source_output = pa_source_output_new(c->protocol->core, &data, corked ? PA_SOURCE_OUTPUT_START_CORKED : 0)))
 | 
			
		||||
    if (!(source_output = pa_source_output_new(c->protocol->core, &data, flags)))
 | 
			
		||||
        return NULL;
 | 
			
		||||
 | 
			
		||||
    s = pa_msgobject_new(record_stream);
 | 
			
		||||
| 
						 | 
				
			
			@ -473,21 +485,30 @@ static record_stream* record_stream_new(
 | 
			
		|||
    s->source_output->push = source_output_push_cb;
 | 
			
		||||
    s->source_output->kill = source_output_kill_cb;
 | 
			
		||||
    s->source_output->get_latency = source_output_get_latency_cb;
 | 
			
		||||
    s->source_output->moved = source_output_moved_cb;
 | 
			
		||||
    s->source_output->suspend = source_output_suspend_cb;
 | 
			
		||||
    s->source_output->userdata = s;
 | 
			
		||||
 | 
			
		||||
    s->memblockq = pa_memblockq_new(
 | 
			
		||||
            0,
 | 
			
		||||
            *maxlength,
 | 
			
		||||
            0,
 | 
			
		||||
            base = pa_frame_size(ss),
 | 
			
		||||
            base = pa_frame_size(&s->source_output->sample_spec),
 | 
			
		||||
            1,
 | 
			
		||||
            0,
 | 
			
		||||
            NULL);
 | 
			
		||||
 | 
			
		||||
    *maxlength = pa_memblockq_get_maxlength(s->memblockq);
 | 
			
		||||
 | 
			
		||||
    s->fragment_size = (fragment_size/base)*base;
 | 
			
		||||
    if (s->fragment_size <= 0)
 | 
			
		||||
        s->fragment_size = base;
 | 
			
		||||
    *maxlength = pa_memblockq_get_maxlength(s->memblockq);
 | 
			
		||||
 | 
			
		||||
    if (s->fragment_size > *maxlength)
 | 
			
		||||
        s->fragment_size = *maxlength;
 | 
			
		||||
 | 
			
		||||
    *ss = s->source_output->sample_spec;
 | 
			
		||||
    *map = s->source_output->channel_map;
 | 
			
		||||
 | 
			
		||||
    pa_idxset_put(c->record_streams, s, &s->index);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -602,8 +623,8 @@ static int playback_stream_process_msg(pa_msgobject *o, int code, void*userdata,
 | 
			
		|||
static playback_stream* playback_stream_new(
 | 
			
		||||
        connection *c,
 | 
			
		||||
        pa_sink *sink,
 | 
			
		||||
        const pa_sample_spec *ss,
 | 
			
		||||
        const pa_channel_map *map,
 | 
			
		||||
        pa_sample_spec *ss,
 | 
			
		||||
        pa_channel_map *map,
 | 
			
		||||
        const char *name,
 | 
			
		||||
        uint32_t *maxlength,
 | 
			
		||||
        uint32_t *tlength,
 | 
			
		||||
| 
						 | 
				
			
			@ -611,8 +632,8 @@ static playback_stream* playback_stream_new(
 | 
			
		|||
        uint32_t *minreq,
 | 
			
		||||
        pa_cvolume *volume,
 | 
			
		||||
        uint32_t syncid,
 | 
			
		||||
        int corked,
 | 
			
		||||
        uint32_t *missing) {
 | 
			
		||||
        uint32_t *missing,
 | 
			
		||||
        pa_sink_input_flags_t flags) {
 | 
			
		||||
 | 
			
		||||
    playback_stream *s, *ssync;
 | 
			
		||||
    pa_sink_input *sink_input;
 | 
			
		||||
| 
						 | 
				
			
			@ -656,7 +677,7 @@ static playback_stream* playback_stream_new(
 | 
			
		|||
    data.client = c->client;
 | 
			
		||||
    data.sync_base = ssync ? ssync->sink_input : NULL;
 | 
			
		||||
 | 
			
		||||
    if (!(sink_input = pa_sink_input_new(c->protocol->core, &data, corked ? PA_SINK_INPUT_START_CORKED : 0)))
 | 
			
		||||
    if (!(sink_input = pa_sink_input_new(c->protocol->core, &data, flags)))
 | 
			
		||||
        return NULL;
 | 
			
		||||
 | 
			
		||||
    s = pa_msgobject_new(playback_stream);
 | 
			
		||||
| 
						 | 
				
			
			@ -671,17 +692,19 @@ static playback_stream* playback_stream_new(
 | 
			
		|||
    s->sink_input->peek = sink_input_peek_cb;
 | 
			
		||||
    s->sink_input->drop = sink_input_drop_cb;
 | 
			
		||||
    s->sink_input->kill = sink_input_kill_cb;
 | 
			
		||||
    s->sink_input->moved = sink_input_moved_cb;
 | 
			
		||||
    s->sink_input->suspend = sink_input_suspend_cb;
 | 
			
		||||
    s->sink_input->userdata = s;
 | 
			
		||||
 | 
			
		||||
    start_index = ssync ? pa_memblockq_get_read_index(ssync->memblockq) : 0;
 | 
			
		||||
 | 
			
		||||
    silence = pa_silence_memblock_new(c->protocol->core->mempool, ss, 0);
 | 
			
		||||
    silence = pa_silence_memblock_new(c->protocol->core->mempool, &s->sink_input->sample_spec, 0);
 | 
			
		||||
 | 
			
		||||
    s->memblockq = pa_memblockq_new(
 | 
			
		||||
            start_index,
 | 
			
		||||
            *maxlength,
 | 
			
		||||
            *tlength,
 | 
			
		||||
            pa_frame_size(ss),
 | 
			
		||||
            pa_frame_size(&s->sink_input->sample_spec),
 | 
			
		||||
            *prebuf,
 | 
			
		||||
            *minreq,
 | 
			
		||||
            silence);
 | 
			
		||||
| 
						 | 
				
			
			@ -694,6 +717,9 @@ static playback_stream* playback_stream_new(
 | 
			
		|||
    *minreq = (uint32_t) pa_memblockq_get_minreq(s->memblockq);
 | 
			
		||||
    *missing = (uint32_t) pa_memblockq_pop_missing(s->memblockq);
 | 
			
		||||
 | 
			
		||||
    *ss = s->sink_input->sample_spec;
 | 
			
		||||
    *map = s->sink_input->channel_map;
 | 
			
		||||
 | 
			
		||||
    s->minreq = pa_memblockq_get_minreq(s->memblockq);
 | 
			
		||||
    pa_atomic_store(&s->missing, 0);
 | 
			
		||||
    s->drain_request = 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -1022,6 +1048,7 @@ static void sink_input_drop_cb(pa_sink_input *i, size_t length) {
 | 
			
		|||
/*     pa_log("after_drop: %u %u", pa_memblockq_get_length(s->memblockq), pa_memblockq_is_readable(s->memblockq)); */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Called from main context */
 | 
			
		||||
static void sink_input_kill_cb(pa_sink_input *i) {
 | 
			
		||||
    playback_stream *s;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1033,6 +1060,42 @@ static void sink_input_kill_cb(pa_sink_input *i) {
 | 
			
		|||
    playback_stream_unlink(s);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Called from main context */
 | 
			
		||||
static void sink_input_suspend_cb(pa_sink_input *i, pa_bool_t suspend) {
 | 
			
		||||
    playback_stream *s;
 | 
			
		||||
    pa_tagstruct *t;
 | 
			
		||||
 | 
			
		||||
    pa_sink_input_assert_ref(i);
 | 
			
		||||
    s = PLAYBACK_STREAM(i->userdata);
 | 
			
		||||
    playback_stream_assert_ref(s);
 | 
			
		||||
 | 
			
		||||
    t = pa_tagstruct_new(NULL, 0);
 | 
			
		||||
    pa_tagstruct_putu32(t, PA_COMMAND_PLAYBACK_STREAM_SUSPENDED);
 | 
			
		||||
    pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
 | 
			
		||||
    pa_tagstruct_putu32(t, s->index);
 | 
			
		||||
    pa_tagstruct_put_boolean(t, suspend);
 | 
			
		||||
    pa_pstream_send_tagstruct(s->connection->pstream, t);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Called from main context */
 | 
			
		||||
static void sink_input_moved_cb(pa_sink_input *i) {
 | 
			
		||||
    playback_stream *s;
 | 
			
		||||
    pa_tagstruct *t;
 | 
			
		||||
 | 
			
		||||
    pa_sink_input_assert_ref(i);
 | 
			
		||||
    s = PLAYBACK_STREAM(i->userdata);
 | 
			
		||||
    playback_stream_assert_ref(s);
 | 
			
		||||
 | 
			
		||||
    t = pa_tagstruct_new(NULL, 0);
 | 
			
		||||
    pa_tagstruct_putu32(t, PA_COMMAND_PLAYBACK_STREAM_MOVED);
 | 
			
		||||
    pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
 | 
			
		||||
    pa_tagstruct_putu32(t, s->index);
 | 
			
		||||
    pa_tagstruct_putu32(t, i->sink->index);
 | 
			
		||||
    pa_tagstruct_puts(t, i->sink->name);
 | 
			
		||||
    pa_tagstruct_put_boolean(t, pa_sink_get_state(i->sink) == PA_SINK_SUSPENDED);
 | 
			
		||||
    pa_pstream_send_tagstruct(s->connection->pstream, t);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*** source_output callbacks ***/
 | 
			
		||||
 | 
			
		||||
/* Called from thread context */
 | 
			
		||||
| 
						 | 
				
			
			@ -1070,6 +1133,41 @@ static pa_usec_t source_output_get_latency_cb(pa_source_output *o) {
 | 
			
		|||
    return pa_bytes_to_usec(pa_memblockq_get_length(s->memblockq), &o->sample_spec);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Called from main context */
 | 
			
		||||
static void source_output_suspend_cb(pa_source_output *o, pa_bool_t suspend) {
 | 
			
		||||
    record_stream *s;
 | 
			
		||||
    pa_tagstruct *t;
 | 
			
		||||
 | 
			
		||||
    pa_source_output_assert_ref(o);
 | 
			
		||||
    s = RECORD_STREAM(o->userdata);
 | 
			
		||||
    record_stream_assert_ref(s);
 | 
			
		||||
 | 
			
		||||
    t = pa_tagstruct_new(NULL, 0);
 | 
			
		||||
    pa_tagstruct_putu32(t, PA_COMMAND_RECORD_STREAM_SUSPENDED);
 | 
			
		||||
    pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
 | 
			
		||||
    pa_tagstruct_putu32(t, s->index);
 | 
			
		||||
    pa_tagstruct_put_boolean(t, suspend);
 | 
			
		||||
    pa_pstream_send_tagstruct(s->connection->pstream, t);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Called from main context */
 | 
			
		||||
static void source_output_moved_cb(pa_source_output *o) {
 | 
			
		||||
    record_stream *s;
 | 
			
		||||
    pa_tagstruct *t;
 | 
			
		||||
 | 
			
		||||
    pa_source_output_assert_ref(o);
 | 
			
		||||
    s = RECORD_STREAM(o->userdata);
 | 
			
		||||
    record_stream_assert_ref(s);
 | 
			
		||||
 | 
			
		||||
    t = pa_tagstruct_new(NULL, 0);
 | 
			
		||||
    pa_tagstruct_putu32(t, PA_COMMAND_RECORD_STREAM_MOVED);
 | 
			
		||||
    pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
 | 
			
		||||
    pa_tagstruct_putu32(t, s->index);
 | 
			
		||||
    pa_tagstruct_putu32(t, o->source->index);
 | 
			
		||||
    pa_tagstruct_puts(t, o->source->name);
 | 
			
		||||
    pa_pstream_send_tagstruct(s->connection->pstream, t);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*** pdispatch callbacks ***/
 | 
			
		||||
 | 
			
		||||
static void protocol_error(connection *c) {
 | 
			
		||||
| 
						 | 
				
			
			@ -1104,6 +1202,8 @@ static void command_create_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GC
 | 
			
		|||
    pa_sink *sink = NULL;
 | 
			
		||||
    pa_cvolume volume;
 | 
			
		||||
    int corked;
 | 
			
		||||
    int no_remap = 0, no_remix = 0, fix_format = 0, fix_rate = 0, fix_channels = 0, no_move = 0, variable_rate = 0;
 | 
			
		||||
    pa_sink_input_flags_t flags = 0;
 | 
			
		||||
 | 
			
		||||
    connection_assert_ref(c);
 | 
			
		||||
    pa_assert(t);
 | 
			
		||||
| 
						 | 
				
			
			@ -1122,9 +1222,27 @@ static void command_create_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GC
 | 
			
		|||
            PA_TAG_U32, &minreq,
 | 
			
		||||
            PA_TAG_U32, &syncid,
 | 
			
		||||
            PA_TAG_CVOLUME, &volume,
 | 
			
		||||
            PA_TAG_INVALID) < 0 ||
 | 
			
		||||
        !pa_tagstruct_eof(t) ||
 | 
			
		||||
        !name) {
 | 
			
		||||
            PA_TAG_INVALID) < 0 || !name) {
 | 
			
		||||
        protocol_error(c);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (c->version >= 12)  {
 | 
			
		||||
        /* Since 0.9.8 the user can ask for a couple of additional flags */
 | 
			
		||||
 | 
			
		||||
        if (pa_tagstruct_get_boolean(t, &no_remap) < 0 ||
 | 
			
		||||
            pa_tagstruct_get_boolean(t, &no_remix) < 0 ||
 | 
			
		||||
            pa_tagstruct_get_boolean(t, &fix_format) < 0 ||
 | 
			
		||||
            pa_tagstruct_get_boolean(t, &fix_rate) < 0 ||
 | 
			
		||||
            pa_tagstruct_get_boolean(t, &fix_channels) < 0 ||
 | 
			
		||||
            pa_tagstruct_get_boolean(t, &no_move) < 0 ||
 | 
			
		||||
            pa_tagstruct_get_boolean(t, &variable_rate) < 0) {
 | 
			
		||||
            protocol_error(c);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!pa_tagstruct_eof(t)) {
 | 
			
		||||
        protocol_error(c);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -1136,8 +1254,8 @@ static void command_create_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GC
 | 
			
		|||
    CHECK_VALIDITY(c->pstream, pa_sample_spec_valid(&ss), tag, PA_ERR_INVALID);
 | 
			
		||||
    CHECK_VALIDITY(c->pstream, pa_cvolume_valid(&volume), tag, PA_ERR_INVALID);
 | 
			
		||||
    CHECK_VALIDITY(c->pstream, map.channels == ss.channels && volume.channels == ss.channels, tag, PA_ERR_INVALID);
 | 
			
		||||
    CHECK_VALIDITY(c->pstream, maxlength > 0 && maxlength <= MAX_MEMBLOCKQ_LENGTH, tag, PA_ERR_INVALID);
 | 
			
		||||
    CHECK_VALIDITY(c->pstream, maxlength >= pa_frame_size(&ss), tag, PA_ERR_INVALID);
 | 
			
		||||
    CHECK_VALIDITY(c->pstream, maxlength > 0, tag, PA_ERR_INVALID);
 | 
			
		||||
    CHECK_VALIDITY(c->pstream, maxlength <= MAX_MEMBLOCKQ_LENGTH, tag, PA_ERR_INVALID);
 | 
			
		||||
 | 
			
		||||
    if (sink_index != PA_INVALID_INDEX) {
 | 
			
		||||
        sink = pa_idxset_get_by_index(c->protocol->core->sinks, sink_index);
 | 
			
		||||
| 
						 | 
				
			
			@ -1147,7 +1265,17 @@ static void command_create_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GC
 | 
			
		|||
        CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    s = playback_stream_new(c, sink, &ss, &map, name, &maxlength, &tlength, &prebuf, &minreq, &volume, syncid, corked, &missing);
 | 
			
		||||
    flags =
 | 
			
		||||
        (corked ?  PA_SINK_INPUT_START_CORKED : 0) |
 | 
			
		||||
        (no_remap ?  PA_SINK_INPUT_NO_REMAP : 0) |
 | 
			
		||||
        (no_remix ?  PA_SINK_INPUT_NO_REMIX : 0) |
 | 
			
		||||
        (fix_format ?  PA_SINK_INPUT_FIX_FORMAT : 0) |
 | 
			
		||||
        (fix_rate ?  PA_SINK_INPUT_FIX_RATE : 0) |
 | 
			
		||||
        (fix_channels ?  PA_SINK_INPUT_FIX_CHANNELS : 0) |
 | 
			
		||||
        (no_move ?  PA_SINK_INPUT_DONT_MOVE : 0) |
 | 
			
		||||
        (variable_rate ?  PA_SINK_INPUT_VARIABLE_RATE : 0);
 | 
			
		||||
 | 
			
		||||
    s = playback_stream_new(c, sink, &ss, &map, name, &maxlength, &tlength, &prebuf, &minreq, &volume, syncid, &missing, flags);
 | 
			
		||||
    CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_INVALID);
 | 
			
		||||
 | 
			
		||||
    reply = reply_new(tag);
 | 
			
		||||
| 
						 | 
				
			
			@ -1159,7 +1287,7 @@ static void command_create_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GC
 | 
			
		|||
/*     pa_log("initial request is %u", missing); */
 | 
			
		||||
 | 
			
		||||
    if (c->version >= 9) {
 | 
			
		||||
        /* Since 0.9 we support sending the buffer metrics back to the client */
 | 
			
		||||
        /* Since 0.9.0 we support sending the buffer metrics back to the client */
 | 
			
		||||
 | 
			
		||||
        pa_tagstruct_putu32(reply, (uint32_t) maxlength);
 | 
			
		||||
        pa_tagstruct_putu32(reply, (uint32_t) tlength);
 | 
			
		||||
| 
						 | 
				
			
			@ -1167,6 +1295,20 @@ static void command_create_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GC
 | 
			
		|||
        pa_tagstruct_putu32(reply, (uint32_t) minreq);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (c->version >= 12) {
 | 
			
		||||
        /* Since 0.9.8 we support sending the chosen sample
 | 
			
		||||
         * spec/channel map/device/suspend status back to the
 | 
			
		||||
         * client */
 | 
			
		||||
 | 
			
		||||
        pa_tagstruct_put_sample_spec(reply, &ss);
 | 
			
		||||
        pa_tagstruct_put_channel_map(reply, &map);
 | 
			
		||||
 | 
			
		||||
        pa_tagstruct_putu32(reply, s->sink_input->sink->index);
 | 
			
		||||
        pa_tagstruct_puts(reply, s->sink_input->sink->name);
 | 
			
		||||
 | 
			
		||||
        pa_tagstruct_put_boolean(reply, pa_sink_get_state(s->sink_input->sink) == PA_SINK_SUSPENDED);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pa_pstream_send_tagstruct(c->pstream, reply);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1239,6 +1381,8 @@ static void command_create_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_
 | 
			
		|||
    pa_tagstruct *reply;
 | 
			
		||||
    pa_source *source = NULL;
 | 
			
		||||
    int corked;
 | 
			
		||||
    int no_remap = 0, no_remix = 0, fix_format = 0, fix_rate = 0, fix_channels = 0, no_move = 0, variable_rate = 0;
 | 
			
		||||
    pa_source_output_flags_t flags = 0;
 | 
			
		||||
 | 
			
		||||
    connection_assert_ref(c);
 | 
			
		||||
    pa_assert(t);
 | 
			
		||||
| 
						 | 
				
			
			@ -1250,18 +1394,48 @@ static void command_create_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_
 | 
			
		|||
        pa_tagstruct_gets(t, &source_name) < 0 ||
 | 
			
		||||
        pa_tagstruct_getu32(t, &maxlength) < 0 ||
 | 
			
		||||
        pa_tagstruct_get_boolean(t, &corked) < 0 ||
 | 
			
		||||
        pa_tagstruct_getu32(t, &fragment_size) < 0 ||
 | 
			
		||||
        !pa_tagstruct_eof(t)) {
 | 
			
		||||
        pa_tagstruct_getu32(t, &fragment_size) < 0) {
 | 
			
		||||
        protocol_error(c);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (c->version >= 12)  {
 | 
			
		||||
        /* Since 0.9.8 the user can ask for a couple of additional flags */
 | 
			
		||||
 | 
			
		||||
        if (pa_tagstruct_get_boolean(t, &no_remap) < 0 ||
 | 
			
		||||
            pa_tagstruct_get_boolean(t, &no_remix) < 0 ||
 | 
			
		||||
            pa_tagstruct_get_boolean(t, &fix_format) < 0 ||
 | 
			
		||||
            pa_tagstruct_get_boolean(t, &fix_rate) < 0 ||
 | 
			
		||||
            pa_tagstruct_get_boolean(t, &fix_channels) < 0 ||
 | 
			
		||||
            pa_tagstruct_get_boolean(t, &no_move) < 0 ||
 | 
			
		||||
            pa_tagstruct_get_boolean(t, &variable_rate) < 0) {
 | 
			
		||||
            protocol_error(c);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!pa_tagstruct_eof(t)) {
 | 
			
		||||
        protocol_error(c);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    flags =
 | 
			
		||||
        (corked ?  PA_SOURCE_OUTPUT_START_CORKED : 0) |
 | 
			
		||||
        (no_remap ?  PA_SOURCE_OUTPUT_NO_REMAP : 0) |
 | 
			
		||||
        (no_remix ?  PA_SOURCE_OUTPUT_NO_REMIX : 0) |
 | 
			
		||||
        (fix_format ?  PA_SOURCE_OUTPUT_FIX_FORMAT : 0) |
 | 
			
		||||
        (fix_rate ?  PA_SOURCE_OUTPUT_FIX_RATE : 0) |
 | 
			
		||||
        (fix_channels ?  PA_SOURCE_OUTPUT_FIX_CHANNELS : 0) |
 | 
			
		||||
        (no_move ?  PA_SOURCE_OUTPUT_DONT_MOVE : 0) |
 | 
			
		||||
        (variable_rate ?  PA_SOURCE_OUTPUT_VARIABLE_RATE : 0);
 | 
			
		||||
 | 
			
		||||
    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
 | 
			
		||||
    CHECK_VALIDITY(c->pstream, name && pa_utf8_valid(name), tag, PA_ERR_INVALID);
 | 
			
		||||
    CHECK_VALIDITY(c->pstream, pa_sample_spec_valid(&ss), tag, PA_ERR_INVALID);
 | 
			
		||||
    CHECK_VALIDITY(c->pstream, pa_channel_map_valid(&map), tag, PA_ERR_INVALID);
 | 
			
		||||
    CHECK_VALIDITY(c->pstream, source_index != PA_INVALID_INDEX || !source_name || (*source_name && pa_utf8_valid(source_name)), tag, PA_ERR_INVALID);
 | 
			
		||||
    CHECK_VALIDITY(c->pstream, map.channels == ss.channels, tag, PA_ERR_INVALID);
 | 
			
		||||
    CHECK_VALIDITY(c->pstream, maxlength > 0, tag, PA_ERR_INVALID);
 | 
			
		||||
    CHECK_VALIDITY(c->pstream, maxlength <= MAX_MEMBLOCKQ_LENGTH, tag, PA_ERR_INVALID);
 | 
			
		||||
 | 
			
		||||
    if (source_index != PA_INVALID_INDEX) {
 | 
			
		||||
| 
						 | 
				
			
			@ -1272,7 +1446,7 @@ static void command_create_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_
 | 
			
		|||
        CHECK_VALIDITY(c->pstream, source, tag, PA_ERR_NOENTITY);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    s = record_stream_new(c, source, &ss, &map, name, &maxlength, fragment_size, corked);
 | 
			
		||||
    s = record_stream_new(c, source, &ss, &map, name, &maxlength, fragment_size, flags);
 | 
			
		||||
    CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_INVALID);
 | 
			
		||||
 | 
			
		||||
    reply = reply_new(tag);
 | 
			
		||||
| 
						 | 
				
			
			@ -1287,6 +1461,20 @@ static void command_create_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_
 | 
			
		|||
        pa_tagstruct_putu32(reply, (uint32_t) s->fragment_size);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (c->version >= 12) {
 | 
			
		||||
        /* Since 0.9.8 we support sending the chosen sample
 | 
			
		||||
         * spec/channel map/device/suspend status back to the
 | 
			
		||||
         * client */
 | 
			
		||||
 | 
			
		||||
        pa_tagstruct_put_sample_spec(reply, &ss);
 | 
			
		||||
        pa_tagstruct_put_channel_map(reply, &map);
 | 
			
		||||
 | 
			
		||||
        pa_tagstruct_putu32(reply, s->source_output->source->index);
 | 
			
		||||
        pa_tagstruct_puts(reply, s->source_output->source->name);
 | 
			
		||||
 | 
			
		||||
        pa_tagstruct_put_boolean(reply, pa_source_get_state(s->source_output->source) == PA_SOURCE_SUSPENDED);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pa_pstream_send_tagstruct(c->pstream, reply);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -2291,6 +2479,134 @@ static void command_flush_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_U
 | 
			
		|||
    pa_pstream_send_simple_ack(c->pstream, tag);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
 | 
			
		||||
    connection *c = CONNECTION(userdata);
 | 
			
		||||
    uint32_t idx;
 | 
			
		||||
    uint32_t maxlength, tlength, prebuf, minreq, fragsize;
 | 
			
		||||
    pa_tagstruct *reply;
 | 
			
		||||
 | 
			
		||||
    connection_assert_ref(c);
 | 
			
		||||
    pa_assert(t);
 | 
			
		||||
 | 
			
		||||
    if (pa_tagstruct_getu32(t, &idx) < 0) {
 | 
			
		||||
        protocol_error(c);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
 | 
			
		||||
 | 
			
		||||
    if (command == PA_COMMAND_SET_PLAYBACK_STREAM_BUFFER_ATTR) {
 | 
			
		||||
        playback_stream *s;
 | 
			
		||||
 | 
			
		||||
        s = pa_idxset_get_by_index(c->output_streams, idx);
 | 
			
		||||
        CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
 | 
			
		||||
        CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
 | 
			
		||||
 | 
			
		||||
        if (pa_tagstruct_get(
 | 
			
		||||
                    t,
 | 
			
		||||
                    PA_TAG_U32, &maxlength,
 | 
			
		||||
                    PA_TAG_U32, &tlength,
 | 
			
		||||
                    PA_TAG_U32, &prebuf,
 | 
			
		||||
                    PA_TAG_U32, &minreq,
 | 
			
		||||
                    PA_TAG_INVALID) < 0 ||
 | 
			
		||||
            !pa_tagstruct_eof(t)) {
 | 
			
		||||
            protocol_error(c);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        CHECK_VALIDITY(c->pstream, maxlength > 0, tag, PA_ERR_INVALID);
 | 
			
		||||
        CHECK_VALIDITY(c->pstream, maxlength <= MAX_MEMBLOCKQ_LENGTH, tag, PA_ERR_INVALID);
 | 
			
		||||
 | 
			
		||||
        pa_memblockq_set_maxlength(s->memblockq, maxlength);
 | 
			
		||||
        pa_memblockq_set_tlength(s->memblockq, tlength);
 | 
			
		||||
        pa_memblockq_set_prebuf(s->memblockq, prebuf);
 | 
			
		||||
        pa_memblockq_set_minreq(s->memblockq, minreq);
 | 
			
		||||
 | 
			
		||||
        reply = reply_new(tag);
 | 
			
		||||
        pa_tagstruct_putu32(reply, (uint32_t) pa_memblockq_get_maxlength(s->memblockq));
 | 
			
		||||
        pa_tagstruct_putu32(reply, (uint32_t) pa_memblockq_get_tlength(s->memblockq));
 | 
			
		||||
        pa_tagstruct_putu32(reply, (uint32_t) pa_memblockq_get_prebuf(s->memblockq));
 | 
			
		||||
        pa_tagstruct_putu32(reply, (uint32_t) pa_memblockq_get_minreq(s->memblockq));
 | 
			
		||||
 | 
			
		||||
    } else {
 | 
			
		||||
        record_stream *s;
 | 
			
		||||
        size_t base;
 | 
			
		||||
        pa_assert(command == PA_COMMAND_SET_RECORD_STREAM_BUFFER_ATTR);
 | 
			
		||||
 | 
			
		||||
        s = pa_idxset_get_by_index(c->record_streams, idx);
 | 
			
		||||
        CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
 | 
			
		||||
 | 
			
		||||
        if (pa_tagstruct_get(
 | 
			
		||||
                    t,
 | 
			
		||||
                    PA_TAG_U32, &maxlength,
 | 
			
		||||
                    PA_TAG_U32, &fragsize,
 | 
			
		||||
                    PA_TAG_INVALID) < 0 ||
 | 
			
		||||
            !pa_tagstruct_eof(t)) {
 | 
			
		||||
            protocol_error(c);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        CHECK_VALIDITY(c->pstream, maxlength > 0, tag, PA_ERR_INVALID);
 | 
			
		||||
        CHECK_VALIDITY(c->pstream, maxlength <= MAX_MEMBLOCKQ_LENGTH, tag, PA_ERR_INVALID);
 | 
			
		||||
 | 
			
		||||
        pa_memblockq_set_maxlength(s->memblockq, maxlength);
 | 
			
		||||
 | 
			
		||||
        base = pa_frame_size(&s->source_output->sample_spec);
 | 
			
		||||
        s->fragment_size = (fragsize/base)*base;
 | 
			
		||||
        if (s->fragment_size <= 0)
 | 
			
		||||
            s->fragment_size = base;
 | 
			
		||||
 | 
			
		||||
        if (s->fragment_size > pa_memblockq_get_maxlength(s->memblockq))
 | 
			
		||||
            s->fragment_size = pa_memblockq_get_maxlength(s->memblockq);
 | 
			
		||||
 | 
			
		||||
        reply = reply_new(tag);
 | 
			
		||||
        pa_tagstruct_putu32(reply, (uint32_t) pa_memblockq_get_maxlength(s->memblockq));
 | 
			
		||||
        pa_tagstruct_putu32(reply, s->fragment_size);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pa_pstream_send_tagstruct(c->pstream, reply);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void command_update_stream_sample_rate(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
 | 
			
		||||
    connection *c = CONNECTION(userdata);
 | 
			
		||||
    uint32_t idx;
 | 
			
		||||
    uint32_t rate;
 | 
			
		||||
 | 
			
		||||
    connection_assert_ref(c);
 | 
			
		||||
    pa_assert(t);
 | 
			
		||||
 | 
			
		||||
    if (pa_tagstruct_getu32(t, &idx) < 0 ||
 | 
			
		||||
        pa_tagstruct_getu32(t, &rate) < 0 ||
 | 
			
		||||
        !pa_tagstruct_eof(t)) {
 | 
			
		||||
        protocol_error(c);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
 | 
			
		||||
    CHECK_VALIDITY(c->pstream, rate > 0 && rate <= PA_RATE_MAX, tag, PA_ERR_INVALID);
 | 
			
		||||
 | 
			
		||||
    if (command == PA_COMMAND_UPDATE_PLAYBACK_STREAM_SAMPLE_RATE) {
 | 
			
		||||
        playback_stream *s;
 | 
			
		||||
 | 
			
		||||
        s = pa_idxset_get_by_index(c->output_streams, idx);
 | 
			
		||||
        CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
 | 
			
		||||
        CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
 | 
			
		||||
 | 
			
		||||
        pa_sink_input_set_rate(s->sink_input, rate);
 | 
			
		||||
 | 
			
		||||
    } else {
 | 
			
		||||
        record_stream *s;
 | 
			
		||||
        pa_assert(command == PA_COMMAND_UPDATE_RECORD_STREAM_SAMPLE_RATE);
 | 
			
		||||
 | 
			
		||||
        s = pa_idxset_get_by_index(c->record_streams, idx);
 | 
			
		||||
        CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
 | 
			
		||||
 | 
			
		||||
        pa_source_output_set_rate(s->source_output, rate);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pa_pstream_send_simple_ack(c->pstream, tag);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void command_set_default_sink_or_source(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
 | 
			
		||||
    connection *c = CONNECTION(userdata);
 | 
			
		||||
    const char *s;
 | 
			
		||||
| 
						 | 
				
			
			@ -2340,6 +2656,7 @@ static void command_set_stream_name(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t com
 | 
			
		|||
 | 
			
		||||
    } else {
 | 
			
		||||
        record_stream *s;
 | 
			
		||||
        pa_assert(command == PA_COMMAND_SET_RECORD_STREAM_NAME);
 | 
			
		||||
 | 
			
		||||
        s = pa_idxset_get_by_index(c->record_streams, idx);
 | 
			
		||||
        CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -93,7 +93,7 @@ pa_sink_input* pa_sink_input_new(
 | 
			
		|||
 | 
			
		||||
    pa_sink_input *i;
 | 
			
		||||
    pa_resampler *resampler = NULL;
 | 
			
		||||
    char st[PA_SAMPLE_SPEC_SNPRINT_MAX];
 | 
			
		||||
    char st[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
 | 
			
		||||
 | 
			
		||||
    pa_assert(core);
 | 
			
		||||
    pa_assert(data);
 | 
			
		||||
| 
						 | 
				
			
			@ -132,6 +132,24 @@ pa_sink_input* pa_sink_input_new(
 | 
			
		|||
    pa_return_null_if_fail(pa_cvolume_valid(&data->volume));
 | 
			
		||||
    pa_return_null_if_fail(data->volume.channels == data->sample_spec.channels);
 | 
			
		||||
 | 
			
		||||
    if (flags & PA_SINK_INPUT_FIX_FORMAT)
 | 
			
		||||
        data->sample_spec.format = data->sink->sample_spec.format;
 | 
			
		||||
 | 
			
		||||
    if (flags & PA_SINK_INPUT_FIX_RATE)
 | 
			
		||||
        data->sample_spec.rate = data->sink->sample_spec.rate;
 | 
			
		||||
 | 
			
		||||
    if (flags & PA_SINK_INPUT_FIX_CHANNELS) {
 | 
			
		||||
        data->sample_spec.channels = data->sink->sample_spec.channels;
 | 
			
		||||
        data->channel_map = data->sink->channel_map;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pa_assert(pa_sample_spec_valid(&data->sample_spec));
 | 
			
		||||
    pa_assert(pa_channel_map_valid(&data->channel_map));
 | 
			
		||||
 | 
			
		||||
    /* Due to the fixing of the sample spec the volume might not match anymore */
 | 
			
		||||
    if (data->volume.channels != data->sample_spec.channels)
 | 
			
		||||
        pa_cvolume_set(&data->volume, data->sample_spec.channels, pa_cvolume_avg(&data->volume));
 | 
			
		||||
 | 
			
		||||
    if (!data->muted_is_set)
 | 
			
		||||
        data->muted = 0;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -140,6 +158,9 @@ pa_sink_input* pa_sink_input_new(
 | 
			
		|||
 | 
			
		||||
    pa_return_null_if_fail(data->resample_method < PA_RESAMPLER_MAX);
 | 
			
		||||
 | 
			
		||||
    if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], data) < 0)
 | 
			
		||||
        return NULL;
 | 
			
		||||
 | 
			
		||||
    if (pa_idxset_size(data->sink->inputs) >= PA_MAX_INPUTS_PER_SINK) {
 | 
			
		||||
        pa_log_warn("Failed to create sink input: too many inputs per sink.");
 | 
			
		||||
        return NULL;
 | 
			
		||||
| 
						 | 
				
			
			@ -154,7 +175,9 @@ pa_sink_input* pa_sink_input_new(
 | 
			
		|||
                      &data->sample_spec, &data->channel_map,
 | 
			
		||||
                      &data->sink->sample_spec, &data->sink->channel_map,
 | 
			
		||||
                      data->resample_method,
 | 
			
		||||
                      (flags & PA_SINK_INPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0))) {
 | 
			
		||||
                      ((flags & PA_SINK_INPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) |
 | 
			
		||||
                      ((flags & PA_SINK_INPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) |
 | 
			
		||||
                      (core->disable_remixing || (flags & PA_SINK_INPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0)))) {
 | 
			
		||||
            pa_log_warn("Unsupported resampling operation.");
 | 
			
		||||
            return NULL;
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -199,6 +222,7 @@ pa_sink_input* pa_sink_input_new(
 | 
			
		|||
    i->attach = NULL;
 | 
			
		||||
    i->detach = NULL;
 | 
			
		||||
    i->suspend = NULL;
 | 
			
		||||
    i->moved = NULL;
 | 
			
		||||
    i->userdata = NULL;
 | 
			
		||||
 | 
			
		||||
    i->thread_info.state = i->state;
 | 
			
		||||
| 
						 | 
				
			
			@ -215,11 +239,12 @@ pa_sink_input* pa_sink_input_new(
 | 
			
		|||
    pa_assert_se(pa_idxset_put(core->sink_inputs, pa_sink_input_ref(i), &i->index) == 0);
 | 
			
		||||
    pa_assert_se(pa_idxset_put(i->sink->inputs, i, NULL) == 0);
 | 
			
		||||
 | 
			
		||||
    pa_log_info("Created input %u \"%s\" on %s with sample spec %s",
 | 
			
		||||
    pa_log_info("Created input %u \"%s\" on %s with sample spec %s and channel map %s",
 | 
			
		||||
                i->index,
 | 
			
		||||
                i->name,
 | 
			
		||||
                i->sink->name,
 | 
			
		||||
                pa_sample_spec_snprint(st, sizeof(st), &i->sample_spec));
 | 
			
		||||
                pa_sample_spec_snprint(st, sizeof(st), &i->sample_spec),
 | 
			
		||||
                pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map));
 | 
			
		||||
 | 
			
		||||
    /* Don't forget to call pa_sink_input_put! */
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -307,6 +332,7 @@ void pa_sink_input_unlink(pa_sink_input *i) {
 | 
			
		|||
    i->attach = NULL;
 | 
			
		||||
    i->detach = NULL;
 | 
			
		||||
    i->suspend = NULL;
 | 
			
		||||
    i->moved = NULL;
 | 
			
		||||
 | 
			
		||||
    if (linked) {
 | 
			
		||||
        pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_REMOVE, i->index);
 | 
			
		||||
| 
						 | 
				
			
			@ -709,6 +735,7 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, int immediately) {
 | 
			
		|||
    pa_sink *origin;
 | 
			
		||||
    pa_usec_t silence_usec = 0;
 | 
			
		||||
    pa_sink_input_move_info info;
 | 
			
		||||
    pa_sink_input_move_hook_data hook_data;
 | 
			
		||||
 | 
			
		||||
    pa_sink_input_assert_ref(i);
 | 
			
		||||
    pa_assert(PA_SINK_INPUT_LINKED(i->state));
 | 
			
		||||
| 
						 | 
				
			
			@ -750,14 +777,18 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, int immediately) {
 | 
			
		|||
                      &i->sample_spec, &i->channel_map,
 | 
			
		||||
                      &dest->sample_spec, &dest->channel_map,
 | 
			
		||||
                      i->resample_method,
 | 
			
		||||
                      (i->flags & PA_SINK_INPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0))) {
 | 
			
		||||
                      ((i->flags & PA_SINK_INPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) |
 | 
			
		||||
                      ((i->flags & PA_SINK_INPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) |
 | 
			
		||||
                      (i->core->disable_remixing || (i->flags & PA_SINK_INPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0)))) {
 | 
			
		||||
            pa_log_warn("Unsupported resampling operation.");
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
    } else
 | 
			
		||||
        new_resampler = NULL;
 | 
			
		||||
 | 
			
		||||
    pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE], i);
 | 
			
		||||
    hook_data.sink_input = i;
 | 
			
		||||
    hook_data.destination = dest;
 | 
			
		||||
    pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE], &hook_data);
 | 
			
		||||
 | 
			
		||||
    memset(&info, 0, sizeof(info));
 | 
			
		||||
    info.sink_input = i;
 | 
			
		||||
| 
						 | 
				
			
			@ -870,6 +901,9 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, int immediately) {
 | 
			
		|||
    pa_sink_update_status(origin);
 | 
			
		||||
    pa_sink_update_status(dest);
 | 
			
		||||
 | 
			
		||||
    if (i->moved)
 | 
			
		||||
        i->moved(i);
 | 
			
		||||
 | 
			
		||||
    pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_POST], i);
 | 
			
		||||
 | 
			
		||||
    pa_log_debug("Successfully moved sink input %i from %s to %s.", i->index, origin->name, dest->name);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -53,7 +53,12 @@ static inline pa_bool_t PA_SINK_INPUT_LINKED(pa_sink_input_state_t x) {
 | 
			
		|||
typedef enum pa_sink_input_flags {
 | 
			
		||||
    PA_SINK_INPUT_VARIABLE_RATE = 1,
 | 
			
		||||
    PA_SINK_INPUT_DONT_MOVE = 2,
 | 
			
		||||
    PA_SINK_INPUT_START_CORKED = 4
 | 
			
		||||
    PA_SINK_INPUT_START_CORKED = 4,
 | 
			
		||||
    PA_SINK_INPUT_NO_REMAP = 8,
 | 
			
		||||
    PA_SINK_INPUT_NO_REMIX = 16,
 | 
			
		||||
    PA_SINK_INPUT_FIX_FORMAT = 32,
 | 
			
		||||
    PA_SINK_INPUT_FIX_RATE = 64,
 | 
			
		||||
    PA_SINK_INPUT_FIX_CHANNELS = 128
 | 
			
		||||
} pa_sink_input_flags_t;
 | 
			
		||||
 | 
			
		||||
struct pa_sink_input {
 | 
			
		||||
| 
						 | 
				
			
			@ -107,7 +112,11 @@ struct pa_sink_input {
 | 
			
		|||
 | 
			
		||||
    /* If non-NULL called whenever the the sink this input is attached
 | 
			
		||||
     * to suspends or resumes. Called from main context */
 | 
			
		||||
    void (*suspend) (pa_sink_input *i, int b);   /* may be NULL */
 | 
			
		||||
    void (*suspend) (pa_sink_input *i, pa_bool_t b);   /* may be NULL */
 | 
			
		||||
 | 
			
		||||
    /* If non-NULL called whenever the the sink this input is attached
 | 
			
		||||
     * to changes. Called from main context */
 | 
			
		||||
    void (*moved) (pa_sink_input *i);   /* may be NULL */
 | 
			
		||||
 | 
			
		||||
    /* Supposed to unlink and destroy this stream. Called from main
 | 
			
		||||
     * context. */
 | 
			
		||||
| 
						 | 
				
			
			@ -181,6 +190,11 @@ typedef struct pa_sink_input_new_data {
 | 
			
		|||
    pa_sink_input *sync_base;
 | 
			
		||||
} pa_sink_input_new_data;
 | 
			
		||||
 | 
			
		||||
typedef struct pa_sink_input_move_hook_data {
 | 
			
		||||
    pa_sink_input *sink_input;
 | 
			
		||||
    pa_sink *destination;
 | 
			
		||||
} pa_sink_input_move_hook_data;
 | 
			
		||||
 | 
			
		||||
pa_sink_input_new_data* pa_sink_input_new_data_init(pa_sink_input_new_data *data);
 | 
			
		||||
void pa_sink_input_new_data_set_sample_spec(pa_sink_input_new_data *data, const pa_sample_spec *spec);
 | 
			
		||||
void pa_sink_input_new_data_set_channel_map(pa_sink_input_new_data *data, const pa_channel_map *map);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -149,23 +149,16 @@ pa_sink* pa_sink_new(
 | 
			
		|||
 | 
			
		||||
static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
 | 
			
		||||
    int ret;
 | 
			
		||||
    pa_bool_t suspend_change;
 | 
			
		||||
 | 
			
		||||
    pa_assert(s);
 | 
			
		||||
 | 
			
		||||
    if (s->state == state)
 | 
			
		||||
        return 0;
 | 
			
		||||
 | 
			
		||||
    if ((s->state == PA_SINK_SUSPENDED && PA_SINK_OPENED(state)) ||
 | 
			
		||||
        (PA_SINK_OPENED(s->state) && state == PA_SINK_SUSPENDED)) {
 | 
			
		||||
        pa_sink_input *i;
 | 
			
		||||
        uint32_t idx;
 | 
			
		||||
 | 
			
		||||
        /* We're suspending or resuming, tell everyone about it */
 | 
			
		||||
 | 
			
		||||
        for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx)))
 | 
			
		||||
            if (i->suspend)
 | 
			
		||||
                i->suspend(i, state == PA_SINK_SUSPENDED);
 | 
			
		||||
    }
 | 
			
		||||
    suspend_change =
 | 
			
		||||
        (s->state == PA_SINK_SUSPENDED && PA_SINK_OPENED(state)) ||
 | 
			
		||||
        (PA_SINK_OPENED(s->state) && state == PA_SINK_SUSPENDED);
 | 
			
		||||
 | 
			
		||||
    if (s->set_state)
 | 
			
		||||
        if ((ret = s->set_state(s, state)) < 0)
 | 
			
		||||
| 
						 | 
				
			
			@ -176,8 +169,20 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
 | 
			
		|||
 | 
			
		||||
    s->state = state;
 | 
			
		||||
 | 
			
		||||
    if (suspend_change) {
 | 
			
		||||
        pa_sink_input *i;
 | 
			
		||||
        uint32_t idx;
 | 
			
		||||
 | 
			
		||||
        /* We're suspending or resuming, tell everyone about it */
 | 
			
		||||
 | 
			
		||||
        for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx)))
 | 
			
		||||
            if (i->suspend)
 | 
			
		||||
                i->suspend(i, state == PA_SINK_SUSPENDED);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (state != PA_SINK_UNLINKED) /* if we enter UNLINKED state pa_sink_unlink() will fire the apropriate events */
 | 
			
		||||
        pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_STATE_CHANGED], s);
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -71,7 +71,7 @@ pa_source_output* pa_source_output_new(
 | 
			
		|||
 | 
			
		||||
    pa_source_output *o;
 | 
			
		||||
    pa_resampler *resampler = NULL;
 | 
			
		||||
    char st[PA_SAMPLE_SPEC_SNPRINT_MAX];
 | 
			
		||||
    char st[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
 | 
			
		||||
 | 
			
		||||
    pa_assert(core);
 | 
			
		||||
    pa_assert(data);
 | 
			
		||||
| 
						 | 
				
			
			@ -103,11 +103,28 @@ pa_source_output* pa_source_output_new(
 | 
			
		|||
    pa_return_null_if_fail(pa_channel_map_valid(&data->channel_map));
 | 
			
		||||
    pa_return_null_if_fail(data->channel_map.channels == data->sample_spec.channels);
 | 
			
		||||
 | 
			
		||||
    if (flags & PA_SOURCE_OUTPUT_FIX_FORMAT)
 | 
			
		||||
        data->sample_spec.format = data->source->sample_spec.format;
 | 
			
		||||
 | 
			
		||||
    if (flags & PA_SOURCE_OUTPUT_FIX_RATE)
 | 
			
		||||
        data->sample_spec.rate = data->source->sample_spec.rate;
 | 
			
		||||
 | 
			
		||||
    if (flags & PA_SOURCE_OUTPUT_FIX_CHANNELS) {
 | 
			
		||||
        data->sample_spec.channels = data->source->sample_spec.channels;
 | 
			
		||||
        data->channel_map = data->source->channel_map;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pa_assert(pa_sample_spec_valid(&data->sample_spec));
 | 
			
		||||
    pa_assert(pa_channel_map_valid(&data->channel_map));
 | 
			
		||||
 | 
			
		||||
    if (data->resample_method == PA_RESAMPLER_INVALID)
 | 
			
		||||
        data->resample_method = core->resample_method;
 | 
			
		||||
 | 
			
		||||
    pa_return_null_if_fail(data->resample_method < PA_RESAMPLER_MAX);
 | 
			
		||||
 | 
			
		||||
    if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE], data) < 0)
 | 
			
		||||
        return NULL;
 | 
			
		||||
 | 
			
		||||
    if (pa_idxset_size(data->source->outputs) >= PA_MAX_OUTPUTS_PER_SOURCE) {
 | 
			
		||||
        pa_log("Failed to create source output: too many outputs per source.");
 | 
			
		||||
        return NULL;
 | 
			
		||||
| 
						 | 
				
			
			@ -122,7 +139,9 @@ pa_source_output* pa_source_output_new(
 | 
			
		|||
                      &data->source->sample_spec, &data->source->channel_map,
 | 
			
		||||
                      &data->sample_spec, &data->channel_map,
 | 
			
		||||
                      data->resample_method,
 | 
			
		||||
                      (flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0))) {
 | 
			
		||||
                      ((flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) |
 | 
			
		||||
                      ((flags & PA_SOURCE_OUTPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) |
 | 
			
		||||
                      (core->disable_remixing || (flags & PA_SOURCE_OUTPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0)))) {
 | 
			
		||||
            pa_log_warn("Unsupported resampling operation.");
 | 
			
		||||
            return NULL;
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -153,6 +172,7 @@ pa_source_output* pa_source_output_new(
 | 
			
		|||
    o->detach = NULL;
 | 
			
		||||
    o->attach = NULL;
 | 
			
		||||
    o->suspend = NULL;
 | 
			
		||||
    o->moved = NULL;
 | 
			
		||||
    o->userdata = NULL;
 | 
			
		||||
 | 
			
		||||
    o->thread_info.state = o->state;
 | 
			
		||||
| 
						 | 
				
			
			@ -163,11 +183,12 @@ pa_source_output* pa_source_output_new(
 | 
			
		|||
    pa_assert_se(pa_idxset_put(core->source_outputs, o, &o->index) == 0);
 | 
			
		||||
    pa_assert_se(pa_idxset_put(o->source->outputs, pa_source_output_ref(o), NULL) == 0);
 | 
			
		||||
 | 
			
		||||
    pa_log_info("Created output %u \"%s\" on %s with sample spec %s",
 | 
			
		||||
    pa_log_info("Created output %u \"%s\" on %s with sample spec %s and channel map %s",
 | 
			
		||||
                o->index,
 | 
			
		||||
                o->name,
 | 
			
		||||
                o->source->name,
 | 
			
		||||
                pa_sample_spec_snprint(st, sizeof(st), &o->sample_spec));
 | 
			
		||||
                pa_sample_spec_snprint(st, sizeof(st), &o->sample_spec),
 | 
			
		||||
                pa_channel_map_snprint(cm, sizeof(cm), &o->channel_map));
 | 
			
		||||
 | 
			
		||||
    /* Don't forget to call pa_source_output_put! */
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -229,6 +250,7 @@ void pa_source_output_unlink(pa_source_output*o) {
 | 
			
		|||
    o->attach = NULL;
 | 
			
		||||
    o->detach = NULL;
 | 
			
		||||
    o->suspend = NULL;
 | 
			
		||||
    o->moved = NULL;
 | 
			
		||||
 | 
			
		||||
    if (linked) {
 | 
			
		||||
        pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_REMOVE, o->index);
 | 
			
		||||
| 
						 | 
				
			
			@ -379,6 +401,7 @@ pa_resample_method_t pa_source_output_get_resample_method(pa_source_output *o) {
 | 
			
		|||
int pa_source_output_move_to(pa_source_output *o, pa_source *dest) {
 | 
			
		||||
    pa_source *origin;
 | 
			
		||||
    pa_resampler *new_resampler = NULL;
 | 
			
		||||
    pa_source_output_move_hook_data hook_data;
 | 
			
		||||
 | 
			
		||||
    pa_source_output_assert_ref(o);
 | 
			
		||||
    pa_assert(PA_SOURCE_OUTPUT_LINKED(o->state));
 | 
			
		||||
| 
						 | 
				
			
			@ -415,13 +438,17 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest) {
 | 
			
		|||
                      &dest->sample_spec, &dest->channel_map,
 | 
			
		||||
                      &o->sample_spec, &o->channel_map,
 | 
			
		||||
                      o->resample_method,
 | 
			
		||||
                      (o->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0))) {
 | 
			
		||||
                      ((o->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) |
 | 
			
		||||
                      ((o->flags & PA_SOURCE_OUTPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) |
 | 
			
		||||
                      (o->core->disable_remixing || (o->flags & PA_SOURCE_OUTPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0)))) {
 | 
			
		||||
            pa_log_warn("Unsupported resampling operation.");
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE], o);
 | 
			
		||||
    hook_data.source_output = o;
 | 
			
		||||
    hook_data.destination = dest;
 | 
			
		||||
    pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE], &hook_data);
 | 
			
		||||
 | 
			
		||||
    /* Okey, let's move it */
 | 
			
		||||
    pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_REMOVE_OUTPUT, o, 0, NULL);
 | 
			
		||||
| 
						 | 
				
			
			@ -447,6 +474,9 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest) {
 | 
			
		|||
    pa_source_update_status(origin);
 | 
			
		||||
    pa_source_update_status(dest);
 | 
			
		||||
 | 
			
		||||
    if (o->moved)
 | 
			
		||||
        o->moved(o);
 | 
			
		||||
 | 
			
		||||
    pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_POST], o);
 | 
			
		||||
 | 
			
		||||
    pa_log_debug("Successfully moved source output %i from %s to %s.", o->index, origin->name, dest->name);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -49,7 +49,12 @@ static inline pa_bool_t PA_SOURCE_OUTPUT_LINKED(pa_source_output_state_t x) {
 | 
			
		|||
typedef enum pa_source_output_flags {
 | 
			
		||||
    PA_SOURCE_OUTPUT_VARIABLE_RATE = 1,
 | 
			
		||||
    PA_SOURCE_OUTPUT_DONT_MOVE = 2,
 | 
			
		||||
    PA_SOURCE_OUTPUT_START_CORKED = 4
 | 
			
		||||
    PA_SOURCE_OUTPUT_START_CORKED = 4,
 | 
			
		||||
    PA_SOURCE_OUTPUT_NO_REMAP = 8,
 | 
			
		||||
    PA_SOURCE_OUTPUT_NO_REMIX = 16,
 | 
			
		||||
    PA_SOURCE_OUTPUT_FIX_FORMAT = 32,
 | 
			
		||||
    PA_SOURCE_OUTPUT_FIX_RATE = 64,
 | 
			
		||||
    PA_SOURCE_OUTPUT_FIX_CHANNELS = 128
 | 
			
		||||
} pa_source_output_flags_t;
 | 
			
		||||
 | 
			
		||||
struct pa_source_output {
 | 
			
		||||
| 
						 | 
				
			
			@ -81,9 +86,13 @@ struct pa_source_output {
 | 
			
		|||
     * disconnected from its source. Called from IO thread context */
 | 
			
		||||
    void (*detach) (pa_source_output *o);           /* may be NULL */
 | 
			
		||||
 | 
			
		||||
    /* If non-NULL called whenever the the source this output is attached
 | 
			
		||||
     * to changes. Called from main context */
 | 
			
		||||
    void (*moved) (pa_source_output *o);   /* may be NULL */
 | 
			
		||||
 | 
			
		||||
    /* If non-NULL called whenever the the source this output is attached
 | 
			
		||||
     * to suspends or resumes. Called from main context */
 | 
			
		||||
    void (*suspend) (pa_source_output *o, int b);   /* may be NULL */
 | 
			
		||||
    void (*suspend) (pa_source_output *o, pa_bool_t b);   /* may be NULL */
 | 
			
		||||
 | 
			
		||||
    /* Supposed to unlink and destroy this stream. Called from main
 | 
			
		||||
     * context. */
 | 
			
		||||
| 
						 | 
				
			
			@ -135,6 +144,11 @@ typedef struct pa_source_output_new_data {
 | 
			
		|||
    pa_resample_method_t resample_method;
 | 
			
		||||
} pa_source_output_new_data;
 | 
			
		||||
 | 
			
		||||
typedef struct pa_source_output_move_hook_data {
 | 
			
		||||
    pa_source_output *source_output;
 | 
			
		||||
    pa_source *destination;
 | 
			
		||||
} pa_source_output_move_hook_data;
 | 
			
		||||
 | 
			
		||||
pa_source_output_new_data* pa_source_output_new_data_init(pa_source_output_new_data *data);
 | 
			
		||||
void pa_source_output_new_data_set_sample_spec(pa_source_output_new_data *data, const pa_sample_spec *spec);
 | 
			
		||||
void pa_source_output_new_data_set_channel_map(pa_source_output_new_data *data, const pa_channel_map *map);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -126,23 +126,16 @@ pa_source* pa_source_new(
 | 
			
		|||
 | 
			
		||||
static int source_set_state(pa_source *s, pa_source_state_t state) {
 | 
			
		||||
    int ret;
 | 
			
		||||
    pa_bool_t suspend_change;
 | 
			
		||||
 | 
			
		||||
    pa_assert(s);
 | 
			
		||||
 | 
			
		||||
    if (s->state == state)
 | 
			
		||||
        return 0;
 | 
			
		||||
 | 
			
		||||
    if ((s->state == PA_SOURCE_SUSPENDED && PA_SOURCE_OPENED(state)) ||
 | 
			
		||||
        (PA_SOURCE_OPENED(s->state) && state == PA_SOURCE_SUSPENDED)) {
 | 
			
		||||
        pa_source_output *o;
 | 
			
		||||
        uint32_t idx;
 | 
			
		||||
 | 
			
		||||
        /* We're suspending or resuming, tell everyone about it */
 | 
			
		||||
 | 
			
		||||
        for (o = PA_SOURCE_OUTPUT(pa_idxset_first(s->outputs, &idx)); o; o = PA_SOURCE_OUTPUT(pa_idxset_next(s->outputs, &idx)))
 | 
			
		||||
            if (o->suspend)
 | 
			
		||||
                o->suspend(o, state == PA_SINK_SUSPENDED);
 | 
			
		||||
    }
 | 
			
		||||
    suspend_change =
 | 
			
		||||
        (s->state == PA_SOURCE_SUSPENDED && PA_SOURCE_OPENED(state)) ||
 | 
			
		||||
        (PA_SOURCE_OPENED(s->state) && state == PA_SOURCE_SUSPENDED);
 | 
			
		||||
 | 
			
		||||
    if (s->set_state)
 | 
			
		||||
        if ((ret = s->set_state(s, state)) < 0)
 | 
			
		||||
| 
						 | 
				
			
			@ -153,8 +146,20 @@ static int source_set_state(pa_source *s, pa_source_state_t state) {
 | 
			
		|||
 | 
			
		||||
    s->state = state;
 | 
			
		||||
 | 
			
		||||
    if (suspend_change) {
 | 
			
		||||
        pa_source_output *o;
 | 
			
		||||
        uint32_t idx;
 | 
			
		||||
 | 
			
		||||
        /* We're suspending or resuming, tell everyone about it */
 | 
			
		||||
 | 
			
		||||
        for (o = PA_SOURCE_OUTPUT(pa_idxset_first(s->outputs, &idx)); o; o = PA_SOURCE_OUTPUT(pa_idxset_next(s->outputs, &idx)))
 | 
			
		||||
            if (o->suspend)
 | 
			
		||||
                o->suspend(o, state == PA_SINK_SUSPENDED);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (state != PA_SOURCE_UNLINKED) /* if we enter UNLINKED state pa_source_unlink() will fire the apropriate events */
 | 
			
		||||
        pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_STATE_CHANGED], s);
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -40,7 +40,7 @@
 | 
			
		|||
 | 
			
		||||
#define TIME_EVENT_USEC 50000
 | 
			
		||||
 | 
			
		||||
#if PA_API_VERSION < 9
 | 
			
		||||
#if PA_API_VERSION < 10
 | 
			
		||||
#error Invalid PulseAudio API version
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -69,6 +69,8 @@ static pa_sample_spec sample_spec = {
 | 
			
		|||
static pa_channel_map channel_map;
 | 
			
		||||
static int channel_map_set = 0;
 | 
			
		||||
 | 
			
		||||
static pa_stream_flags_t flags = 0;
 | 
			
		||||
 | 
			
		||||
/* A shortcut for terminating the application */
 | 
			
		||||
static void quit(int ret) {
 | 
			
		||||
    assert(mainloop_api);
 | 
			
		||||
| 
						 | 
				
			
			@ -105,7 +107,8 @@ static void do_stream_write(size_t length) {
 | 
			
		|||
 | 
			
		||||
/* This is called whenever new data may be written to the stream */
 | 
			
		||||
static void stream_write_callback(pa_stream *s, size_t length, void *userdata) {
 | 
			
		||||
    assert(s && length);
 | 
			
		||||
    assert(s);
 | 
			
		||||
    assert(length > 0);
 | 
			
		||||
 | 
			
		||||
    if (stdio_event)
 | 
			
		||||
        mainloop_api->io_enable(stdio_event, PA_IO_EVENT_INPUT);
 | 
			
		||||
| 
						 | 
				
			
			@ -119,7 +122,8 @@ static void stream_write_callback(pa_stream *s, size_t length, void *userdata) {
 | 
			
		|||
/* This is called whenever new data may is available */
 | 
			
		||||
static void stream_read_callback(pa_stream *s, size_t length, void *userdata) {
 | 
			
		||||
    const void *data;
 | 
			
		||||
    assert(s && length);
 | 
			
		||||
    assert(s);
 | 
			
		||||
    assert(length > 0);
 | 
			
		||||
 | 
			
		||||
    if (stdio_event)
 | 
			
		||||
        mainloop_api->io_enable(stdio_event, PA_IO_EVENT_OUTPUT);
 | 
			
		||||
| 
						 | 
				
			
			@ -130,7 +134,8 @@ static void stream_read_callback(pa_stream *s, size_t length, void *userdata) {
 | 
			
		|||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    assert(data && length);
 | 
			
		||||
    assert(data);
 | 
			
		||||
    assert(length > 0);
 | 
			
		||||
 | 
			
		||||
    if (buffer) {
 | 
			
		||||
        fprintf(stderr, "Buffer overrun, dropping incoming data\n");
 | 
			
		||||
| 
						 | 
				
			
			@ -159,6 +164,7 @@ static void stream_state_callback(pa_stream *s, void *userdata) {
 | 
			
		|||
        case PA_STREAM_READY:
 | 
			
		||||
            if (verbose) {
 | 
			
		||||
                const pa_buffer_attr *a;
 | 
			
		||||
                char cmt[PA_CHANNEL_MAP_SNPRINT_MAX], sst[PA_SAMPLE_SPEC_SNPRINT_MAX];
 | 
			
		||||
 | 
			
		||||
                fprintf(stderr, "Stream successfully created.\n");
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -172,9 +178,16 @@ static void stream_state_callback(pa_stream *s, void *userdata) {
 | 
			
		|||
                        assert(mode == RECORD);
 | 
			
		||||
                        fprintf(stderr, "Buffer metrics: maxlength=%u, fragsize=%u\n", a->maxlength, a->fragsize);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                fprintf(stderr, "Using sample spec '%s', channel map '%s'.\n",
 | 
			
		||||
                        pa_sample_spec_snprint(sst, sizeof(sst), pa_stream_get_sample_spec(s)),
 | 
			
		||||
                        pa_channel_map_snprint(cmt, sizeof(cmt), pa_stream_get_channel_map(s)));
 | 
			
		||||
 | 
			
		||||
                fprintf(stderr, "Connected to device %s (%u, %ssuspended).\n",
 | 
			
		||||
                        pa_stream_get_device_name(s),
 | 
			
		||||
                        pa_stream_get_device_index(s),
 | 
			
		||||
                        pa_stream_is_suspended(s) ? "" : "not ");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            break;
 | 
			
		||||
| 
						 | 
				
			
			@ -186,6 +199,24 @@ static void stream_state_callback(pa_stream *s, void *userdata) {
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void stream_suspended_callback(pa_stream *s, void *userdata) {
 | 
			
		||||
    assert(s);
 | 
			
		||||
 | 
			
		||||
    if (verbose) {
 | 
			
		||||
        if (pa_stream_is_suspended(s))
 | 
			
		||||
            fprintf(stderr, "Stream device suspended.\n");
 | 
			
		||||
        else
 | 
			
		||||
            fprintf(stderr, "Stream device resumed.\n");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void stream_moved_callback(pa_stream *s, void *userdata) {
 | 
			
		||||
    assert(s);
 | 
			
		||||
 | 
			
		||||
    if (verbose)
 | 
			
		||||
        fprintf(stderr, "Stream moved to device %s (%u, %ssuspended).\n", pa_stream_get_device_name(s), pa_stream_get_device_index(s), pa_stream_is_suspended(s) ? "" : "not ");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* This is called whenever the context status changes */
 | 
			
		||||
static void context_state_callback(pa_context *c, void *userdata) {
 | 
			
		||||
    assert(c);
 | 
			
		||||
| 
						 | 
				
			
			@ -199,7 +230,8 @@ static void context_state_callback(pa_context *c, void *userdata) {
 | 
			
		|||
        case PA_CONTEXT_READY: {
 | 
			
		||||
            int r;
 | 
			
		||||
 | 
			
		||||
            assert(c && !stream);
 | 
			
		||||
            assert(c);
 | 
			
		||||
            assert(!stream);
 | 
			
		||||
 | 
			
		||||
            if (verbose)
 | 
			
		||||
                fprintf(stderr, "Connection established.\n");
 | 
			
		||||
| 
						 | 
				
			
			@ -212,16 +244,18 @@ static void context_state_callback(pa_context *c, void *userdata) {
 | 
			
		|||
            pa_stream_set_state_callback(stream, stream_state_callback, NULL);
 | 
			
		||||
            pa_stream_set_write_callback(stream, stream_write_callback, NULL);
 | 
			
		||||
            pa_stream_set_read_callback(stream, stream_read_callback, NULL);
 | 
			
		||||
            pa_stream_set_suspended_callback(stream, stream_suspended_callback, NULL);
 | 
			
		||||
            pa_stream_set_moved_callback(stream, stream_moved_callback, NULL);
 | 
			
		||||
 | 
			
		||||
            if (mode == PLAYBACK) {
 | 
			
		||||
                pa_cvolume cv;
 | 
			
		||||
                if ((r = pa_stream_connect_playback(stream, device, NULL, 0, pa_cvolume_set(&cv, sample_spec.channels, volume), NULL)) < 0) {
 | 
			
		||||
                if ((r = pa_stream_connect_playback(stream, device, NULL, flags, pa_cvolume_set(&cv, sample_spec.channels, volume), NULL)) < 0) {
 | 
			
		||||
                    fprintf(stderr, "pa_stream_connect_playback() failed: %s\n", pa_strerror(pa_context_errno(c)));
 | 
			
		||||
                    goto fail;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
            } else {
 | 
			
		||||
                if ((r = pa_stream_connect_record(stream, device, NULL, 0)) < 0) {
 | 
			
		||||
                if ((r = pa_stream_connect_record(stream, device, NULL, flags)) < 0) {
 | 
			
		||||
                    fprintf(stderr, "pa_stream_connect_record() failed: %s\n", pa_strerror(pa_context_errno(c)));
 | 
			
		||||
                    goto fail;
 | 
			
		||||
                }
 | 
			
		||||
| 
						 | 
				
			
			@ -280,7 +314,10 @@ static void stream_drain_complete(pa_stream*s, int success, void *userdata) {
 | 
			
		|||
static void stdin_callback(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
 | 
			
		||||
    size_t l, w = 0;
 | 
			
		||||
    ssize_t r;
 | 
			
		||||
    assert(a == mainloop_api && e && stdio_event == e);
 | 
			
		||||
 | 
			
		||||
    assert(a == mainloop_api);
 | 
			
		||||
    assert(e);
 | 
			
		||||
    assert(stdio_event == e);
 | 
			
		||||
 | 
			
		||||
    if (buffer) {
 | 
			
		||||
        mainloop_api->io_enable(stdio_event, PA_IO_EVENT_NULL);
 | 
			
		||||
| 
						 | 
				
			
			@ -330,7 +367,10 @@ static void stdin_callback(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_even
 | 
			
		|||
/* Some data may be written to STDOUT */
 | 
			
		||||
static void stdout_callback(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
 | 
			
		||||
    ssize_t r;
 | 
			
		||||
    assert(a == mainloop_api && e && stdio_event == e);
 | 
			
		||||
 | 
			
		||||
    assert(a == mainloop_api);
 | 
			
		||||
    assert(e);
 | 
			
		||||
    assert(stdio_event == e);
 | 
			
		||||
 | 
			
		||||
    if (!buffer) {
 | 
			
		||||
        mainloop_api->io_enable(stdio_event, PA_IO_EVENT_NULL);
 | 
			
		||||
| 
						 | 
				
			
			@ -429,7 +469,16 @@ static void help(const char *argv0) {
 | 
			
		|||
           "                                        float32be, ulaw, alaw (defaults to s16ne)\n"
 | 
			
		||||
           "      --channels=CHANNELS               The number of channels, 1 for mono, 2 for stereo\n"
 | 
			
		||||
           "                                        (defaults to 2)\n"
 | 
			
		||||
           "      --channel-map=CHANNELMAP          Channel map to use instead of the default\n",
 | 
			
		||||
           "      --channel-map=CHANNELMAP          Channel map to use instead of the default\n"
 | 
			
		||||
           "      --fix-format                      Take the sample format from the sink the stream is\n"
 | 
			
		||||
           "                                        being connected to.\n"
 | 
			
		||||
           "      --fix-rate                        Take the sampling rate from the sink the stream is\n"
 | 
			
		||||
           "                                        being connected to.\n"
 | 
			
		||||
           "      --fix-channels                    Take the number of channels and the channel map\n"
 | 
			
		||||
           "                                        from the sink the stream is being connected to.\n"
 | 
			
		||||
           "      --no-remix                        Don't upmix or downmix channels.\n"
 | 
			
		||||
           "      --no-remap                        Map channels by index instead of name.\n"
 | 
			
		||||
           ,
 | 
			
		||||
           argv0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -441,6 +490,11 @@ enum {
 | 
			
		|||
    ARG_SAMPLEFORMAT,
 | 
			
		||||
    ARG_CHANNELS,
 | 
			
		||||
    ARG_CHANNELMAP,
 | 
			
		||||
    ARG_FIX_FORMAT,
 | 
			
		||||
    ARG_FIX_RATE,
 | 
			
		||||
    ARG_FIX_CHANNELS,
 | 
			
		||||
    ARG_NO_REMAP,
 | 
			
		||||
    ARG_NO_REMIX
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int main(int argc, char *argv[]) {
 | 
			
		||||
| 
						 | 
				
			
			@ -464,6 +518,11 @@ int main(int argc, char *argv[]) {
 | 
			
		|||
        {"format",      1, NULL, ARG_SAMPLEFORMAT},
 | 
			
		||||
        {"channels",    1, NULL, ARG_CHANNELS},
 | 
			
		||||
        {"channel-map", 1, NULL, ARG_CHANNELMAP},
 | 
			
		||||
        {"fix-format",  0, NULL, ARG_FIX_FORMAT},
 | 
			
		||||
        {"fix-rate",    0, NULL, ARG_FIX_RATE},
 | 
			
		||||
        {"fix-channels",0, NULL, ARG_FIX_CHANNELS},
 | 
			
		||||
        {"no-remap",    0, NULL, ARG_NO_REMAP},
 | 
			
		||||
        {"no-remix",    0, NULL, ARG_NO_REMIX},
 | 
			
		||||
        {NULL,          0, NULL, 0}
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -549,6 +608,26 @@ int main(int argc, char *argv[]) {
 | 
			
		|||
                channel_map_set = 1;
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            case ARG_FIX_CHANNELS:
 | 
			
		||||
                flags |= PA_STREAM_FIX_CHANNELS;
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            case ARG_FIX_RATE:
 | 
			
		||||
                flags |= PA_STREAM_FIX_RATE;
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            case ARG_FIX_FORMAT:
 | 
			
		||||
                flags |= PA_STREAM_FIX_FORMAT;
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            case ARG_NO_REMIX:
 | 
			
		||||
                flags |= PA_STREAM_NO_REMIX_CHANNELS;
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            case ARG_NO_REMAP:
 | 
			
		||||
                flags |= PA_STREAM_NO_REMAP_CHANNELS;
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            default:
 | 
			
		||||
                goto quit;
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -50,7 +50,7 @@ int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char*argv[]) {
 | 
			
		|||
    size_t ibuf_index, ibuf_length, obuf_index, obuf_length;
 | 
			
		||||
    fd_set ifds, ofds;
 | 
			
		||||
 | 
			
		||||
    if (pa_pid_file_check_running(&pid) < 0) {
 | 
			
		||||
    if (pa_pid_file_check_running(&pid, "pulseaudio") < 0) {
 | 
			
		||||
        pa_log("no PulseAudio daemon running");
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -75,7 +75,7 @@ int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char*argv[]) {
 | 
			
		|||
        if (r >= 0)
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        if (pa_pid_file_kill(SIGUSR2, NULL) < 0) {
 | 
			
		||||
        if (pa_pid_file_kill(SIGUSR2, NULL, "pulseaudio") < 0) {
 | 
			
		||||
            pa_log("failed to kill PulseAudio daemon.");
 | 
			
		||||
            goto fail;
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue