From 1ea514787b74f81593ce2c494fd426873c3c4a78 Mon Sep 17 00:00:00 2001 From: lolodomo Date: Wed, 12 Jul 2023 21:43:55 +0200 Subject: [PATCH] [allplay] Support for more audio streams through the HTTP audio servlet (#15201) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [allplay] Support for more audio streams through the HTTP audio servlet Related to #15113 Signed-off-by: Laurent Garnier Signed-off-by: Jørgen Austvik --- .../allplay/internal/AllPlayAudioSink.java | 90 +++++++++---------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/bundles/org.openhab.binding.allplay/src/main/java/org/openhab/binding/allplay/internal/AllPlayAudioSink.java b/bundles/org.openhab.binding.allplay/src/main/java/org/openhab/binding/allplay/internal/AllPlayAudioSink.java index 76f54b8b5025f..774505dd43254 100644 --- a/bundles/org.openhab.binding.allplay/src/main/java/org/openhab/binding/allplay/internal/AllPlayAudioSink.java +++ b/bundles/org.openhab.binding.allplay/src/main/java/org/openhab/binding/allplay/internal/AllPlayAudioSink.java @@ -13,15 +13,18 @@ package org.openhab.binding.allplay.internal; import java.io.IOException; -import java.util.HashSet; +import java.io.InputStream; import java.util.Locale; import java.util.Set; +import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.allplay.internal.handler.AllPlayHandler; import org.openhab.core.audio.AudioFormat; import org.openhab.core.audio.AudioHTTPServer; import org.openhab.core.audio.AudioSink; +import org.openhab.core.audio.AudioSinkAsync; import org.openhab.core.audio.AudioStream; +import org.openhab.core.audio.StreamServed; import org.openhab.core.audio.URLAudioStream; import org.openhab.core.audio.UnsupportedAudioFormatException; import org.openhab.core.audio.UnsupportedAudioStreamException; @@ -36,23 +39,16 @@ * * @author Dominic Lerbs - Initial contribution */ -public class AllPlayAudioSink implements AudioSink { +public class AllPlayAudioSink extends AudioSinkAsync { private final Logger logger = LoggerFactory.getLogger(AllPlayAudioSink.class); - private static final HashSet SUPPORTED_FORMATS = new HashSet<>(); - private static final HashSet> SUPPORTED_STREAMS = new HashSet<>(); + private static final Set SUPPORTED_FORMATS = Set.of(AudioFormat.MP3, AudioFormat.WAV); + private static final Set> SUPPORTED_STREAMS = Set.of(AudioStream.class); private final AllPlayHandler handler; private final AudioHTTPServer audioHTTPServer; private final String callbackUrl; - static { - SUPPORTED_FORMATS.add(AudioFormat.MP3); - SUPPORTED_FORMATS.add(AudioFormat.WAV); - - SUPPORTED_STREAMS.add(AudioStream.class); - } - /** * @param handler The related {@link AllPlayHandler} * @param audioHTTPServer The {@link AudioHTTPServer} for serving the stream @@ -75,13 +71,41 @@ public String getLabel(Locale locale) { } @Override - public void process(AudioStream audioStream) + protected void processAsynchronously(@Nullable AudioStream audioStream) throws UnsupportedAudioFormatException, UnsupportedAudioStreamException { + if (audioStream == null) { + return; + } + String url; + if (audioStream instanceof URLAudioStream urlAudioStream) { + // it is an external URL, the speaker can access it itself and play it + url = urlAudioStream.getURL(); + tryClose(audioStream); + } else if (callbackUrl != null) { + StreamServed streamServed; + try { + streamServed = audioHTTPServer.serve(audioStream, 10, true); + } catch (IOException e) { + tryClose(audioStream); + throw new UnsupportedAudioStreamException( + "AllPlay was not able to handle the audio stream (cache on disk failed).", + audioStream.getClass(), e); + } + url = callbackUrl + streamServed.url(); + streamServed.playEnd().thenRun(() -> this.playbackFinished(audioStream)); + } else { + logger.warn("We do not have any callback url, so AllPlay cannot play the audio stream!"); + tryClose(audioStream); + return; + } try { - String url = convertAudioStreamToUrl(audioStream); handler.playUrl(url); - } catch (SpeakerException | AllPlayCallbackException e) { - logger.warn("Unable to play audio stream on speaker {}", getId(), e); + } catch (SpeakerException e) { + if (logger.isDebugEnabled()) { + logger.warn("Unable to play audio stream on speaker {}", getId(), e); + } else { + logger.warn("Unable to play audio stream on speaker {}: {}", getId(), e.getMessage()); + } } } @@ -113,36 +137,12 @@ public void setVolume(PercentType volume) throws IOException { } } - /** - * Converts the given {@link AudioStream} into an URL which can be used for streaming. - * - * @param audioStream The incoming {@link AudioStream} - * @return The URL to use for streaming - * @throws AllPlayCallbackException Exception if the URL cannot be created - */ - private String convertAudioStreamToUrl(AudioStream audioStream) throws AllPlayCallbackException { - if (audioStream instanceof URLAudioStream) { - // it is an external URL, the speaker can access it itself and play it - return ((URLAudioStream) audioStream).getURL(); - } else { - return createUrlForLocalHttpServer(audioStream); - } - } - - private String createUrlForLocalHttpServer(AudioStream audioStream) throws AllPlayCallbackException { - if (callbackUrl != null) { - String relativeUrl = audioHTTPServer.serve(audioStream); - return callbackUrl + relativeUrl; - } else { - throw new AllPlayCallbackException("Unable to play audio stream as callback URL is not set"); - } - } - - @SuppressWarnings("serial") - private class AllPlayCallbackException extends Exception { - - public AllPlayCallbackException(String message) { - super(message); + private void tryClose(@Nullable InputStream is) { + if (is != null) { + try { + is.close(); + } catch (IOException ignored) { + } } } }