I have made a radio app which works perfectly fine. I'm able to play the radio stream and fetch the metadata too. The streaming service is from shoutcast.
The only problem is that, I'm adding the url as the datasource to the media player and then fetching the title and artist every 5 seconds.
Is there any way, I can just make one http request and then split the audio and the metadata and then send it to the media player?
Code for fetching Metadata.
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; }
And passing the url to the datasource of the media player
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 Answers
Answers 1
The task you try to achieve is not something that is easy to do. (Tho it is not impossible.) The metadata of the stream is only "downloaded" at the start of the stream, so changing it afterwards would have no effect reading the metainformations "cached" from the stream. To read the new properties you have to restart the stream, this will fetch the new headers etc. (But it could cause a break in the stream so it is not really recommended.)
In sound technology it is common to use watermarking. It is a process where you enrich your sound with some kind of data in the not (quality) destructing way. (Usage on youtube.) Altogh it is hard to do, there are some ways to hide your information in the stream. I recommend to read this to achive the goal you want to reach.
Because your hardware is a mobile phone and not all of the Android devices are strong enough you should consider some new http requests. The audio processing is not a cheap thing speaking in terms of CPU, memory etc. There is some other options to consider if you do so. The five second polling is not the best way to get the information, because you could display false information to the user and false information is worse than nothing. I recommend a server side push based on mqtt. Here is a pretty nice example of the usage. A solution based on this method insted of polling is using less traffic and is more accurate.
0 comments:
Post a Comment