Skip to content

Instantly share code, notes, and snippets.

@tulskiy
Created June 4, 2011 17:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tulskiy/1008126 to your computer and use it in GitHub Desktop.
Save tulskiy/1008126 to your computer and use it in GitHub Desktop.
JAAD icy metadata read support
Index: src/net/sourceforge/jaad/Radio.java
===================================================================
--- src/net/sourceforge/jaad/Radio.java (date 1307127146000)
+++ src/net/sourceforge/jaad/Radio.java (revision )
@@ -16,6 +16,7 @@
*/
package net.sourceforge.jaad;
+import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
@@ -59,15 +60,7 @@
SourceDataLine line = null;
byte[] b;
try {
- final URL url = new URL(arg);
- final InputStream in = url.openStream();
- //skip icy header
- final BufferedReader br = new BufferedReader(new InputStreamReader(in));
- String x;
- do {
- x = br.readLine();
- }
- while(x!=null&&!x.trim().equals(""));
+ InputStream in = new BufferedInputStream(IcyInputStream.create(new URL(arg)), 10000);
final ADTSDemultiplexer adts = new ADTSDemultiplexer(in);
AudioFormat aufmt = new AudioFormat(adts.getSampleFrequency(), 16, adts.getChannelCount(), true, true);
Index: src/net/sourceforge/jaad/IcyInputStream.java
===================================================================
--- src/net/sourceforge/jaad/IcyInputStream.java (revision )
+++ src/net/sourceforge/jaad/IcyInputStream.java (revision )
@@ -0,0 +1,176 @@
+/*
+ * Copyright (c) 2008, 2009, 2010, 2011 Denis Tulskiy
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with this work. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.sourceforge.jaad;
+
+import java.io.BufferedInputStream;
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Author: Denis Tulskiy
+ * Date: 4/10/11
+ */
+public class IcyInputStream extends FilterInputStream {
+ private static final Logger logger = Logger.getLogger("musique");
+
+ private int metaInt = 0;
+ private int bytesRead = 0;
+ private URLConnection connection;
+ private String contentType;
+
+ public static IcyInputStream create(URL url) {
+ try {
+ URLConnection connection = url.openConnection();
+ connection.setRequestProperty("Icy-Metadata", "1");
+ IcyInputStream icyInputStream = new IcyInputStream(new BufferedInputStream(connection.getInputStream()));
+ icyInputStream.setConnection(connection);
+ icyInputStream.init();
+ return icyInputStream;
+ } catch (IOException e) {
+ logger.log(Level.WARNING, "Error opening Icy stream", e);
+ }
+ return null;
+ }
+
+ private IcyInputStream(InputStream in) {
+ super(in);
+ }
+
+ private void setConnection(URLConnection connection) {
+ this.connection = connection;
+ }
+
+ public String getContentType() {
+ return contentType;
+ }
+
+ public String readLine() {
+ try {
+ int ch = read();
+
+ StringBuilder sb = new StringBuilder();
+ while (ch != '\n' && ch != '\r' && ch >= 0) {
+ sb.append((char) ch);
+ ch = read();
+ }
+
+ if (ch == '\n' || ch == '\r') {
+ //noinspection ResultOfMethodCallIgnored
+ read();
+ }
+ return sb.toString();
+ } catch (IOException e) {
+ logger.log(Level.WARNING, "Error reading Icy stream", e);
+ }
+ return null;
+ }
+
+
+ private void init() {
+ contentType = connection.getContentType();
+ String metaIntString = "0";
+ if (contentType.equals("unknown/unknown")) {
+ //Java does not parse non-standart headers
+ //used by SHOUTCast
+ logger.fine("Reading SHOUTCast response");
+ String s = readLine();
+ if (!s.equals("ICY 200 OK")) {
+ logger.warning("SHOUTCast invalid response: " + s);
+ return;
+ }
+
+ while (true) {
+ s = readLine();
+
+ if (s.isEmpty()) {
+ break;
+ }
+
+ String[] ss = s.split(":");
+ if (ss[0].equals("icy-metaint")) {
+ metaIntString = ss[1];
+ } else if (ss[0].equals("icy-genre")) {
+ System.out.println("Genre: " + ss[1]);
+ } else if (ss[0].equals("icy-name")) {
+ System.out.println("Album: " + ss[1]);
+ } else if (ss[0].equals("content-type")) {
+ contentType = ss[1];
+ System.out.println("Content Type: " + contentType);
+ }
+ }
+ } else {
+ metaIntString = connection.getHeaderField("icy-metaint");
+ System.out.println("Genre: " + connection.getHeaderField("icy-genre"));
+ System.out.println("Album: " + connection.getHeaderField("icy-name"));
+ }
+ try {
+ metaInt = Integer.parseInt(metaIntString.trim());
+ logger.fine("Reading metadata information every " + metaInt + " bytes");
+ } catch (NumberFormatException e) {
+ metaInt = 0;
+ }
+ logger.fine("Content type is: " + contentType);
+ }
+
+ @Override
+ public int read(byte[] b, int off, int len) throws IOException {
+ if (metaInt > 0) {
+ int bytesToMeta = metaInt - bytesRead;
+
+ if (bytesToMeta == 0) {
+ readMeta();
+ } else if (bytesToMeta > 0 && bytesToMeta < len) {
+ len = bytesToMeta;
+ }
+ }
+
+ int read = super.read(b, off, len);
+ bytesRead += read;
+ return read;
+ }
+
+ private void readMeta() throws IOException {
+ int size = read() * 16;
+ if (size > 1) {
+ byte[] meta = new byte[size];
+ int i = super.read(meta, 0, size);
+ if (i != size) {
+ throw new RuntimeException("WTF");
+ }
+ String metaString = new String(meta, 0, i, "UTF-8");
+ String title = "StreamTitle='";
+ if (metaString.startsWith(title)) {
+ String[] ss = metaString.substring(title.length(), metaString.indexOf(";") - 1).split(" - ");
+ if (ss.length > 0) {
+ if (ss.length > 1) {
+ System.out.println("Artist: " + ss[0]);
+ System.out.println("Title: " + ss[1]);
+ } else {
+ System.out.println("Title: " + ss[0]);
+ }
+ }
+ }
+ }
+ bytesRead = 0;
+ }
+}
@daniel-seidl
Copy link

I know this might be very old code, but I am trying to use this gist right now and can not figure out how to get title or artist out of this? I create an instance of icyinputstream but read only gives me ints and readline does not seem to obtain readable characters. If you still look at this, I'd be thrilled to hear of you :). Thanks for sharing the code and best regards.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment