From d4ac7683931ed6e0af31b1d881736b232a62b85f Mon Sep 17 00:00:00 2001 From: Laurent Garnier Date: Mon, 19 Jun 2023 21:07:19 +0200 Subject: [PATCH] [freebox] Support for more audio streams through the HTTP audio servlet Related to #15113 Signed-off-by: Laurent Garnier --- .../internal/FreeboxAirPlayAudioSink.java | 79 +++++++++++-------- 1 file changed, 45 insertions(+), 34 deletions(-) diff --git a/bundles/org.openhab.binding.freebox/src/main/java/org/openhab/binding/freebox/internal/FreeboxAirPlayAudioSink.java b/bundles/org.openhab.binding.freebox/src/main/java/org/openhab/binding/freebox/internal/FreeboxAirPlayAudioSink.java index aff314d448684..06b8cee20a9fe 100644 --- a/bundles/org.openhab.binding.freebox/src/main/java/org/openhab/binding/freebox/internal/FreeboxAirPlayAudioSink.java +++ b/bundles/org.openhab.binding.freebox/src/main/java/org/openhab/binding/freebox/internal/FreeboxAirPlayAudioSink.java @@ -27,8 +27,9 @@ 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.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; @@ -43,9 +44,10 @@ * This makes an AirPlay device to serve as an {@link AudioSink}- * * @author Laurent Garnier - Initial contribution for AudioSink and notifications + * @author Laurent Garnier - Support for more audio streams through the HTTP audio servlet */ @NonNullByDefault -public class FreeboxAirPlayAudioSink implements AudioSink { +public class FreeboxAirPlayAudioSink extends AudioSinkAsync { private final Logger logger = LoggerFactory.getLogger(FreeboxAirPlayAudioSink.class); @@ -59,15 +61,11 @@ public class FreeboxAirPlayAudioSink implements AudioSink { private static final AudioFormat MP3_320 = new AudioFormat(CONTAINER_NONE, CODEC_MP3, null, null, 320000, null); private static final Set SUPPORTED_FORMATS = new HashSet<>(); - private static final HashSet> SUPPORTED_STREAMS = new HashSet<>(); + private static final Set> SUPPORTED_STREAMS = Set.of(AudioStream.class); private AudioHTTPServer audioHTTPServer; private FreeboxThingHandler handler; private @Nullable String callbackUrl; - static { - SUPPORTED_STREAMS.add(AudioStream.class); - } - public FreeboxAirPlayAudioSink(FreeboxThingHandler handler, AudioHTTPServer audioHTTPServer, @Nullable String callbackUrl) { this.handler = handler; @@ -103,7 +101,7 @@ public String getId() { } @Override - public void process(@Nullable AudioStream audioStream) + protected void processAsynchronously(@Nullable AudioStream audioStream) throws UnsupportedAudioFormatException, UnsupportedAudioStreamException { if (!ThingHandlerHelper.isHandlerInitialized(handler) || ((handler.getThing().getStatus() == ThingStatus.OFFLINE) @@ -122,35 +120,48 @@ public void process(@Nullable AudioStream audioStream) return; } - String url = null; - if (audioStream instanceof URLAudioStream) { + if (audioStream instanceof URLAudioStream urlAudioStream) { // it is an external URL, we can access it directly - URLAudioStream urlAudioStream = (URLAudioStream) audioStream; - url = urlAudioStream.getURL(); - } else { - if (callbackUrl != null) { - // we serve it on our own HTTP server - String relativeUrl; - if (audioStream instanceof FixedLengthAudioStream) { - relativeUrl = audioHTTPServer.serve((FixedLengthAudioStream) audioStream, 20); - } else { - relativeUrl = audioHTTPServer.serve(audioStream); + try { + logger.debug("AirPlay audio sink: process url {}", urlAudioStream.getURL()); + handler.playMedia(urlAudioStream.getURL()); + } catch (FreeboxException e) { + logger.warn("Audio stream playback failed: {}", e.getMessage()); + } + try { + audioStream.close(); + } catch (IOException e) { + logger.debug("Exception while closing audioStream"); + } + } else if (callbackUrl != null) { + // we serve it on our own HTTP server + StreamServed streamServed; + try { + streamServed = audioHTTPServer.serve(audioStream, 5, true); + } catch (IOException e) { + try { + audioStream.close(); + } catch (IOException ex) { + logger.debug("Exception while closing audioStream"); } - url = callbackUrl + relativeUrl; - } else { - logger.warn("We do not have any callback url, so AirPlay device cannot play the audio stream!"); + throw new UnsupportedAudioStreamException( + "AirPlay device was not able to handle the audio stream (cache on disk failed).", + audioStream.getClass(), e); + } + streamServed.playEnd().thenRun(() -> this.playbackFinished(audioStream)); + try { + logger.debug("AirPlay audio sink: process url {}", callbackUrl + streamServed.url()); + handler.playMedia(callbackUrl + streamServed.url()); + } catch (FreeboxException e) { + logger.warn("Audio stream playback failed: {}", e.getMessage()); + } + } else { + logger.warn("We do not have any callback url, so AirPlay device cannot play the audio stream!"); + try { + audioStream.close(); + } catch (IOException e) { + logger.debug("Exception while closing audioStream"); } - } - try { - audioStream.close(); - } catch (IOException e) { - logger.debug("Exception while closing audioStream", e); - } - try { - logger.debug("AirPlay audio sink: process url {}", url); - handler.playMedia(url); - } catch (FreeboxException e) { - logger.warn("Audio stream playback failed: {}", e.getMessage()); } }