ssize_t pa_iochannel_write_with_creds(pa_iochannel*io, const void*data, size_t l, const pa_creds *ucred) { ssize_t r; struct msghdr mh; struct iovec iov; union { struct cmsghdr hdr; uint8_t data[CMSG_SPACE(sizeof(struct ucred))]; } cmsg; struct ucred *u; pa_assert(io); pa_assert(data); pa_assert(l); pa_assert(io->ofd >= 0); pa_zero(iov); iov.iov_base = (void*) data; iov.iov_len = l; pa_zero(cmsg); cmsg.hdr.cmsg_len = CMSG_LEN(sizeof(struct ucred)); cmsg.hdr.cmsg_level = SOL_SOCKET; cmsg.hdr.cmsg_type = SCM_CREDENTIALS; u = (struct ucred*) CMSG_DATA(&cmsg.hdr); u->pid = getpid(); if (ucred) { u->uid = ucred->uid; u->gid = ucred->gid; } else { u->uid = getuid(); u->gid = getgid(); } pa_zero(mh); mh.msg_iov = &iov; mh.msg_iovlen = 1; mh.msg_control = &cmsg; mh.msg_controllen = sizeof(cmsg); if ((r = sendmsg(io->ofd, &mh, MSG_NOSIGNAL)) >= 0) { io->writable = io->hungup = false; enable_events(io); } return r; }
/* For more details on FD passing, check the cmsg(3) manpage * and IETF RFC #2292: "Advanced Sockets API for IPv6" */ ssize_t pa_iochannel_write_with_fds(pa_iochannel*io, const void*data, size_t l, int nfd, const int *fds) { ssize_t r; int *msgdata; struct msghdr mh; struct iovec iov; union { struct cmsghdr hdr; uint8_t data[CMSG_SPACE(sizeof(int) * MAX_ANCIL_DATA_FDS)]; } cmsg; pa_assert(io); pa_assert(data); pa_assert(l); pa_assert(io->ofd >= 0); pa_assert(fds); pa_assert(nfd > 0); pa_assert(nfd <= MAX_ANCIL_DATA_FDS); pa_zero(iov); iov.iov_base = (void*) data; iov.iov_len = l; pa_zero(cmsg); cmsg.hdr.cmsg_level = SOL_SOCKET; cmsg.hdr.cmsg_type = SCM_RIGHTS; msgdata = (int*) CMSG_DATA(&cmsg.hdr); memcpy(msgdata, fds, nfd * sizeof(int)); cmsg.hdr.cmsg_len = CMSG_LEN(sizeof(int) * nfd); pa_zero(mh); mh.msg_iov = &iov; mh.msg_iovlen = 1; mh.msg_control = &cmsg; /* If we followed the example on the cmsg man page, we'd use * sizeof(cmsg.data) here, but if nfd < MAX_ANCIL_DATA_FDS, then the data * buffer is larger than needed, and the kernel doesn't like it if we set * msg_controllen to a larger than necessary value. The commit message for * commit 451d1d6762 contains a longer explanation. */ mh.msg_controllen = CMSG_SPACE(sizeof(int) * nfd); if ((r = sendmsg(io->ofd, &mh, MSG_NOSIGNAL)) >= 0) { io->writable = io->hungup = false; enable_events(io); } return r; }
int pa_sound_file_too_big_to_cache(const char *fname) { SNDFILE*sf = NULL; SF_INFO sfi; pa_sample_spec ss; pa_assert(fname); pa_zero(sfi); if (!(sf = sf_open(fname, SFM_READ, &sfi))) { pa_log("Failed to open file %s", fname); return -1; } if (pa_sndfile_read_sample_spec(sf, &ss) < 0) { pa_log("Failed to determine file sample format."); sf_close(sf); return -1; } sf_close(sf); if ((pa_frame_size(&ss) * (size_t) sfi.frames) > PA_SCACHE_ENTRY_SIZE_MAX) { pa_log("File too large: %s", fname); return 1; } return 0; }
pa_source_new_data* pa_source_new_data_init(pa_source_new_data *data) { pa_assert(data); pa_zero(*data); data->proplist = pa_proplist_new(); return data; }
pa_source_output_new_data* pa_source_output_new_data_init(pa_source_output_new_data *data) { pa_assert(data); pa_zero(*data); data->resample_method = PA_RESAMPLER_INVALID; data->proplist = pa_proplist_new(); return data; }
pa_srbchannel* pa_srbchannel_new(pa_mainloop_api *m, pa_mempool *p) { int capacity; int readfd; struct srbheader *srh; pa_srbchannel* sr = pa_xmalloc0(sizeof(pa_srbchannel)); sr->mainloop = m; sr->memblock = pa_memblock_new_pool(p, -1); if (!sr->memblock) goto fail; srh = pa_memblock_acquire(sr->memblock); pa_zero(*srh); sr->rb_read.memory = (uint8_t*) srh + PA_ALIGN(sizeof(*srh)); srh->readbuf_offset = sr->rb_read.memory - (uint8_t*) srh; capacity = (pa_memblock_get_length(sr->memblock) - srh->readbuf_offset) / 2; sr->rb_write.memory = PA_ALIGN_PTR(sr->rb_read.memory + capacity); srh->writebuf_offset = sr->rb_write.memory - (uint8_t*) srh; capacity = PA_MIN(capacity, srh->writebuf_offset - srh->readbuf_offset); pa_log_debug("SHM block is %d bytes, ringbuffer capacity is 2 * %d bytes", (int) pa_memblock_get_length(sr->memblock), capacity); srh->capacity = sr->rb_read.capacity = sr->rb_write.capacity = capacity; sr->rb_read.count = &srh->read_count; sr->rb_write.count = &srh->write_count; sr->sem_read = pa_fdsem_new_shm(&srh->read_semdata); if (!sr->sem_read) goto fail; sr->sem_write = pa_fdsem_new_shm(&srh->write_semdata); if (!sr->sem_write) goto fail; readfd = pa_fdsem_get(sr->sem_read); #ifdef DEBUG_SRBCHANNEL pa_log("Enabling io event on fd %d", readfd); #endif sr->read_event = m->io_new(m, readfd, PA_IO_EVENT_INPUT, semread_cb, sr); m->io_enable(sr->read_event, PA_IO_EVENT_INPUT); return sr; fail: pa_srbchannel_free(sr); return NULL; }
int pa_sndfile_read_sample_spec(SNDFILE *sf, pa_sample_spec *ss) { SF_INFO sfi; int sf_errno; pa_assert(sf); pa_assert(ss); pa_zero(sfi); if ((sf_errno = sf_command(sf, SFC_GET_CURRENT_SF_INFO, &sfi, sizeof(sfi)))) { pa_log_error("sndfile: %s", sf_error_number(sf_errno)); return -1; } switch (sfi.format & SF_FORMAT_SUBMASK) { case SF_FORMAT_PCM_16: case SF_FORMAT_PCM_U8: case SF_FORMAT_PCM_S8: ss->format = PA_SAMPLE_S16NE; break; case SF_FORMAT_PCM_24: ss->format = PA_SAMPLE_S24NE; break; case SF_FORMAT_PCM_32: ss->format = PA_SAMPLE_S32NE; break; case SF_FORMAT_ULAW: ss->format = PA_SAMPLE_ULAW; break; case SF_FORMAT_ALAW: ss->format = PA_SAMPLE_ALAW; break; case SF_FORMAT_FLOAT: case SF_FORMAT_DOUBLE: default: ss->format = PA_SAMPLE_FLOAT32NE; break; } ss->rate = (uint32_t) sfi.samplerate; ss->channels = (uint8_t) sfi.channels; if (!pa_sample_spec_valid(ss)) return -1; return 0; }
static struct entry* read_entry(struct userdata *u, const char *name) { pa_datum key, data; struct entry *e; pa_assert(u); pa_assert(name); key.data = (char*) name; key.size = strlen(name); pa_zero(data); if (!pa_database_get(u->database, &key, &data)) goto fail; if (data.size != sizeof(struct entry)) { pa_log_debug("Database contains entry for device %s of wrong size %lu != %lu. Probably due to upgrade, ignoring.", name, (unsigned long) data.size, (unsigned long) sizeof(struct entry)); goto fail; } e = (struct entry*) data.data; if (e->version != ENTRY_VERSION) { pa_log_debug("Version of database entry for device %s doesn't match our version. Probably due to upgrade, ignoring.", name); goto fail; } if (!memchr(e->port, 0, sizeof(e->port))) { pa_log_warn("Database contains entry for device %s with missing NUL byte in port name", name); goto fail; } if (e->volume_valid && !pa_channel_map_valid(&e->channel_map)) { pa_log_warn("Invalid channel map stored in database for device %s", name); goto fail; } if (e->volume_valid && (!pa_cvolume_valid(&e->volume) || !pa_cvolume_compatible_with_channel_map(&e->volume, &e->channel_map))) { pa_log_warn("Volume and channel map don't match in database entry for device %s", name); goto fail; } return e; fail: pa_datum_free(&data); return NULL; }
void pa_shm_free(pa_shm *m) { pa_assert(m); pa_assert(m->ptr); pa_assert(m->size > 0); #ifdef MAP_FAILED pa_assert(m->ptr != MAP_FAILED); #endif if (m->type == PA_MEM_TYPE_PRIVATE) { privatemem_free(m); goto finish; } #if defined(HAVE_SHM_OPEN) || defined(HAVE_MEMFD) if (munmap(m->ptr, PA_PAGE_ALIGN(m->size)) < 0) pa_log("munmap() failed: %s", pa_cstrerror(errno)); #ifdef HAVE_SHM_OPEN if (m->type == PA_MEM_TYPE_SHARED_POSIX && m->do_unlink) { char fn[32]; segment_name(fn, sizeof(fn), m->id); if (shm_unlink(fn) < 0) pa_log(" shm_unlink(%s) failed: %s", fn, pa_cstrerror(errno)); } #endif #ifdef HAVE_MEMFD if (m->type == PA_MEM_TYPE_SHARED_MEMFD && m->fd != -1) pa_assert_se(pa_close(m->fd) == 0); #endif #else /* We shouldn't be here without shm or memfd support */ pa_assert_not_reached(); #endif finish: pa_zero(*m); }
void pa_shm_free(pa_shm *m) { pa_assert(m); pa_assert(m->ptr); pa_assert(m->size > 0); #ifdef MAP_FAILED pa_assert(m->ptr != MAP_FAILED); #endif if (!m->shared) { #ifdef MAP_ANONYMOUS if (munmap(m->ptr, m->size) < 0) pa_log("munmap() failed: %s", pa_cstrerror(errno)); #elif defined(HAVE_POSIX_MEMALIGN) free(m->ptr); #else pa_xfree(m->ptr); #endif } else { #ifdef HAVE_SHM_OPEN if (munmap(m->ptr, PA_PAGE_ALIGN(m->size)) < 0) pa_log("munmap() failed: %s", pa_cstrerror(errno)); if (m->do_unlink) { char fn[32]; segment_name(fn, sizeof(fn), m->id); if (shm_unlink(fn) < 0) pa_log(" shm_unlink(%s) failed: %s", fn, pa_cstrerror(errno)); } #else /* We shouldn't be here without shm support */ pa_assert_not_reached(); #endif } pa_zero(*m); }
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; }
/* This is called whenever the context status changes */ static void context_state_callback(pa_context *c, void *userdata) { pa_assert(c); switch (pa_context_get_state(c)) { case PA_CONTEXT_CONNECTING: case PA_CONTEXT_AUTHORIZING: case PA_CONTEXT_SETTING_NAME: break; case PA_CONTEXT_READY: { pa_buffer_attr buffer_attr; pa_assert(c); pa_assert(!stream); if (verbose) pa_log(_("Connection established.%s"), CLEAR_LINE); if (!(stream = pa_stream_new_with_proplist(c, NULL, &sample_spec, &channel_map, proplist))) { pa_log(_("pa_stream_new() failed: %s"), pa_strerror(pa_context_errno(c))); goto fail; } 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); pa_stream_set_underflow_callback(stream, stream_underflow_callback, NULL); pa_stream_set_overflow_callback(stream, stream_overflow_callback, NULL); pa_stream_set_started_callback(stream, stream_started_callback, NULL); pa_stream_set_event_callback(stream, stream_event_callback, NULL); pa_stream_set_buffer_attr_callback(stream, stream_buffer_attr_callback, NULL); pa_zero(buffer_attr); buffer_attr.maxlength = (uint32_t) -1; buffer_attr.prebuf = (uint32_t) -1; if (latency_msec > 0) { buffer_attr.fragsize = buffer_attr.tlength = pa_usec_to_bytes(latency_msec * PA_USEC_PER_MSEC, &sample_spec); flags |= PA_STREAM_ADJUST_LATENCY; } else if (latency > 0) { buffer_attr.fragsize = buffer_attr.tlength = (uint32_t) latency; flags |= PA_STREAM_ADJUST_LATENCY; } else buffer_attr.fragsize = buffer_attr.tlength = (uint32_t) -1; if (process_time_msec > 0) { buffer_attr.minreq = pa_usec_to_bytes(process_time_msec * PA_USEC_PER_MSEC, &sample_spec); } else if (process_time > 0) buffer_attr.minreq = (uint32_t) process_time; else buffer_attr.minreq = (uint32_t) -1; if (mode == PLAYBACK) { pa_cvolume cv; if (pa_stream_connect_playback(stream, device, &buffer_attr, flags, volume_is_set ? pa_cvolume_set(&cv, sample_spec.channels, volume) : NULL, NULL) < 0) { pa_log(_("pa_stream_connect_playback() failed: %s"), pa_strerror(pa_context_errno(c))); goto fail; } } else { if (pa_stream_connect_record(stream, device, latency > 0 ? &buffer_attr : NULL, flags) < 0) { pa_log(_("pa_stream_connect_record() failed: %s"), pa_strerror(pa_context_errno(c))); goto fail; } } break; } case PA_CONTEXT_TERMINATED: quit(0); break; case PA_CONTEXT_FAILED: default: pa_log(_("Connection failure: %s"), pa_strerror(pa_context_errno(c))); goto fail; } return; fail: quit(1); }
static void ext_device_restore_read_device_formats_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { pa_operation *o = userdata; int eol = 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, FALSE) < 0) goto finish; eol = -1; } else { uint8_t j; while (!pa_tagstruct_eof(t)) { pa_ext_device_restore_info i; pa_zero(i); if (pa_tagstruct_getu32(t, &i.type) < 0 || pa_tagstruct_getu32(t, &i.index) < 0 || pa_tagstruct_getu8(t, &i.n_formats) < 0) { pa_context_fail(o->context, PA_ERR_PROTOCOL); goto finish; } if (PA_DEVICE_TYPE_SINK != i.type && PA_DEVICE_TYPE_SOURCE != i.type) { pa_context_fail(o->context, PA_ERR_PROTOCOL); goto finish; } if (i.index == PA_INVALID_INDEX) { pa_context_fail(o->context, PA_ERR_PROTOCOL); goto finish; } if (i.n_formats > 0) { i.formats = pa_xnew0(pa_format_info*, i.n_formats); for (j = 0; j < i.n_formats; j++) { pa_format_info *f = i.formats[j] = pa_format_info_new(); if (pa_tagstruct_get_format_info(t, f) < 0) { uint8_t k; pa_context_fail(o->context, PA_ERR_PROTOCOL); for (k = 0; k < j+1; k++) pa_format_info_free(i.formats[k]); pa_xfree(i.formats); goto finish; } } } if (o->callback) { pa_ext_device_restore_read_device_formats_cb_t cb = (pa_ext_device_restore_read_device_formats_cb_t) o->callback; cb(o->context, &i, 0, o->userdata); } for (j = 0; j < i.n_formats; j++) pa_format_info_free(i.formats[j]); pa_xfree(i.formats); } }
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; }
static void server_info_cb(pa_context *c, const pa_server_info *info, void *userdata) { struct sound_dev **pDevices = userdata; pa_buffer_attr rec_ba; pa_buffer_attr play_ba; pa_sample_spec ss; pa_sample_spec default_ss; pa_stream_flags_t pb_flags = PA_STREAM_NOFLAGS; pa_stream_flags_t rec_flags = PA_STREAM_ADJUST_LATENCY; default_ss = info->sample_spec; printf("Connected to %s \n", info->host_name); while(*pDevices) { struct sound_dev *dev = *pDevices++; const char *dev_name; pa_stream *s; pa_zero(rec_ba); pa_zero(play_ba); if (dev->name[5] == ':') dev_name = dev->name + 6; // the device name is given; "pulse:alsa_input.pci-0000_00_1b.0.analog-stereo" else dev_name = NULL; // the device name is "pulse" for the default device if (quisk_sound_state.verbose_pulse) printf("Opening Device %s ", dev_name); //Construct sample specification. Use S16LE if availiable. Default to Float32 for others. //If the source/sink is not Float32, Pulseaudio will convert it (uses CPU) //dev->sample_bytes = (int)pa_frame_size(&ss) / ss.channels; if (default_ss.format == PA_SAMPLE_S16LE) { dev->sample_bytes = 2; ss.format = default_ss.format; } else { dev->sample_bytes = 4; ss.format = PA_SAMPLE_FLOAT32LE; } ss.rate = dev->sample_rate; ss.channels = dev->num_channels; rec_ba.maxlength = (uint32_t) -1; rec_ba.fragsize = (uint32_t) SAMP_BUFFER_SIZE / 16; //higher numbers eat cpu on reading monitor streams. play_ba.maxlength = (uint32_t) -1; play_ba.prebuf = (uint32_t) (dev->sample_bytes * ss.channels * dev->latency_frames); //play_ba.tlength = (uint32_t) -1; play_ba.tlength = play_ba.prebuf; if (dev->latency_frames == 0) play_ba.minreq = (uint32_t) 0; //verify this is sane else play_ba.minreq = (uint32_t) -1; if (dev->stream_dir_record) { if (!(s = pa_stream_new(c, dev->stream_description, &ss, NULL))) { printf("pa_stream_new() failed: %s", pa_strerror(pa_context_errno(c))); exit(1); } if (pa_stream_connect_record(s, dev_name, &rec_ba, rec_flags) < 0) { printf("pa_stream_connect_record() failed: %s", pa_strerror(pa_context_errno(c))); exit(1); } pa_stream_set_overflow_callback(s, stream_overflow_callback, dev); } else { pa_cvolume cv; pa_volume_t volume = PA_VOLUME_NORM; if (!(s = pa_stream_new(c, dev->stream_description, &ss, NULL))) { printf("pa_stream_new() failed: %s", pa_strerror(pa_context_errno(c))); exit(1); } if (pa_stream_connect_playback(s, dev_name, &play_ba, pb_flags, pa_cvolume_set(&cv, ss.channels, volume), NULL) < 0) { printf("pa_stream_connect_playback() failed: %s", pa_strerror(pa_context_errno(c))); exit(1); } pa_stream_set_underflow_callback(s, stream_underflow_callback, dev); } pa_stream_set_state_callback(s, stream_state_callback, dev); pa_stream_set_started_callback(s, stream_started_callback, dev); dev->handle = (void*)s; //save memory address for stream in handle int i; for(i=0;i < PA_LIST_SIZE;i++) { //save address for stream for easy exit if (!(OpenPulseDevices[i])) { OpenPulseDevices[i] = dev->handle; break; } } } }
void pa_datum_free(pa_datum *d) { pa_assert(d); free(d->data); /* gdbm uses raw malloc/free hence we should do that here, too */ pa_zero(d); }
int main(int argc, char*argv[]) { pid_t pid; int fd = -1; int ret = 1, i; struct sockaddr_un sa; char *ibuf = NULL; char *obuf = NULL; size_t buf_size, ibuf_size, ibuf_index, ibuf_length, obuf_size, obuf_index, obuf_length; char *cli; bool ibuf_eof, obuf_eof, ibuf_closed, obuf_closed; struct pollfd pollfd[3]; struct pollfd *watch_socket, *watch_stdin, *watch_stdout; int stdin_type = 0, stdout_type = 0, fd_type = 0; char *bn = NULL; int c; static const struct option long_options[] = { {"version", 0, NULL, ARG_VERSION}, {"help", 0, NULL, 'h'}, {NULL, 0, NULL, 0} }; setlocale(LC_ALL, ""); #ifdef ENABLE_NLS bindtextdomain(GETTEXT_PACKAGE, PULSE_LOCALEDIR); #endif bn = pa_path_get_filename(argv[0]); while ((c = getopt_long(argc, argv, "h", long_options, NULL)) != -1) { switch (c) { case 'h' : help(bn); ret = 0; goto quit; case ARG_VERSION: printf(_("pacmd %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; default: goto quit; } } if (pa_pid_file_check_running(&pid, "pulseaudio") < 0) { pa_log(_("No PulseAudio daemon running, or not running as session daemon.")); goto quit; } if ((fd = pa_socket_cloexec(PF_UNIX, SOCK_STREAM, 0)) < 0) { pa_log(_("socket(PF_UNIX, SOCK_STREAM, 0): %s"), strerror(errno)); goto quit; } pa_zero(sa); sa.sun_family = AF_UNIX; if (!(cli = pa_runtime_path("cli"))) goto quit; pa_strlcpy(sa.sun_path, cli, sizeof(sa.sun_path)); pa_xfree(cli); for (i = 0; i < 5; i++) { int r; if ((r = connect(fd, (struct sockaddr*) &sa, sizeof(sa))) < 0 && (errno != ECONNREFUSED && errno != ENOENT)) { pa_log(_("connect(): %s"), strerror(errno)); goto quit; } if (r >= 0) break; if (pa_pid_file_kill(SIGUSR2, NULL, "pulseaudio") < 0) { pa_log(_("Failed to kill PulseAudio daemon.")); goto quit; } pa_msleep(300); } if (i >= 5) { pa_log(_("Daemon not responding.")); goto quit; } buf_size = pa_pipe_buf(fd); ibuf_size = PA_MIN(buf_size, pa_pipe_buf(STDIN_FILENO)); ibuf = pa_xmalloc(ibuf_size); obuf_size = PA_MIN(buf_size, pa_pipe_buf(STDOUT_FILENO)); obuf = pa_xmalloc(obuf_size); ibuf_index = ibuf_length = obuf_index = obuf_length = 0; ibuf_eof = obuf_eof = ibuf_closed = obuf_closed = false; if (argc > 1) { for (i = 1; i < argc; i++) { size_t k; k = PA_MIN(ibuf_size - ibuf_length, strlen(argv[i])); memcpy(ibuf + ibuf_length, argv[i], k); ibuf_length += k; if (ibuf_length < ibuf_size) { ibuf[ibuf_length] = i < argc-1 ? ' ' : '\n'; ibuf_length++; } } ibuf_eof = true; } if (!ibuf_eof && isatty(STDIN_FILENO)) { /* send hello to enable interactive mode (welcome message, prompt) */ if (pa_write(fd, "hello\n", 6, &fd_type) < 0) { pa_log(_("write(): %s"), strerror(errno)); goto quit; } } for (;;) { struct pollfd *p; if (ibuf_eof && obuf_eof && ibuf_length <= 0 && obuf_length <= 0) break; if (ibuf_length <= 0 && ibuf_eof && !ibuf_closed) { shutdown(fd, SHUT_WR); ibuf_closed = true; } if (obuf_length <= 0 && obuf_eof && !obuf_closed) { shutdown(fd, SHUT_RD); obuf_closed = true; } pa_zero(pollfd); p = pollfd; if (ibuf_length > 0 || (!obuf_eof && obuf_length <= 0)) { watch_socket = p++; watch_socket->fd = fd; watch_socket->events = (ibuf_length > 0 ? POLLOUT : 0) | (!obuf_eof && obuf_length <= 0 ? POLLIN : 0); } else watch_socket = NULL; if (!ibuf_eof && ibuf_length <= 0) { watch_stdin = p++; watch_stdin->fd = STDIN_FILENO; watch_stdin->events = POLLIN; } else watch_stdin = NULL; if (obuf_length > 0) { watch_stdout = p++; watch_stdout->fd = STDOUT_FILENO; watch_stdout->events = POLLOUT; } else watch_stdout = NULL; if (pa_poll(pollfd, p-pollfd, -1) < 0) { if (errno == EINTR) continue; pa_log(_("poll(): %s"), strerror(errno)); goto quit; } if (watch_stdin) { if (watch_stdin->revents & POLLIN) { ssize_t r; pa_assert(ibuf_length <= 0); if ((r = pa_read(STDIN_FILENO, ibuf, ibuf_size, &stdin_type)) <= 0) { if (r < 0) { pa_log(_("read(): %s"), strerror(errno)); goto quit; } ibuf_eof = true; } else { ibuf_length = (size_t) r; ibuf_index = 0; } } else if (watch_stdin->revents & POLLHUP) ibuf_eof = true; } if (watch_socket) { if (watch_socket->revents & POLLIN) { ssize_t r; pa_assert(obuf_length <= 0); if ((r = pa_read(fd, obuf, obuf_size, &fd_type)) <= 0) { if (r < 0) { pa_log(_("read(): %s"), strerror(errno)); goto quit; } obuf_eof = true; } else { obuf_length = (size_t) r; obuf_index = 0; } } else if (watch_socket->revents & POLLHUP) obuf_eof = true; } if (watch_stdout) { if (watch_stdout->revents & POLLHUP) { obuf_eof = true; obuf_length = 0; } else if (watch_stdout->revents & POLLOUT) { ssize_t r; pa_assert(obuf_length > 0); if ((r = pa_write(STDOUT_FILENO, obuf + obuf_index, obuf_length, &stdout_type)) < 0) { pa_log(_("write(): %s"), strerror(errno)); goto quit; } obuf_length -= (size_t) r; obuf_index += obuf_index; } } if (watch_socket) { if (watch_socket->revents & POLLHUP) { ibuf_eof = true; ibuf_length = 0; } if (watch_socket->revents & POLLOUT) { ssize_t r; pa_assert(ibuf_length > 0); if ((r = pa_write(fd, ibuf + ibuf_index, ibuf_length, &fd_type)) < 0) { pa_log(_("write(): %s"), strerror(errno)); goto quit; } ibuf_length -= (size_t) r; ibuf_index += obuf_index; } } } ret = 0; quit: if (fd >= 0) pa_close(fd); pa_xfree(obuf); pa_xfree(ibuf); return ret; }
/* * Iterate the main loop while recording is on. * This function runs under it's own thread called by audio_pulse_start * args: * data - pointer to user data (audio context) * * asserts: * data is not null * * returns: pointer to error code */ static void *pulse_read_audio(void *data) { audio_context_t *audio_ctx = (audio_context_t *) data; /*assertions*/ assert(audio_ctx != NULL); if(verbosity > 0) printf("AUDIO: (pulseaudio) read thread started\n"); pa_mainloop *pa_ml; pa_mainloop_api *pa_mlapi; pa_buffer_attr bufattr; pa_sample_spec ss; pa_stream_flags_t flags = 0; int r; int pa_ready = 0; /* Create a mainloop API and connection to the default server */ pa_ml = pa_mainloop_new(); pa_mlapi = pa_mainloop_get_api(pa_ml); pa_ctx = pa_context_new(pa_mlapi, "guvcview Pulse API"); if(pa_context_connect(pa_ctx, NULL, 0, NULL) < 0) { fprintf(stderr,"AUDIO: PULSE - unable to connect to server: pa_context_connect failed\n"); finish(pa_ctx, pa_ml); return ((void *) -1); } /* * This function defines a callback so the server will tell us it's state. * Our callback will wait for the state to be ready. The callback will * modify the variable to 1 so we know when we have a connection and it's * ready. * If there's an error, the callback will set pa_ready to 2 */ pa_context_set_state_callback(pa_ctx, pa_state_cb, &pa_ready); /* * This function defines a time event callback (called every TIME_EVENT_USEC) */ //pa_context_rttime_new(pa_ctx, pa_rtclock_now() + TIME_EVENT_USEC, time_event_callback, NULL); /* * We can't do anything until PA is ready, so just iterate the mainloop * and continue */ while (pa_ready == 0) { pa_mainloop_iterate(pa_ml, 1, NULL); } if (pa_ready == 2) { finish(pa_ctx, pa_ml); return ((void *) -1); } /* set the sample spec (frame rate, channels and format) */ ss.rate = audio_ctx->samprate; ss.channels = audio_ctx->channels; ss.format = PA_SAMPLE_FLOAT32LE; /*for PCM -> PA_SAMPLE_S16LE*/ recordstream = pa_stream_new(pa_ctx, "Record", &ss, NULL); if (!recordstream) fprintf(stderr, "AUDIO: (pulseaudio) pa_stream_new failed (chan:%d rate:%d)\n", ss.channels, ss.rate); /* define the callbacks */ pa_stream_set_read_callback(recordstream, stream_request_cb, (void *) audio_ctx); // Set properties of the record buffer pa_zero(bufattr); /* optimal value for all is (uint32_t)-1 ~= 2 sec */ bufattr.maxlength = (uint32_t) -1; bufattr.prebuf = (uint32_t) -1; bufattr.minreq = (uint32_t) -1; if (audio_ctx->latency > 0) { bufattr.fragsize = bufattr.tlength = pa_usec_to_bytes((audio_ctx->latency * 1000) * PA_USEC_PER_MSEC, &ss); flags |= PA_STREAM_ADJUST_LATENCY; } else bufattr.fragsize = bufattr.tlength = (uint32_t) -1; flags |= PA_STREAM_INTERPOLATE_TIMING; flags |= PA_STREAM_AUTO_TIMING_UPDATE; char * dev = audio_ctx->list_devices[audio_ctx->device].name; if(verbosity > 0) printf("AUDIO: (pulseaudio) connecting to device %s\n\t (channels %d rate %d)\n", dev, ss.channels, ss.rate); r = pa_stream_connect_record(recordstream, dev, &bufattr, flags); if (r < 0) { fprintf(stderr, "AUDIO: (pulseaudio) skip latency adjustment\n"); /* * Old pulse audio servers don't like the ADJUST_LATENCY flag, * so retry without that */ r = pa_stream_connect_record(recordstream, dev, &bufattr, PA_STREAM_INTERPOLATE_TIMING| PA_STREAM_AUTO_TIMING_UPDATE); } if (r < 0) { fprintf(stderr, "AUDIO: (pulseaudio) pa_stream_connect_record failed\n"); finish(pa_ctx, pa_ml); return ((void *) -1); } get_latency(recordstream); /* * Iterate the main loop while streaming. The second argument is whether * or not the iteration should block until something is ready to be * done. Set it to zero for non-blocking. */ while (audio_ctx->stream_flag == AUDIO_STRM_ON) { pa_mainloop_iterate(pa_ml, 1, NULL); } if(verbosity > 0) printf("AUDIO: (pulseaudio) stream terminated(%i)\n", audio_ctx->stream_flag); pa_stream_disconnect (recordstream); pa_stream_unref (recordstream); finish(pa_ctx, pa_ml); return ((void *) 0); }
static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) { struct userdata *u = userdata; struct entry entry, *old; char *name; pa_datum key, data; pa_assert(c); pa_assert(u); if (t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW) && t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE) && t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW) && t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE)) return; pa_zero(entry); entry.version = ENTRY_VERSION; if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) { pa_sink *sink; if (!(sink = pa_idxset_get_by_index(c->sinks, idx))) return; name = pa_sprintf_malloc("sink:%s", sink->name); if ((old = read_entry(u, name))) entry = *old; if (sink->save_volume) { entry.channel_map = sink->channel_map; entry.volume = *pa_sink_get_volume(sink, FALSE); entry.volume_valid = TRUE; } if (sink->save_muted) { entry.muted = pa_sink_get_mute(sink, FALSE); entry.muted_valid = TRUE; } if (sink->save_port) { pa_strlcpy(entry.port, sink->active_port ? sink->active_port->name : "", sizeof(entry.port)); entry.port_valid = TRUE; } } else { pa_source *source; pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE); if (!(source = pa_idxset_get_by_index(c->sources, idx))) return; name = pa_sprintf_malloc("source:%s", source->name); if ((old = read_entry(u, name))) entry = *old; if (source->save_volume) { entry.channel_map = source->channel_map; entry.volume = *pa_source_get_volume(source, FALSE); entry.volume_valid = TRUE; } if (source->save_muted) { entry.muted = pa_source_get_mute(source, FALSE); entry.muted_valid = TRUE; } if (source->save_port) { pa_strlcpy(entry.port, source->active_port ? source->active_port->name : "", sizeof(entry.port)); entry.port_valid = TRUE; } } if (old) { if (entries_equal(old, &entry)) { pa_xfree(old); pa_xfree(name); return; } pa_xfree(old); } key.data = name; key.size = strlen(name); data.data = &entry; data.size = sizeof(entry); pa_log_info("Storing volume/mute/port for device %s.", name); pa_database_set(u->database, &key, &data, TRUE); pa_xfree(name); trigger_save(u); }
/* This is called whenever the context status changes */ static void context_state_callback(pa_context *c, void *userdata) { fail_unless(c != NULL); switch (pa_context_get_state(c)) { case PA_CONTEXT_CONNECTING: case PA_CONTEXT_AUTHORIZING: case PA_CONTEXT_SETTING_NAME: break; case PA_CONTEXT_READY: { pa_stream_flags_t flags = PA_STREAM_AUTO_TIMING_UPDATE; pa_buffer_attr attr; static const pa_sample_spec ss = { .format = PA_SAMPLE_S16LE, .rate = 44100, .channels = 2 }; pa_zero(attr); attr.maxlength = (uint32_t) -1; attr.tlength = latency > 0 ? (uint32_t) pa_usec_to_bytes(latency, &ss) : (uint32_t) -1; attr.prebuf = (uint32_t) -1; attr.minreq = (uint32_t) -1; attr.fragsize = (uint32_t) -1; #ifdef INTERPOLATE flags |= PA_STREAM_INTERPOLATE_TIMING; #endif if (latency > 0) flags |= PA_STREAM_ADJUST_LATENCY; pa_log("Connection established"); stream = pa_stream_new(c, "interpol-test", &ss, NULL); fail_unless(stream != NULL); if (playback) { pa_assert_se(pa_stream_connect_playback(stream, NULL, &attr, flags, NULL, NULL) == 0); pa_stream_set_write_callback(stream, stream_write_cb, NULL); } else { pa_assert_se(pa_stream_connect_record(stream, NULL, &attr, flags) == 0); pa_stream_set_read_callback(stream, stream_read_cb, NULL); } pa_stream_set_latency_update_callback(stream, stream_latency_cb, NULL); break; } case PA_CONTEXT_TERMINATED: break; case PA_CONTEXT_FAILED: default: pa_log_error("Context error: %s", pa_strerror(pa_context_errno(c))); ck_abort(); } } START_TEST (interpol_test) { pa_threaded_mainloop* m = NULL; int k; struct timeval start, last_info = { 0, 0 }; pa_usec_t old_t = 0, old_rtc = 0; #ifdef CORK bool corked = false; #endif /* Set up a new main loop */ m = pa_threaded_mainloop_new(); fail_unless(m != NULL); mainloop_api = pa_threaded_mainloop_get_api(m); fail_unless(mainloop_api != NULL); context = pa_context_new(mainloop_api, bname); fail_unless(context != NULL); pa_context_set_state_callback(context, context_state_callback, NULL); fail_unless(pa_context_connect(context, NULL, 0, NULL) >= 0); pa_gettimeofday(&start); fail_unless(pa_threaded_mainloop_start(m) >= 0); /* #ifdef CORK */ for (k = 0; k < 20000; k++) /* #else */ /* for (k = 0; k < 2000; k++) */ /* #endif */ { bool success = false, changed = false; pa_usec_t t, rtc, d; struct timeval now, tv; bool playing = false; pa_threaded_mainloop_lock(m); if (stream) { const pa_timing_info *info; if (pa_stream_get_time(stream, &t) >= 0 && pa_stream_get_latency(stream, &d, NULL) >= 0) success = true; if ((info = pa_stream_get_timing_info(stream))) { if (memcmp(&last_info, &info->timestamp, sizeof(struct timeval))) { changed = true; last_info = info->timestamp; } if (info->playing) playing = true; } } pa_threaded_mainloop_unlock(m); pa_gettimeofday(&now); if (success) { #ifdef CORK bool cork_now; #endif rtc = pa_timeval_diff(&now, &start); pa_log_info("%i\t%llu\t%llu\t%llu\t%llu\t%lli\t%u\t%u\t%llu\t%llu\n", k, (unsigned long long) rtc, (unsigned long long) t, (unsigned long long) (rtc-old_rtc), (unsigned long long) (t-old_t), (signed long long) rtc - (signed long long) t, changed, playing, (unsigned long long) latency, (unsigned long long) d); fflush(stdout); old_t = t; old_rtc = rtc; #ifdef CORK cork_now = (rtc / (2*PA_USEC_PER_SEC)) % 2 == 1; if (corked != cork_now) { pa_threaded_mainloop_lock(m); pa_operation_unref(pa_stream_cork(stream, cork_now, NULL, NULL)); pa_threaded_mainloop_unlock(m); pa_log(cork_now ? "Corking" : "Uncorking"); corked = cork_now; } #endif } /* Spin loop, ugly but normal usleep() is just too badly grained */ tv = now; while (pa_timeval_diff(pa_gettimeofday(&now), &tv) < 1000) pa_thread_yield(); } if (m) pa_threaded_mainloop_stop(m); if (stream) { pa_stream_disconnect(stream); pa_stream_unref(stream); } if (context) { pa_context_disconnect(context); pa_context_unref(context); } if (m) pa_threaded_mainloop_free(m); } END_TEST int main(int argc, char *argv[]) { int failed = 0; Suite *s; TCase *tc; SRunner *sr; if (!getenv("MAKE_CHECK")) pa_log_set_level(PA_LOG_DEBUG); bname = argv[0]; playback = argc <= 1 || !pa_streq(argv[1], "-r"); latency = (argc >= 2 && !pa_streq(argv[1], "-r")) ? atoi(argv[1]) : (argc >= 3 ? atoi(argv[2]) : 0); s = suite_create("Interpol"); tc = tcase_create("interpol"); tcase_add_test(tc, interpol_test); tcase_set_timeout(tc, 5 * 60); suite_add_tcase(s, tc); sr = srunner_create(s); srunner_run_all(sr, CK_NORMAL); failed = srunner_ntests_failed(sr); srunner_free(sr); return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; }
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; }
// This callback gets called when our context changes state. We really only // care about when it's ready or if it has failed void state_cb(pa_context *c, void *userdata) { pa_context_state_t state; int *pa_ready = userdata; printf("State changed\n"); state = pa_context_get_state(c); switch (state) { // There are just here for reference case PA_CONTEXT_UNCONNECTED: case PA_CONTEXT_CONNECTING: case PA_CONTEXT_AUTHORIZING: case PA_CONTEXT_SETTING_NAME: default: break; case PA_CONTEXT_FAILED: case PA_CONTEXT_TERMINATED: *pa_ready = 2; break; case PA_CONTEXT_READY: { pa_buffer_attr buffer_attr; if (verbose) printf("Connection established.%s\n", CLEAR_LINE); if (!(stream = pa_stream_new(c, "JanPlayback", &sample_spec, NULL))) { printf("pa_stream_new() failed: %s", pa_strerror(pa_context_errno(c))); exit(1); // goto fail; } 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); pa_stream_set_underflow_callback(stream, stream_underflow_callback, NULL); pa_stream_set_overflow_callback(stream, stream_overflow_callback, NULL); pa_stream_set_started_callback(stream, stream_started_callback, NULL); pa_stream_set_event_callback(stream, stream_event_callback, NULL); pa_stream_set_buffer_attr_callback(stream, stream_buffer_attr_callback, NULL); pa_zero(buffer_attr); buffer_attr.maxlength = (uint32_t) -1; buffer_attr.prebuf = (uint32_t) -1; pa_cvolume cv; if (pa_stream_connect_playback(stream, NULL, &buffer_attr, flags, NULL, NULL) < 0) { printf("pa_stream_connect_playback() failed: %s", pa_strerror(pa_context_errno(c))); exit(1); //goto fail; } else { printf("Set playback callback\n"); } pa_stream_trigger(stream, stream_success, NULL); } break; } }
static void inotify_cb( pa_mainloop_api*a, pa_io_event* e, int fd, pa_io_event_flags_t events, void *userdata) { struct { struct inotify_event e; char name[NAME_MAX]; } buf; struct userdata *u = userdata; static int type = 0; bool deleted = false; struct device *d; void *state; for (;;) { ssize_t r; struct inotify_event *event; pa_zero(buf); if ((r = pa_read(fd, &buf, sizeof(buf), &type)) <= 0) { if (r < 0 && errno == EAGAIN) break; pa_log("read() from inotify failed: %s", r < 0 ? pa_cstrerror(errno) : "EOF"); goto fail; } event = &buf.e; while (r > 0) { size_t len; if ((size_t) r < sizeof(struct inotify_event)) { pa_log("read() too short."); goto fail; } len = sizeof(struct inotify_event) + event->len; if ((size_t) r < len) { pa_log("Payload missing."); goto fail; } /* From udev we get the guarantee that the control * device's ACL is changed last. To avoid races when ACLs * are changed we hence watch only the control device */ if (((event->mask & IN_ATTRIB) && pa_startswith(event->name, "controlC"))) PA_HASHMAP_FOREACH(d, u->devices, state) if (control_node_belongs_to_device(d, event->name)) d->need_verify = true; /* ALSA doesn't really give us any guarantee on the closing * order, so let's simply hope */ if (((event->mask & IN_CLOSE_WRITE) && pa_startswith(event->name, "pcmC"))) PA_HASHMAP_FOREACH(d, u->devices, state) if (pcm_node_belongs_to_device(d, event->name)) d->need_verify = true; /* /dev/snd/ might have been removed */ if ((event->mask & (IN_DELETE_SELF|IN_MOVE_SELF))) deleted = true; event = (struct inotify_event*) ((uint8_t*) event + len); r -= len; } } PA_HASHMAP_FOREACH(d, u->devices, state) if (d->need_verify) { d->need_verify = false; verify_access(u, d); } if (!deleted) return; fail: if (u->inotify_io) { a->io_free(u->inotify_io); u->inotify_io = NULL; } if (u->inotify_fd >= 0) { pa_close(u->inotify_fd); u->inotify_fd = -1; } }
ssize_t pa_iochannel_read_with_ancil_data(pa_iochannel*io, void*data, size_t l, pa_cmsg_ancil_data *ancil_data) { ssize_t r; struct msghdr mh; struct iovec iov; union { struct cmsghdr hdr; uint8_t data[CMSG_SPACE(sizeof(struct ucred)) + CMSG_SPACE(sizeof(int) * MAX_ANCIL_DATA_FDS)]; } cmsg; pa_assert(io); pa_assert(data); pa_assert(l); pa_assert(io->ifd >= 0); pa_assert(ancil_data); if (io->ifd_type > 0) { ancil_data->creds_valid = false; ancil_data->nfd = 0; return pa_iochannel_read(io, data, l); } iov.iov_base = data; iov.iov_len = l; pa_zero(mh); mh.msg_iov = &iov; mh.msg_iovlen = 1; mh.msg_control = &cmsg; mh.msg_controllen = sizeof(cmsg); if ((r = recvmsg(io->ifd, &mh, 0)) >= 0) { struct cmsghdr *cmh; ancil_data->creds_valid = false; ancil_data->nfd = 0; for (cmh = CMSG_FIRSTHDR(&mh); cmh; cmh = CMSG_NXTHDR(&mh, cmh)) { if (cmh->cmsg_level != SOL_SOCKET) continue; if (cmh->cmsg_type == SCM_CREDENTIALS) { struct ucred u; pa_assert(cmh->cmsg_len == CMSG_LEN(sizeof(struct ucred))); memcpy(&u, CMSG_DATA(cmh), sizeof(struct ucred)); ancil_data->creds.gid = u.gid; ancil_data->creds.uid = u.uid; ancil_data->creds_valid = true; } else if (cmh->cmsg_type == SCM_RIGHTS) { int nfd = (cmh->cmsg_len - CMSG_LEN(0)) / sizeof(int); if (nfd > MAX_ANCIL_DATA_FDS) { int i; pa_log("Trying to receive too many file descriptors!"); for (i = 0; i < nfd; i++) pa_close(((int*) CMSG_DATA(cmh))[i]); continue; } memcpy(ancil_data->fds, CMSG_DATA(cmh), nfd * sizeof(int)); ancil_data->nfd = nfd; ancil_data->close_fds_on_cleanup = true; } } io->readable = io->hungup = false; enable_events(io); } if (r == -1 && errno == ENOTSOCK) { io->ifd_type = 1; return pa_iochannel_read_with_ancil_data(io, data, l, ancil_data); } return r; }