[pulseaudio] Add "idle timeout" to Pulseaudio audio sink (#10731)
* 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>
This commit is contained in:
parent
6d68746b60
commit
7109475929
@ -18,6 +18,8 @@ import java.net.Socket;
|
|||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Set;
|
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.convert.MpegFormatConversionProvider;
|
||||||
import javazoom.spi.mpeg.sampled.file.MpegAudioFileReader;
|
import javazoom.spi.mpeg.sampled.file.MpegAudioFileReader;
|
||||||
|
|
||||||
@ -52,17 +54,21 @@ public class PulseAudioAudioSink implements AudioSink {
|
|||||||
private static final HashSet<Class<? extends AudioStream>> SUPPORTED_STREAMS = new HashSet<>();
|
private static final HashSet<Class<? extends AudioStream>> SUPPORTED_STREAMS = new HashSet<>();
|
||||||
|
|
||||||
private PulseaudioHandler pulseaudioHandler;
|
private PulseaudioHandler pulseaudioHandler;
|
||||||
|
private ScheduledExecutorService scheduler;
|
||||||
|
|
||||||
private @Nullable Socket clientSocket;
|
private @Nullable Socket clientSocket;
|
||||||
|
|
||||||
|
private boolean isIdle = true;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
SUPPORTED_FORMATS.add(AudioFormat.WAV);
|
SUPPORTED_FORMATS.add(AudioFormat.WAV);
|
||||||
SUPPORTED_FORMATS.add(AudioFormat.MP3);
|
SUPPORTED_FORMATS.add(AudioFormat.MP3);
|
||||||
SUPPORTED_STREAMS.add(FixedLengthAudioStream.class);
|
SUPPORTED_STREAMS.add(FixedLengthAudioStream.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public PulseAudioAudioSink(PulseaudioHandler pulseaudioHandler) {
|
public PulseAudioAudioSink(PulseaudioHandler pulseaudioHandler, ScheduledExecutorService scheduler) {
|
||||||
this.pulseaudioHandler = pulseaudioHandler;
|
this.pulseaudioHandler = pulseaudioHandler;
|
||||||
|
this.scheduler = scheduler;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -120,11 +126,14 @@ public class PulseAudioAudioSink implements AudioSink {
|
|||||||
* Disconnect the socket to pulseaudio simple protocol
|
* Disconnect the socket to pulseaudio simple protocol
|
||||||
*/
|
*/
|
||||||
public void disconnect() {
|
public void disconnect() {
|
||||||
if (clientSocket != null) {
|
if (clientSocket != null && isIdle) {
|
||||||
|
logger.debug("Disconnecting");
|
||||||
try {
|
try {
|
||||||
clientSocket.close();
|
clientSocket.close();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
logger.debug("Stream still running or socket not open");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,6 +162,7 @@ public class PulseAudioAudioSink implements AudioSink {
|
|||||||
connectIfNeeded();
|
connectIfNeeded();
|
||||||
if (audioInputStream != null && clientSocket != null) {
|
if (audioInputStream != null && clientSocket != null) {
|
||||||
// send raw audio to the socket and to pulse audio
|
// send raw audio to the socket and to pulse audio
|
||||||
|
isIdle = false;
|
||||||
audioInputStream.transferTo(clientSocket.getOutputStream());
|
audioInputStream.transferTo(clientSocket.getOutputStream());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -178,9 +188,16 @@ public class PulseAudioAudioSink implements AudioSink {
|
|||||||
audioInputStream.close();
|
audioInputStream.close();
|
||||||
}
|
}
|
||||||
audioStream.close();
|
audioStream.close();
|
||||||
|
scheduleDisconnect();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
isIdle = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void scheduleDisconnect() {
|
||||||
|
logger.debug("Scheduling disconnect");
|
||||||
|
scheduler.schedule(this::disconnect, pulseaudioHandler.getIdleTimeout(), TimeUnit.MILLISECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@ -53,6 +53,7 @@ public class PulseaudioBindingConstants {
|
|||||||
public static final String DEVICE_PARAMETER_NAME = "name";
|
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_ACTIVATION = "activateSimpleProtocolSink";
|
||||||
public static final String DEVICE_PARAMETER_AUDIO_SINK_PORT = "simpleProtocolSinkPort";
|
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 String MODULE_SIMPLE_PROTOCOL_TCP_NAME = "module-simple-protocol-tcp";
|
||||||
public static final int MODULE_SIMPLE_PROTOCOL_TCP_DEFAULT_PORT = 4711;
|
public static final int MODULE_SIMPLE_PROTOCOL_TCP_DEFAULT_PORT = 4711;
|
||||||
|
|||||||
@ -118,7 +118,7 @@ public class PulseaudioHandler extends BaseThingHandler implements DeviceStatusL
|
|||||||
public void run() {
|
public void run() {
|
||||||
// Register the sink as an audio sink in openhab
|
// Register the sink as an audio sink in openhab
|
||||||
logger.trace("Registering an audio sink for pulse audio sink thing {}", thing.getUID());
|
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);
|
setAudioSink(audioSink);
|
||||||
try {
|
try {
|
||||||
audioSink.connectIfNeeded();
|
audioSink.connectIfNeeded();
|
||||||
@ -128,6 +128,8 @@ public class PulseaudioHandler extends BaseThingHandler implements DeviceStatusL
|
|||||||
} catch (InterruptedException i) {
|
} catch (InterruptedException i) {
|
||||||
logger.info("Interrupted during sink audio connection: {}", i.getMessage());
|
logger.info("Interrupted during sink audio connection: {}", i.getMessage());
|
||||||
return;
|
return;
|
||||||
|
} finally {
|
||||||
|
audioSink.scheduleDisconnect();
|
||||||
}
|
}
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
ServiceRegistration<AudioSink> reg = (ServiceRegistration<AudioSink>) bundleContext
|
ServiceRegistration<AudioSink> reg = (ServiceRegistration<AudioSink>) bundleContext
|
||||||
@ -367,6 +369,11 @@ public class PulseaudioHandler extends BaseThingHandler implements DeviceStatusL
|
|||||||
.orElse(simpleTcpPortPref);
|
.orElse(simpleTcpPortPref);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getIdleTimeout() {
|
||||||
|
return ((BigDecimal) getThing().getConfiguration()
|
||||||
|
.get(PulseaudioBindingConstants.DEVICE_PARAMETER_AUDIO_SINK_IDLE_TIMEOUT)).intValue();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDeviceRemoved(PulseaudioBridgeHandler bridge, AbstractAudioDeviceConfig device) {
|
public void onDeviceRemoved(PulseaudioBridgeHandler bridge, AbstractAudioDeviceConfig device) {
|
||||||
if (device.getPaName().equals(name)) {
|
if (device.getPaName().equals(name)) {
|
||||||
|
|||||||
@ -33,6 +33,13 @@
|
|||||||
<description>Default Port to allocate for use by module-simple-protocol-tcp on the pulseaudio server</description>
|
<description>Default Port to allocate for use by module-simple-protocol-tcp on the pulseaudio server</description>
|
||||||
<default>4711</default>
|
<default>4711</default>
|
||||||
</parameter>
|
</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>
|
</config-description>
|
||||||
</thing-type>
|
</thing-type>
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user