Skip to content

Commit

Permalink
[amplipi] Support for more audio streams through the HTTP audio servl…
Browse files Browse the repository at this point in the history
…et (openhab#15199)

Related to openhab#15113

Signed-off-by: Laurent Garnier <lg.hc@free.fr>
Signed-off-by: Jørgen Austvik <jaustvik@acm.org>
  • Loading branch information
lolodomo authored and austvik committed Mar 27, 2024
1 parent 4b0b80e commit a49657d
Showing 1 changed file with 45 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,17 @@
package org.openhab.binding.amplipi.internal.audio;

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

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.amplipi.internal.AmpliPiHandler;
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.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 @@ -39,59 +40,66 @@
*
*/
@NonNullByDefault
public class PAAudioSink implements AudioSink, ThingHandlerService {
public class PAAudioSink extends AudioSinkSync implements ThingHandlerService {

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<Class<? extends AudioStream>> SUPPORTED_AUDIO_STREAMS = Set
.of(FixedLengthAudioStream.class, URLAudioStream.class);
private static final Set<Class<? extends AudioStream>> SUPPORTED_AUDIO_STREAMS = Set.of(AudioStream.class);

private @Nullable AmpliPiHandler handler;

private @Nullable PercentType volume;

@Override
public void process(@Nullable AudioStream audioStream)
protected void processSynchronously(@Nullable AudioStream audioStream)
throws UnsupportedAudioFormatException, UnsupportedAudioStreamException {
if (audioStream == null) {
// in case the audioStream is null, this should be interpreted as a request to end any currently playing
// 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;
}
AmpliPiHandler localHandler = this.handler;
if (localHandler != null) {
try (AudioStream stream = audioStream) {
logger.debug("Received audio stream of format {}", audioStream.getFormat());
String audioUrl;
if (audioStream instanceof URLAudioStream) {
// it is an external URL, so we can directly pass this on.
URLAudioStream urlAudioStream = (URLAudioStream) audioStream;
audioUrl = urlAudioStream.getURL();
} else if (audioStream instanceof FixedLengthAudioStream) {
String callbackUrl = localHandler.getCallbackUrl();
if (callbackUrl == null) {
throw new UnsupportedAudioStreamException(
"Cannot play audio since no callback url is available.", audioStream.getClass());
} else {
// we need to serve it for a while, hence only
// FixedLengthAudioStreams are supported.
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;
if (localHandler == null) {
tryClose(audioStream);
return;
}
logger.debug("Received audio stream of format {}", audioStream.getFormat());
String callbackUrl = localHandler.getCallbackUrl();
String audioUrl;
if (audioStream instanceof URLAudioStream urlAudioStream) {
// it is an external URL, so we can directly pass this on.
audioUrl = urlAudioStream.getURL();
tryClose(audioStream);
} else if (callbackUrl != null) {
// we need to serve it for a while
StreamServed streamServed;
try {
streamServed = localHandler.getAudioHTTPServer().serve(audioStream, 10, true);
} 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) {
}
}
}
Expand Down

0 comments on commit a49657d

Please sign in to comment.