static void pulse_stop_playback(ALCdevice *device) //{{{ { pulse_data *data = device->ExtraData; if(!data->stream) return; data->killNow = AL_TRUE; if(data->thread) { pa_threaded_mainloop_signal(data->loop, 0); StopThread(data->thread); data->thread = NULL; } data->killNow = AL_FALSE; pa_threaded_mainloop_lock(data->loop); #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); } //}}}
static void pulse_close(ALCdevice *device) { pulse_data *data = device->ExtraData; pa_threaded_mainloop_lock(data->loop); if(data->stream) { pa_stream_set_moved_callback(data->stream, NULL, NULL); #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); } pa_context_disconnect(data->context); pa_context_unref(data->context); pa_threaded_mainloop_unlock(data->loop); pa_threaded_mainloop_stop(data->loop); pa_threaded_mainloop_free(data->loop); free(data->device_name); device->ExtraData = NULL; pa_xfree(data); }
void QPulseAudioThread::reconnect(SourceContainer::const_iterator pos = s_sourceList.end()) { if (s_sourceList.empty()) return; if (pos != s_sourceList.end()) { s_sourcePosition = pos; qDebug() << "reconnecting with" << *pos; } else s_sourcePosition = scanForPlaybackMonitor(); if (s_sourcePosition == s_sourceList.end()) { s_sourcePosition = s_sourceList.begin(); } if (stream && (pa_stream_get_state(stream) == PA_STREAM_READY)) { //qDebug() << "disconnect"; pa_stream_disconnect ( stream ); // pa_stream_unref(stream); //qDebug() << "* return *"; } if ( ! ( stream = pa_stream_new ( context, stream_name, &sample_spec, channel_map_set ? &channel_map : NULL ) ) ) { fprintf ( stderr, "pa_stream_new() failed: %s\n", pa_strerror ( pa_context_errno ( context ) ) ); return; } pa_stream_set_state_callback ( stream, stream_state_callback, &s_sourceList ); pa_stream_set_read_callback ( stream, stream_read_callback, &s_sourceList ); pa_stream_set_moved_callback(stream, stream_moved_callback, &s_sourceList ); switch (pa_stream_get_state(stream)) { case PA_STREAM_UNCONNECTED:// The stream is not yet connected to any sink or source. qDebug() << "unconnected: connecting..."; connectHelper(s_sourcePosition); break; case PA_STREAM_CREATING ://The stream is being created. break; case PA_STREAM_READY :// The stream is established, you may pass audio data to it now. qDebug() << "stream is still ready, waiting for callback..."; break; case PA_STREAM_FAILED :// An error occured that made the stream invalid. qDebug() << "stream is now invalid. great."; break; case PA_STREAM_TERMINATED:// The stream has been terminated cleanly. qDebug() << "terminated..."; break; } }
AudioStream::~AudioStream() { PulseMainLoopLock lock(mainloop_); pa_stream_disconnect(audiostream_); // make sure we don't get any further callback pa_stream_set_state_callback(audiostream_, NULL, NULL); pa_stream_set_write_callback(audiostream_, NULL, NULL); pa_stream_set_read_callback(audiostream_, NULL, NULL); pa_stream_set_moved_callback(audiostream_, NULL, NULL); pa_stream_set_underflow_callback(audiostream_, NULL, NULL); pa_stream_set_overflow_callback(audiostream_, NULL, NULL); pa_stream_unref(audiostream_); }
AudioStream::AudioStream(pa_context *c, pa_threaded_mainloop *m, const char *desc, int type, unsigned samplrate, const PaDeviceInfos* infos, bool ec) : audiostream_(0), mainloop_(m) { const pa_channel_map channel_map = infos->channel_map; pa_sample_spec sample_spec = { PA_SAMPLE_S16LE, // PA_SAMPLE_FLOAT32LE, samplrate, channel_map.channels }; RING_DBG("%s: trying to create stream with device %s (%dHz, %d channels)", desc, infos->name.c_str(), samplrate, channel_map.channels); assert(pa_sample_spec_valid(&sample_spec)); assert(pa_channel_map_valid(&channel_map)); std::unique_ptr<pa_proplist, decltype(pa_proplist_free)&> pl (pa_proplist_new(), pa_proplist_free); pa_proplist_sets(pl.get(), PA_PROP_FILTER_WANT, "echo-cancel"); audiostream_ = pa_stream_new_with_proplist(c, desc, &sample_spec, &channel_map, ec ? pl.get() : nullptr); if (!audiostream_) { RING_ERR("%s: pa_stream_new() failed : %s" , desc, pa_strerror(pa_context_errno(c))); throw std::runtime_error("Could not create stream\n"); } pa_buffer_attr attributes; attributes.maxlength = pa_usec_to_bytes(160 * PA_USEC_PER_MSEC, &sample_spec); attributes.tlength = pa_usec_to_bytes(80 * PA_USEC_PER_MSEC, &sample_spec); attributes.prebuf = 0; attributes.fragsize = pa_usec_to_bytes(80 * PA_USEC_PER_MSEC, &sample_spec); attributes.minreq = (uint32_t) -1; { PulseMainLoopLock lock(mainloop_); const pa_stream_flags_t flags = static_cast<pa_stream_flags_t>(PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE); if (type == PLAYBACK_STREAM || type == RINGTONE_STREAM) { pa_stream_connect_playback(audiostream_, infos->name.empty() ? NULL : infos->name.c_str(), &attributes, flags, NULL, NULL); } else if (type == CAPTURE_STREAM) { pa_stream_connect_record(audiostream_, infos->name.empty() ? NULL : infos->name.c_str(), &attributes, flags); } } pa_stream_set_state_callback(audiostream_, [](pa_stream* s, void* user_data){ static_cast<AudioStream*>(user_data)->stateChanged(s); }, this); pa_stream_set_moved_callback(audiostream_, [](pa_stream* s, void* user_data){ static_cast<AudioStream*>(user_data)->moved(s); }, this); }
// 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; } }
/* 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 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; } //}}}
/* OpenAL */ static ALCenum pulse_open_playback(ALCdevice *device, const ALCchar *device_name) { const char *pulse_name = NULL; pa_stream_flags_t flags; pa_sample_spec spec; pulse_data *data; pa_operation *o; if(device_name) { ALuint i; if(!allDevNameMap) probe_devices(AL_FALSE); for(i = 0; i < numDevNames; i++) { if(strcmp(device_name, allDevNameMap[i].name) == 0) { pulse_name = allDevNameMap[i].device_name; break; } } if(i == numDevNames) return ALC_INVALID_VALUE; } if(pulse_open(device) == ALC_FALSE) return ALC_INVALID_VALUE; data = device->ExtraData; pa_threaded_mainloop_lock(data->loop); flags = PA_STREAM_FIX_FORMAT | PA_STREAM_FIX_RATE | PA_STREAM_FIX_CHANNELS; if(!GetConfigValueBool("pulse", "allow-moves", 0)) flags |= PA_STREAM_DONT_MOVE; spec.format = PA_SAMPLE_S16NE; spec.rate = 44100; spec.channels = 2; data->stream = connect_playback_stream(pulse_name, data->loop, data->context, flags, NULL, &spec, NULL); if(!data->stream) { pa_threaded_mainloop_unlock(data->loop); pulse_close(device); return ALC_INVALID_VALUE; } data->device_name = strdup(pa_stream_get_device_name(data->stream)); o = pa_context_get_sink_info_by_name(data->context, data->device_name, sink_name_callback, device); wait_for_operation(o, data->loop); pa_stream_set_moved_callback(data->stream, stream_moved_callback, device); pa_threaded_mainloop_unlock(data->loop); return ALC_NO_ERROR; }
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_START_CORKED|PA_STREAM_ADJUST_LATENCY; if(!GetConfigValueBool("pulse", "allow-moves", 0)) flags |= PA_STREAM_DONT_MOVE; 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_stream_set_moved_callback(data->stream, stream_moved_callback, device); pa_threaded_mainloop_unlock(data->loop); return ALC_NO_ERROR; fail: pulse_close(device); return ALC_INVALID_VALUE; }
static ALCboolean pulse_reset_playback(ALCdevice *device) { pulse_data *data = device->ExtraData; pa_stream_flags_t flags = 0; pa_channel_map chanmap; ALuint len; pa_threaded_mainloop_lock(data->loop); if(data->stream) { pa_stream_set_moved_callback(data->stream, NULL, NULL); #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; if(!GetConfigValueBool("pulse", "allow-moves", 0)) 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. */ device->NumUpdates = (ALuint)((ALdouble)device->NumUpdates / device->Frequency * data->spec.rate + 0.5); data->attr.minreq = device->UpdateSize * pa_frame_size(&data->spec); data->attr.tlength = data->attr.minreq * clampu(device->NumUpdates, 2, 16); data->attr.maxlength = -1; 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; } pa_stream_set_moved_callback(data->stream, stream_moved_callback, device); #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); len = data->attr.minreq / pa_frame_size(&data->spec); if((CPUCapFlags&CPU_CAP_SSE)) len = (len+3)&~3; device->NumUpdates = (ALuint)((ALdouble)device->NumUpdates/len*device->UpdateSize + 0.5); device->NumUpdates = clampu(device->NumUpdates, 2, 16); device->UpdateSize = len; pa_threaded_mainloop_unlock(data->loop); return ALC_TRUE; }
static int Open(vlc_object_t *obj) { demux_t *demux = (demux_t *)obj; demux_sys_t *sys = malloc(sizeof (*sys)); if (unlikely(sys == NULL)) return VLC_ENOMEM; sys->context = vlc_pa_connect(obj, &sys->mainloop); if (sys->context == NULL) { free(sys); return VLC_EGENERIC; } sys->stream = NULL; sys->es = NULL; sys->discontinuity = false; sys->caching = INT64_C(1000) * var_InheritInteger(obj, "live-caching"); demux->p_sys = sys; /* Stream parameters */ struct pa_sample_spec ss; ss.format = PA_SAMPLE_S16NE; ss.rate = 48000; ss.channels = 2; assert(pa_sample_spec_valid(&ss)); struct pa_channel_map map; map.channels = 2; map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT; map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT; assert(pa_channel_map_valid(&map)); const pa_stream_flags_t flags = PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_ADJUST_LATENCY | PA_STREAM_FIX_FORMAT | PA_STREAM_FIX_RATE /*| PA_STREAM_FIX_CHANNELS*/; const char *dev = NULL; if (demux->psz_location != NULL && demux->psz_location[0] != '\0') dev = demux->psz_location; struct pa_buffer_attr attr = { .maxlength = -1, .fragsize = pa_usec_to_bytes(sys->caching, &ss) / 2, }; es_format_t fmt; /* Create record stream */ pa_stream *s; pa_operation *op; pa_threaded_mainloop_lock(sys->mainloop); s = pa_stream_new(sys->context, "audio stream", &ss, &map); if (s == NULL) goto error; sys->stream = s; pa_stream_set_state_callback(s, stream_state_cb, sys->mainloop); pa_stream_set_read_callback(s, stream_read_cb, demux); pa_stream_set_buffer_attr_callback(s, stream_buffer_attr_cb, demux); pa_stream_set_moved_callback(s, stream_moved_cb, demux); pa_stream_set_overflow_callback(s, stream_overflow_cb, demux); pa_stream_set_started_callback(s, stream_started_cb, demux); pa_stream_set_suspended_callback(s, stream_suspended_cb, demux); pa_stream_set_underflow_callback(s, stream_underflow_cb, demux); if (pa_stream_connect_record(s, dev, &attr, flags) < 0 || stream_wait(s, sys->mainloop)) { vlc_pa_error(obj, "cannot connect record stream", sys->context); goto error; } /* The ES should be initialized before stream_read_cb(), but how? */ const struct pa_sample_spec *pss = pa_stream_get_sample_spec(s); if ((unsigned)pss->format >= sizeof (fourccs) / sizeof (fourccs[0])) { msg_Err(obj, "unknown PulseAudio sample format %u", (unsigned)pss->format); goto error; } vlc_fourcc_t format = fourccs[pss->format]; if (format == 0) { /* FIXME: should renegotiate something else */ msg_Err(obj, "unsupported PulseAudio sample format %u", (unsigned)pss->format); goto error; } es_format_Init(&fmt, AUDIO_ES, format); fmt.audio.i_physical_channels = fmt.audio.i_original_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT; fmt.audio.i_channels = ss.channels; fmt.audio.i_rate = pss->rate; fmt.audio.i_bitspersample = aout_BitsPerSample(format); fmt.audio.i_blockalign = fmt.audio.i_bitspersample * ss.channels / 8; fmt.i_bitrate = fmt.audio.i_bitspersample * ss.channels * pss->rate; sys->framesize = fmt.audio.i_blockalign; sys->es = es_out_Add (demux->out, &fmt); /* Update the buffer attributes according to actual format */ attr.fragsize = pa_usec_to_bytes(sys->caching, pss) / 2; op = pa_stream_set_buffer_attr(s, &attr, stream_success_cb, sys->mainloop); if (likely(op != NULL)) { while (pa_operation_get_state(op) == PA_OPERATION_RUNNING) pa_threaded_mainloop_wait(sys->mainloop); pa_operation_unref(op); } stream_buffer_attr_cb(s, demux); pa_threaded_mainloop_unlock(sys->mainloop); demux->pf_demux = NULL; demux->pf_control = Control; return VLC_SUCCESS; error: pa_threaded_mainloop_unlock(sys->mainloop); Close(obj); return VLC_EGENERIC; } static void Close (vlc_object_t *obj) { demux_t *demux = (demux_t *)obj; demux_sys_t *sys = demux->p_sys; pa_stream *s = sys->stream; if (likely(s != NULL)) { pa_threaded_mainloop_lock(sys->mainloop); pa_stream_disconnect(s); pa_stream_set_state_callback(s, NULL, NULL); pa_stream_set_read_callback(s, NULL, NULL); pa_stream_set_buffer_attr_callback(s, NULL, NULL); pa_stream_set_moved_callback(s, NULL, NULL); pa_stream_set_overflow_callback(s, NULL, NULL); pa_stream_set_started_callback(s, NULL, NULL); pa_stream_set_suspended_callback(s, NULL, NULL); pa_stream_set_underflow_callback(s, NULL, NULL); pa_stream_unref(s); pa_threaded_mainloop_unlock(sys->mainloop); } vlc_pa_disconnect(obj, sys->context, sys->mainloop); free(sys); }