Skip to content

Commit

Permalink
[pulseaudio] Add "idle timeout" to Pulseaudio audio sink (openhab#10731)
Browse files Browse the repository at this point in the history
* Add "idle timeout" to pulseaudio audio sink to allow the sink to disconnect after being idle

Signed-off-by: Timo Litzius <dev@dbzman-online.eu>
  • Loading branch information
Dbzman authored and thinkingstone committed Nov 7, 2021
1 parent e2d7f00 commit 3dc8154
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 3 deletions.
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

0 comments on commit 3dc8154

Please sign in to comment.