[heos] Support for more audio streams through the HTTP audio servlet (#15196)
Related to #15113 Signed-off-by: Laurent Garnier <lg.hc@free.fr>
This commit is contained in:
parent
5c32f80c3b
commit
7c9f66ffb3
|
@ -13,7 +13,7 @@
|
||||||
package org.openhab.binding.heos.internal.api;
|
package org.openhab.binding.heos.internal.api;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.HashSet;
|
import java.io.InputStream;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
@ -24,11 +24,13 @@ import org.openhab.binding.heos.internal.resources.Telnet.ReadException;
|
||||||
import org.openhab.core.audio.AudioFormat;
|
import org.openhab.core.audio.AudioFormat;
|
||||||
import org.openhab.core.audio.AudioHTTPServer;
|
import org.openhab.core.audio.AudioHTTPServer;
|
||||||
import org.openhab.core.audio.AudioSink;
|
import org.openhab.core.audio.AudioSink;
|
||||||
|
import org.openhab.core.audio.AudioSinkAsync;
|
||||||
import org.openhab.core.audio.AudioStream;
|
import org.openhab.core.audio.AudioStream;
|
||||||
import org.openhab.core.audio.FileAudioStream;
|
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.URLAudioStream;
|
||||||
import org.openhab.core.audio.UnsupportedAudioFormatException;
|
import org.openhab.core.audio.UnsupportedAudioFormatException;
|
||||||
|
import org.openhab.core.audio.UnsupportedAudioStreamException;
|
||||||
import org.openhab.core.audio.utils.AudioStreamUtils;
|
import org.openhab.core.audio.utils.AudioStreamUtils;
|
||||||
import org.openhab.core.library.types.PercentType;
|
import org.openhab.core.library.types.PercentType;
|
||||||
import org.openhab.core.thing.util.ThingHandlerHelper;
|
import org.openhab.core.thing.util.ThingHandlerHelper;
|
||||||
|
@ -39,22 +41,15 @@ import org.slf4j.LoggerFactory;
|
||||||
* This makes HEOS to serve as an {@link AudioSink}.
|
* This makes HEOS to serve as an {@link AudioSink}.
|
||||||
*
|
*
|
||||||
* @author Johannes Einig - Initial contribution
|
* @author Johannes Einig - Initial contribution
|
||||||
|
* @author Laurent Garnier - Extend AudioSinkAsync
|
||||||
*/
|
*/
|
||||||
@NonNullByDefault
|
@NonNullByDefault
|
||||||
public class HeosAudioSink implements AudioSink {
|
public class HeosAudioSink extends AudioSinkAsync {
|
||||||
private final Logger logger = LoggerFactory.getLogger(HeosAudioSink.class);
|
private final Logger logger = LoggerFactory.getLogger(HeosAudioSink.class);
|
||||||
|
|
||||||
private static final Set<AudioFormat> SUPPORTED_AUDIO_FORMATS = new HashSet<>();
|
private static final Set<AudioFormat> SUPPORTED_AUDIO_FORMATS = Set.of(AudioFormat.WAV, AudioFormat.MP3,
|
||||||
private static final Set<Class<? extends AudioStream>> SUPPORTED_AUDIO_STREAMS = new HashSet<>();
|
AudioFormat.AAC);
|
||||||
|
private static final Set<Class<? extends AudioStream>> SUPPORTED_AUDIO_STREAMS = Set.of(AudioStream.class);
|
||||||
static {
|
|
||||||
SUPPORTED_AUDIO_FORMATS.add(AudioFormat.WAV);
|
|
||||||
SUPPORTED_AUDIO_FORMATS.add(AudioFormat.MP3);
|
|
||||||
SUPPORTED_AUDIO_FORMATS.add(AudioFormat.AAC);
|
|
||||||
|
|
||||||
SUPPORTED_AUDIO_STREAMS.add(URLAudioStream.class);
|
|
||||||
SUPPORTED_AUDIO_STREAMS.add(FixedLengthAudioStream.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
private final HeosThingBaseHandler handler;
|
private final HeosThingBaseHandler handler;
|
||||||
private final AudioHTTPServer audioHTTPServer;
|
private final AudioHTTPServer audioHTTPServer;
|
||||||
|
@ -77,42 +72,72 @@ public class HeosAudioSink implements AudioSink {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void process(@Nullable AudioStream audioStream) throws UnsupportedAudioFormatException {
|
protected void processAsynchronously(@Nullable AudioStream audioStream)
|
||||||
try {
|
throws UnsupportedAudioFormatException, UnsupportedAudioStreamException {
|
||||||
if (audioStream instanceof URLAudioStream) {
|
if (!ThingHandlerHelper.isHandlerInitialized(handler)) {
|
||||||
// it is an external URL, the speaker can access it itself and play it.
|
logger.debug("HEOS speaker '{}' is not initialized - status is {}", handler.getThing().getUID(),
|
||||||
URLAudioStream urlAudioStream = (URLAudioStream) audioStream;
|
handler.getThing().getStatus());
|
||||||
handler.playURL(urlAudioStream.getURL());
|
tryClose(audioStream);
|
||||||
} else if (audioStream instanceof FixedLengthAudioStream) {
|
return;
|
||||||
if (callbackUrl != null) {
|
}
|
||||||
// we serve it on our own HTTP server for 30 seconds as HEOS requests the stream several times
|
|
||||||
String relativeUrl = audioHTTPServer.serve((FixedLengthAudioStream) audioStream, 30);
|
if (audioStream == null) {
|
||||||
String url = callbackUrl + relativeUrl + AudioStreamUtils.EXTENSION_SEPARATOR;
|
return;
|
||||||
AudioFormat audioFormat = audioStream.getFormat();
|
}
|
||||||
if (!ThingHandlerHelper.isHandlerInitialized(handler)) {
|
|
||||||
logger.debug("HEOS speaker '{}' is not initialized - status is {}", handler.getThing().getUID(),
|
AudioFormat audioFormat = audioStream.getFormat();
|
||||||
handler.getThing().getStatus());
|
if (!AudioFormat.MP3.isCompatible(audioFormat) && !AudioFormat.WAV.isCompatible(audioFormat)
|
||||||
} else if (AudioFormat.MP3.isCompatible(audioFormat)) {
|
&& !AudioFormat.AAC.isCompatible(audioFormat)) {
|
||||||
handler.playURL(url + FileAudioStream.MP3_EXTENSION);
|
tryClose(audioStream);
|
||||||
} else if (AudioFormat.WAV.isCompatible(audioFormat)) {
|
throw new UnsupportedAudioFormatException("HEOS speaker only supports MP3, WAV and AAC formats.",
|
||||||
handler.playURL(url + FileAudioStream.WAV_EXTENSION);
|
audioFormat);
|
||||||
} else if (AudioFormat.AAC.isCompatible(audioFormat)) {
|
}
|
||||||
handler.playURL(url + FileAudioStream.AAC_EXTENSION);
|
|
||||||
} else {
|
String url;
|
||||||
throw new UnsupportedAudioFormatException("HEOS only supports MP3, WAV and AAC.", audioFormat);
|
if (audioStream instanceof URLAudioStream urlAudioStream) {
|
||||||
}
|
// it is an external URL, the speaker can access it itself and play it.
|
||||||
} else {
|
url = urlAudioStream.getURL();
|
||||||
logger.warn("We do not have any callback url, so HEOS cannot play the audio stream!");
|
tryClose(audioStream);
|
||||||
}
|
} else if (callbackUrl != null) {
|
||||||
} else {
|
StreamServed streamServed;
|
||||||
throw new UnsupportedAudioFormatException(
|
try {
|
||||||
"HEOS can only handle FixedLengthAudioStreams & URLAudioStream.", null);
|
streamServed = audioHTTPServer.serve(audioStream, 10, true);
|
||||||
|
} catch (IOException e) {
|
||||||
|
tryClose(audioStream);
|
||||||
|
throw new UnsupportedAudioStreamException(
|
||||||
|
"HEOS was not able to handle the audio stream (cache on disk failed).", audioStream.getClass(),
|
||||||
|
e);
|
||||||
}
|
}
|
||||||
|
url = callbackUrl + streamServed.url() + AudioStreamUtils.EXTENSION_SEPARATOR;
|
||||||
|
if (AudioFormat.MP3.isCompatible(audioFormat)) {
|
||||||
|
url += FileAudioStream.MP3_EXTENSION;
|
||||||
|
} else if (AudioFormat.WAV.isCompatible(audioFormat)) {
|
||||||
|
url += FileAudioStream.WAV_EXTENSION;
|
||||||
|
} else if (AudioFormat.AAC.isCompatible(audioFormat)) {
|
||||||
|
url += FileAudioStream.AAC_EXTENSION;
|
||||||
|
}
|
||||||
|
streamServed.playEnd().thenRun(() -> this.playbackFinished(audioStream));
|
||||||
|
} else {
|
||||||
|
logger.warn("We do not have any callback url, so HEOS cannot play the audio stream!");
|
||||||
|
tryClose(audioStream);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
handler.playURL(url);
|
||||||
} catch (IOException | ReadException e) {
|
} catch (IOException | ReadException e) {
|
||||||
logger.warn("Failed to play audio stream: {}", e.getMessage());
|
logger.warn("Failed to play audio stream: {}", e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void tryClose(@Nullable InputStream is) {
|
||||||
|
if (is != null) {
|
||||||
|
try {
|
||||||
|
is.close();
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<AudioFormat> getSupportedFormats() {
|
public Set<AudioFormat> getSupportedFormats() {
|
||||||
return SUPPORTED_AUDIO_FORMATS;
|
return SUPPORTED_AUDIO_FORMATS;
|
||||||
|
|
Loading…
Reference in New Issue