pa_channel_map* pa_channel_map_init_extend(pa_channel_map *m, unsigned channels, pa_channel_map_def_t def) { unsigned c; pa_assert(m); pa_assert(channels > 0); pa_assert(channels <= PA_CHANNELS_MAX); pa_assert(def < PA_CHANNEL_MAP_DEF_MAX); pa_channel_map_init(m); for (c = channels; c > 0; c--) { if (pa_channel_map_init_auto(m, c, def)) { unsigned i = 0; for (; c < channels; c++) { m->map[c] = PA_CHANNEL_POSITION_AUX0 + i; i++; } m->channels = (uint8_t) channels; return m; } } return NULL; }
static pa_channel_map * xmms_pulse_backend_default_channel_map (pa_channel_map *m, int channels) { assert(m); assert(channels > 0); assert(channels <= PA_CHANNELS_MAX); pa_channel_map_init(m); m->channels = (uint8_t) channels; switch (channels) { case 4: m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT; m->map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT; m->map[2] = PA_CHANNEL_POSITION_REAR_LEFT; m->map[3] = PA_CHANNEL_POSITION_REAR_RIGHT; return m; case 5: m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT; m->map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT; m->map[2] = PA_CHANNEL_POSITION_FRONT_CENTER; m->map[3] = PA_CHANNEL_POSITION_REAR_LEFT; m->map[4] = PA_CHANNEL_POSITION_REAR_RIGHT; return m; case 7: m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT; m->map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT; m->map[2] = PA_CHANNEL_POSITION_FRONT_CENTER; m->map[3] = PA_CHANNEL_POSITION_LFE; m->map[4] = PA_CHANNEL_POSITION_REAR_LEFT; m->map[5] = PA_CHANNEL_POSITION_REAR_RIGHT; m->map[6] = PA_CHANNEL_POSITION_REAR_CENTER; return m; case 8: m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT; m->map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT; m->map[2] = PA_CHANNEL_POSITION_FRONT_CENTER; m->map[3] = PA_CHANNEL_POSITION_LFE; m->map[4] = PA_CHANNEL_POSITION_REAR_LEFT; m->map[5] = PA_CHANNEL_POSITION_REAR_RIGHT; m->map[6] = PA_CHANNEL_POSITION_SIDE_LEFT; m->map[7] = PA_CHANNEL_POSITION_SIDE_RIGHT; return m; default: return pa_channel_map_init_auto (m, channels, PA_CHANNEL_MAP_WAVEEX); } }
static ALCboolean pulse_reset_playback(ALCdevice *device) //{{{ { pulse_data *data = device->ExtraData; pa_stream_flags_t flags = 0; pa_channel_map chanmap; pa_threaded_mainloop_lock(data->loop); if(!(device->Flags&DEVICE_CHANNELS_REQUEST)) { pa_operation *o; o = pa_context_get_sink_info_by_name(data->context, data->device_name, sink_info_callback, device); while(pa_operation_get_state(o) == PA_OPERATION_RUNNING) pa_threaded_mainloop_wait(data->loop); pa_operation_unref(o); } if(!(device->Flags&DEVICE_FREQUENCY_REQUEST)) flags |= PA_STREAM_FIX_RATE; data->frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType); data->attr.prebuf = -1; data->attr.fragsize = -1; data->attr.minreq = device->UpdateSize * data->frame_size; data->attr.tlength = data->attr.minreq * maxu(device->NumUpdates, 2); data->attr.maxlength = -1; flags |= PA_STREAM_EARLY_REQUESTS; flags |= PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE; switch(device->FmtType) { case DevFmtByte: device->FmtType = DevFmtUByte; /* fall-through */ case DevFmtUByte: data->spec.format = PA_SAMPLE_U8; break; case DevFmtUShort: device->FmtType = DevFmtShort; /* fall-through */ case DevFmtShort: data->spec.format = PA_SAMPLE_S16NE; break; case DevFmtFloat: data->spec.format = PA_SAMPLE_FLOAT32NE; break; } data->spec.rate = device->Frequency; data->spec.channels = ChannelsFromDevFmt(device->FmtChans); if(pa_sample_spec_valid(&data->spec) == 0) { ERR("Invalid sample format\n"); pa_threaded_mainloop_unlock(data->loop); return ALC_FALSE; } if(!pa_channel_map_init_auto(&chanmap, data->spec.channels, PA_CHANNEL_MAP_WAVEEX)) { ERR("Couldn't build map for channel count (%d)!\n", data->spec.channels); pa_threaded_mainloop_unlock(data->loop); return ALC_FALSE; } SetDefaultWFXChannelOrder(device); data->stream = connect_playback_stream(device, flags, &data->attr, &data->spec, &chanmap); if(!data->stream) { pa_threaded_mainloop_unlock(data->loop); return ALC_FALSE; } pa_stream_set_state_callback(data->stream, stream_state_callback2, device); data->spec = *(pa_stream_get_sample_spec(data->stream)); if(device->Frequency != data->spec.rate) { pa_operation *o; if((device->Flags&DEVICE_FREQUENCY_REQUEST)) ERR("Failed to set frequency %dhz, got %dhz instead\n", device->Frequency, data->spec.rate); device->Flags &= ~DEVICE_FREQUENCY_REQUEST; /* Server updated our playback rate, so modify the buffer attribs * accordingly. */ data->attr.minreq = (ALuint64)(data->attr.minreq/data->frame_size) * data->spec.rate / device->Frequency * data->frame_size; data->attr.tlength = data->attr.minreq * maxu(device->NumUpdates, 2); o = pa_stream_set_buffer_attr(data->stream, &data->attr, stream_success_callback, device); while(pa_operation_get_state(o) == PA_OPERATION_RUNNING) pa_threaded_mainloop_wait(data->loop); pa_operation_unref(o); device->Frequency = data->spec.rate; } #if PA_CHECK_VERSION(0,9,15) if(pa_stream_set_buffer_attr_callback) pa_stream_set_buffer_attr_callback(data->stream, stream_buffer_attr_callback, device); #endif pa_stream_set_moved_callback(data->stream, stream_device_callback, device); pa_stream_set_write_callback(data->stream, stream_write_callback, device); pa_stream_set_underflow_callback(data->stream, stream_signal_callback, device); data->attr = *(pa_stream_get_buffer_attr(data->stream)); ERR("PulseAudio returned minreq=%d, tlength=%d\n", data->attr.minreq, data->attr.tlength); device->UpdateSize = data->attr.minreq / data->frame_size; device->NumUpdates = (data->attr.tlength/data->frame_size) / device->UpdateSize; while(device->NumUpdates <= 2) { pa_operation *o; ERR("minreq too high - expect lag or break up\n"); /* Server gave a comparatively large minreq, so modify the tlength. */ device->NumUpdates = 2; data->attr.tlength = data->attr.minreq * device->NumUpdates; o = pa_stream_set_buffer_attr(data->stream, &data->attr, stream_success_callback, device); while(pa_operation_get_state(o) == PA_OPERATION_RUNNING) pa_threaded_mainloop_wait(data->loop); pa_operation_unref(o); data->attr = *(pa_stream_get_buffer_attr(data->stream)); ERR("PulseAudio returned minreq=%d, tlength=%d", data->attr.minreq, data->attr.tlength); device->UpdateSize = data->attr.minreq / data->frame_size; device->NumUpdates = (data->attr.tlength/data->frame_size) / device->UpdateSize; } data->thread = StartThread(PulseProc, device); if(!data->thread) { #if PA_CHECK_VERSION(0,9,15) if(pa_stream_set_buffer_attr_callback) pa_stream_set_buffer_attr_callback(data->stream, NULL, NULL); #endif pa_stream_set_moved_callback(data->stream, NULL, NULL); pa_stream_set_write_callback(data->stream, NULL, NULL); pa_stream_set_underflow_callback(data->stream, NULL, NULL); pa_stream_disconnect(data->stream); pa_stream_unref(data->stream); data->stream = NULL; pa_threaded_mainloop_unlock(data->loop); return ALC_FALSE; } pa_threaded_mainloop_unlock(data->loop); return ALC_TRUE; } //}}}
static ALCenum pulse_open_capture(ALCdevice *device, const ALCchar *device_name) //{{{ { char *pulse_name = NULL; pulse_data *data; pa_stream_flags_t flags = 0; pa_stream_state_t state; pa_channel_map chanmap; if(!allCaptureDevNameMap) probe_devices(AL_TRUE); if(!device_name) device_name = pulse_device; else if(strcmp(device_name, pulse_device) != 0) { ALuint i; for(i = 0;i < numCaptureDevNames;i++) { if(strcmp(device_name, allCaptureDevNameMap[i].name) == 0) { pulse_name = allCaptureDevNameMap[i].device_name; break; } } if(i == numCaptureDevNames) return ALC_INVALID_VALUE; } if(pulse_open(device, device_name) == ALC_FALSE) return ALC_INVALID_VALUE; data = device->ExtraData; pa_threaded_mainloop_lock(data->loop); data->samples = device->UpdateSize * device->NumUpdates; data->frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType); data->samples = maxu(data->samples, 100 * device->Frequency / 1000); if(!(data->ring = CreateRingBuffer(data->frame_size, data->samples))) { pa_threaded_mainloop_unlock(data->loop); goto fail; } data->attr.minreq = -1; data->attr.prebuf = -1; data->attr.maxlength = data->samples * data->frame_size; data->attr.tlength = -1; data->attr.fragsize = minu(data->samples, 50*device->Frequency/1000) * data->frame_size; data->spec.rate = device->Frequency; data->spec.channels = ChannelsFromDevFmt(device->FmtChans); switch(device->FmtType) { case DevFmtUByte: data->spec.format = PA_SAMPLE_U8; break; case DevFmtShort: data->spec.format = PA_SAMPLE_S16NE; break; case DevFmtFloat: data->spec.format = PA_SAMPLE_FLOAT32NE; break; case DevFmtByte: case DevFmtUShort: ERR("Capture format type %#x capture not supported on PulseAudio\n", device->FmtType); pa_threaded_mainloop_unlock(data->loop); goto fail; } if(pa_sample_spec_valid(&data->spec) == 0) { ERR("Invalid sample format\n"); pa_threaded_mainloop_unlock(data->loop); goto fail; } if(!pa_channel_map_init_auto(&chanmap, data->spec.channels, PA_CHANNEL_MAP_WAVEEX)) { ERR("Couldn't build map for channel count (%d)!\n", data->spec.channels); pa_threaded_mainloop_unlock(data->loop); goto fail; } data->stream = pa_stream_new(data->context, "Capture Stream", &data->spec, &chanmap); if(!data->stream) { ERR("pa_stream_new() failed: %s\n", pa_strerror(pa_context_errno(data->context))); pa_threaded_mainloop_unlock(data->loop); goto fail; } pa_stream_set_state_callback(data->stream, stream_state_callback, data->loop); flags |= PA_STREAM_START_CORKED|PA_STREAM_ADJUST_LATENCY; if(pa_stream_connect_record(data->stream, pulse_name, &data->attr, flags) < 0) { ERR("Stream did not connect: %s\n", pa_strerror(pa_context_errno(data->context))); pa_stream_unref(data->stream); data->stream = NULL; pa_threaded_mainloop_unlock(data->loop); goto fail; } while((state=pa_stream_get_state(data->stream)) != PA_STREAM_READY) { if(!PA_STREAM_IS_GOOD(state)) { ERR("Stream did not get ready: %s\n", pa_strerror(pa_context_errno(data->context))); pa_stream_unref(data->stream); data->stream = NULL; pa_threaded_mainloop_unlock(data->loop); goto fail; } pa_threaded_mainloop_wait(data->loop); } pa_stream_set_state_callback(data->stream, stream_state_callback2, device); pa_threaded_mainloop_unlock(data->loop); return ALC_NO_ERROR; fail: pulse_close(device); return ALC_INVALID_VALUE; } //}}}
bool AudioOutputPulseAudio::OpenDevice() { QString fn_log_tag = "OpenDevice, "; if (channels > PULSE_MAX_CHANNELS ) { VBERROR(fn_log_tag + QString("audio channel limit %1, but %2 requested") .arg(PULSE_MAX_CHANNELS).arg(channels)); return false; } sample_spec.rate = samplerate; sample_spec.channels = volume_control.channels = channels; switch (output_format) { case FORMAT_U8: sample_spec.format = PA_SAMPLE_U8; break; case FORMAT_S16: sample_spec.format = PA_SAMPLE_S16NE; break; // define from PA 0.9.15 only #ifdef PA_MAJOR case FORMAT_S24LSB: sample_spec.format = PA_SAMPLE_S24_32NE; break; #endif case FORMAT_S32: sample_spec.format = PA_SAMPLE_S32NE; break; case FORMAT_FLT: sample_spec.format = PA_SAMPLE_FLOAT32NE; break; break; default: VBERROR(fn_log_tag + QString("unsupported sample format %1") .arg(output_format)); return false; } if (!pa_sample_spec_valid(&sample_spec)) { VBERROR(fn_log_tag + "invalid sample spec"); return false; } else { char spec[PA_SAMPLE_SPEC_SNPRINT_MAX]; pa_sample_spec_snprint(spec, sizeof(spec), &sample_spec); VBAUDIO(fn_log_tag + QString("using sample spec %1").arg(spec)); } pa_channel_map *pmap = NULL; if(!(pmap = pa_channel_map_init_auto(&channel_map, channels, PA_CHANNEL_MAP_WAVEEX)) < 0) { VBERROR(fn_log_tag + "failed to init channel map"); return false; } channel_map = *pmap; mainloop = pa_threaded_mainloop_new(); if (!mainloop) { VBERROR(fn_log_tag + "failed to get new threaded mainloop"); return false; } pa_threaded_mainloop_start(mainloop); pa_threaded_mainloop_lock(mainloop); if (!ContextConnect()) { pa_threaded_mainloop_unlock(mainloop); pa_threaded_mainloop_stop(mainloop); return false; } if (!ConnectPlaybackStream()) { pa_threaded_mainloop_unlock(mainloop); pa_threaded_mainloop_stop(mainloop); return false; } pa_threaded_mainloop_unlock(mainloop); return true; }
int pa__init(pa_module*m) { struct userdata *u; pa_modargs *ma = NULL; const char *dst_addr; const char *src_addr; uint32_t port = DEFAULT_PORT, mtu; uint32_t ttl = DEFAULT_TTL; sa_family_t af; int fd = -1, sap_fd = -1; pa_source *s; pa_sample_spec ss; pa_channel_map cm; struct sockaddr_in dst_sa4, dst_sap_sa4, src_sa4, src_sap_sa4; #ifdef HAVE_IPV6 struct sockaddr_in6 dst_sa6, dst_sap_sa6, src_sa6, src_sap_sa6; #endif struct sockaddr_storage sa_dst; pa_source_output *o = NULL; uint8_t payload; char *p; int r, j; socklen_t k; char hn[128], *n; bool loop = false; enum inhibit_auto_suspend inhibit_auto_suspend = INHIBIT_AUTO_SUSPEND_ONLY_WITH_NON_MONITOR_SOURCES; const char *inhibit_auto_suspend_str; pa_source_output_new_data data; pa_assert(m); if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { pa_log("Failed to parse module arguments"); goto fail; } if (!(s = pa_namereg_get(m->core, pa_modargs_get_value(ma, "source", NULL), PA_NAMEREG_SOURCE))) { pa_log("Source does not exist."); goto fail; } if (pa_modargs_get_value_boolean(ma, "loop", &loop) < 0) { pa_log("Failed to parse \"loop\" parameter."); goto fail; } if ((inhibit_auto_suspend_str = pa_modargs_get_value(ma, "inhibit_auto_suspend", NULL))) { if (pa_streq(inhibit_auto_suspend_str, "always")) inhibit_auto_suspend = INHIBIT_AUTO_SUSPEND_ALWAYS; else if (pa_streq(inhibit_auto_suspend_str, "never")) inhibit_auto_suspend = INHIBIT_AUTO_SUSPEND_NEVER; else if (pa_streq(inhibit_auto_suspend_str, "only_with_non_monitor_sources")) inhibit_auto_suspend = INHIBIT_AUTO_SUSPEND_ONLY_WITH_NON_MONITOR_SOURCES; else { pa_log("Failed to parse the \"inhibit_auto_suspend\" parameter."); goto fail; } } ss = s->sample_spec; pa_rtp_sample_spec_fixup(&ss); cm = s->channel_map; if (pa_modargs_get_sample_spec(ma, &ss) < 0) { pa_log("Failed to parse sample specification"); goto fail; } if (!pa_rtp_sample_spec_valid(&ss)) { pa_log("Specified sample type not compatible with RTP"); goto fail; } if (ss.channels != cm.channels) pa_channel_map_init_auto(&cm, ss.channels, PA_CHANNEL_MAP_AIFF); payload = pa_rtp_payload_from_sample_spec(&ss); mtu = (uint32_t) pa_frame_align(DEFAULT_MTU, &ss); if (pa_modargs_get_value_u32(ma, "mtu", &mtu) < 0 || mtu < 1 || mtu % pa_frame_size(&ss) != 0) { pa_log("Invalid MTU."); goto fail; } port = DEFAULT_PORT + ((uint32_t) (rand() % 512) << 1); if (pa_modargs_get_value_u32(ma, "port", &port) < 0 || port < 1 || port > 0xFFFF) { pa_log("port= expects a numerical argument between 1 and 65535."); goto fail; } if (port & 1) pa_log_warn("Port number not even as suggested in RFC3550!"); if (pa_modargs_get_value_u32(ma, "ttl", &ttl) < 0 || ttl < 1 || ttl > 0xFF) { pa_log("ttl= expects a numerical argument between 1 and 255."); goto fail; } src_addr = pa_modargs_get_value(ma, "source_ip", DEFAULT_SOURCE_IP); if (inet_pton(AF_INET, src_addr, &src_sa4.sin_addr) > 0) { src_sa4.sin_family = af = AF_INET; src_sa4.sin_port = htons(0); memset(&src_sa4.sin_zero, 0, sizeof(src_sa4.sin_zero)); src_sap_sa4 = src_sa4; #ifdef HAVE_IPV6 } else if (inet_pton(AF_INET6, src_addr, &src_sa6.sin6_addr) > 0) { src_sa6.sin6_family = af = AF_INET6; src_sa6.sin6_port = htons(0); src_sa6.sin6_flowinfo = 0; src_sa6.sin6_scope_id = 0; src_sap_sa6 = src_sa6; #endif } else { pa_log("Invalid source address '%s'", src_addr); goto fail; } dst_addr = pa_modargs_get_value(ma, "destination", NULL); if (dst_addr == NULL) dst_addr = pa_modargs_get_value(ma, "destination_ip", DEFAULT_DESTINATION_IP); if (inet_pton(AF_INET, dst_addr, &dst_sa4.sin_addr) > 0) { dst_sa4.sin_family = af = AF_INET; dst_sa4.sin_port = htons((uint16_t) port); memset(&dst_sa4.sin_zero, 0, sizeof(dst_sa4.sin_zero)); dst_sap_sa4 = dst_sa4; dst_sap_sa4.sin_port = htons(SAP_PORT); #ifdef HAVE_IPV6 } else if (inet_pton(AF_INET6, dst_addr, &dst_sa6.sin6_addr) > 0) { dst_sa6.sin6_family = af = AF_INET6; dst_sa6.sin6_port = htons((uint16_t) port); dst_sa6.sin6_flowinfo = 0; dst_sa6.sin6_scope_id = 0; dst_sap_sa6 = dst_sa6; dst_sap_sa6.sin6_port = htons(SAP_PORT); #endif } else { pa_log("Invalid destination '%s'", dst_addr); goto fail; } if ((fd = pa_socket_cloexec(af, SOCK_DGRAM, 0)) < 0) { pa_log("socket() failed: %s", pa_cstrerror(errno)); goto fail; } if (af == AF_INET && bind(fd, (struct sockaddr*) &src_sa4, sizeof(src_sa4)) < 0) { pa_log("bind() failed: %s", pa_cstrerror(errno)); goto fail; #ifdef HAVE_IPV6 } else if (af == AF_INET6 && bind(fd, (struct sockaddr*) &src_sa6, sizeof(src_sa6)) < 0) { pa_log("bind() failed: %s", pa_cstrerror(errno)); goto fail; #endif } if (af == AF_INET && connect(fd, (struct sockaddr*) &dst_sa4, sizeof(dst_sa4)) < 0) { pa_log("connect() failed: %s", pa_cstrerror(errno)); goto fail; #ifdef HAVE_IPV6 } else if (af == AF_INET6 && connect(fd, (struct sockaddr*) &dst_sa6, sizeof(dst_sa6)) < 0) { pa_log("connect() failed: %s", pa_cstrerror(errno)); goto fail; #endif } if ((sap_fd = pa_socket_cloexec(af, SOCK_DGRAM, 0)) < 0) { pa_log("socket() failed: %s", pa_cstrerror(errno)); goto fail; } if (af == AF_INET && bind(sap_fd, (struct sockaddr*) &src_sap_sa4, sizeof(src_sap_sa4)) < 0) { pa_log("bind() failed: %s", pa_cstrerror(errno)); goto fail; #ifdef HAVE_IPV6 } else if (af == AF_INET6 && bind(sap_fd, (struct sockaddr*) &src_sap_sa6, sizeof(src_sap_sa6)) < 0) { pa_log("bind() failed: %s", pa_cstrerror(errno)); goto fail; #endif } if (af == AF_INET && connect(sap_fd, (struct sockaddr*) &dst_sap_sa4, sizeof(dst_sap_sa4)) < 0) { pa_log("connect() failed: %s", pa_cstrerror(errno)); goto fail; #ifdef HAVE_IPV6 } else if (af == AF_INET6 && connect(sap_fd, (struct sockaddr*) &dst_sap_sa6, sizeof(dst_sap_sa6)) < 0) { pa_log("connect() failed: %s", pa_cstrerror(errno)); goto fail; #endif } j = loop; if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &j, sizeof(j)) < 0 || setsockopt(sap_fd, IPPROTO_IP, IP_MULTICAST_LOOP, &j, sizeof(j)) < 0) { pa_log("IP_MULTICAST_LOOP failed: %s", pa_cstrerror(errno)); goto fail; } if (ttl != DEFAULT_TTL) { int _ttl = (int) ttl; if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &_ttl, sizeof(_ttl)) < 0) { pa_log("IP_MULTICAST_TTL failed: %s", pa_cstrerror(errno)); goto fail; } if (setsockopt(sap_fd, IPPROTO_IP, IP_MULTICAST_TTL, &_ttl, sizeof(_ttl)) < 0) { pa_log("IP_MULTICAST_TTL (sap) failed: %s", pa_cstrerror(errno)); goto fail; } } /* If the socket queue is full, let's drop packets */ pa_make_fd_nonblock(fd); pa_make_udp_socket_low_delay(fd); pa_source_output_new_data_init(&data); pa_proplist_sets(data.proplist, PA_PROP_MEDIA_NAME, "RTP Monitor Stream"); pa_proplist_sets(data.proplist, "rtp.source", src_addr); pa_proplist_sets(data.proplist, "rtp.destination", dst_addr); pa_proplist_setf(data.proplist, "rtp.mtu", "%lu", (unsigned long) mtu); pa_proplist_setf(data.proplist, "rtp.port", "%lu", (unsigned long) port); pa_proplist_setf(data.proplist, "rtp.ttl", "%lu", (unsigned long) ttl); data.driver = __FILE__; data.module = m; pa_source_output_new_data_set_source(&data, s, false, true); pa_source_output_new_data_set_sample_spec(&data, &ss); pa_source_output_new_data_set_channel_map(&data, &cm); data.flags |= get_dont_inhibit_auto_suspend_flag(s, inhibit_auto_suspend); pa_source_output_new(&o, m->core, &data); pa_source_output_new_data_done(&data); if (!o) { pa_log("failed to create source output."); goto fail; } o->parent.process_msg = source_output_process_msg; o->push = source_output_push_cb; o->moving = source_output_moving_cb; o->kill = source_output_kill_cb; pa_log_info("Configured source latency of %llu ms.", (unsigned long long) pa_source_output_set_requested_latency(o, pa_bytes_to_usec(mtu, &o->sample_spec)) / PA_USEC_PER_MSEC); m->userdata = o->userdata = u = pa_xnew(struct userdata, 1); u->module = m; u->source_output = o; u->memblockq = pa_memblockq_new( "module-rtp-send memblockq", 0, MEMBLOCKQ_MAXLENGTH, MEMBLOCKQ_MAXLENGTH, &ss, 1, 0, 0, NULL); u->mtu = mtu; k = sizeof(sa_dst); pa_assert_se((r = getsockname(fd, (struct sockaddr*) &sa_dst, &k)) >= 0); n = pa_xstrdup(pa_modargs_get_value(ma, "stream_name", NULL)); if (n == NULL) n = pa_sprintf_malloc("PulseAudio RTP Stream on %s", pa_get_fqdn(hn, sizeof(hn))); if (af == AF_INET) { p = pa_sdp_build(af, (void*) &((struct sockaddr_in*) &sa_dst)->sin_addr, (void*) &dst_sa4.sin_addr, n, (uint16_t) port, payload, &ss); #ifdef HAVE_IPV6 } else { p = pa_sdp_build(af, (void*) &((struct sockaddr_in6*) &sa_dst)->sin6_addr, (void*) &dst_sa6.sin6_addr, n, (uint16_t) port, payload, &ss); #endif } pa_xfree(n); pa_rtp_context_init_send(&u->rtp_context, fd, m->core->cookie, payload, pa_frame_size(&ss)); pa_sap_context_init_send(&u->sap_context, sap_fd, p); pa_log_info("RTP stream initialized with mtu %u on %s:%u from %s ttl=%u, SSRC=0x%08x, payload=%u, initial sequence #%u", mtu, dst_addr, port, src_addr, ttl, u->rtp_context.ssrc, payload, u->rtp_context.sequence); pa_log_info("SDP-Data:\n%s\nEOF", p); pa_sap_send(&u->sap_context, 0); u->sap_event = pa_core_rttime_new(m->core, pa_rtclock_now() + SAP_INTERVAL, sap_event_cb, u); u->inhibit_auto_suspend = inhibit_auto_suspend; pa_source_output_put(u->source_output); pa_modargs_free(ma); return 0; fail: if (ma) pa_modargs_free(ma); if (fd >= 0) pa_close(fd); if (sap_fd >= 0) pa_close(sap_fd); return -1; }
/***************************************************************************** * Open: open the audio device *****************************************************************************/ static int Open ( vlc_object_t *p_this ) { aout_instance_t *p_aout = (aout_instance_t *)p_this; struct aout_sys_t * p_sys; struct pa_sample_spec ss; const struct pa_buffer_attr *buffer_attr; struct pa_buffer_attr a; struct pa_channel_map map; /* Allocate structures */ p_aout->output.p_sys = p_sys = calloc( 1, sizeof( aout_sys_t ) ); if( p_sys == NULL ) return VLC_ENOMEM; PULSE_DEBUG( "Pulse start initialization"); ss.channels = aout_FormatNbChannels( &p_aout->output.output ); /* Get the input stream channel count */ /* Setup the pulse audio stream based on the input stream count */ switch(ss.channels) { case 8: p_aout->output.output.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER | AOUT_CHAN_MIDDLELEFT | AOUT_CHAN_MIDDLERIGHT | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT | AOUT_CHAN_LFE; break; case 6: p_aout->output.output.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT | AOUT_CHAN_LFE; break; case 4: p_aout->output.output.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT; break; case 2: p_aout->output.output.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT; break; case 1: p_aout->output.output.i_physical_channels = AOUT_CHAN_CENTER; break; default: msg_Err(p_aout,"Invalid number of channels"); goto fail; } /* Add a quick command line info message */ msg_Info(p_aout, "No. of Audio Channels: %d", ss.channels); ss.rate = p_aout->output.output.i_rate; ss.format = PA_SAMPLE_FLOAT32NE; p_aout->output.output.i_format = VLC_CODEC_FL32; if (!pa_sample_spec_valid(&ss)) { msg_Err(p_aout,"Invalid sample spec"); goto fail; } /* Reduce overall latency to 200mS to reduce audible clicks * Also pulse minreq and internal buffers are now 20mS which reduces resampling */ a.tlength = pa_bytes_per_second(&ss)/5; a.maxlength = a.tlength * 2; a.prebuf = a.tlength / 2; a.minreq = a.tlength / 10; /* Buffer size is 20mS */ p_sys->buffer_size = a.minreq; /* Initialise the speaker map setup above */ pa_channel_map_init_auto(&map, ss.channels, PA_CHANNEL_MAP_ALSA); if (!(p_sys->mainloop = pa_threaded_mainloop_new())) { msg_Err(p_aout, "Failed to allocate main loop"); goto fail; } if (!(p_sys->context = pa_context_new(pa_threaded_mainloop_get_api(p_sys->mainloop), _( PULSE_CLIENT_NAME )))) { msg_Err(p_aout, "Failed to allocate context"); goto fail; } pa_context_set_state_callback(p_sys->context, context_state_cb, p_aout); PULSE_DEBUG( "Pulse before context connect"); if (pa_context_connect(p_sys->context, NULL, 0, NULL) < 0) { msg_Err(p_aout, "Failed to connect to server: %s", pa_strerror(pa_context_errno(p_sys->context))); goto fail; } PULSE_DEBUG( "Pulse after context connect"); pa_threaded_mainloop_lock(p_sys->mainloop); if (pa_threaded_mainloop_start(p_sys->mainloop) < 0) { msg_Err(p_aout, "Failed to start main loop"); goto unlock_and_fail; } msg_Dbg(p_aout, "Pulse mainloop started"); /* Wait until the context is ready */ pa_threaded_mainloop_wait(p_sys->mainloop); if (pa_context_get_state(p_sys->context) != PA_CONTEXT_READY) { msg_Err(p_aout, "Failed to connect to server: %s", pa_strerror(pa_context_errno(p_sys->context))); goto unlock_and_fail; } if (!(p_sys->stream = pa_stream_new(p_sys->context, "audio stream", &ss, &map))) { msg_Err(p_aout, "Failed to create stream: %s", pa_strerror(pa_context_errno(p_sys->context))); goto unlock_and_fail; } PULSE_DEBUG( "Pulse after new stream"); pa_stream_set_state_callback(p_sys->stream, stream_state_cb, p_aout); pa_stream_set_write_callback(p_sys->stream, stream_request_cb, p_aout); pa_stream_set_latency_update_callback(p_sys->stream, stream_latency_update_cb, p_aout); if (pa_stream_connect_playback(p_sys->stream, NULL, &a, PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE|PA_STREAM_ADJUST_LATENCY, NULL, NULL) < 0) { msg_Err(p_aout, "Failed to connect stream: %s", pa_strerror(pa_context_errno(p_sys->context))); goto unlock_and_fail; } PULSE_DEBUG("Pulse stream connect"); /* Wait until the stream is ready */ pa_threaded_mainloop_wait(p_sys->mainloop); msg_Dbg(p_aout,"Pulse stream connected"); if (pa_stream_get_state(p_sys->stream) != PA_STREAM_READY) { msg_Err(p_aout, "Failed to connect to server: %s", pa_strerror(pa_context_errno(p_sys->context))); goto unlock_and_fail; } PULSE_DEBUG("Pulse after stream get status"); pa_threaded_mainloop_unlock(p_sys->mainloop); buffer_attr = pa_stream_get_buffer_attr(p_sys->stream); p_aout->output.i_nb_samples = buffer_attr->minreq / pa_frame_size(&ss); p_aout->output.pf_play = Play; aout_VolumeSoftInit(p_aout); msg_Dbg(p_aout, "Pulse initialized successfully"); { char cmt[PA_CHANNEL_MAP_SNPRINT_MAX], sst[PA_SAMPLE_SPEC_SNPRINT_MAX]; msg_Dbg(p_aout, "Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u", buffer_attr->maxlength, buffer_attr->tlength, buffer_attr->prebuf, buffer_attr->minreq); msg_Dbg(p_aout, "Using sample spec '%s', channel map '%s'.", pa_sample_spec_snprint(sst, sizeof(sst), pa_stream_get_sample_spec(p_sys->stream)), pa_channel_map_snprint(cmt, sizeof(cmt), pa_stream_get_channel_map(p_sys->stream))); msg_Dbg(p_aout, "Connected to device %s (%u, %ssuspended).", pa_stream_get_device_name(p_sys->stream), pa_stream_get_device_index(p_sys->stream), pa_stream_is_suspended(p_sys->stream) ? "" : "not "); } return VLC_SUCCESS; unlock_and_fail: msg_Dbg(p_aout, "Pulse initialization unlock and fail"); if (p_sys->mainloop) pa_threaded_mainloop_unlock(p_sys->mainloop); fail: msg_Err(p_aout, "Pulse initialization failed"); uninit(p_aout); return VLC_EGENERIC; }
/* Called from main context */ pa_source* pa_source_new( pa_core *core, pa_source_new_data *data, pa_source_flags_t flags) { pa_source *s; const char *name; char st[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX]; char *pt; pa_assert(core); pa_assert(data); pa_assert(data->name); pa_assert_ctl_context(); s = pa_msgobject_new(pa_source); if (!(name = pa_namereg_register(core, data->name, PA_NAMEREG_SOURCE, s, data->namereg_fail))) { pa_log_debug("Failed to register name %s.", data->name); pa_xfree(s); return NULL; } pa_source_new_data_set_name(data, name); if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_NEW], data) < 0) { pa_xfree(s); pa_namereg_unregister(core, name); return NULL; } /* FIXME, need to free s here on failure */ pa_return_null_if_fail(!data->driver || pa_utf8_valid(data->driver)); pa_return_null_if_fail(data->name && pa_utf8_valid(data->name) && data->name[0]); pa_return_null_if_fail(data->sample_spec_is_set && pa_sample_spec_valid(&data->sample_spec)); if (!data->channel_map_is_set) pa_return_null_if_fail(pa_channel_map_init_auto(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT)); pa_return_null_if_fail(pa_channel_map_valid(&data->channel_map)); pa_return_null_if_fail(data->channel_map.channels == data->sample_spec.channels); if (!data->volume_is_set) pa_cvolume_reset(&data->volume, data->sample_spec.channels); pa_return_null_if_fail(pa_cvolume_valid(&data->volume)); pa_return_null_if_fail(pa_cvolume_compatible(&data->volume, &data->sample_spec)); if (!data->muted_is_set) data->muted = FALSE; if (data->card) pa_proplist_update(data->proplist, PA_UPDATE_MERGE, data->card->proplist); pa_device_init_description(data->proplist); pa_device_init_icon(data->proplist, FALSE); pa_device_init_intended_roles(data->proplist); if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_FIXATE], data) < 0) { pa_xfree(s); pa_namereg_unregister(core, name); return NULL; } s->parent.parent.free = source_free; s->parent.process_msg = pa_source_process_msg; s->core = core; s->state = PA_SOURCE_INIT; s->flags = flags; s->priority = 0; s->suspend_cause = 0; s->name = pa_xstrdup(name); s->proplist = pa_proplist_copy(data->proplist); s->driver = pa_xstrdup(pa_path_get_filename(data->driver)); s->module = data->module; s->card = data->card; s->priority = pa_device_init_priority(s->proplist); s->sample_spec = data->sample_spec; s->channel_map = data->channel_map; s->outputs = pa_idxset_new(NULL, NULL); s->n_corked = 0; s->monitor_of = NULL; s->output_from_master = NULL; s->volume = data->volume; pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels); s->base_volume = PA_VOLUME_NORM; s->n_volume_steps = PA_VOLUME_NORM+1; s->muted = data->muted; s->refresh_volume = s->refresh_muted = FALSE; reset_callbacks(s); s->userdata = NULL; s->asyncmsgq = NULL; /* As a minor optimization we just steal the list instead of * copying it here */ s->ports = data->ports; data->ports = NULL; s->active_port = NULL; s->save_port = FALSE; if (data->active_port && s->ports) if ((s->active_port = pa_hashmap_get(s->ports, data->active_port))) s->save_port = data->save_port; if (!s->active_port && s->ports) { void *state; pa_device_port *p; PA_HASHMAP_FOREACH(p, s->ports, state) if (!s->active_port || p->priority > s->active_port->priority) s->active_port = p; }
static int init(int rate_hz, int channels, int format, int flags) { struct pa_sample_spec ss; struct pa_channel_map map; const struct format_map_s *fmt_map; char *devarg = NULL; char *host = NULL; char *sink = NULL; char *version = pa_get_library_version(); if (ao_subdevice) { devarg = strdup(ao_subdevice); sink = strchr(devarg, ':'); if (sink) *sink++ = 0; if (devarg[0]) host = devarg; } broken_pause = 0; // not sure which versions are affected, assume 0.9.11* to 0.9.14* // known bad: 0.9.14, 0.9.13 // known good: 0.9.9, 0.9.10, 0.9.15 // to test: pause, wait ca. 5 seconds framestep and see if MPlayer hangs somewhen if (strncmp(version, "0.9.1", 5) == 0 && version[5] >= '1' && version[5] <= '4') { mp_msg(MSGT_AO, MSGL_WARN, "[pulse] working around probably broken pause functionality,\n" " see http://www.pulseaudio.org/ticket/440\n"); broken_pause = 1; } ss.channels = channels; ss.rate = rate_hz; ao_data.samplerate = rate_hz; ao_data.channels = channels; fmt_map = format_maps; while (fmt_map->mp_format != format) { if (fmt_map->mp_format == AF_FORMAT_UNKNOWN) { mp_msg(MSGT_AO, MSGL_V, "AO: [pulse] Unsupported format, using default\n"); fmt_map = format_maps; break; } fmt_map++; } ao_data.format = fmt_map->mp_format; ss.format = fmt_map->pa_format; if (!pa_sample_spec_valid(&ss)) { mp_msg(MSGT_AO, MSGL_ERR, "AO: [pulse] Invalid sample spec\n"); goto fail; } pa_channel_map_init_auto(&map, ss.channels, PA_CHANNEL_MAP_ALSA); ao_data.bps = pa_bytes_per_second(&ss); pa_cvolume_reset(&volume, ss.channels); if (!(mainloop = pa_threaded_mainloop_new())) { mp_msg(MSGT_AO, MSGL_ERR, "AO: [pulse] Failed to allocate main loop\n"); goto fail; } if (!(context = pa_context_new(pa_threaded_mainloop_get_api(mainloop), PULSE_CLIENT_NAME))) { mp_msg(MSGT_AO, MSGL_ERR, "AO: [pulse] Failed to allocate context\n"); goto fail; } pa_context_set_state_callback(context, context_state_cb, NULL); if (pa_context_connect(context, host, 0, NULL) < 0) goto fail; pa_threaded_mainloop_lock(mainloop); if (pa_threaded_mainloop_start(mainloop) < 0) goto unlock_and_fail; /* Wait until the context is ready */ pa_threaded_mainloop_wait(mainloop); if (pa_context_get_state(context) != PA_CONTEXT_READY) goto unlock_and_fail; if (!(stream = pa_stream_new(context, "audio stream", &ss, &map))) goto unlock_and_fail; pa_stream_set_state_callback(stream, stream_state_cb, NULL); pa_stream_set_write_callback(stream, stream_request_cb, NULL); pa_stream_set_latency_update_callback(stream, stream_latency_update_cb, NULL); if (pa_stream_connect_playback(stream, sink, NULL, PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE, &volume, NULL) < 0) goto unlock_and_fail; /* Wait until the stream is ready */ pa_threaded_mainloop_wait(mainloop); if (pa_stream_get_state(stream) != PA_STREAM_READY) goto unlock_and_fail; pa_threaded_mainloop_unlock(mainloop); free(devarg); return 1; unlock_and_fail: if (mainloop) pa_threaded_mainloop_unlock(mainloop); fail: if (context) GENERIC_ERR_MSG(context, "Init failed"); free(devarg); uninit(1); return 0; }
static ALCboolean pulse_reset_playback(ALCdevice *device) { pulse_data *data = device->ExtraData; pa_stream_flags_t flags = 0; pa_channel_map chanmap; pa_threaded_mainloop_lock(data->loop); if(data->stream) { #if PA_CHECK_VERSION(0,9,15) if(pa_stream_set_buffer_attr_callback) pa_stream_set_buffer_attr_callback(data->stream, NULL, NULL); #endif pa_stream_disconnect(data->stream); pa_stream_unref(data->stream); data->stream = NULL; } if(!(device->Flags&DEVICE_CHANNELS_REQUEST)) { pa_operation *o; o = pa_context_get_sink_info_by_name(data->context, data->device_name, sink_info_callback, device); WAIT_FOR_OPERATION(o, data->loop); } if(!(device->Flags&DEVICE_FREQUENCY_REQUEST)) flags |= PA_STREAM_FIX_RATE; flags |= PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE; flags |= PA_STREAM_ADJUST_LATENCY; flags |= PA_STREAM_START_CORKED; flags |= PA_STREAM_DONT_MOVE; switch(device->FmtType) { case DevFmtByte: device->FmtType = DevFmtUByte; /* fall-through */ case DevFmtUByte: data->spec.format = PA_SAMPLE_U8; break; case DevFmtUShort: device->FmtType = DevFmtShort; /* fall-through */ case DevFmtShort: data->spec.format = PA_SAMPLE_S16NE; break; case DevFmtUInt: device->FmtType = DevFmtInt; /* fall-through */ case DevFmtInt: data->spec.format = PA_SAMPLE_S32NE; break; case DevFmtFloat: data->spec.format = PA_SAMPLE_FLOAT32NE; break; } data->spec.rate = device->Frequency; data->spec.channels = ChannelsFromDevFmt(device->FmtChans); if(pa_sample_spec_valid(&data->spec) == 0) { ERR("Invalid sample format\n"); pa_threaded_mainloop_unlock(data->loop); return ALC_FALSE; } if(!pa_channel_map_init_auto(&chanmap, data->spec.channels, PA_CHANNEL_MAP_WAVEEX)) { ERR("Couldn't build map for channel count (%d)!\n", data->spec.channels); pa_threaded_mainloop_unlock(data->loop); return ALC_FALSE; } SetDefaultWFXChannelOrder(device); data->attr.fragsize = -1; data->attr.prebuf = 0; data->attr.minreq = device->UpdateSize * pa_frame_size(&data->spec); data->attr.tlength = data->attr.minreq * maxu(device->NumUpdates, 2); data->attr.maxlength = -1; data->stream = connect_playback_stream(data->device_name, data->loop, data->context, flags, &data->attr, &data->spec, &chanmap); if(!data->stream) { pa_threaded_mainloop_unlock(data->loop); return ALC_FALSE; } pa_stream_set_state_callback(data->stream, stream_state_callback2, device); data->spec = *(pa_stream_get_sample_spec(data->stream)); if(device->Frequency != data->spec.rate) { pa_operation *o; /* Server updated our playback rate, so modify the buffer attribs * accordingly. */ data->attr.minreq = (ALuint64)device->UpdateSize * data->spec.rate / device->Frequency * pa_frame_size(&data->spec); data->attr.tlength = data->attr.minreq * maxu(device->NumUpdates, 2); data->attr.prebuf = 0; o = pa_stream_set_buffer_attr(data->stream, &data->attr, stream_success_callback, device); WAIT_FOR_OPERATION(o, data->loop); device->Frequency = data->spec.rate; } #if PA_CHECK_VERSION(0,9,15) if(pa_stream_set_buffer_attr_callback) pa_stream_set_buffer_attr_callback(data->stream, stream_buffer_attr_callback, device); #endif stream_buffer_attr_callback(data->stream, device); device->NumUpdates = device->UpdateSize*device->NumUpdates / (data->attr.minreq/pa_frame_size(&data->spec)); device->NumUpdates = maxu(device->NumUpdates, 2); device->UpdateSize = data->attr.minreq / pa_frame_size(&data->spec); pa_threaded_mainloop_unlock(data->loop); return ALC_TRUE; }
static ALCenum pulse_open_capture(ALCdevice *device, const ALCchar *device_name) { const char *pulse_name = NULL; pa_stream_flags_t flags = 0; pa_channel_map chanmap; pulse_data *data; pa_operation *o; ALuint samples; if(device_name) { ALuint i; if(!allCaptureDevNameMap) probe_devices(AL_TRUE); for(i = 0;i < numCaptureDevNames;i++) { if(strcmp(device_name, allCaptureDevNameMap[i].name) == 0) { pulse_name = allCaptureDevNameMap[i].device_name; break; } } if(i == numCaptureDevNames) return ALC_INVALID_VALUE; } if(pulse_open(device) == ALC_FALSE) return ALC_INVALID_VALUE; data = device->ExtraData; pa_threaded_mainloop_lock(data->loop); data->spec.rate = device->Frequency; data->spec.channels = ChannelsFromDevFmt(device->FmtChans); switch(device->FmtType) { case DevFmtUByte: data->spec.format = PA_SAMPLE_U8; break; case DevFmtShort: data->spec.format = PA_SAMPLE_S16NE; break; case DevFmtInt: data->spec.format = PA_SAMPLE_S32NE; break; case DevFmtFloat: data->spec.format = PA_SAMPLE_FLOAT32NE; break; case DevFmtByte: case DevFmtUShort: case DevFmtUInt: ERR("%s capture samples not supported\n", DevFmtTypeString(device->FmtType)); pa_threaded_mainloop_unlock(data->loop); goto fail; } if(pa_sample_spec_valid(&data->spec) == 0) { ERR("Invalid sample format\n"); pa_threaded_mainloop_unlock(data->loop); goto fail; } if(!pa_channel_map_init_auto(&chanmap, data->spec.channels, PA_CHANNEL_MAP_WAVEEX)) { ERR("Couldn't build map for channel count (%d)!\n", data->spec.channels); pa_threaded_mainloop_unlock(data->loop); goto fail; } samples = device->UpdateSize * device->NumUpdates; samples = maxu(samples, 100 * device->Frequency / 1000); data->attr.minreq = -1; data->attr.prebuf = -1; data->attr.maxlength = samples * pa_frame_size(&data->spec); data->attr.tlength = -1; data->attr.fragsize = minu(samples, 50*device->Frequency/1000) * pa_frame_size(&data->spec); flags |= PA_STREAM_DONT_MOVE; flags |= PA_STREAM_START_CORKED|PA_STREAM_ADJUST_LATENCY; data->stream = connect_record_stream(pulse_name, data->loop, data->context, flags, &data->attr, &data->spec, &chanmap); if(!data->stream) { pa_threaded_mainloop_unlock(data->loop); goto fail; } pa_stream_set_state_callback(data->stream, stream_state_callback2, device); data->device_name = strdup(pa_stream_get_device_name(data->stream)); o = pa_context_get_source_info_by_name(data->context, data->device_name, source_name_callback, device); WAIT_FOR_OPERATION(o, data->loop); pa_threaded_mainloop_unlock(data->loop); return ALC_NO_ERROR; fail: pulse_close(device); return ALC_INVALID_VALUE; }
int cubeb_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_name, cubeb_stream_params stream_params, unsigned int latency, cubeb_data_callback data_callback, cubeb_state_callback state_callback, void * user_ptr) { pa_sample_spec ss; cubeb_stream * stm; pa_operation * o; pa_buffer_attr battr; pa_channel_map map; if (stream_params.rate < 1 || stream_params.rate > 192000 || stream_params.channels < 1 || stream_params.channels > 32 || latency < 1 || latency > 2000) { return CUBEB_ERROR_INVALID_FORMAT; } switch (stream_params.format) { case CUBEB_SAMPLE_S16LE: ss.format = PA_SAMPLE_S16LE; break; case CUBEB_SAMPLE_S16BE: ss.format = PA_SAMPLE_S16BE; break; case CUBEB_SAMPLE_FLOAT32LE: ss.format = PA_SAMPLE_FLOAT32LE; break; case CUBEB_SAMPLE_FLOAT32BE: ss.format = PA_SAMPLE_FLOAT32BE; break; default: return CUBEB_ERROR_INVALID_FORMAT; } ss.rate = stream_params.rate; ss.channels = stream_params.channels; /* XXX check that this does the right thing for Vorbis and WaveEx */ pa_channel_map_init_auto(&map, ss.channels, PA_CHANNEL_MAP_DEFAULT); stm = calloc(1, sizeof(*stm)); assert(stm); stm->context = context; assert(stm->context); stm->data_callback = data_callback; stm->state_callback = state_callback; stm->user_ptr = user_ptr; stm->sample_spec = ss; battr.maxlength = -1; battr.tlength = pa_usec_to_bytes(latency * 1000, &stm->sample_spec); battr.prebuf = -1; battr.minreq = battr.tlength / 2; battr.fragsize = -1; pa_threaded_mainloop_lock(stm->context->mainloop); stm->stream = pa_stream_new(stm->context->context, stream_name, &ss, &map); pa_stream_set_state_callback(stm->stream, stream_state_callback, stm->context->mainloop); pa_stream_set_write_callback(stm->stream, stream_request_callback, stm); pa_stream_connect_playback(stm->stream, NULL, &battr, PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_START_CORKED, NULL, NULL); pa_threaded_mainloop_unlock(stm->context->mainloop); stream_state_wait(stm, PA_STREAM_READY); /* force a timing update now, otherwise timing info does not become valid until some point after initialization has completed. */ pa_threaded_mainloop_lock(stm->context->mainloop); o = pa_stream_update_timing_info(stm->stream, stream_success_callback, stm->context->mainloop); operation_wait(stm->context, o); pa_operation_unref(o); pa_threaded_mainloop_unlock(stm->context->mainloop); *stream = stm; return CUBEB_OK; }
void PulseAudioPlayer::OpenStream() { if (open) CloseStream(); // Initialise a mainloop //printf("Initialising threaded main loop\n"); mainloop = pa_threaded_mainloop_new(); if (!mainloop) { throw agi::AudioPlayerOpenError("Failed to initialise PulseAudio threaded mainloop object", 0); } //printf("Starting main loop\n"); pa_threaded_mainloop_start(mainloop); // Create context //printf("Creating context\n"); context = pa_context_new(pa_threaded_mainloop_get_api(mainloop), "Aegisub"); if (!context) { pa_threaded_mainloop_free(mainloop); throw agi::AudioPlayerOpenError("Failed to create PulseAudio context", 0); } pa_context_set_state_callback(context, (pa_context_notify_cb_t)pa_context_notify, this); // Connect the context //printf("Connecting context\n"); pa_context_connect(context, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL); // Wait for connection while (true) { context_notify.Wait(); if (cstate == PA_CONTEXT_READY) { break; } else if (cstate == PA_CONTEXT_FAILED) { // eww paerror = pa_context_errno(context); pa_context_unref(context); pa_threaded_mainloop_stop(mainloop); pa_threaded_mainloop_free(mainloop); throw agi::AudioPlayerOpenError(std::string("PulseAudio reported error: ") + pa_strerror(paerror), 0); } // otherwise loop once more } //printf("Context connected\n"); // Set up stream bpf = provider->GetChannels() * provider->GetBytesPerSample(); pa_sample_spec ss; ss.format = PA_SAMPLE_S16LE; // FIXME ss.rate = provider->GetSampleRate(); ss.channels = provider->GetChannels(); pa_channel_map map; pa_channel_map_init_auto(&map, ss.channels, PA_CHANNEL_MAP_DEFAULT); //printf("Creating stream\n"); stream = pa_stream_new(context, "Sound", &ss, &map); if (!stream) { // argh! pa_context_disconnect(context); pa_context_unref(context); pa_threaded_mainloop_stop(mainloop); pa_threaded_mainloop_free(mainloop); throw agi::AudioPlayerOpenError("PulseAudio could not create stream", 0); } pa_stream_set_state_callback(stream, (pa_stream_notify_cb_t)pa_stream_notify, this); pa_stream_set_write_callback(stream, (pa_stream_request_cb_t)pa_stream_write, this); // Connect stream //printf("Connecting playback stream\n"); paerror = pa_stream_connect_playback(stream, NULL, NULL, (pa_stream_flags_t)(PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_NOT_MONOTONOUS|PA_STREAM_AUTO_TIMING_UPDATE), NULL, NULL); if (paerror) { printf("PulseAudio reported error: %s (%d)\n", pa_strerror(paerror), paerror); throw agi::AudioPlayerOpenError(std::string("PulseAudio reported error: ") + pa_strerror(paerror), 0); } while (true) { stream_notify.Wait(); if (sstate == PA_STREAM_READY) { break; } else if (sstate == PA_STREAM_FAILED) { paerror = pa_context_errno(context); printf("PulseAudio player: Stream connection failed: %s (%d)\n", pa_strerror(paerror), paerror); throw agi::AudioPlayerOpenError("PulseAudio player: Something went wrong connecting the stream", 0); } } //printf("Connected playback stream, now playing\n\n"); // Hopefully this marks success //printf("Finished opening PulseAudio\n\n"); open = true; }
INT16 m1sdr_Init(int sample_rate) { int format, stereo, rate, fsize, err, state; unsigned int nfreq, periodtime; snd_pcm_hw_params_t *hwparams; #ifdef USE_SDL SDL_AudioSpec aspec; #endif pa_channel_map chanmap; pa_buffer_attr my_pa_attr; hw_present = 0; m1sdr_Callback = NULL; nDSoundSegLen = sample_rate / 60; switch (lnxdrv_apimode) { case 0: // SDL #ifdef USE_SDL SDL_InitSubSystem(SDL_INIT_AUDIO); m1sdr_SetSamplesPerTick(sample_rate/60); playbuf = 0; writebuf = 1; aspec.freq = sample_rate; aspec.format = AUDIO_S16SYS; // keep endian independant aspec.channels = 2; aspec.samples = 512; // has to be a power of 2, and we want it smaller than our buffer size aspec.callback = sdl_callback; aspec.userdata = 0; if (SDL_OpenAudio(&aspec, NULL) < 0) { printf("ERROR: can't open SDL audio\n"); return 0; } // make sure we don't start yet SDL_PauseAudio(1); #endif break; case 1: // ALSA // Try to open audio device if ((err = snd_pcm_open(&pHandle, "default", SND_PCM_STREAM_PLAYBACK, 0)) < 0) { fprintf(stderr, "ALSA: Could not open soundcard (%s)\n", snd_strerror(err)); hw_present = 0; return 0; } if ((err = snd_pcm_hw_params_malloc(&hwparams)) < 0) { fprintf (stderr, "cannot allocate hardware parameter structure (%s)\n", snd_strerror(err)); return 0; } // Init hwparams with full configuration space if ((err = snd_pcm_hw_params_any(pHandle, hwparams)) < 0) { fprintf(stderr, "ALSA: couldn't set hw params (%s)\n", snd_strerror(err)); hw_present = 0; return 0; } // Set access type if ((err = snd_pcm_hw_params_set_access(pHandle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { fprintf(stderr, "ALSA: can't set access (%s)\n", snd_strerror(err)); return 0; } // Set sample format if ((err = snd_pcm_hw_params_set_format(pHandle, hwparams, SND_PCM_FORMAT_S16)) < 0) { fprintf(stderr, "ALSA: can't set format (%s)\n", snd_strerror(err)); return 0; } // Set sample rate (nearest possible) nfreq = sample_rate; if ((err = snd_pcm_hw_params_set_rate_near(pHandle, hwparams, &nfreq, 0)) < 0) { fprintf(stderr, "ALSA: can't set sample rate (%s)\n", snd_strerror(err)); return 0; } // Set number of channels if ((err = snd_pcm_hw_params_set_channels(pHandle, hwparams, 2)) < 0) { fprintf(stderr, "ALSA: can't set stereo (%s)\n", snd_strerror(err)); return 0; } // Set period time (nearest possible) periodtime = 16; if ((err = snd_pcm_hw_params_set_period_time_near(pHandle, hwparams, &periodtime, 0)) < 0) { fprintf(stderr, "ALSA: can't set period time (%s)\n", snd_strerror(err)); return 0; } // Apply HW parameter settings to PCM device and prepare device if ((err = snd_pcm_hw_params(pHandle, hwparams)) < 0) { fprintf(stderr, "ALSA: unable to install hw_params (%s)\n", snd_strerror(err)); snd_pcm_hw_params_free(hwparams); return 0; } snd_pcm_hw_params_free(hwparams); if ((err = snd_pcm_prepare(pHandle)) < 0) { fprintf (stderr, "cannot prepare audio interface for use (%s)\n", snd_strerror(err)); return 0; } break; case 2: // OSS audiofd = open("/dev/dsp", O_WRONLY, 0); if (audiofd == -1) { audiofd = open("/dev/dsp1", O_WRONLY, 0); if (audiofd == -1) { perror("/dev/dsp1"); return(0); } } // reset things ioctl(audiofd, SNDCTL_DSP_RESET, 0); is_broken_driver = 0; num_frags = NUM_FRAGS_NORMAL; // set the buffer size we want fsize = OSS_FRAGMENT; if (ioctl(audiofd, SNDCTL_DSP_SETFRAGMENT, &fsize) == - 1) { perror("SNDCTL_DSP_SETFRAGMENT"); return(0); } // set 16-bit output format = AFMT_S16_NE; // 16 bit signed "native"-endian if (ioctl(audiofd, SNDCTL_DSP_SETFMT, &format) == - 1) { perror("SNDCTL_DSP_SETFMT"); return(0); } // now set stereo stereo = 1; if (ioctl(audiofd, SNDCTL_DSP_STEREO, &stereo) == - 1) { perror("SNDCTL_DSP_STEREO"); return(0); } // and the sample rate rate = sample_rate; if (ioctl(audiofd, SNDCTL_DSP_SPEED, &rate) == - 1) { perror("SNDCTL_DSP_SPEED"); return(0); } // and make sure that did what we wanted ioctl(audiofd, SNDCTL_DSP_GETBLKSIZE, &fsize); break; case 3: // PulseAudio sample_spec.format = PA_SAMPLE_S16NE; sample_spec.rate = sample_rate; sample_spec.channels = 2; my_pa_context = NULL; my_pa_stream = NULL; my_pa_mainloop = NULL; my_pa_mainloop_api = NULL; #if !PULSE_USE_SIMPLE // get default channel mapping pa_channel_map_init_auto(&chanmap, sample_spec.channels, PA_CHANNEL_MAP_WAVEEX); if (!(my_pa_mainloop = pa_mainloop_new())) { fprintf(stderr, "pa_mainloop_new() failed\n"); return 0; } my_pa_mainloop_api = pa_mainloop_get_api(my_pa_mainloop); /* if (pa_signal_init(my_pa_mainloop_api) != 0) { fprintf(stderr, "pa_signal_init() failed\n"); return 0; }*/ /* Create a new connection context */ if (!(my_pa_context = pa_context_new(my_pa_mainloop_api, "Audio Overload"))) { fprintf(stderr, "pa_context_new() failed\n"); return 0; } /* set the context state CB */ // pa_context_set_state_callback(my_pa_context, context_state_callback, NULL); /* Connect the context */ if (pa_context_connect(my_pa_context, NULL, (pa_context_flags_t)0, NULL) < 0) { fprintf(stderr, "pa_context_connect() failed: %s", pa_strerror(pa_context_errno(my_pa_context))); return 0; } do { pa_mainloop_iterate(my_pa_mainloop, 1, NULL); state = pa_context_get_state(my_pa_context); if (!PA_CONTEXT_IS_GOOD((pa_context_state_t)state)) { printf("PA CONTEXT NOT GOOD\n"); hw_present = 0; return 0; } } while (state != PA_CONTEXT_READY); if (!(my_pa_stream = pa_stream_new(my_pa_context, "Audio Overload", &sample_spec, &chanmap))) { fprintf(stderr, "pa_stream_new() failed: %s\n", pa_strerror(pa_context_errno(my_pa_context))); return 0; } memset(&my_pa_attr, 0, sizeof(my_pa_attr)); my_pa_attr.tlength = nDSoundSegLen * 4 * 4; my_pa_attr.prebuf = -1; my_pa_attr.maxlength = -1; my_pa_attr.minreq = nDSoundSegLen * 4 * 2; if ((err = pa_stream_connect_playback(my_pa_stream, NULL, &my_pa_attr, PA_STREAM_ADJUST_LATENCY, NULL, NULL)) < 0) { fprintf(stderr, "pa_stream_connect_playback() failed: %s\n", pa_strerror(pa_context_errno(my_pa_context))); return 0; } do { pa_mainloop_iterate(my_pa_mainloop, 1, NULL); state = pa_stream_get_state(my_pa_stream); if (!PA_STREAM_IS_GOOD((pa_stream_state_t)state)) { printf("PA STREAM NOT GOOD\n"); hw_present = 0; return 0; } } while (state != PA_STREAM_READY); // printf("PulseAudio setup OK so far, len %d\n", nDSoundSegLen*4); #else my_simple = NULL; #endif break; } hw_present = 1; return (1); }
void pa_sample_spec_mimefy(pa_sample_spec *ss, pa_channel_map *cm) { pa_assert(pa_channel_map_compatible(cm, ss)); /* Turns the sample type passed in into the next 'better' one that * can be encoded for HTTP. If there is no 'better' one we pick * the 'best' one that is 'worse'. */ if (ss->channels > 2) ss->channels = 2; if (ss->rate > 44100) ss->rate = 48000; else if (ss->rate > 32000) ss->rate = 44100; else if (ss->rate > 24000) ss->rate = 32000; else if (ss->rate > 22050) ss->rate = 24000; else if (ss->rate > 16000) ss->rate = 22050; else if (ss->rate > 11025) ss->rate = 16000; else if (ss->rate > 8000) ss->rate = 11025; else ss->rate = 8000; switch (ss->format) { case PA_SAMPLE_S24BE: case PA_SAMPLE_S24LE: case PA_SAMPLE_S24_32LE: case PA_SAMPLE_S24_32BE: case PA_SAMPLE_S32LE: case PA_SAMPLE_S32BE: case PA_SAMPLE_FLOAT32LE: case PA_SAMPLE_FLOAT32BE: ss->format = PA_SAMPLE_S24BE; break; case PA_SAMPLE_S16BE: case PA_SAMPLE_S16LE: ss->format = PA_SAMPLE_S16BE; break; case PA_SAMPLE_ULAW: case PA_SAMPLE_ALAW: if (ss->rate == 8000 && ss->channels == 1) ss->format = PA_SAMPLE_ULAW; else ss->format = PA_SAMPLE_S16BE; break; case PA_SAMPLE_U8: ss->format = PA_SAMPLE_U8; break; case PA_SAMPLE_MAX: case PA_SAMPLE_INVALID: pa_assert_not_reached(); } pa_channel_map_init_auto(cm, ss->channels, PA_CHANNEL_MAP_DEFAULT); pa_assert(pa_sample_spec_is_mime(ss, cm)); }
static int init(struct ao *ao, char *params) { struct pa_sample_spec ss; struct pa_channel_map map; char *devarg = NULL; char *host = NULL; char *sink = NULL; const char *version = pa_get_library_version(); struct priv *priv = talloc_zero(ao, struct priv); ao->priv = priv; if (params) { devarg = strdup(params); sink = strchr(devarg, ':'); if (sink) *sink++ = 0; if (devarg[0]) host = devarg; } priv->broken_pause = false; /* not sure which versions are affected, assume 0.9.11* to 0.9.14* * known bad: 0.9.14, 0.9.13 * known good: 0.9.9, 0.9.10, 0.9.15 * To test: pause, wait ca. 5 seconds, framestep and see if MPlayer * hangs somewhen. */ if (strncmp(version, "0.9.1", 5) == 0 && version[5] >= '1' && version[5] <= '4') { mp_msg(MSGT_AO, MSGL_WARN, "[pulse] working around probably broken pause functionality,\n" " see http://www.pulseaudio.org/ticket/440\n"); priv->broken_pause = true; } ss.channels = ao->channels; ss.rate = ao->samplerate; const struct format_map *fmt_map = format_maps; while (fmt_map->mp_format != ao->format) { if (fmt_map->mp_format == AF_FORMAT_UNKNOWN) { mp_msg(MSGT_AO, MSGL_V, "AO: [pulse] Unsupported format, using default\n"); fmt_map = format_maps; break; } fmt_map++; } ao->format = fmt_map->mp_format; ss.format = fmt_map->pa_format; if (!pa_sample_spec_valid(&ss)) { mp_msg(MSGT_AO, MSGL_ERR, "AO: [pulse] Invalid sample spec\n"); goto fail; } pa_channel_map_init_auto(&map, ss.channels, PA_CHANNEL_MAP_ALSA); ao->bps = pa_bytes_per_second(&ss); if (!(priv->mainloop = pa_threaded_mainloop_new())) { mp_msg(MSGT_AO, MSGL_ERR, "AO: [pulse] Failed to allocate main loop\n"); goto fail; } if (!(priv->context = pa_context_new(pa_threaded_mainloop_get_api( priv->mainloop), PULSE_CLIENT_NAME))) { mp_msg(MSGT_AO, MSGL_ERR, "AO: [pulse] Failed to allocate context\n"); goto fail; } pa_context_set_state_callback(priv->context, context_state_cb, ao); if (pa_context_connect(priv->context, host, 0, NULL) < 0) goto fail; pa_threaded_mainloop_lock(priv->mainloop); if (pa_threaded_mainloop_start(priv->mainloop) < 0) goto unlock_and_fail; /* Wait until the context is ready */ pa_threaded_mainloop_wait(priv->mainloop); if (pa_context_get_state(priv->context) != PA_CONTEXT_READY) goto unlock_and_fail; if (!(priv->stream = pa_stream_new(priv->context, "audio stream", &ss, &map))) goto unlock_and_fail; pa_stream_set_state_callback(priv->stream, stream_state_cb, ao); pa_stream_set_write_callback(priv->stream, stream_request_cb, ao); pa_stream_set_latency_update_callback(priv->stream, stream_latency_update_cb, ao); pa_buffer_attr bufattr = { .maxlength = -1, .tlength = pa_usec_to_bytes(1000000, &ss), .prebuf = -1, .minreq = -1, .fragsize = -1, }; if (pa_stream_connect_playback(priv->stream, sink, &bufattr, PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE, NULL, NULL) < 0) goto unlock_and_fail; /* Wait until the stream is ready */ pa_threaded_mainloop_wait(priv->mainloop); if (pa_stream_get_state(priv->stream) != PA_STREAM_READY) goto unlock_and_fail; pa_threaded_mainloop_unlock(priv->mainloop); free(devarg); return 0; unlock_and_fail: if (priv->mainloop) pa_threaded_mainloop_unlock(priv->mainloop); fail: if (priv->context) GENERIC_ERR_MSG(priv->context, "Init failed"); free(devarg); uninit(ao, true); return -1; } static void cork(struct ao *ao, bool pause) { struct priv *priv = ao->priv; pa_threaded_mainloop_lock(priv->mainloop); priv->retval = 0; if (!waitop(priv, pa_stream_cork(priv->stream, pause, success_cb, ao)) || !priv->retval) GENERIC_ERR_MSG(priv->context, "pa_stream_cork() failed"); } // Play the specified data to the pulseaudio server static int play(struct ao *ao, void *data, int len, int flags) { struct priv *priv = ao->priv; /* For some reason Pulseaudio behaves worse if this is done after * the write - rapidly repeated seeks result in bogus increasing * reported latency. */ if (priv->did_reset) cork(ao, false); pa_threaded_mainloop_lock(priv->mainloop); if (pa_stream_write(priv->stream, data, len, NULL, 0, PA_SEEK_RELATIVE) < 0) { GENERIC_ERR_MSG(priv->context, "pa_stream_write() failed"); len = -1; } if (priv->did_reset) { priv->did_reset = false; if (!waitop(priv, pa_stream_update_timing_info(priv->stream, success_cb, ao)) || !priv->retval) GENERIC_ERR_MSG(priv->context, "pa_stream_UPP() failed"); } else pa_threaded_mainloop_unlock(priv->mainloop); return len; } // Reset the audio stream, i.e. flush the playback buffer on the server side static void reset(struct ao *ao) { // pa_stream_flush() works badly if not corked cork(ao, true); struct priv *priv = ao->priv; pa_threaded_mainloop_lock(priv->mainloop); priv->retval = 0; if (!waitop(priv, pa_stream_flush(priv->stream, success_cb, ao)) || !priv->retval) GENERIC_ERR_MSG(priv->context, "pa_stream_flush() failed"); priv->did_reset = true; }
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; pa_assert_se(pa_channel_map_init_auto(&cm, ss.channels, PA_CHANNEL_MAP_AUX)); pa_channel_map_init_auto(&cm, ss.channels, PA_CHANNEL_MAP_DEFAULT); for (l = txt; l; l = l->next) { char *key, *value; pa_assert_se(avahi_string_list_get_pair(l, &key, &value, NULL) == 0); if (strcmp(key, "device") == 0) { pa_xfree(device); device = value; value = NULL; } else if (strcmp(key, "rate") == 0) ss.rate = atoi(value); else if (strcmp(key, "channels") == 0) ss.channels = atoi(value); else if (strcmp(key, "format") == 0) ss.format = pa_parse_sample_format(value); else if (strcmp(key, "channel_map") == 0) { 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_assert_se(pa_channel_map_init_auto(&cm, ss.channels, PA_CHANNEL_MAP_AUX)); pa_channel_map_init_auto(&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 module-tunnel-%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); }