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

[squeezebox] Support for more audio streams through the audio servlet #15194

Merged
merged 2 commits into from
Jul 6, 2023
Merged
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 @@ -12,17 +12,20 @@
*/
package org.openhab.binding.squeezebox.internal;

import java.util.HashSet;
import java.io.IOException;
import java.io.InputStream;
import java.util.Locale;
import java.util.Set;

import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.squeezebox.internal.handler.SqueezeBoxPlayerHandler;
import org.openhab.core.audio.AudioFormat;
import org.openhab.core.audio.AudioHTTPServer;
import org.openhab.core.audio.AudioSink;
import org.openhab.core.audio.AudioSinkSync;
import org.openhab.core.audio.AudioStream;
import org.openhab.core.audio.FileAudioStream;
import org.openhab.core.audio.FixedLengthAudioStream;
import org.openhab.core.audio.StreamServed;
import org.openhab.core.audio.URLAudioStream;
import org.openhab.core.audio.UnsupportedAudioFormatException;
import org.openhab.core.audio.UnsupportedAudioStreamException;
Expand All @@ -38,25 +41,17 @@
* @author Mark Hilbush - Initial contribution
* @author Mark Hilbush - Add callbackUrl
*/
public class SqueezeBoxAudioSink implements AudioSink {
public class SqueezeBoxAudioSink extends AudioSinkSync {
private final Logger logger = LoggerFactory.getLogger(SqueezeBoxAudioSink.class);

private static final HashSet<AudioFormat> SUPPORTED_FORMATS = new HashSet<>();
private static final HashSet<Class<? extends AudioStream>> SUPPORTED_STREAMS = new HashSet<>();
private static final Set<AudioFormat> SUPPORTED_FORMATS = Set.of(AudioFormat.WAV, AudioFormat.MP3);
private static final Set<Class<? extends AudioStream>> SUPPORTED_STREAMS = Set.of(AudioStream.class);

// Needed because Squeezebox does multiple requests for the stream
private static final int STREAM_TIMEOUT = 15;
private static final int STREAM_TIMEOUT = 10;

private String callbackUrl;

static {
SUPPORTED_FORMATS.add(AudioFormat.WAV);
SUPPORTED_FORMATS.add(AudioFormat.MP3);

SUPPORTED_STREAMS.add(FixedLengthAudioStream.class);
SUPPORTED_STREAMS.add(URLAudioStream.class);
}

private AudioHTTPServer audioHTTPServer;
private SqueezeBoxPlayerHandler playerHandler;

Expand All @@ -81,43 +76,64 @@ public String getLabel(Locale locale) {
}

@Override
public void process(AudioStream audioStream)
public void processSynchronously(AudioStream audioStream)
throws UnsupportedAudioFormatException, UnsupportedAudioStreamException {
if (audioStream == null) {
return;
}
AudioFormat format = audioStream.getFormat();
if (!AudioFormat.WAV.isCompatible(format) && !AudioFormat.MP3.isCompatible(format)) {
tryClose(audioStream);
throw new UnsupportedAudioFormatException("Currently only MP3 and WAV formats are supported: ", format);
}

String url;
if (audioStream instanceof URLAudioStream) {
url = ((URLAudioStream) audioStream).getURL();
lolodomo marked this conversation as resolved.
Show resolved Hide resolved
} else if (audioStream instanceof FixedLengthAudioStream) {
// Since Squeezebox will make multiple requests for the stream, set a timeout on the stream
url = audioHTTPServer.serve((FixedLengthAudioStream) audioStream, STREAM_TIMEOUT).toString();

if (AudioFormat.WAV.isCompatible(format)) {
url += AudioStreamUtils.EXTENSION_SEPARATOR + FileAudioStream.WAV_EXTENSION;
} else if (AudioFormat.MP3.isCompatible(format)) {
url += AudioStreamUtils.EXTENSION_SEPARATOR + FileAudioStream.MP3_EXTENSION;
}

// Form the URL for streaming the notification from the OH2 web server
// Use the callback URL if it is set in the binding configuration
String host = callbackUrl == null || callbackUrl.isEmpty() ? playerHandler.getHostAndPort() : callbackUrl;
if (host == null) {
logger.warn("Unable to get host/port from which to stream notification");
return;
}
url = host + url;
tryClose(audioStream);
} else {
throw new UnsupportedAudioStreamException(
"SqueezeBox can only handle URLAudioStream or FixedLengthAudioStreams.", null);
try {
// Since Squeezebox will make multiple requests for the stream, set multiple to true
StreamServed streamServed = audioHTTPServer.serve(audioStream, STREAM_TIMEOUT, true);
url = streamServed.url();

if (AudioFormat.WAV.isCompatible(format)) {
url += AudioStreamUtils.EXTENSION_SEPARATOR + FileAudioStream.WAV_EXTENSION;
} else if (AudioFormat.MP3.isCompatible(format)) {
url += AudioStreamUtils.EXTENSION_SEPARATOR + FileAudioStream.MP3_EXTENSION;
}

// Form the URL for streaming the notification from the OH web server
// Use the callback URL if it is set in the binding configuration
String host = callbackUrl == null || callbackUrl.isEmpty() ? playerHandler.getHostAndPort()
: callbackUrl;
if (host == null) {
logger.warn("Unable to get host/port from which to stream notification");
tryClose(audioStream);
return;
lolodomo marked this conversation as resolved.
Show resolved Hide resolved
}
url = host + url;
} catch (IOException e) {
tryClose(audioStream);
throw new UnsupportedAudioStreamException(
"Squeezebox binding was not able to handle the audio stream (cache on disk failed)",
audioStream.getClass(), e);
}
}

logger.debug("Processing audioStream {} of format {}", url, format);
playerHandler.playNotificationSoundURI(new StringType(url));
}

private void tryClose(@Nullable InputStream is) {
if (is != null) {
try {
is.close();
} catch (IOException ignored) {
}
}
}

@Override
public Set<AudioFormat> getSupportedFormats() {
return SUPPORTED_FORMATS;
Expand Down