Last active
November 12, 2020 01:03
-
-
Save MTDdk/a7fc37e9c86e991d3e948ca40dab5a5a to your computer and use it in GitHub Desktop.
Continously extract StreamTitle (MP3 metadata) from SHOUTcast / IceCast audio streaming
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.io.IOException; | |
import java.io.InputStream; | |
import java.net.URI; | |
import java.net.http.HttpClient; | |
import java.net.http.HttpHeaders; | |
import java.net.http.HttpRequest; | |
import java.net.http.HttpResponse.BodyHandlers; | |
import java.nio.charset.StandardCharsets; | |
/** | |
* Uses java.net.http from Java 11 | |
*/ | |
public class IceCastMetaData { | |
static final int DEFAULT_METADATA_INTERVAL = 8192; | |
public static void main(String[] args) { | |
URI streamUri = URI.create("https://radioserver/"); | |
HttpClient client = HttpClient.newBuilder().build(); | |
HttpRequest request = HttpRequest.newBuilder() | |
.GET() | |
.uri(streamUri) | |
.header("Icy-MetaData", "1") // instruct the server, that we want the metadata | |
.build(); | |
// This could certainly also be done with using Apache HttpClient, | |
// instead of the built-in Java 11 HttpClient | |
client.sendAsync(request, BodyHandlers.ofInputStream()) | |
.thenAccept(resp -> { | |
// Retrieve the headers of the server response | |
HttpHeaders headers = resp.headers(); | |
// "icy-metaint" = how often the metadata is sent in the stream | |
// This might not be sent by the server, and then it defaults to DEFAULT_METADATA_INTERVAL | |
int metaInterval = headers.firstValue("icy-metaint").map(Integer::parseInt).orElse(DEFAULT_METADATA_INTERVAL); | |
// Get the InputStream of the audio stream | |
try (InputStream stream = resp.body()) { | |
while (true) { | |
// Read a chunk of music data (as we are not using it, just skipping) | |
stream.skipNBytes(metaInterval); | |
// Extract the metadata from the chunk | |
String meta = readMetaData(stream); | |
// See if we got something useful from this current chunk | |
if (!meta.isEmpty() && meta.startsWith("StreamTitle")) { | |
// If we got some metadata, then it will be on the form: "StreamTitle='<song title>';" | |
// That is, it always starts with "StreamTitle='" and ends with "';" | |
System.out.println("Now playing :: " + meta.substring("StreamTitle=".length(), meta.indexOf(';'))); | |
} | |
} | |
} catch (IOException e) { | |
e.printStackTrace(); | |
} | |
}) | |
.join(); | |
} | |
private static String readMetaData(InputStream stream) throws IOException { | |
// The next byte after a music chunk indicates the length of the metadata | |
int length = stream.read(); | |
if (length < 1) return ""; // no new metadata is present (the song is playing) | |
// Multiply by 16 to get the number of bytes, that holds the data | |
int metaChunkSize = length * 16; | |
byte[] metaChunk = stream.readNBytes(metaChunkSize); | |
return new String(metaChunk, 0, metaChunkSize, StandardCharsets.ISO_8859_1); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment