如何在不单独请求元数据和流式传输的情况下将元数据和轨道与shoutcast流分离

时间:2016-05-31 19:58:39

标签: java android shoutcast internet-radio

我制作了一个非常好用的收音机应用程序。我也可以播放广播流并获取元数据。流媒体服务来自shoutcast。

唯一的问题是,我将url作为数据源添加到媒体播放器,然后每5秒获取一次标题和艺术家。

有什么办法,我可以发一个http请求然后分割音频和元数据,然后发送到媒体播放器?

获取元数据的代码。

private void retreiveMetadata() throws IOException {

    int metaDataOffset = 0;

    OkHttpClient client = new OkHttpClient();
    Request request = new Request.Builder()
        .addHeader("Icy-MetaData", "1")
        .addHeader("Connection", "close")
        .addHeader("Accept", "")
        .url(streamUrl)
        .build();

    request.headers("");


    Response response = client.newCall(request).execute();
    InputStream stream = response.body().byteStream();

    //Map<String, List<String>> headers = response..getHeaderFields();

    if (!response.headers("icy-metaint").equals("")) {
        // Headers are sent via HTTP

        String icyMetaInt = response.headers("icy-metaint").toString();
        icyMetaInt = icyMetaInt.replace("[", "");
        icyMetaInt = icyMetaInt.replace("]", "");

        metaDataOffset = Integer.parseInt(icyMetaInt);
    } else {

        // Headers are sent within a stream
        StringBuilder strHeaders = new StringBuilder();
        char c;
        while ((c = (char)stream.read()) != -1) {
            strHeaders.append(c);
            if (strHeaders.length() > 5 && (strHeaders.substring((strHeaders.length() - 4), strHeaders.length()).equals("\r\n\r\n"))) {
                // end of headers
                break;
            }
        }

        // Match headers to get metadata offset within a stream
        Pattern p = Pattern.compile("\\r\\n(icy-metaint):\\s*(.*)\\r\\n");
        Matcher m = p.matcher(strHeaders.toString());

        if (m.find()) {
            metaDataOffset = Integer.parseInt(m.group(2));
        }
    }

    // In case no data was sent
    if (metaDataOffset == 0) {
        isError = true;
        return;
    }

    // Read metadata
    int b;
    int count = 0;
    int metaDataLength = 4080; // 4080 is the max length
    boolean inData = false;
    StringBuilder metaData = new StringBuilder();
    // Stream position should be either at the beginning or right after headers
    while ((b = stream.read()) != -1) {
        count++;

        // Length of the metadata
        if (count == metaDataOffset + 1) {
            metaDataLength = b * 16;
        }

        if (count > metaDataOffset + 1 && count < (metaDataOffset + metaDataLength)) {
            inData = true;
        } else {
            inData = false;
        }

        if (inData) {
            if (b != 0) {
                metaData.append((char)b);
            }
        }

        if (count > (metaDataOffset + metaDataLength)) {
            break;
        }

    }

    // Set the data
    metadata = IcyStreamMeta.parseMetadata(metaData.toString());

    // Close
    stream.close();
}

public static Map<String, String> parseMetadata(String metaString) {
    Map<String, String> metadata = new HashMap();
    String[] metaParts = metaString.split(";");
    Pattern p = Pattern.compile("^([a-zA-Z]+)=\\'([^\\']*)\\'$");
    Matcher m;
    for (int i = 0; i < metaParts.length; i++) {
        m = p.matcher(metaParts[i]);
        if (m.find()) {
            metadata.put((String)m.group(1), (String)m.group(2));
        }
    }

    return metadata;
}

将url传递给媒体播放器的数据源

String url = "http://68.68.109.106:8356/";
mp = new MediaPlayer();
mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
try {

    mp.setDataSource(url);
    mp.prepare();

} catch (IllegalArgumentException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    Toast.makeText(context, e.toString(), Toast.LENGTH_SHORT).show();
} catch (SecurityException e) {
    // TODO Auto-generated catch block
    Log.e(TAG, "SecurityException");
    Toast.makeText(context, e.toString(), Toast.LENGTH_SHORT).show();
} catch (IllegalStateException e) {
    // TODO Auto-generated catch block
    Log.e(TAG, "IllegalStateException");
    Toast.makeText(context, e.toString(), Toast.LENGTH_SHORT).show();
} catch (IOException e) {
    // TODO Auto-generated catch block
    Log.e(TAG, "IOException");
    Toast.makeText(context, e.toString(), Toast.LENGTH_SHORT).show();
}

1 个答案:

答案 0 :(得分:1)

您尝试实现的任务并不容易。 (这不是不可能的。)流的元数据只是&#34;下载&#34;在流的开头,所以之后更改它将无法读取元信息&#34;缓存&#34;从溪流。要读取新属性,您必须重新启动流,这将获取新的标题等。(但它可能会导致流中断,因此不建议这样做。)

在声音技术中,通常使用watermarking。这是一个以非(质量)破坏方式用某种数据丰富你的声音的过程。 (Usage on youtube.)Altogh很难做到,有一些方法可以在流中隐藏您的信息。我建议您阅读this以达到您想要达到的目标。

由于您的硬件是手机而且并非所有Android设备都足够强大,您应该考虑一些新的http请求。在CPU,内存等方面,音频处理并不便宜。如果你这样做,还有其他一些选择。五秒钟轮询不是获取信息的最佳方式,因为您可能向用户显示虚假信息,而虚假信息比什么都没有。我建议基于mqtt的服务器端推送。 Here是一个非常好用的例子。基于此方法的解决方案是使用较少的流量并且更准确。