Audiolänge einer MP3 Datei erhalten ohne diese vollständig zu laden

Diskutiere Audiolänge einer MP3 Datei erhalten ohne diese vollständig zu laden im Allgemeine Java-Themen Forum; Hallöchen! Hab mal wieder ein recht spezifisches Problem. Ich bin gerade dabei mithilfe von JLayer und der MP3Spi Audiodateien abzuspielen. Da es...

  1. RalleYTN
    RalleYTN Mitglied
    Hallöchen!
    Hab mal wieder ein recht spezifisches Problem.
    Ich bin gerade dabei mithilfe von JLayer und der MP3Spi Audiodateien abzuspielen.
    Da es sich um Musik handelt dachte ich mir es wäre besser die Dateien zu Streamen anstatt alles über einen Clip in den RAM zu laden und einen OutOfMemoryError zu riskieren.
    Der Player für die MP3 Datei soll natürlich auch fähig sein die Länge des Contents anzuzeigen.

    Problem:
    Ich würde für WAV Dateien die Länge wie folgt berechnen
    Code (Java):
    length = (long)(1000000 * (audioInputStream.getFrameLength() / audioInputStream.getFormat().getFrameRate()));
    Das funktioniert jedoch nicht, da audioInputStream.getFrameLength() bei MP3s -1 liefert.

    Ich habe bereits versucht die Header mithilfe der Klassen die mit den Bibliotheken kommen auszulesen.
    Code (Java):

    AudioFileFormat audioFileFormat = AudioSystem.getAudioFileFormat(this.resource);
                 
    if(audioFileFormat instanceof TAudioFileFormat) {
                     
       Map<?, ?> headers = ((TAudioFileFormat)audioFileFormat).properties();
       headers.forEach((key, value) -> System.out.println(key + ": " + value));
    }
     
    Ergebnis:
    Code (Text):

    mp3.copyright: false
    date: 2016
    mp3.framesize.bytes: 1040
    mp3.vbr: false
    mp3.frequency.hz: 44100
    mp3.framerate.fps: 38.28125
    mp3.id3tag.track: 1
    mp3.id3tag.v2: java.io.ByteArrayInputStream@b81eda8
    mp3.channels: 2
    mp3.vbr.scale: 0
    mp3.version.encoding: MPEG1L3
    mp3.bitrate.nominal.bps: 320000
    mp3.version.layer: 3
    mp3.id3tag.v2.version: 3
    mp3.padding: false
    mp3.header.pos: 1130
    comment: ?ÿ?ww.dvdvideosoft.com?
    mp3.version.mpeg: 1
    mp3.mode: 1
    mp3.crc: false
    mp3.original: true
     
    Jetzt weiss ich jedoch nicht weiter. :/
     
  2. Vielleicht hilft dir dieser Java-Kurs hier weiter --> (hier klicken)
  3. Tobse
    Tobse Bekanntes Mitglied
    Sieht für mich so aus, als ob du da mit einer Unschärfe von +-2-3 Skunden gut hinkommen kannst:
    Du kennst mp3.framerate.fps, ich vermute mal, dass das Frames Per Second ist. Du weisst durch mp3.framesize.bytes auch, dass ein Frame 1040 Frames groß ist. Wenn die ganze Datei also 3MiB (= 3.145.728 bytes) groß ist, sind es 3145728 / 1040 = ~3024 Frames und damit 3024 / 38,28125 = 79 Sekunden.

    Ich kenne das MP3-Format nicht so gut. Sicher weiss ich, dass die Framegröße aufgrund der High- und Lowcuts ggü. WAV oder FLAC deutlich geringer ist und damit Speicherplatz gespart wird. Wenn aber zusätzlich noch irgendeine Kompression (wie bspw. GZip) dabei ist, wird das mit dem ausrechnen der Dauer eher schwierig.

    Ein anderer Punkt: je nachdem, wo deine App läuft, brauchst du dir um die OutOfMemoryException nicht so viele Sorgen machen. Wenn du keine Referenzen zu den Dateien rumliegen lässt sollte der GC gut hinterherkommen. Und auf einem System mit mehreren GB Arbeitsspeicher kannst du ne menge MP3s in den RAM laden, bevor du ein Problem bekommst.

    Natürlich kannst du nicht jedes mal die Datei in dem RAM lesen, wenn du Infos dazu möchtest. Ich vermute die gängigen Audio-Player (WMP, Groove under Windows, Rythmbox und Bashee unter Linux bspw) haben eine SQLite Datenbank, in der sie solche schwer erarbeiteten Infos zwischenspeichern. Java + SQLite geht, mit H2DB kannst du aber auch arbeiten, wenn du auf native Libraries verzichten möchtest.
     
  4. RalleYTN
    RalleYTN Mitglied
    Kommt dem Ergebnis nahe hat jedoch schon bei einem 6 Sekunden clip einen Verlust von 0,5 Sekunden.
    Wenn ich nicht eine Meldung mit "mark not supported" bekommen würde, dann könnt ich ganz einfach einmal durch den AudioInputStream gehen und die bytes zählen (wären an dem Punkt nämlich nicht mehr komprimiert) und danach den AudioInputStream resetten und normal weiter arbeiten.

    Die Lösung hat mich auf jeden Fall ein Stück weiter gebracht, jedoch ist sie nicht genau genug.
     
  5. RalleYTN
    RalleYTN Mitglied
    HEUREKA!!!
    Code (Java):

    int frameLength = 0;
    byte[] buffer = new byte[this.audioInputStream.getFormat().getFrameSize()];

    while(this.audioInputStream.read(buffer) != -1) {
                           
       frameLength++;
    }

    this.audioInputStream.close();
    this.audioInputStream = AbstractAudio.getAudioInputStream(this.resource, this.fileFormat);
    this.length = (long)(1000000 * (frameLength / this.audioInputStream.getFormat().getFrameRate()));
     
    Ich resette den AudioInputStream jetzt einfach indem ich ihn schließe und neu initializiere.
    Die Header brauche ich nicht. Der korrekte FrameSize ist im AudioFormat gespeichert.
    Um an die Gesamtmenge der Frames zu kommen lese ich einfach alle frames einzeln aus und zähle Sie.
    Danach kann ich ganz normal weiterrechnen.
     
  6. Tobse
    Tobse Bekanntes Mitglied
    Das wird so nicht funktionieren. Read liest nicht zwangsläufig so viele bytes, wie der Buffer groß ist. Du musst den Rückgabewert von read aufsummieren und durch die FrameSize teilen, um ein akkurates Ergebnis zu bekommen.
     
  7. dzim
    dzim Bekanntes Mitglied
    Könntet ihr nicht den MediaPlayer von JavaFX einbinden (https://docs.oracle.com/javase/8/javafx/api/javafx/scene/media/MediaPlayer.html), der bietet eine Methode #getTotalDuration():
    .

    Ah nee...
    Aber dafür: Über #getMedia() kommt man an die abzuspielenden Daten.
    https://docs.oracle.com/javase/8/javafx/api/javafx/scene/media/Media.html
    Und dort: #getDuration() - sollte also gut verwendbar sein! Und da man Media auch ohne den MediaPlayer erstellen kann, brauchst du keine JavaFX-GUI-Komponenten unnütz zu erstellen.
     
  8. Tobse
    Tobse Bekanntes Mitglied
    @dzim Und wer sagt, dass das damit die Datei nicht komplett in den RAM lädt? Und JavaFX nur wegen einer Klasse einzubinden ist es ggf. nicht wert
     
  9. dzim
    dzim Bekanntes Mitglied
    Zum einen: Das sagt niemand, Zum anderen: Soweit ich die JavaDoc richtig verstanden hab, ist das wie Streaming. Aber statt grundsätzlich meiner Idee ablehnend gegenüber zu stehen, hättest du ja einfach eine kleine Anwendung implementieren können, die den Test macht und den Speicherverbrauch der JVM tracken können.

    Ausserdem: "Einbinden" klingt ja fast so, als wäre dafür ein separates Modul (MAven, Gradle) nötig, was im Moment schlicht falsch wäre...
     
  10. Tobse
    Tobse Bekanntes Mitglied
    Den Speicherverbrauch bei einer 3MB Datei und 200MB Heapspace? Wird schwierig. Ich sehe das deshalb kritisch, weil JavaFX eine große Dependency ist (die Runtime Downloads sind ~7MB). Einfach lustig Dependencies in ein Projekt reinzukloppen, weil man nur eine Klasse braucht, macht z.B. das NodeJS/NPM Ökosystem komplett kaputt. Ich halte sowas daher für Bad-Practice.

    Es wäre glaub eher angebracht, eine MP3-Library einzubinden. Die kann vmtl das gleiche, ist nicht auf am System installierte Codecs angewiesen und man kann für so Ein Projekt vmtl mehr davon benutzen. Ich wäre nicht verwundert, wenn der JavaFX Media-Player auf Linux ohne Codec nicht tut.
     
    JStein52 gefällt das.
  11. thecain
    thecain Aktives Mitglied
    javafx ist doch im jre, wenn ich mich nicht irre. Ich sehe das Problem nicht.

    Ob man die Länge auslesen kann, wird mMn davon abhängen ob sie im ID3 Header ist, sonst werden es wohl nur Annäherungen sein, ohne das ganze File zu laden.
     
Die Seite wird geladen...

Audiolänge einer MP3 Datei erhalten ohne diese vollständig zu laden - Ähnliche Themen

ArrayListe in einer JComboBox anzeigen
ArrayListe in einer JComboBox anzeigen im Forum Java Basics - Anfänger-Themen
Ergebnis einer Funktion direkt in Variable speichern
Ergebnis einer Funktion direkt in Variable speichern im Forum Für Verirrte - Fragen zu JavaScript
Mehrere Zeilen zu einer Zeile zusammenfügen und in eine Datei schreiben
Mehrere Zeilen zu einer Zeile zusammenfügen und in eine Datei schreiben im Forum Java Basics - Anfänger-Themen
Gespeicherte Daten von einer LinkedList auf vier LinkedList verteilen
Gespeicherte Daten von einer LinkedList auf vier LinkedList verteilen im Forum Allgemeine Java-Themen
Einlesen und bearbeiten einer text Datei / Zeile
Einlesen und bearbeiten einer text Datei / Zeile im Forum Java Basics - Anfänger-Themen
Thema: Audiolänge einer MP3 Datei erhalten ohne diese vollständig zu laden

Besucher kamen mit folgenden Begriffen auf unsere Seite:

  1. Windows 10 Audiodauer anzeigen