int pa_scache_add_item( pa_core *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map, const pa_memchunk *chunk, pa_proplist *p, uint32_t *idx) { pa_scache_entry *e; char st[PA_SAMPLE_SPEC_SNPRINT_MAX]; pa_channel_map tmap; pa_assert(c); pa_assert(name); pa_assert(!ss || pa_sample_spec_valid(ss)); pa_assert(!map || (pa_channel_map_valid(map) && ss && pa_channel_map_compatible(map, ss))); if (ss && !map) { pa_channel_map_init_extend(&tmap, ss->channels, PA_CHANNEL_MAP_DEFAULT); map = &tmap; } if (chunk && chunk->length > PA_SCACHE_ENTRY_SIZE_MAX) return -1; if (!(e = scache_add_item(c, name))) return -1; pa_sample_spec_init(&e->sample_spec); pa_channel_map_init(&e->channel_map); pa_cvolume_init(&e->volume); e->volume_is_set = FALSE; if (ss) { e->sample_spec = *ss; pa_cvolume_reset(&e->volume, ss->channels); } if (map) e->channel_map = *map; if (chunk) { e->memchunk = *chunk; pa_memblock_ref(e->memchunk.memblock); } if (p) pa_proplist_update(e->proplist, PA_UPDATE_REPLACE, p); if (idx) *idx = e->index; pa_log_debug("Created sample \"%s\" (#%d), %lu bytes with sample spec %s", name, e->index, (unsigned long) e->memchunk.length, pa_sample_spec_snprint(st, sizeof(st), &e->sample_spec)); return 0; }
int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) { int r = -1; FILE *f = NULL; struct channel_conf_info ci; pa_config_item table[] = { { "daemonize", pa_config_parse_bool, &c->daemonize, NULL }, { "fail", pa_config_parse_bool, &c->fail, NULL }, { "high-priority", pa_config_parse_bool, &c->high_priority, NULL }, { "realtime-scheduling", pa_config_parse_bool, &c->realtime_scheduling, NULL }, { "disallow-module-loading", pa_config_parse_bool, &c->disallow_module_loading, NULL }, { "allow-module-loading", pa_config_parse_not_bool, &c->disallow_module_loading, NULL }, { "disallow-exit", pa_config_parse_bool, &c->disallow_exit, NULL }, { "allow-exit", pa_config_parse_not_bool, &c->disallow_exit, NULL }, { "use-pid-file", pa_config_parse_bool, &c->use_pid_file, NULL }, { "system-instance", pa_config_parse_bool, &c->system_instance, NULL }, #ifdef HAVE_DBUS { "local-server-type", parse_server_type, c, NULL }, #endif { "no-cpu-limit", pa_config_parse_bool, &c->no_cpu_limit, NULL }, { "cpu-limit", pa_config_parse_not_bool, &c->no_cpu_limit, NULL }, { "disable-shm", pa_config_parse_bool, &c->disable_shm, NULL }, { "enable-shm", pa_config_parse_not_bool, &c->disable_shm, NULL }, { "enable-memfd", pa_config_parse_not_bool, &c->disable_memfd, NULL }, { "flat-volumes", pa_config_parse_bool, &c->flat_volumes, NULL }, { "lock-memory", pa_config_parse_bool, &c->lock_memory, NULL }, { "enable-deferred-volume", pa_config_parse_bool, &c->deferred_volume, NULL }, { "exit-idle-time", pa_config_parse_int, &c->exit_idle_time, NULL }, { "scache-idle-time", pa_config_parse_int, &c->scache_idle_time, NULL }, { "realtime-priority", parse_rtprio, c, NULL }, { "dl-search-path", pa_config_parse_string, &c->dl_search_path, NULL }, { "default-script-file", pa_config_parse_string, &c->default_script_file, NULL }, { "log-target", parse_log_target, c, NULL }, { "log-level", parse_log_level, c, NULL }, { "verbose", parse_log_level, c, NULL }, { "resample-method", parse_resample_method, c, NULL }, { "default-sample-format", parse_sample_format, c, NULL }, { "default-sample-rate", parse_sample_rate, c, NULL }, { "alternate-sample-rate", parse_alternate_sample_rate, c, NULL }, { "default-sample-channels", parse_sample_channels, &ci, NULL }, { "default-channel-map", parse_channel_map, &ci, NULL }, { "default-fragments", parse_fragments, c, NULL }, { "default-fragment-size-msec", parse_fragment_size_msec, c, NULL }, { "deferred-volume-safety-margin-usec", pa_config_parse_unsigned, &c->deferred_volume_safety_margin_usec, NULL }, { "deferred-volume-extra-delay-usec", pa_config_parse_int, &c->deferred_volume_extra_delay_usec, NULL }, { "nice-level", parse_nice_level, c, NULL }, { "avoid-resampling", pa_config_parse_bool, &c->avoid_resampling, NULL }, { "disable-remixing", pa_config_parse_bool, &c->disable_remixing, NULL }, { "enable-remixing", pa_config_parse_not_bool, &c->disable_remixing, NULL }, { "remixing-use-all-sink-channels", pa_config_parse_bool, &c->remixing_use_all_sink_channels, NULL }, { "disable-lfe-remixing", pa_config_parse_bool, &c->disable_lfe_remixing, NULL }, { "enable-lfe-remixing", pa_config_parse_not_bool, &c->disable_lfe_remixing, NULL }, { "lfe-crossover-freq", pa_config_parse_unsigned, &c->lfe_crossover_freq, NULL }, { "load-default-script-file", pa_config_parse_bool, &c->load_default_script_file, NULL }, { "shm-size-bytes", pa_config_parse_size, &c->shm_size, NULL }, { "log-meta", pa_config_parse_bool, &c->log_meta, NULL }, { "log-time", pa_config_parse_bool, &c->log_time, NULL }, { "log-backtrace", pa_config_parse_unsigned, &c->log_backtrace, NULL }, #ifdef HAVE_SYS_RESOURCE_H { "rlimit-fsize", parse_rlimit, &c->rlimit_fsize, NULL }, { "rlimit-data", parse_rlimit, &c->rlimit_data, NULL }, { "rlimit-stack", parse_rlimit, &c->rlimit_stack, NULL }, { "rlimit-core", parse_rlimit, &c->rlimit_core, NULL }, #ifdef RLIMIT_RSS { "rlimit-rss", parse_rlimit, &c->rlimit_rss, NULL }, #endif #ifdef RLIMIT_NOFILE { "rlimit-nofile", parse_rlimit, &c->rlimit_nofile, NULL }, #endif #ifdef RLIMIT_AS { "rlimit-as", parse_rlimit, &c->rlimit_as, NULL }, #endif #ifdef RLIMIT_NPROC { "rlimit-nproc", parse_rlimit, &c->rlimit_nproc, NULL }, #endif #ifdef RLIMIT_MEMLOCK { "rlimit-memlock", parse_rlimit, &c->rlimit_memlock, NULL }, #endif #ifdef RLIMIT_LOCKS { "rlimit-locks", parse_rlimit, &c->rlimit_locks, NULL }, #endif #ifdef RLIMIT_SIGPENDING { "rlimit-sigpending", parse_rlimit, &c->rlimit_sigpending, NULL }, #endif #ifdef RLIMIT_MSGQUEUE { "rlimit-msgqueue", parse_rlimit, &c->rlimit_msgqueue, NULL }, #endif #ifdef RLIMIT_NICE { "rlimit-nice", parse_rlimit, &c->rlimit_nice, NULL }, #endif #ifdef RLIMIT_RTPRIO { "rlimit-rtprio", parse_rlimit, &c->rlimit_rtprio, NULL }, #endif #ifdef RLIMIT_RTTIME { "rlimit-rttime", parse_rlimit, &c->rlimit_rttime, NULL }, #endif #endif { NULL, NULL, NULL, NULL }, }; pa_xfree(c->config_file); c->config_file = NULL; f = filename ? pa_fopen_cloexec(c->config_file = pa_xstrdup(filename), "r") : pa_open_config_file(DEFAULT_CONFIG_FILE, DEFAULT_CONFIG_FILE_USER, ENV_CONFIG_FILE, &c->config_file); if (!f && errno != ENOENT) { pa_log_warn(_("Failed to open configuration file: %s"), pa_cstrerror(errno)); goto finish; } ci.default_channel_map_set = ci.default_sample_spec_set = false; ci.conf = c; r = f ? pa_config_parse(c->config_file, f, table, NULL, true, NULL) : 0; if (r >= 0) { /* Make sure that channel map and sample spec fit together */ if (ci.default_sample_spec_set && ci.default_channel_map_set && c->default_channel_map.channels != c->default_sample_spec.channels) { pa_log_error(_("The specified default channel map has a different number of channels than the specified default number of channels.")); r = -1; goto finish; } else if (ci.default_sample_spec_set) pa_channel_map_init_extend(&c->default_channel_map, c->default_sample_spec.channels, PA_CHANNEL_MAP_DEFAULT); else if (ci.default_channel_map_set) c->default_sample_spec.channels = c->default_channel_map.channels; } finish: if (f) fclose(f); return r; }
int main(int argc, char *argv[]) { pa_mainloop* m = NULL; int ret = 1, c; char *bn, *server = NULL; pa_time_event *time_event = NULL; const char *filename = NULL; static const struct option long_options[] = { {"record", 0, NULL, 'r'}, {"playback", 0, NULL, 'p'}, {"device", 1, NULL, 'd'}, {"server", 1, NULL, 's'}, {"client-name", 1, NULL, 'n'}, {"stream-name", 1, NULL, ARG_STREAM_NAME}, {"version", 0, NULL, ARG_VERSION}, {"help", 0, NULL, 'h'}, {"verbose", 0, NULL, 'v'}, {"volume", 1, NULL, ARG_VOLUME}, {"rate", 1, NULL, ARG_SAMPLERATE}, {"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}, {"latency", 1, NULL, ARG_LATENCY}, {"process-time", 1, NULL, ARG_PROCESS_TIME}, {"property", 1, NULL, ARG_PROPERTY}, {"raw", 0, NULL, ARG_RAW}, {"file-format", 2, NULL, ARG_FILE_FORMAT}, {"list-file-formats", 0, NULL, ARG_LIST_FILE_FORMATS}, {"latency-msec", 1, NULL, ARG_LATENCY_MSEC}, {"process-time-msec", 1, NULL, ARG_PROCESS_TIME_MSEC}, {NULL, 0, NULL, 0} }; setlocale(LC_ALL, ""); bindtextdomain(GETTEXT_PACKAGE, PULSE_LOCALEDIR); bn = pa_path_get_filename(argv[0]); if (strstr(bn, "play")) { mode = PLAYBACK; raw = FALSE; } else if (strstr(bn, "record")) { mode = RECORD; raw = FALSE; } else if (strstr(bn, "cat")) { mode = PLAYBACK; raw = TRUE; } if (strstr(bn, "rec") || strstr(bn, "mon")) { mode = RECORD; raw = TRUE; } proplist = pa_proplist_new(); while ((c = getopt_long(argc, argv, "rpd:s:n:hv", long_options, NULL)) != -1) { switch (c) { case 'h' : help(bn); ret = 0; goto quit; case ARG_VERSION: printf(_("pacat %s\n" "Compiled with libpulse %s\n" "Linked with libpulse %s\n"), PACKAGE_VERSION, pa_get_headers_version(), pa_get_library_version()); ret = 0; goto quit; case 'r': mode = RECORD; break; case 'p': mode = PLAYBACK; break; case 'd': pa_xfree(device); device = pa_xstrdup(optarg); break; case 's': pa_xfree(server); server = pa_xstrdup(optarg); break; case 'n': { char *t; if (!(t = pa_locale_to_utf8(optarg)) || pa_proplist_sets(proplist, PA_PROP_APPLICATION_NAME, t) < 0) { pa_log(_("Invalid client name '%s'"), t ? t : optarg); pa_xfree(t); goto quit; } pa_xfree(t); break; } case ARG_STREAM_NAME: { char *t; if (!(t = pa_locale_to_utf8(optarg)) || pa_proplist_sets(proplist, PA_PROP_MEDIA_NAME, t) < 0) { pa_log(_("Invalid stream name '%s'"), t ? t : optarg); pa_xfree(t); goto quit; } pa_xfree(t); break; } case 'v': verbose = 1; break; case ARG_VOLUME: { int v = atoi(optarg); volume = v < 0 ? 0U : (pa_volume_t) v; volume_is_set = TRUE; break; } case ARG_CHANNELS: sample_spec.channels = (uint8_t) atoi(optarg); sample_spec_set = TRUE; break; case ARG_SAMPLEFORMAT: sample_spec.format = pa_parse_sample_format(optarg); sample_spec_set = TRUE; break; case ARG_SAMPLERATE: sample_spec.rate = (uint32_t) atoi(optarg); sample_spec_set = TRUE; break; case ARG_CHANNELMAP: if (!pa_channel_map_parse(&channel_map, optarg)) { pa_log(_("Invalid channel map '%s'"), optarg); goto quit; } channel_map_set = TRUE; 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; case ARG_LATENCY: if (((latency = (size_t) atoi(optarg))) <= 0) { pa_log(_("Invalid latency specification '%s'"), optarg); goto quit; } break; case ARG_PROCESS_TIME: if (((process_time = (size_t) atoi(optarg))) <= 0) { pa_log(_("Invalid process time specification '%s'"), optarg); goto quit; } break; case ARG_LATENCY_MSEC: if (((latency_msec = (int32_t) atoi(optarg))) <= 0) { pa_log(_("Invalid latency specification '%s'"), optarg); goto quit; } break; case ARG_PROCESS_TIME_MSEC: if (((process_time_msec = (int32_t) atoi(optarg))) <= 0) { pa_log(_("Invalid process time specification '%s'"), optarg); goto quit; } break; case ARG_PROPERTY: { char *t; if (!(t = pa_locale_to_utf8(optarg)) || pa_proplist_setp(proplist, t) < 0) { pa_xfree(t); pa_log(_("Invalid property '%s'"), optarg); goto quit; } pa_xfree(t); break; } case ARG_RAW: raw = TRUE; break; case ARG_FILE_FORMAT: raw = FALSE; if (optarg) { if ((file_format = pa_sndfile_format_from_string(optarg)) < 0) { pa_log(_("Unknown file format %s."), optarg); goto quit; } } raw = FALSE; break; case ARG_LIST_FILE_FORMATS: pa_sndfile_dump_formats(); ret = 0; goto quit; default: goto quit; } } if (!pa_sample_spec_valid(&sample_spec)) { pa_log(_("Invalid sample specification")); goto quit; } if (optind+1 == argc) { int fd; filename = argv[optind]; if ((fd = open(argv[optind], mode == PLAYBACK ? O_RDONLY : O_WRONLY|O_TRUNC|O_CREAT, 0666)) < 0) { pa_log(_("open(): %s"), strerror(errno)); goto quit; } if (dup2(fd, mode == PLAYBACK ? STDIN_FILENO : STDOUT_FILENO) < 0) { pa_log(_("dup2(): %s"), strerror(errno)); goto quit; } pa_close(fd); } else if (optind+1 <= argc) { pa_log(_("Too many arguments.")); goto quit; } if (!raw) { SF_INFO sfi; pa_zero(sfi); if (mode == RECORD) { /* This might patch up the sample spec */ if (pa_sndfile_write_sample_spec(&sfi, &sample_spec) < 0) { pa_log(_("Failed to generate sample specification for file.")); goto quit; } /* Transparently upgrade classic .wav to wavex for multichannel audio */ if (file_format <= 0) { if ((sample_spec.channels == 2 && (!channel_map_set || (channel_map.map[0] == PA_CHANNEL_POSITION_LEFT && channel_map.map[1] == PA_CHANNEL_POSITION_RIGHT))) || (sample_spec.channels == 1 && (!channel_map_set || (channel_map.map[0] == PA_CHANNEL_POSITION_MONO)))) file_format = SF_FORMAT_WAV; else file_format = SF_FORMAT_WAVEX; } sfi.format |= file_format; } if (!(sndfile = sf_open_fd(mode == RECORD ? STDOUT_FILENO : STDIN_FILENO, mode == RECORD ? SFM_WRITE : SFM_READ, &sfi, 0))) { pa_log(_("Failed to open audio file.")); goto quit; } if (mode == PLAYBACK) { if (sample_spec_set) pa_log(_("Warning: specified sample specification will be overwritten with specification from file.")); if (pa_sndfile_read_sample_spec(sndfile, &sample_spec) < 0) { pa_log(_("Failed to determine sample specification from file.")); goto quit; } sample_spec_set = TRUE; if (!channel_map_set) { /* Allow the user to overwrite the channel map on the command line */ if (pa_sndfile_read_channel_map(sndfile, &channel_map) < 0) { if (sample_spec.channels > 2) pa_log(_("Warning: Failed to determine channel map from file.")); } else channel_map_set = TRUE; } } } if (!channel_map_set) pa_channel_map_init_extend(&channel_map, sample_spec.channels, PA_CHANNEL_MAP_DEFAULT); if (!pa_channel_map_compatible(&channel_map, &sample_spec)) { pa_log(_("Channel map doesn't match sample specification")); goto quit; } if (!raw) { pa_proplist *sfp; if (mode == PLAYBACK) readf_function = pa_sndfile_readf_function(&sample_spec); else { if (pa_sndfile_write_channel_map(sndfile, &channel_map) < 0) pa_log(_("Warning: failed to write channel map to file.")); writef_function = pa_sndfile_writef_function(&sample_spec); } /* Fill in libsndfile prop list data */ sfp = pa_proplist_new(); pa_sndfile_init_proplist(sndfile, sfp); pa_proplist_update(proplist, PA_UPDATE_MERGE, sfp); pa_proplist_free(sfp); } if (verbose) { char tss[PA_SAMPLE_SPEC_SNPRINT_MAX], tcm[PA_CHANNEL_MAP_SNPRINT_MAX]; pa_log(_("Opening a %s stream with sample specification '%s' and channel map '%s'."), mode == RECORD ? _("recording") : _("playback"), pa_sample_spec_snprint(tss, sizeof(tss), &sample_spec), pa_channel_map_snprint(tcm, sizeof(tcm), &channel_map)); } /* Fill in client name if none was set */ if (!pa_proplist_contains(proplist, PA_PROP_APPLICATION_NAME)) { char *t; if ((t = pa_locale_to_utf8(bn))) { pa_proplist_sets(proplist, PA_PROP_APPLICATION_NAME, t); pa_xfree(t); } } /* Fill in media name if none was set */ if (!pa_proplist_contains(proplist, PA_PROP_MEDIA_NAME)) { const char *t; if ((t = filename) || (t = pa_proplist_gets(proplist, PA_PROP_APPLICATION_NAME))) pa_proplist_sets(proplist, PA_PROP_MEDIA_NAME, t); } /* Set up a new main loop */ if (!(m = pa_mainloop_new())) { pa_log(_("pa_mainloop_new() failed.")); goto quit; } mainloop_api = pa_mainloop_get_api(m); pa_assert_se(pa_signal_init(mainloop_api) == 0); pa_signal_new(SIGINT, exit_signal_callback, NULL); pa_signal_new(SIGTERM, exit_signal_callback, NULL); #ifdef SIGUSR1 pa_signal_new(SIGUSR1, sigusr1_signal_callback, NULL); #endif pa_disable_sigpipe(); if (raw) { if (!(stdio_event = mainloop_api->io_new(mainloop_api, mode == PLAYBACK ? STDIN_FILENO : STDOUT_FILENO, mode == PLAYBACK ? PA_IO_EVENT_INPUT : PA_IO_EVENT_OUTPUT, mode == PLAYBACK ? stdin_callback : stdout_callback, NULL))) { pa_log(_("io_new() failed.")); goto quit; } } /* Create a new connection context */ if (!(context = pa_context_new_with_proplist(mainloop_api, NULL, proplist))) { pa_log(_("pa_context_new() failed.")); goto quit; } pa_context_set_state_callback(context, context_state_callback, NULL); /* Connect the context */ if (pa_context_connect(context, server, 0, NULL) < 0) { pa_log(_("pa_context_connect() failed: %s"), pa_strerror(pa_context_errno(context))); goto quit; } if (verbose) { if (!(time_event = pa_context_rttime_new(context, pa_rtclock_now() + TIME_EVENT_USEC, time_event_callback, NULL))) { pa_log(_("pa_context_rttime_new() failed.")); goto quit; } } /* Run the main loop */ if (pa_mainloop_run(m, &ret) < 0) { pa_log(_("pa_mainloop_run() failed.")); goto quit; } quit: if (stream) pa_stream_unref(stream); if (context) pa_context_unref(context); if (stdio_event) { pa_assert(mainloop_api); mainloop_api->io_free(stdio_event); } if (time_event) { pa_assert(mainloop_api); mainloop_api->time_free(time_event); } if (m) { pa_signal_done(); pa_mainloop_free(m); } pa_xfree(buffer); pa_xfree(server); pa_xfree(device); if (sndfile) sf_close(sndfile); if (proplist) pa_proplist_free(proplist); return ret; }
static void resolver_cb( AvahiServiceResolver *r, AvahiIfIndex interface, AvahiProtocol protocol, AvahiResolverEvent event, const char *name, const char *type, const char *domain, const char *host_name, const AvahiAddress *a, uint16_t port, AvahiStringList *txt, AvahiLookupResultFlags flags, void *userdata) { struct userdata *u = userdata; struct tunnel *tnl; pa_assert(u); tnl = tunnel_new(interface, protocol, name, type, domain); if (event != AVAHI_RESOLVER_FOUND) pa_log("Resolving of '%s' failed: %s", name, avahi_strerror(avahi_client_errno(u->client))); else { char *device = NULL, *dname, *module_name, *args; const char *t; char at[AVAHI_ADDRESS_STR_MAX], cmt[PA_CHANNEL_MAP_SNPRINT_MAX]; pa_sample_spec ss; pa_channel_map cm; AvahiStringList *l; pa_bool_t channel_map_set = FALSE; pa_module *m; ss = u->core->default_sample_spec; cm = u->core->default_channel_map; for (l = txt; l; l = l->next) { char *key, *value; pa_assert_se(avahi_string_list_get_pair(l, &key, &value, NULL) == 0); if (pa_streq(key, "device")) { pa_xfree(device); device = value; value = NULL; } else if (pa_streq(key, "rate")) ss.rate = (uint32_t) atoi(value); else if (pa_streq(key, "channels")) ss.channels = (uint8_t) atoi(value); else if (pa_streq(key, "format")) ss.format = pa_parse_sample_format(value); else if (pa_streq(key, "channel_map")) { pa_channel_map_parse(&cm, value); channel_map_set = TRUE; } avahi_free(key); avahi_free(value); } if (!channel_map_set && cm.channels != ss.channels) pa_channel_map_init_extend(&cm, ss.channels, PA_CHANNEL_MAP_DEFAULT); if (!pa_sample_spec_valid(&ss)) { pa_log("Service '%s' contains an invalid sample specification.", name); avahi_free(device); goto finish; } if (!pa_channel_map_valid(&cm) || cm.channels != ss.channels) { pa_log("Service '%s' contains an invalid channel map.", name); avahi_free(device); goto finish; } if (device) dname = pa_sprintf_malloc("tunnel.%s.%s", host_name, device); else dname = pa_sprintf_malloc("tunnel.%s", host_name); if (!pa_namereg_is_valid_name(dname)) { pa_log("Cannot construct valid device name from credentials of service '%s'.", dname); avahi_free(device); pa_xfree(dname); goto finish; } t = strstr(type, "sink") ? "sink" : "source"; module_name = pa_sprintf_malloc("module-tunnel-%s", t); args = pa_sprintf_malloc("server=[%s]:%u " "%s=%s " "format=%s " "channels=%u " "rate=%u " "%s_name=%s " "channel_map=%s", avahi_address_snprint(at, sizeof(at), a), port, t, device, pa_sample_format_to_string(ss.format), ss.channels, ss.rate, t, dname, pa_channel_map_snprint(cmt, sizeof(cmt), &cm)); pa_log_debug("Loading %s with arguments '%s'", module_name, args); if ((m = pa_module_load(u->core, module_name, args))) { tnl->module_index = m->index; pa_hashmap_put(u->tunnels, tnl, tnl); tnl = NULL; } pa_xfree(module_name); pa_xfree(dname); pa_xfree(args); avahi_free(device); } finish: avahi_service_resolver_free(r); if (tnl) tunnel_free(tnl); }
int pa__init(pa_module*m) { struct userdata *u = NULL; pa_sample_spec ss; pa_channel_map map; pa_modargs *ma = NULL; jack_status_t status; const char *server_name, *client_name; uint32_t channels = 0; pa_bool_t do_connect = TRUE; unsigned i; const char **ports = NULL, **p; pa_sink_new_data data; pa_assert(m); jack_set_error_function(jack_error_func); if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { pa_log("Failed to parse module arguments."); goto fail; } if (pa_modargs_get_value_boolean(ma, "connect", &do_connect) < 0) { pa_log("Failed to parse connect= argument."); goto fail; } server_name = pa_modargs_get_value(ma, "server_name", NULL); client_name = pa_modargs_get_value(ma, "client_name", "PulseAudio JACK Sink"); m->userdata = u = pa_xnew0(struct userdata, 1); u->core = m->core; u->module = m; u->saved_frame_time_valid = FALSE; u->rtpoll = pa_rtpoll_new(); pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll); /* The queue linking the JACK thread and our RT thread */ u->jack_msgq = pa_asyncmsgq_new(0); /* The msgq from the JACK RT thread should have an even higher * priority than the normal message queues, to match the guarantee * all other drivers make: supplying the audio device with data is * the top priority -- and as long as that is possible we don't do * anything else */ u->rtpoll_item = pa_rtpoll_item_new_asyncmsgq_read(u->rtpoll, PA_RTPOLL_EARLY-1, u->jack_msgq); if (!(u->client = jack_client_open(client_name, server_name ? JackServerName : JackNullOption, &status, server_name))) { pa_log("jack_client_open() failed."); goto fail; } ports = jack_get_ports(u->client, NULL, JACK_DEFAULT_AUDIO_TYPE, JackPortIsPhysical|JackPortIsInput); channels = 0; for (p = ports; *p; p++) channels++; if (!channels) channels = m->core->default_sample_spec.channels; if (pa_modargs_get_value_u32(ma, "channels", &channels) < 0 || channels <= 0 || channels > PA_CHANNELS_MAX) { pa_log("Failed to parse channels= argument."); goto fail; } if (channels == m->core->default_channel_map.channels) map = m->core->default_channel_map; else pa_channel_map_init_extend(&map, channels, PA_CHANNEL_MAP_ALSA); if (pa_modargs_get_channel_map(ma, NULL, &map) < 0 || map.channels != channels) { pa_log("Failed to parse channel_map= argument."); goto fail; } pa_log_info("Successfully connected as '%s'", jack_get_client_name(u->client)); u->channels = ss.channels = (uint8_t) channels; ss.rate = jack_get_sample_rate(u->client); ss.format = PA_SAMPLE_FLOAT32NE; pa_assert(pa_sample_spec_valid(&ss)); for (i = 0; i < ss.channels; i++) { if (!(u->port[i] = jack_port_register(u->client, pa_channel_position_to_string(map.map[i]), JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput|JackPortIsTerminal, 0))) { pa_log("jack_port_register() failed."); goto fail; } } pa_sink_new_data_init(&data); data.driver = __FILE__; data.module = m; pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME)); pa_sink_new_data_set_sample_spec(&data, &ss); pa_sink_new_data_set_channel_map(&data, &map); pa_proplist_sets(data.proplist, PA_PROP_DEVICE_API, "jack"); if (server_name) pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, server_name); pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Jack sink (%s)", jack_get_client_name(u->client)); pa_proplist_sets(data.proplist, "jack.client_name", jack_get_client_name(u->client)); if (pa_modargs_get_proplist(ma, "sink_properties", data.proplist, PA_UPDATE_REPLACE) < 0) { pa_log("Invalid properties"); pa_sink_new_data_done(&data); goto fail; } u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY); pa_sink_new_data_done(&data); if (!u->sink) { pa_log("Failed to create sink."); goto fail; } u->sink->parent.process_msg = sink_process_msg; u->sink->userdata = u; pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq); pa_sink_set_rtpoll(u->sink, u->rtpoll); pa_sink_set_max_request(u->sink, jack_get_buffer_size(u->client) * pa_frame_size(&u->sink->sample_spec)); jack_set_process_callback(u->client, jack_process, u); jack_on_shutdown(u->client, jack_shutdown, u); jack_set_thread_init_callback(u->client, jack_init, u); jack_set_buffer_size_callback(u->client, jack_buffer_size, u); if (!(u->thread = pa_thread_new(thread_func, u))) { pa_log("Failed to create thread."); goto fail; } if (jack_activate(u->client)) { pa_log("jack_activate() failed"); goto fail; } if (do_connect) { for (i = 0, p = ports; i < ss.channels; i++, p++) { if (!*p) { pa_log("Not enough physical output ports, leaving unconnected."); break; } pa_log_info("Connecting %s to %s", jack_port_name(u->port[i]), *p); if (jack_connect(u->client, jack_port_name(u->port[i]), *p)) { pa_log("Failed to connect %s to %s, leaving unconnected.", jack_port_name(u->port[i]), *p); break; } } } pa_sink_put(u->sink); free(ports); pa_modargs_free(ma); return 0; fail: if (ma) pa_modargs_free(ma); free(ports); pa__done(m); return -1; }
int pa_sound_file_load( pa_mempool *pool, const char *fname, pa_sample_spec *ss, pa_channel_map *map, pa_memchunk *chunk, pa_proplist *p) { SNDFILE *sf = NULL; SF_INFO sfi; int ret = -1; size_t l; sf_count_t (*readf_function)(SNDFILE *sndfile, void *ptr, sf_count_t frames) = NULL; void *ptr = NULL; int fd; pa_assert(fname); pa_assert(ss); pa_assert(chunk); pa_memchunk_reset(chunk); if ((fd = pa_open_cloexec(fname, O_RDONLY, 0)) < 0) { pa_log("Failed to open file %s: %s", fname, pa_cstrerror(errno)); goto finish; } #ifdef HAVE_POSIX_FADVISE if (posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL) < 0) { pa_log_warn("POSIX_FADV_SEQUENTIAL failed: %s", pa_cstrerror(errno)); goto finish; } else pa_log_debug("POSIX_FADV_SEQUENTIAL succeeded."); #endif pa_zero(sfi); if (!(sf = sf_open_fd(fd, SFM_READ, &sfi, 1))) { pa_log("Failed to open file %s", fname); goto finish; } fd = -1; if (pa_sndfile_read_sample_spec(sf, ss) < 0) { pa_log("Failed to determine file sample format."); goto finish; } if ((map && pa_sndfile_read_channel_map(sf, map) < 0)) { if (ss->channels > 2) pa_log("Failed to determine file channel map, synthesizing one."); pa_channel_map_init_extend(map, ss->channels, PA_CHANNEL_MAP_DEFAULT); } if (p) pa_sndfile_init_proplist(sf, p); if ((l = pa_frame_size(ss) * (size_t) sfi.frames) > PA_SCACHE_ENTRY_SIZE_MAX) { pa_log("File too large"); goto finish; } chunk->memblock = pa_memblock_new(pool, l); chunk->index = 0; chunk->length = l; readf_function = pa_sndfile_readf_function(ss); ptr = pa_memblock_acquire(chunk->memblock); if ((readf_function && readf_function(sf, ptr, sfi.frames) != sfi.frames) || (!readf_function && sf_read_raw(sf, ptr, (sf_count_t) l) != (sf_count_t) l)) { pa_log("Premature file end"); goto finish; } ret = 0; finish: if (sf) sf_close(sf); if (ptr) pa_memblock_release(chunk->memblock); if (ret != 0 && chunk->memblock) pa_memblock_unref(chunk->memblock); if (fd >= 0) pa_close(fd); return ret; }
int pa__init(pa_module*m) { struct userdata *u = NULL; pa_sample_spec ss; pa_channel_map map; pa_modargs *ma = NULL; pa_sink_new_data data; size_t nbytes; pa_assert(m); pa_web_debug_func_t func = pa_get_user_data(PA_USER_WEB_FUNC); if (!func) { pa_log_error("Failed to get user web func from user data"); goto fail; } ma = pa_modargs_new(m->argument, valid_modargs); if (!ma) { pa_log("Failed to parse module arguments."); goto fail; } ss = m->core->default_sample_spec; map = m->core->default_channel_map; if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) { pa_log("Invalid sample format specification or channel map"); goto fail; } pa_modargs_get_value_u32(ma, "fixed_rate", &ss.rate); uint32_t c = 0; pa_modargs_get_value_u32(ma, "fixed_channels", &c); if (c) ss.channels = c; const char* fixed_fmt = pa_modargs_get_value(ma, "fixed_format", NULL); if (fixed_fmt) ss.format = pa_parse_sample_format(fixed_fmt); if (ss.channels != map.channels) { pa_log_error("sample_spec channels[%d] != channel_map channels[%d]", ss.channels, map.channels); pa_channel_map_init_extend(&map, ss.channels, PA_CHANNEL_MAP_DEFAULT); pa_log_debug("after init extend mmap.channels=%d", map.channels); } m->userdata = u = pa_xnew0(struct userdata, 1); u->core = m->core; u->module = m; u->rtpoll = pa_rtpoll_new(); u->data_func = web_data_func; pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll); pa_sink_new_data_init(&data); data.driver = __FILE__; data.module = m; pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME)); pa_sink_new_data_set_sample_spec(&data, &ss); pa_sink_new_data_set_channel_map(&data, &map); pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, _("Remote Output")); pa_proplist_sets(data.proplist, PA_PROP_DEVICE_CLASS, "abstract"); if (pa_modargs_get_proplist(ma, "sink_properties", data.proplist, PA_UPDATE_REPLACE) < 0) { pa_log("Invalid properties"); pa_sink_new_data_done(&data); goto fail; } u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY); pa_sink_new_data_done(&data); if (!u->sink) { pa_log("Failed to create sink object."); goto fail; } u->sink->parent.process_msg = sink_process_msg; u->sink->update_requested_latency = sink_update_requested_latency_cb; u->sink->userdata = u; pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq); pa_sink_set_rtpoll(u->sink, u->rtpoll); u->block_usec = BLOCK_USEC; nbytes = pa_usec_to_bytes(u->block_usec, &u->sink->sample_spec); pa_sink_set_max_rewind(u->sink, nbytes); pa_sink_set_max_request(u->sink, nbytes); // call websocket to start func(1, NULL, ss.rate, ss.format); u->thread = pa_thread_new("remote-sink", thread_func, u); if (!u->thread) { pa_log("Failed to create thread."); goto fail; } pa_sink_set_latency_range(u->sink, 0, BLOCK_USEC); pa_sink_put(u->sink); pa_modargs_free(ma); return 0; fail: if (ma) pa_modargs_free(ma); pa__done(m); return -1; }
static int pulse_set_spec(ddb_waveformat_t *fmt) { memcpy (&plugin.fmt, fmt, sizeof (ddb_waveformat_t)); if (!plugin.fmt.channels) { // generic format plugin.fmt.bps = 16; plugin.fmt.is_float = 0; plugin.fmt.channels = 2; plugin.fmt.samplerate = 44100; plugin.fmt.channelmask = 3; } trace ("format %dbit %s %dch %dHz channelmask=%X\n", plugin.fmt.bps, plugin.fmt.is_float ? "float" : "int", plugin.fmt.channels, plugin.fmt.samplerate, plugin.fmt.channelmask); ss.channels = plugin.fmt.channels; // Try to auto-configure the channel map, see <pulse/channelmap.h> for details pa_channel_map channel_map; pa_channel_map_init_extend(&channel_map, ss.channels, PA_CHANNEL_MAP_WAVEEX); trace ("pulse: channels: %d\n", ss.channels); // Read samplerate from config //ss.rate = deadbeef->conf_get_int(CONFSTR_PULSE_SAMPLERATE, 44100); ss.rate = plugin.fmt.samplerate; trace ("pulse: samplerate: %d\n", ss.rate); switch (plugin.fmt.bps) { case 8: ss.format = PA_SAMPLE_U8; break; case 16: ss.format = PA_SAMPLE_S16LE; break; case 24: ss.format = PA_SAMPLE_S24LE; break; case 32: if (plugin.fmt.is_float) { ss.format = PA_SAMPLE_FLOAT32LE; } else { ss.format = PA_SAMPLE_S32LE; } break; default: return -1; }; if (s) { pa_simple_free(s); } pa_buffer_attr * attr = NULL; //attr->maxlength = Maximum length of the buffer. //attr->tlength = Playback only: target length of the buffer. //attr->prebuf = Playback only: pre-buffering. //attr->minreq = Playback only: minimum request. //attr->fragsize = Recording only: fragment size. buffer_size = deadbeef->conf_get_int(CONFSTR_PULSE_BUFFERSIZE, PULSE_DEFAULT_BUFFERSIZE); // TODO: where list of all available devices? add this option to config too.. char * dev = NULL; int error; // Read serveraddr from config deadbeef->conf_lock (); const char * server = deadbeef->conf_get_str_fast (CONFSTR_PULSE_SERVERADDR, NULL); if (server) { server = strcmp(server, "default") ? server : NULL; } s = pa_simple_new(server, "Deadbeef", PA_STREAM_PLAYBACK, dev, "Music", &ss, &channel_map, attr, &error); deadbeef->conf_unlock (); if (!s) { trace ("pulse_init failed (%d)\n", error); return -1; } return 0; }
bool QPulseAudioInput::open() { if (m_opened) return true; QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance(); if (!pulseEngine->context() || pa_context_get_state(pulseEngine->context()) != PA_CONTEXT_READY) { setError(QAudio::FatalError); setState(QAudio::StoppedState); return false; } pa_sample_spec spec = QPulseAudioInternal::audioFormatToSampleSpec(m_format); if (!pa_sample_spec_valid(&spec)) { setError(QAudio::OpenError); setState(QAudio::StoppedState); return false; } m_spec = spec; #ifdef DEBUG_PULSE // QTime now(QTime::currentTime()); // qDebug()<<now.second()<<"s "<<now.msec()<<"ms :open()"; #endif if (m_streamName.isNull()) m_streamName = QString(QLatin1String("QtmPulseStream-%1-%2")).arg(::getpid()).arg(quintptr(this)).toUtf8(); #ifdef DEBUG_PULSE qDebug() << "Format: " << QPulseAudioInternal::sampleFormatToQString(spec.format); qDebug() << "Rate: " << spec.rate; qDebug() << "Channels: " << spec.channels; qDebug() << "Frame size: " << pa_frame_size(&spec); #endif pulseEngine->lock(); pa_channel_map channel_map; pa_channel_map_init_extend(&channel_map, spec.channels, PA_CHANNEL_MAP_DEFAULT); if (!pa_channel_map_compatible(&channel_map, &spec)) qWarning() << "Channel map doesn't match sample specification!"; m_stream = pa_stream_new(pulseEngine->context(), m_streamName.constData(), &spec, &channel_map); pa_stream_set_state_callback(m_stream, inputStreamStateCallback, this); pa_stream_set_read_callback(m_stream, inputStreamReadCallback, this); pa_stream_set_underflow_callback(m_stream, inputStreamUnderflowCallback, this); pa_stream_set_overflow_callback(m_stream, inputStreamOverflowCallback, this); m_periodSize = pa_usec_to_bytes(PeriodTimeMs*1000, &spec); int flags = 0; pa_buffer_attr buffer_attr; buffer_attr.maxlength = (uint32_t) -1; buffer_attr.prebuf = (uint32_t) -1; buffer_attr.tlength = (uint32_t) -1; buffer_attr.minreq = (uint32_t) -1; flags |= PA_STREAM_ADJUST_LATENCY; if (m_bufferSize > 0) buffer_attr.fragsize = (uint32_t) m_bufferSize; else buffer_attr.fragsize = (uint32_t) m_periodSize; if (pa_stream_connect_record(m_stream, m_device.data(), &buffer_attr, (pa_stream_flags_t)flags) < 0) { qWarning() << "pa_stream_connect_record() failed!"; pa_stream_unref(m_stream); m_stream = 0; pulseEngine->unlock(); setError(QAudio::OpenError); setState(QAudio::StoppedState); return false; } while (pa_stream_get_state(m_stream) != PA_STREAM_READY) pa_threaded_mainloop_wait(pulseEngine->mainloop()); const pa_buffer_attr *actualBufferAttr = pa_stream_get_buffer_attr(m_stream); m_periodSize = actualBufferAttr->fragsize; m_periodTime = pa_bytes_to_usec(m_periodSize, &spec) / 1000; if (actualBufferAttr->tlength != (uint32_t)-1) m_bufferSize = actualBufferAttr->tlength; pulseEngine->unlock(); connect(pulseEngine, &QPulseAudioEngine::contextFailed, this, &QPulseAudioInput::onPulseContextFailed); m_opened = true; m_timer->start(m_periodTime); m_clockStamp.restart(); m_timeStamp.restart(); m_elapsedTimeOffset = 0; m_totalTimeValue = 0; return true; }
void pulse_initialize_audio_step_2(pa_context *context) { pa_proplist *stream_proplist; pa_sample_spec sample_specification; pa_channel_map channel_map; pa_buffer_attr buffer_attributes; pa_stream *stream; /* Now we need to add our mono audio channel to the connection */ /* SAMPLE_SPEC: we describe the data going into our channel */ sample_specification.format = PA_SAMPLE_S16NE; sample_specification.rate = AUDIO_SAMPLE_RATE_IN_HZ; sample_specification.channels = 1; g_assert(pa_sample_spec_valid(&sample_specification)); /* BUFFER_ATTRIBUTES: Here we set the buffering behavior of the * audio. We want low latency. One wiki suggests that to set a specific latency, 1. use pa_usec_to_bytes(&ss, ...) to convert the latency from a time unit to bytes 2. use the PA_STREAM_ADJUST_LATENCY flag 3. set pa_buffer_attr::tlength to latency in samples 4. set rest of pa_buffer_attr to -1 http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/Developer/Clients/LatencyControl */ buffer_attributes.tlength = pa_usec_to_bytes (AUDIO_LATENCY_REQUESTED_IN_MILLISECONDS * MICROSECONDS_PER_MILLISECOND, &sample_specification); buffer_attributes.maxlength = -1; /* -1 == default */ buffer_attributes.prebuf = -1; buffer_attributes.minreq = -1; buffer_attributes.fragsize = -1; /* PROPERTY_LIST: Then we set up a structure to hold PulseAudio * properties for this. */ stream_proplist = xpa_proplist_new(); xpa_proplist_sets(stream_proplist, PA_PROP_MEDIA_NAME, "mono channel"); xpa_proplist_sets(stream_proplist, PA_PROP_MEDIA_ROLE, "game"); /* CHANNEL_MAP: Then we say which speakers we're using. The game is mono, so - that makes it simple. */ pa_channel_map_init_extend(&channel_map, 1, PA_CHANNEL_MAP_DEFAULT); if (!pa_channel_map_compatible(&channel_map, &sample_specification)) g_error("Channel map doesn't match sample specification"); { char tss[100], tcm[100]; g_debug("Opening a stream with sample specification '%s' and channel map '%s'.", pa_sample_spec_snprint(tss, sizeof(tss), &sample_specification), pa_channel_map_snprint(tcm, sizeof(tcm), &channel_map)); } /* STREAM: Group everything together as a stream */ g_assert (pulse.context == context); stream = xpa_stream_new_with_proplist(pulse.context, "mono channel", /* Stream name */ &sample_specification, &channel_map, stream_proplist); xpa_proplist_free (stream_proplist); xpa_stream_set_started_callback(stream, cb_audio_stream_started, NULL); xpa_stream_set_write_callback(stream, cb_audio_stream_write, NULL); pulse.samples_written = 0U; /* Connect the stream to the audio loop */ xpa_stream_connect_playback_to_default_device (stream, context, &buffer_attributes, /* PA_STREAM_ADJUST_LATENCY | */ PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE); /* Finally done! */ g_debug("PulseAudio initialization complete"); }
int pa_play_file( pa_sink *sink, const char *fname, const pa_cvolume *volume) { file_stream *u = NULL; pa_sample_spec ss; pa_channel_map cm; pa_sink_input_new_data data; int fd; SF_INFO sfi; pa_memchunk silence; pa_assert(sink); pa_assert(fname); u = pa_msgobject_new(file_stream); u->parent.parent.free = file_stream_free; u->parent.process_msg = file_stream_process_msg; u->core = sink->core; u->sink_input = NULL; u->sndfile = NULL; u->readf_function = NULL; u->memblockq = NULL; if ((fd = pa_open_cloexec(fname, O_RDONLY, 0)) < 0) { pa_log("Failed to open file %s: %s", fname, pa_cstrerror(errno)); goto fail; } /* FIXME: For now we just use posix_fadvise to avoid page faults * when accessing the file data. Eventually we should move the * file reader into the main event loop and pass the data over the * asyncmsgq. */ #ifdef HAVE_POSIX_FADVISE if (posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL) < 0) { pa_log_warn("POSIX_FADV_SEQUENTIAL failed: %s", pa_cstrerror(errno)); goto fail; } else pa_log_debug("POSIX_FADV_SEQUENTIAL succeeded."); if (posix_fadvise(fd, 0, 0, POSIX_FADV_WILLNEED) < 0) { pa_log_warn("POSIX_FADV_WILLNEED failed: %s", pa_cstrerror(errno)); goto fail; } else pa_log_debug("POSIX_FADV_WILLNEED succeeded."); #endif pa_zero(sfi); if (!(u->sndfile = sf_open_fd(fd, SFM_READ, &sfi, 1))) { pa_log("Failed to open file %s", fname); goto fail; } fd = -1; if (pa_sndfile_read_sample_spec(u->sndfile, &ss) < 0) { pa_log("Failed to determine file sample format."); goto fail; } if (pa_sndfile_read_channel_map(u->sndfile, &cm) < 0) { if (ss.channels > 2) pa_log_info("Failed to determine file channel map, synthesizing one."); pa_channel_map_init_extend(&cm, ss.channels, PA_CHANNEL_MAP_DEFAULT); } u->readf_function = pa_sndfile_readf_function(&ss); pa_sink_input_new_data_init(&data); pa_sink_input_new_data_set_sink(&data, sink, FALSE); data.driver = __FILE__; pa_sink_input_new_data_set_sample_spec(&data, &ss); pa_sink_input_new_data_set_channel_map(&data, &cm); pa_sink_input_new_data_set_volume(&data, volume); pa_proplist_sets(data.proplist, PA_PROP_MEDIA_NAME, pa_path_get_filename(fname)); pa_proplist_sets(data.proplist, PA_PROP_MEDIA_FILENAME, fname); pa_sndfile_init_proplist(u->sndfile, data.proplist); pa_sink_input_new(&u->sink_input, sink->core, &data); pa_sink_input_new_data_done(&data); if (!u->sink_input) goto fail; u->sink_input->pop = sink_input_pop_cb; u->sink_input->process_rewind = sink_input_process_rewind_cb; u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb; u->sink_input->kill = sink_input_kill_cb; u->sink_input->state_change = sink_input_state_change_cb; u->sink_input->userdata = u; pa_sink_input_get_silence(u->sink_input, &silence); u->memblockq = pa_memblockq_new("sound-file-stream memblockq", 0, MEMBLOCKQ_MAXLENGTH, 0, &ss, 1, 1, 0, &silence); pa_memblock_unref(silence.memblock); pa_sink_input_put(u->sink_input); /* The reference to u is dangling here, because we want to keep * this stream around until it is fully played. */ return 0; fail: file_stream_unref(u); if (fd >= 0) pa_close(fd); return -1; }
/* Called from main context */ int pa_source_output_new( pa_source_output**_o, pa_core *core, pa_source_output_new_data *data) { pa_source_output *o; pa_resampler *resampler = NULL; char st[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX]; int r; char *pt; pa_assert(_o); pa_assert(core); pa_assert(data); pa_assert_ctl_context(); if (data->client) pa_proplist_update(data->proplist, PA_UPDATE_MERGE, data->client->proplist); if ((r = pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], data)) < 0) return r; pa_return_val_if_fail(!data->driver || pa_utf8_valid(data->driver), -PA_ERR_INVALID); if (!data->source) { data->source = pa_namereg_get(core, NULL, PA_NAMEREG_SOURCE); data->save_source = FALSE; } pa_return_val_if_fail(data->source, -PA_ERR_NOENTITY); pa_return_val_if_fail(PA_SOURCE_IS_LINKED(pa_source_get_state(data->source)), -PA_ERR_BADSTATE); pa_return_val_if_fail(!data->direct_on_input || data->direct_on_input->sink == data->source->monitor_of, -PA_ERR_INVALID); if (!data->sample_spec_is_set) data->sample_spec = data->source->sample_spec; pa_return_val_if_fail(pa_sample_spec_valid(&data->sample_spec), -PA_ERR_INVALID); if (!data->channel_map_is_set) { if (pa_channel_map_compatible(&data->source->channel_map, &data->sample_spec)) data->channel_map = data->source->channel_map; else pa_channel_map_init_extend(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT); } pa_return_val_if_fail(pa_channel_map_valid(&data->channel_map), -PA_ERR_INVALID); pa_return_val_if_fail(pa_channel_map_compatible(&data->channel_map, &data->sample_spec), -PA_ERR_INVALID); if (data->flags & PA_SOURCE_OUTPUT_FIX_FORMAT) data->sample_spec.format = data->source->sample_spec.format; if (data->flags & PA_SOURCE_OUTPUT_FIX_RATE) data->sample_spec.rate = data->source->sample_spec.rate; if (data->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_val_if_fail(data->resample_method < PA_RESAMPLER_MAX, -PA_ERR_INVALID); if ((r = pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE], data)) < 0) return r; if ((data->flags & PA_SOURCE_OUTPUT_NO_CREATE_ON_SUSPEND) && pa_source_get_state(data->source) == PA_SOURCE_SUSPENDED) { pa_log("Failed to create source output: source is suspended."); return -PA_ERR_BADSTATE; } 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 -PA_ERR_TOOLARGE; } if ((data->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) || !pa_sample_spec_equal(&data->sample_spec, &data->source->sample_spec) || !pa_channel_map_equal(&data->channel_map, &data->source->channel_map)) { if (!(resampler = pa_resampler_new( core->mempool, &data->source->sample_spec, &data->source->channel_map, &data->sample_spec, &data->channel_map, data->resample_method, ((data->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) | ((data->flags & PA_SOURCE_OUTPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) | (core->disable_remixing || (data->flags & PA_SOURCE_OUTPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0) | (core->disable_lfe_remixing ? PA_RESAMPLER_NO_LFE : 0)))) { pa_log_warn("Unsupported resampling operation."); return -PA_ERR_NOTSUPPORTED; } } o = pa_msgobject_new(pa_source_output); o->parent.parent.free = source_output_free; o->parent.process_msg = pa_source_output_process_msg; o->core = core; o->state = PA_SOURCE_OUTPUT_INIT; o->flags = data->flags; o->proplist = pa_proplist_copy(data->proplist); o->driver = pa_xstrdup(pa_path_get_filename(data->driver)); o->module = data->module; o->source = data->source; o->destination_source = data->destination_source; o->client = data->client; o->actual_resample_method = resampler ? pa_resampler_get_method(resampler) : PA_RESAMPLER_INVALID; o->requested_resample_method = data->resample_method; o->sample_spec = data->sample_spec; o->channel_map = data->channel_map; o->direct_on_input = data->direct_on_input; o->save_source = data->save_source; reset_callbacks(o); o->userdata = NULL; o->thread_info.state = o->state; o->thread_info.attached = FALSE; o->thread_info.sample_spec = o->sample_spec; o->thread_info.resampler = resampler; o->thread_info.requested_source_latency = (pa_usec_t) -1; o->thread_info.direct_on_input = o->direct_on_input; o->thread_info.delay_memblockq = pa_memblockq_new( 0, MEMBLOCKQ_MAXLENGTH, 0, pa_frame_size(&o->source->sample_spec), 0, 1, 0, &o->source->silence); 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); if (o->client) pa_assert_se(pa_idxset_put(o->client->source_outputs, o, NULL) >= 0); if (o->direct_on_input) pa_assert_se(pa_idxset_put(o->direct_on_input->direct_outputs, o, NULL) == 0); pt = pa_proplist_to_string_sep(o->proplist, "\n "); pa_log_info("Created output %u \"%s\" on %s with sample spec %s and channel map %s\n %s", o->index, pa_strnull(pa_proplist_gets(o->proplist, PA_PROP_MEDIA_NAME)), o->source->name, pa_sample_spec_snprint(st, sizeof(st), &o->sample_spec), pa_channel_map_snprint(cm, sizeof(cm), &o->channel_map), pt); pa_xfree(pt); /* Don't forget to call pa_source_output_put! */ *_o = o; return 0; }
pa_core* pa_core_new(pa_mainloop_api *m, bool shared, size_t shm_size) { pa_core* c; pa_mempool *pool; int j; pa_assert(m); if (shared) { if (!(pool = pa_mempool_new(shared, shm_size))) { pa_log_warn("Failed to allocate shared memory pool. Falling back to a normal memory pool."); shared = false; } } if (!shared) { if (!(pool = pa_mempool_new(shared, shm_size))) { pa_log("pa_mempool_new() failed."); return NULL; } } c = pa_msgobject_new(pa_core); c->parent.parent.free = core_free; c->parent.process_msg = core_process_msg; c->state = PA_CORE_STARTUP; c->mainloop = m; c->clients = pa_idxset_new(NULL, NULL); c->cards = pa_idxset_new(NULL, NULL); c->sinks = pa_idxset_new(NULL, NULL); c->sources = pa_idxset_new(NULL, NULL); c->sink_inputs = pa_idxset_new(NULL, NULL); c->source_outputs = pa_idxset_new(NULL, NULL); c->modules = pa_idxset_new(NULL, NULL); c->scache = pa_idxset_new(NULL, NULL); c->namereg = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); c->shared = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); c->default_source = NULL; c->default_sink = NULL; c->default_sample_spec.format = PA_SAMPLE_S16NE; c->default_sample_spec.rate = 44100; c->default_sample_spec.channels = 2; pa_channel_map_init_extend(&c->default_channel_map, c->default_sample_spec.channels, PA_CHANNEL_MAP_DEFAULT); c->default_n_fragments = 4; c->default_fragment_size_msec = 25; c->deferred_volume_safety_margin_usec = 8000; c->deferred_volume_extra_delay_usec = 0; c->module_defer_unload_event = NULL; c->modules_pending_unload = pa_hashmap_new(NULL, NULL); c->subscription_defer_event = NULL; PA_LLIST_HEAD_INIT(pa_subscription, c->subscriptions); PA_LLIST_HEAD_INIT(pa_subscription_event, c->subscription_event_queue); c->subscription_event_last = NULL; c->mempool = pool; pa_silence_cache_init(&c->silence_cache); if (shared && !(c->rw_mempool = pa_mempool_new(shared, shm_size))) pa_log_warn("Failed to allocate shared writable memory pool."); if (c->rw_mempool) pa_mempool_set_is_remote_writable(c->rw_mempool, true); c->exit_event = NULL; c->scache_auto_unload_event = NULL; c->exit_idle_time = -1; c->scache_idle_time = 20; c->flat_volumes = true; c->disallow_module_loading = false; c->disallow_exit = false; c->running_as_daemon = false; c->realtime_scheduling = false; c->realtime_priority = 5; c->disable_remixing = false; c->disable_lfe_remixing = false; c->lfe_crossover_freq = 120; c->deferred_volume = true; c->resample_method = PA_RESAMPLER_SPEEX_FLOAT_BASE + 1; for (j = 0; j < PA_CORE_HOOK_MAX; j++) pa_hook_init(&c->hooks[j], c); for (j = 0; j < PA_ACCESS_HOOK_MAX; j++) pa_hook_init(&c->access[j], c); pa_random(&c->cookie, sizeof(c->cookie)); #ifdef SIGPIPE pa_check_signal_is_blocked(SIGPIPE); #endif pa_core_check_idle(c); c->state = PA_CORE_RUNNING; return c; }