Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[pulseaudio] Add "idle timeout" to Pulseaudio audio sink #10731

Merged
merged 2 commits into from
Jun 5, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javazoom.spi.mpeg.sampled.convert.MpegFormatConversionProvider;
import javazoom.spi.mpeg.sampled.file.MpegAudioFileReader;

Expand Down Expand Up @@ -52,17 +54,21 @@ public class PulseAudioAudioSink implements AudioSink {
private static final HashSet<Class<? extends AudioStream>> SUPPORTED_STREAMS = new HashSet<>();

private PulseaudioHandler pulseaudioHandler;
private ScheduledExecutorService scheduler;

private @Nullable Socket clientSocket;

private boolean isIdle = true;

static {
SUPPORTED_FORMATS.add(AudioFormat.WAV);
SUPPORTED_FORMATS.add(AudioFormat.MP3);
SUPPORTED_STREAMS.add(FixedLengthAudioStream.class);
}

public PulseAudioAudioSink(PulseaudioHandler pulseaudioHandler) {
public PulseAudioAudioSink(PulseaudioHandler pulseaudioHandler, ScheduledExecutorService scheduler) {
this.pulseaudioHandler = pulseaudioHandler;
this.scheduler = scheduler;
}

@Override
Expand Down Expand Up @@ -120,11 +126,14 @@ public void connectIfNeeded() throws IOException, InterruptedException {
* Disconnect the socket to pulseaudio simple protocol
*/
public void disconnect() {
if (clientSocket != null) {
if (clientSocket != null && isIdle) {
logger.debug("Disconnecting");
try {
clientSocket.close();
} catch (IOException e) {
}
} else {
logger.debug("Stream still running or socket not open");
}
}

Expand Down Expand Up @@ -153,6 +162,7 @@ public void process(@Nullable AudioStream audioStream)
connectIfNeeded();
if (audioInputStream != null && clientSocket != null) {
// send raw audio to the socket and to pulse audio
isIdle = false;
audioInputStream.transferTo(clientSocket.getOutputStream());
break;
}
Expand All @@ -178,9 +188,16 @@ public void process(@Nullable AudioStream audioStream)
audioInputStream.close();
}
audioStream.close();
scheduleDisconnect();
} catch (IOException e) {
}
}
isIdle = true;
}

public void scheduleDisconnect() {
logger.debug("Scheduling disconnect");
scheduler.schedule(this::disconnect, pulseaudioHandler.getIdleTimeout(), TimeUnit.MILLISECONDS);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ public class PulseaudioBindingConstants {
public static final String DEVICE_PARAMETER_NAME = "name";
public static final String DEVICE_PARAMETER_AUDIO_SINK_ACTIVATION = "activateSimpleProtocolSink";
public static final String DEVICE_PARAMETER_AUDIO_SINK_PORT = "simpleProtocolSinkPort";
public static final String DEVICE_PARAMETER_AUDIO_SINK_IDLE_TIMEOUT = "simpleProtocolSinkIdleTimeout";

public static final String MODULE_SIMPLE_PROTOCOL_TCP_NAME = "module-simple-protocol-tcp";
public static final int MODULE_SIMPLE_PROTOCOL_TCP_DEFAULT_PORT = 4711;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ private void audioSinkSetup() {
public void run() {
// Register the sink as an audio sink in openhab
logger.trace("Registering an audio sink for pulse audio sink thing {}", thing.getUID());
PulseAudioAudioSink audioSink = new PulseAudioAudioSink(thisHandler);
PulseAudioAudioSink audioSink = new PulseAudioAudioSink(thisHandler, scheduler);
setAudioSink(audioSink);
try {
audioSink.connectIfNeeded();
Expand All @@ -128,6 +128,8 @@ public void run() {
} catch (InterruptedException i) {
logger.info("Interrupted during sink audio connection: {}", i.getMessage());
return;
} finally {
audioSink.scheduleDisconnect();
}
@SuppressWarnings("unchecked")
ServiceRegistration<AudioSink> reg = (ServiceRegistration<AudioSink>) bundleContext
Expand Down Expand Up @@ -367,6 +369,11 @@ public int getSimpleTcpPort() throws InterruptedException {
.orElse(simpleTcpPortPref);
}

public int getIdleTimeout() {
return ((BigDecimal) getThing().getConfiguration()
.get(PulseaudioBindingConstants.DEVICE_PARAMETER_AUDIO_SINK_IDLE_TIMEOUT)).intValue();
}

@Override
public void onDeviceRemoved(PulseaudioBridgeHandler bridge, AbstractAudioDeviceConfig device) {
if (device.getPaName().equals(name)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@
<description>Default Port to allocate for use by module-simple-protocol-tcp on the pulseaudio server</description>
<default>4711</default>
</parameter>
<parameter name="simpleProtocolSinkIdleTimeout" type="integer" required="false">
<label>Idle Timeout</label>
<description>Timeout in ms after which the connection will be closed when no stream is running. This ensures that
your speaker is not on all the time and the pulseaudio sink can go to idle mode.
</description>
<default>30000</default>
</parameter>
</config-description>
</thing-type>

Expand Down