org.eclipse.smarthome.core.audio.URLAudioStream.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.smarthome.core.audio.URLAudioStream.java

Source

/**
 * Copyright (c) 2014-2017 by the respective copyright holders.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 */
package org.eclipse.smarthome.core.audio;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.Socket;
import java.net.URL;
import java.net.URLConnection;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * This is an AudioStream from an URL. Note that some sinks, like Sonos, can directly handle URL
 * based streams, and therefore can/should call getURL() to get an direct reference to the URL.
 *
 * @author Karel Goderis - Initial contribution and API
 * @author Kai Kreuzer - Refactored to not require a source
 *
 */
public class URLAudioStream extends org.eclipse.smarthome.core.audio.AudioStream {

    private static final Pattern plsStreamPattern = Pattern.compile("^File[0-9]=(.+)$");

    private final Logger logger = LoggerFactory.getLogger(URLAudioStream.class);

    private AudioFormat audioFormat;
    private final InputStream inputStream;
    private String url;

    private Socket shoutCastSocket;

    public URLAudioStream(String url) throws AudioException {
        if (url == null) {
            throw new IllegalArgumentException("url must not be null!");
        }
        this.url = url;
        this.audioFormat = new AudioFormat(AudioFormat.CONTAINER_NONE, AudioFormat.CODEC_MP3, false, 16, null,
                null);
        this.inputStream = createInputStream();
    }

    private InputStream createInputStream() throws AudioException {
        try {
            if (url.toLowerCase().endsWith(".m3u")) {
                InputStream is = new URL(url).openStream();
                String urls = IOUtils.toString(is);
                for (String line : urls.split("\n")) {
                    if (!line.isEmpty() && !line.startsWith("#")) {
                        url = line;
                        break;
                    }
                }
            } else if (url.toLowerCase().endsWith(".pls")) {
                InputStream is = new URL(url).openStream();
                String urls = IOUtils.toString(is);
                for (String line : urls.split("\n")) {
                    if (!line.isEmpty() && line.startsWith("File")) {
                        Matcher matcher = plsStreamPattern.matcher(line);
                        if (matcher.find()) {
                            url = matcher.group(1);
                            break;
                        }
                    }
                }
            }
            URL streamUrl = new URL(url);
            URLConnection connection = streamUrl.openConnection();
            InputStream is = null;
            if (connection.getContentType().equals("unknown/unknown")) {
                // Java does not parse non-standard headers used by SHOUTCast
                int port = streamUrl.getPort() > 0 ? streamUrl.getPort() : 80;
                // Manipulate User-Agent to receive a stream
                shoutCastSocket = new Socket(streamUrl.getHost(), port);

                OutputStream os = shoutCastSocket.getOutputStream();
                String user_agent = "WinampMPEG/5.09";
                String req = "GET / HTTP/1.0\r\nuser-agent: " + user_agent
                        + "\r\nIcy-MetaData: 1\r\nConnection: keep-alive\r\n\r\n";
                os.write(req.getBytes());
                is = shoutCastSocket.getInputStream();
            } else {
                // getInputStream() method is more error-proof than openStream(),
                // because openStream() does openConnection().getInputStream(),
                // which opens a new connection and does not reuse the old one.
                is = connection.getInputStream();
            }
            return is;
        } catch (MalformedURLException e) {
            logger.error("URL '{}' is not a valid url : '{}'", url, e.getMessage());
            throw new AudioException("URL not valid");
        } catch (IOException e) {
            logger.error("Cannot set up stream '{}': {}", url, e);
            throw new AudioException("IO Error");
        }
    }

    @Override
    public AudioFormat getFormat() {
        return audioFormat;
    }

    @Override
    public int read() throws IOException {
        return inputStream.read();
    }

    public String getURL() {
        return url;
    }

    @Override
    public void close() throws IOException {
        super.close();
        if (shoutCastSocket != null) {
            shoutCastSocket.close();
        }
    }

    @Override
    public String toString() {
        return url;
    }
}