[amplipi] Support for more audio streams through the HTTP audio servlet (#15199)

Related to #15113

Signed-off-by: Laurent Garnier <lg.hc@free.fr>
This commit is contained in:
lolodomo 2023-07-08 10:23:08 +02:00 committed by GitHub
parent 2d75536f48
commit 365e900a1f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -13,6 +13,7 @@
package org.openhab.binding.amplipi.internal.audio; package org.openhab.binding.amplipi.internal.audio;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.util.Locale; import java.util.Locale;
import java.util.Set; import java.util.Set;
@ -20,9 +21,9 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.amplipi.internal.AmpliPiHandler; import org.openhab.binding.amplipi.internal.AmpliPiHandler;
import org.openhab.core.audio.AudioFormat; import org.openhab.core.audio.AudioFormat;
import org.openhab.core.audio.AudioSink; import org.openhab.core.audio.AudioSinkSync;
import org.openhab.core.audio.AudioStream; 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.URLAudioStream;
import org.openhab.core.audio.UnsupportedAudioFormatException; import org.openhab.core.audio.UnsupportedAudioFormatException;
import org.openhab.core.audio.UnsupportedAudioStreamException; import org.openhab.core.audio.UnsupportedAudioStreamException;
@ -39,59 +40,66 @@ import org.slf4j.LoggerFactory;
* *
*/ */
@NonNullByDefault @NonNullByDefault
public class PAAudioSink implements AudioSink, ThingHandlerService { public class PAAudioSink extends AudioSinkSync implements ThingHandlerService {
private final Logger logger = LoggerFactory.getLogger(PAAudioSink.class); private final Logger logger = LoggerFactory.getLogger(PAAudioSink.class);
private static final Set<AudioFormat> SUPPORTED_AUDIO_FORMATS = Set.of(AudioFormat.MP3, AudioFormat.WAV); private static final Set<AudioFormat> SUPPORTED_AUDIO_FORMATS = Set.of(AudioFormat.MP3, AudioFormat.WAV);
private static final Set<Class<? extends AudioStream>> SUPPORTED_AUDIO_STREAMS = Set private static final Set<Class<? extends AudioStream>> SUPPORTED_AUDIO_STREAMS = Set.of(AudioStream.class);
.of(FixedLengthAudioStream.class, URLAudioStream.class);
private @Nullable AmpliPiHandler handler; private @Nullable AmpliPiHandler handler;
private @Nullable PercentType volume; private @Nullable PercentType volume;
@Override @Override
public void process(@Nullable AudioStream audioStream) protected void processSynchronously(@Nullable AudioStream audioStream)
throws UnsupportedAudioFormatException, UnsupportedAudioStreamException { throws UnsupportedAudioFormatException, UnsupportedAudioStreamException {
if (audioStream == null) { if (audioStream == null) {
// in case the audioStream is null, this should be interpreted as a request to end any currently playing // in case the audioStream is null, this should be interpreted as a request to end any currently playing
// stream. // stream.
logger.debug("Web Audio sink does not support stopping the currently playing stream."); logger.debug("AmpliPi sink does not support stopping the currently playing stream.");
return; return;
} }
AmpliPiHandler localHandler = this.handler; AmpliPiHandler localHandler = this.handler;
if (localHandler != null) { if (localHandler == null) {
try (AudioStream stream = audioStream) { tryClose(audioStream);
logger.debug("Received audio stream of format {}", audioStream.getFormat()); return;
String audioUrl; }
if (audioStream instanceof URLAudioStream) { logger.debug("Received audio stream of format {}", audioStream.getFormat());
// it is an external URL, so we can directly pass this on. String callbackUrl = localHandler.getCallbackUrl();
URLAudioStream urlAudioStream = (URLAudioStream) audioStream; String audioUrl;
audioUrl = urlAudioStream.getURL(); if (audioStream instanceof URLAudioStream urlAudioStream) {
} else if (audioStream instanceof FixedLengthAudioStream) { // it is an external URL, so we can directly pass this on.
String callbackUrl = localHandler.getCallbackUrl(); audioUrl = urlAudioStream.getURL();
if (callbackUrl == null) { tryClose(audioStream);
throw new UnsupportedAudioStreamException( } else if (callbackUrl != null) {
"Cannot play audio since no callback url is available.", audioStream.getClass()); // we need to serve it for a while
} else { StreamServed streamServed;
// we need to serve it for a while, hence only try {
// FixedLengthAudioStreams are supported. streamServed = localHandler.getAudioHTTPServer().serve(audioStream, 10, true);
String relativeUrl = localHandler.getAudioHTTPServer()
.serve((FixedLengthAudioStream) audioStream, 10).toString();
audioUrl = callbackUrl + relativeUrl;
}
} else {
throw new UnsupportedAudioStreamException(
"Web audio sink can only handle FixedLengthAudioStreams and URLAudioStreams.",
audioStream.getClass());
}
localHandler.playPA(audioUrl, volume);
// we reset the volume value again, so that a next invocation without a volume will again use the zones
// defaults.
volume = null;
} catch (IOException e) { } catch (IOException e) {
logger.debug("Error while closing the audio stream: {}", e.getMessage(), e); tryClose(audioStream);
throw new UnsupportedAudioStreamException(
"AmpliPi was not able to handle the audio stream (cache on disk failed).",
audioStream.getClass(), e);
}
audioUrl = callbackUrl + streamServed.url();
} else {
logger.warn("We do not have any callback url, so AmpliPi cannot play the audio stream!");
tryClose(audioStream);
return;
}
localHandler.playPA(audioUrl, volume);
// we reset the volume value again, so that a next invocation without a volume will again use the zones
// defaults.
volume = null;
}
private void tryClose(@Nullable InputStream is) {
if (is != null) {
try {
is.close();
} catch (IOException ignored) {
} }
} }
} }