# ID3-Tag eines Online-Radios auslesen



## stareck (22. Mrz 2012)

Hallo Zusammen,

ich habe ein Problem mit dem Auslesen des ID3-Tags eines Online-Streams.

Ich habe mir ein kleines Porgramm geschrieben, welches mit Hilfe eines InputStreams
den Online-Stream in eine Datei schreibt.
Diese Datei kann ich dann später z.B. mit dem VLC-Player wiedergeben.

Was ich nun noch gerne realisieren möchte ist, dass ich während des Speicherns der Datei den ID3-Tag auslese. 
Ich habe im Netz schon mehrfach gelesen, das ich das Byte-Array in einen String konvertiere und dann nach "TAG" oder "ID3" suchen soll. Allerdings enthält mein String diese ID3-Header-Infos nicht.

Es sieht mir auch so aus als wenn der Zeichensatz nicht passt.. Habe das Byte-Array schon mit mehreren Zeichensätzen konvertiert, allerdings klappt es nicht...

Hat von euch jemand eine Idee???

Hier ein kurzer Programmausschnitt:

```
InputStream inputStream = null;
FileOutputStream fileOut = null;
inputStream = url.openStream();

File f = new File("Test.mp3");
f.createNewFile();

fileOut = new FileOutputStream(f);

byte[] buffer = new byte[8192];
int r = -1;
while ((r = inputStream.read(buffer)) != -1)
{
    String buf = new String(buffer, "ISO-8859-1");
    //String buf = new String(buffer);
    //System.out.println(buf);
    if (buf.indexOf("TAG") != -1 || buf.indexOf("ID3") != -1)
        System.out.println("ID3 gefunden");
    fileOut.write(buffer, 0, r);
}
```


----------



## AlexSpritze (22. Mrz 2012)

Bist du sicher, dass in dem Stream ID3-Tags enthalten sind? Es ist aber richtig, dass solche Tag-Informationen wie Künstler oder Album im Klartext im Stream liegen.

Es könnte auch sein, dass die Tags am Ende der Datei sind, dann müsstest du warten bis der Stream komplett geladen ist. Das macht für einen Online-Stream natürlich keinen Sinn.  Wäre als sinnvoll, wenn die Tags gleich am Anfang auftauchen. Also in den ersten 8192 Byte, die du einliest.


----------



## stareck (22. Mrz 2012)

Wenn ich den Stream z.B. unter Winamp aufrufe, wird mir der Titel und der Artist angezeigt...


----------



## stareck (22. Mrz 2012)

Hat keiner eine Idee von euch, waran dies liegen kann?


----------



## AlexSpritze (22. Mrz 2012)

Wenn es ein Stream ist, dann erhälst du quasi ein beliebig große MP3, je nachdem, wann du beschließt, dass du "fertig" mit dem Lesen bist? Oder hast du auch irgendwie die Möglichkeit mitzubekommen, wann ein Lied zu Ende ist? Dass du dann die Lieder separiert in verschiedene Stücke teilen kannst?


----------



## stareck (23. Mrz 2012)

Also ich lese den Stream die ganze Zeit durch, wüsste jetzt auch nicht, wie ich erkennen kann, ob das Lied zu Ende ist.
Es kann aber auch nicht sein, dass der Header immer am Liedende steht. Bei Winamp dauert es vielleicht 5 sec. auch mittem im Lied und der ID3-Tag wird angezeigt.

Oder werden diese Infos vielleicht über einen anderen Kanal übertragen?


----------



## nillehammer (23. Mrz 2012)

> Oder werden diese Infos vielleicht über einen anderen Kanal übertragen?


Es ist sehr wahrscheinlich, dass überhaupt keine ID3-Tags gesendet werden. Viele Internetradios benutzen Shoutcast/Icycast zum Streamen. Dort werden die Metadaten anders übertragen. Hier wird das Verfahren sehr anschaulich und vor allem kurz beschrieben: SmackFu: Shoutcast Metadata Protocol


----------



## stareck (23. Mrz 2012)

hab das gerade einmal überflogen, hört sich gut an, hoffe mal das ich es dieses Wochende mal schaffe zu testen....


----------



## stareck (26. Mrz 2012)

Ich hab hier jetzt schon stundenlang herumgetestet, kriege es aber nicht hin...

Hat einer von euch ein kleines Beispiel für mich.

Habe schon diverse Beispiele aus dem Netz getestet, es klappt aber nicht...


----------



## Ralph-Uwe (26. Mrz 2012)

Hi

ich habe mich mit dem Infostream der Radiosender auch schon beschäftigt.
als Anhang findes Du meine Lösung. Sie ist mit Sicherheit noch ausbaufähig.

In den Archiv sind alle nötigen Dateien. 
Die Datei mp3plugin.jar muß mit in das Projekt eingebunden werden.

Ich hoffe Du kannst damit was anfangen.


----------



## stareck (27. Mrz 2012)

Hi Ralph-Uwe,

vielen Dank, dein Programm klappt bestens

Den Radiosender, den ich eingenlich abspielen wollte, macht er allerdings nicht (http://85.239.108.41/90elf_rp01)...

Hast du vielleicht noch einen Tip waran dies liegen kann?

Die werden die Headerdaten dann scheinbar anders übertragen...


Vielen Dnak schonmal!!!


----------



## Ralph-Uwe (27. Mrz 2012)

Hi,

freut mich, dass funktioniert hat. :applaus:

Die Daten werden bei dem voreingestellten Sender im html-Format übertragen.
Wie bei vielen anderen auch.
Bei Deinem Wunschsender scheinen es Datensätze zu sein, die ohne genaue Kenntnisse
des Streams nicht ohne weiteres entschlüsselt werden können.

Vielleicht kann Du bei dem Senderanfragen wie die Daten kodiert sind.


----------



## stareck (27. Mrz 2012)

Den Sender habe ich schon gefragt, die können mir auch nichts sagen, oder wollen es nicht

Aber es muss irgendein Standard sein, da es Winamp erkennt...


----------



## Ralph-Uwe (29. Mrz 2012)

Guten Morgen,

der Sender ist ein DAB-Sender.
Vermutlich werden die Programminformationen mit dem MOT-Protokoll übertragen.

In den folgenden Link findes Du eine Beschreibung.
http://www.lrr.in.tum.de/Par/arch/dab/mpspecs/mot_spec.pdf

Ich hoffe, dass hilft weiter.


----------



## stareck (29. Mrz 2012)

Hi Ralph-Uwe,

danke für den Tipp. Ich werde mal ein bisschen testen...
Hört sich aber nicht gerade einfach an


----------



## stareck (31. Mrz 2012)

Ich komme hier nicht weiter, hat von euch schon einmal jemand das MOT-Protokoll ausgelesen?

wäre echt dankbar wenn mir nochmal einer helfen könnte


----------



## Ralph-Uwe (31. Mrz 2012)

Hallo,

wie weit bist Du den?

Ich bin geraden dabei den HeaderCore auszulesen. :rtfm:


----------



## stareck (31. Mrz 2012)

Ich hatte heute morgen mal versucht den header auszulesen, wusste jetzt aber gar nicht, wie ich den Header erkenne. Das habe ich in dem Dokument von dir nicht gefunden und im Netz hab ich auch nichts gefunden:-(


----------



## Ralph-Uwe (31. Mrz 2012)

Hi 

ab Seite 12 steht der Header beschrieben:
Das Objekt besteht aus 3 Teilen, header core, header extention und body.
Der HeaderCore besteht aus 7 Byte, header extention und body sind variabel.

Als erstes mußt Du die ersten 7 Byte aus dem Stream lesen und den Variabeln BodySize, HeaderSize, 
ContentType und ContentSubType zuordnen. Leider funktioniert das nicht byteweise ;(

Ich bin z.Z. dabei die 7 Bytes in die vier Variabeln aufzuteilen.

Viel Erfolg :toll:


----------



## stareck (31. Mrz 2012)

Hi,

wenn ich die ersten 7 byte des Streams auslese, kommt nur quatsch bei raus, zumindest passen die Werte nicht zu den Werten in der Doku:-(

So lese ich den Stream aus, so erhalte ich nun die binären Daten...

```
while ((r = inputStream.read(buffer)) != -1)
{
  BigInteger bi = new BigInteger(buffer);
  String buf = bi.toString(2);
  String headerCore = buf.substring(0, 57);
}
```

Müsste doch so richtig sein, oder nicht?

Und dies bekomme ich daraus: "-10010101101111110110001011011111111000111101010010111110"
Diese Werte passen dann nicht wirklich zu der Beschreibung...


----------



## Ralph-Uwe (31. Mrz 2012)

Dein gelesenes Byte aus dem Inputstream ist die Variable "r". Diese benutzt Du 
in deinem Code nicht.

ich habe bis jetzt folgendes gemacht:

```
long headerCore = 0;
			for(int i=0;i<7;i++){ // die ersten 7 Byte aus den Stream lesen
				read = playlistStream.read(); // ein Byte aus dem Stream lesen
				headerCore = headerCore << 8; // daten in der Variabel um 8 Bit nach links verschieben
				headerCore = headerCore + read; // gelesenes Byte aus dem Stream addieren
			}
            System.out.println(Long.toBinaryString(headerCore));
```

Ich lese aus dem Stream die ersten 7 Byte und packe sie nacheinander eine eine 
Variable vom Type long. Zwischendurch verschiebe ich die Bits, in der 
Variabel headerCore, um ein Byte (also 8 Bits) nach links.
Auf diese Art hänge ich alle 56 Bits aneinander.

Das Ergebnis sieht zum Beispiel so aus:
11111111111110110101001000000100111010110000011111111101

Das muß jetzt noch den Variabeln: 
BodySize, HeaderSize, ContentType und ContentSubType zuordnet werden. 
Da arbeite ich gerade dran.


----------



## stareck (2. Apr 2012)

Hi Ralph-Uwe,

ich habe mein Programm erweitert, und mir ist aufgefallen, dass bei der header extension (PIL+ParamId) immer andere Werte gesendet werden, hast du dies bei dir auch??


----------



## Ralph-Uwe (2. Apr 2012)

Hallo,

leider hatte ich die letzten Tage nich viel Zeit weiterzumachen.
Deswegen hänge ich noch am HeaderCore.

Mich machen die Daten ich ich herausbekommen habe stutzig, Etwas stimmt da noch nicht.
Was hast Du als Ergebnisse für BodySize, HeaderSize, ContentType und ContenrSubType raus?
Sind Deine Ergebnisse plausiebel?


----------



## stareck (2. Apr 2012)

Hallo,

ich habe heute mittag ein bisschen getestet, ich hab das gefühl das meine Daten auch noch nciht passen...

Diese Daten erhalte ich:
HeaderCore:
Headersize=5376 bytes0 
Bodysize=4081 bytes
Contenttype=111101 (nicht definiert) 
ContentSubtype=111111111

Headerextension:
PLI=00 
ParamID = 000001 (nicht definiert)

Das sieht mir noch nicht korrekt aus...


----------



## HoaX (2. Apr 2012)

Ääääh das ist ein Icy-Stream...

Wenn man einen Binärstream mit einem BufferedReader einliest macht man eh etwas falsch. Des weiteren übergibst du zwar korrekter Weise den Header "icy-metadata", aber du irgnorierst die Antwort-Header und suchst blind HTML-Tags in der Antwort... ich wundere mich nicht dass es nicht geht.


----------



## Ralph-Uwe (2. Apr 2012)

Hi HoaX,

Viele Webradios senden einen Icy-Stream.
Dieser läst sich mit meinem Programm auch problemlos lesen.

http://s7.pop-stream.de:7160

DAB-Sender senden aber in eine anderen Format. Ich vermute, dass 
es sich dabei um das Multimedia Object Transfer (MOT) Protokoll handelt,
da es im zusammenhang mit DAB genannt wird.
http://energyradio.de/hamburg
http://85.239.108.41/90elf_rp01


----------



## HoaX (2. Apr 2012)

Dann schau dir die Streamdaten an, es ist Icy.

Folgender Code funktioniert auch:

```
package org.javaforum.hoax.icy;

import java.io.FilterInputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.List;
import java.util.Map.Entry;

public class IcyInputStream2 extends FilterInputStream {

	private URLConnection connection;
	private int metaDataInterval = -1;
	private int toRead = -1;

	public IcyInputStream2(URL icyURL) throws IOException {
		super(null);
		connection = icyURL.openConnection();
		connection.addRequestProperty("Icy-MetaData", "1");
		in = connection.getInputStream();
		
		readHeader();
	}
	
	protected void readHeader() {
		for(Entry<String, List<String>> header: connection.getHeaderFields().entrySet()) {
			if ("icy-metaint".equalsIgnoreCase(header.getKey())) {
				metaDataInterval = Integer.parseInt(header.getValue().get(0));
				toRead = metaDataInterval;
			}
			if (header.getKey() != null && header.getKey().toLowerCase().startsWith("icy-")) {
				for(String s : header.getValue()) {
					fireEventNewMetaData(header.getKey(), s);
				}
			}
		}
	}
	
	@Override
	public int read() throws IOException {
		if (toRead == 0) {
			readIcyInfo();
			toRead = metaDataInterval;
		}
			
		toRead--;
		if (toRead < 0) {
			toRead = -1;
		}
		int val = in.read();
		return val;
	}
	
	@Override
	public int read(byte[] b) throws IOException {
		return read(b, 0, b.length);
	}
	
	@Override
	public int read(byte[] b, int off, int len) throws IOException {
		int size = off + len;
		for(int i = off; i < size; i++) {
			int d = read();
			if (d == -1) {
				return i - off;
			} else {
				b[i] = (byte) d;
			}
		}
		return len;
	}
	
	private void readIcyInfo() throws IOException {
		int value = in.read();
		int size = value * 16;
		StringBuffer s = new StringBuffer();
		while(size-- > 0) {
			s.append((char)in.read());
		}
		
		if (s.length() > 0) {
			// TODO: s parsen
			fireEventNewMetaData("Length: "+s.length(),s.toString());
		}
	}

	private void fireEventNewMetaData(String key, String value) {
		System.err.println("Neue Metadaten: " + key + " -> " + value);
	}

	
	public static void main(String[] args) throws MalformedURLException, IOException {
		//IcyInputStream2 stream = new IcyInputStream2(new URL("http://85.239.108.41/90elf_rp01"));
		//IcyInputStream2 stream = new IcyInputStream2(new URL("http://mp3.webradio.rockantenne.de:80"));
		IcyInputStream2 stream = new IcyInputStream2(new URL("http://edge.live.mp3.mdn.newmedia.nacamar.net/klassikradiomovie128/livestream.mp3"));
		int d = 0;
		while ((d = stream.read())!= -1) {
			//System.out.write(d);
		}
	}
}
```

Den String aus dem StringBuffer noch zu zerlegen überlasse ich euch.

Ausgabe schaut so aus:

```
Neue Metadaten: icy-url -> http://www.rockantenne.de
Neue Metadaten: icy-br -> 128
Neue Metadaten: icy-reset -> 1
Neue Metadaten: icy-pub -> 0
Neue Metadaten: icy-name -> ROCK ANTENNE MP3 Surround
Neue Metadaten: icy-genre -> Rock
Neue Metadaten: icy-metaint -> 16000
Neue Metadaten: Length: 64 -> StreamTitle='Lenny Kravitz - Rock Star city life';
```


```
Neue Metadaten: icy-url -> http://www.90elf.de
Neue Metadaten: icy-description -> 90elf - Dein Fussball-Radio - Livespiel 1
Neue Metadaten: icy-br -> 64
Neue Metadaten: icy-br -> 64
Neue Metadaten: icy-genre -> Sports
Neue Metadaten: icy-name -> 90elf - Dein Fussball-Radio - Livespiel 1
Neue Metadaten: icy-pub -> 1
Neue Metadaten: icy-metaint -> 16000
Neue Metadaten: Length: 96 -> StreamTitle='';StreamUrl='';adw_ad='true';durationMilliseconds='20140';insertionType='preroll';
Neue Metadaten: Length: 96 -> StreamTitle='DÃ¼sseldorf - St. Pauli 0:0 | 90elf - Dein Fussball-Radio ';StreamUrl='';
```


----------



## Ralph-Uwe (2. Apr 2012)

Ok, hast recht 

Ich werden mir Dein Code mal genauer ansehen, um zu sehen welchen fehler 
ich in meinem Code habe.

Auf jeden Fall vielen Dank für Deine Hilfe. :toll: :applaus:


----------



## stareck (3. Apr 2012)

Vielen Dank HoaX, funktioniert bestens!!!


----------

