MP3-Header auslesen gelingt nicht



  • Hi all, ich möchte gerne den Header einer mp3-Datei auslesen(konkret geht es mir um die Bitrate). Dazu hab ich folgende Infos gefunden:
    http://www.mp3-tech.org/programmer/frame_header.html
    http://de.wikipedia.org/wiki/MP3#Spezifikation

    Der Header einer mp3-Datei beginnt also mit 11 gesetzen bits.
    Ich habe also die Datei Byteweise eingelesen und die gesetzten Bits überprüft, was soweit auch noch gelingt.

    Wenn ich allerdings an der ersten Stelle, an der 11 gesetzte bits hintereinander auftauchen weiter"lese", kommt fast immer Müll dabei raus: Entweder der Header enthält unwahrscheinlicherweise die falschen infos oder ich habe nicht den Frame Header erwischt, was bedeutet, dass es zufälligerweise auch andere Stellen mit 11 gesetzten bits innerhalb der Datei gibt.

    Wie kann ich nun genau herausfinden wo der erste Frame-Header beginnt?

    Oder Mach ich grundsätzlich schon irgendwas falsch?

    Das das Einlesen misslingt kann man eigentlich ausschließen, ich habe die Ergebnisse stichprobenartig mit denen eines Hex-Editors verglichen, und es hat alles gestimmt...

    Ich hab keinen Bereich gefunden, wo das hier konkret reinpasst, aber da ich unter Windows programmiere denke ich, dass es hier am besten hinpasst:)
    Gruß, tXX



  • Das ist ja wohl ganz klar ein Fall für "Rund um die Programmierung", und ganz klar KEIN Fall für WinAPI 🙄

    Oder Mach ich grundsätzlich schon irgendwas falsch?

    Ohne deinen Code zu sehen ist das natürlich besonders leicht zu beantworten, nen?

    Entweder der Header enthält unwahrscheinlicherweise die falschen infos

    Jopp, das ist wirklich unwahrscheinlich.

    oder ich habe nicht den Frame Header erwischt, was bedeutet, dass es zufälligerweise auch andere Stellen mit 11 gesetzten bits innerhalb der Datei gibt.

    Entweder dein Code ist falsch, und findet die falschen Stellen.
    Oder du hast ein File, welches nicht mit MP3 Daten anfängt, sondern mit einem "Tag" oder ähnlichem.
    "Brave" Tags enthalten die "sync" Bitfolge nicht. Allerings gibt es genug arrogante Software von arroganten Programmierern, die per Default "böse" Tags schreiben. z.B.:

    http://id3lib.sourceforge.net/id3guide.html#unsync

    Soll heissen: du solltest checken, ob ein ID3v2 Tag am Anfang des Files steht, und, wenn ja, diesen erstmal überspringen. Und erst danach anfangen MPEG Sync Sequenzen zu suchen.



  • Ups, hab "Rund um die Programmierung" irgendwie übersehen, I'm sorry...
    Wär nett wenn das vll mal jemand dahin verschieben könnte.

    Hier mal ein Codeausschnitt:

    int mp3seek::get_bitrate(QString& filename)
    {
         QFile *file=new QFile(filename);
         file->open(QFile::ReadOnly);
         QDataStream *stream=new QDataStream(file);
    
         int bits_set=0;
         char a[1];
         bool header=false;
    
         for(int i=0; !stream->atEnd(); i++)
         {
             stream->readRawData(a, 1); //Hier wird ein byte aus der Datei in a[] gespeichert
             for(int x=1; x<=8; x++)
             {
                 if(a[0]&(1<<x))        //Wenn das x-te bit gesetzt ist wird "bits_set" um einen wert erhöht
                 {
                     bits_set++;
                 }
                 else
                 {
                     bits_set=0;      //ist das x-te bit nicht gesetzt wird von neuem angefangen zu zählen
                 }
                 if(bits_set>=11)     //sind 11 bits hintereinander gesetzt wird aus der Schleife ausgestiegen
                 {
                    header=true;
                 }
    
             }
             if(header==true) break;
         }
         //hier dann weiter einlesen und Auswerten...
    }
    

    Ist stilistisch an einigen Stellen bestimmt nicht schön, bin eher noch Anfänger.
    Wie zu sehen ist Programmiere ich mit Qt, sollte aber am eigentlichen Problem nichts ändern.

    Ja, es scheint ein ID3-Tag am anfang zu stehen(Die datei beginnt mit ID3).
    Aber mal ne dumme Frage: Wie Groß ist ein Id3v2 Tag? Oder gibts da keine feste Größe?
    Gruß, tXX



  • char a[1];              // signed oder unsigned, was wäre zu empfehlen?
    (...)
     for(int x=1; x<=8; x++) // <- index x!
     {
      if(a[0]&(1<<x))        // <- index x!
      {
    (...)
    

    Was ergibt "1 << 8"?



  • Ich weiß nicht genau worauf du hinauswillst, aber mit

    if(a[0]&(1<<x))
    

    überprüfe ich ob das x-te bit in a[0] gesetzt ist oder nicht. Deswegen das ganze ja auch acht mal - in a[0] befindet sich genau ein byte, das Spalte ich auf in 8 bits indem ich eben für jedes bit innerhalb von a[0] überprüfe ob es gesetzt ist oder nicht.



  • So soll es auch sein. Allerdings prüft "1 << 1" das zweite Bit (und "1 << 8" das neunte). 🙂



  • char last_byte = 0;
         char this_byte = 0;
    
         for(int i=0; !stream->atEnd(); i++)
         {
             last_byte = this_byte;
             stream->readRawData(&this_byte, 1);
    
             if ((static_cast<int>(last_byte) & 0xFF) == 0xFF
              && (static_cast<int>(this_byte) & 0xE0) == 0xE0)
             {
                 // MPEG sync gefunden
                 break;
             }
         }
    


  • @ +gjim+: Achso... Danke für die Info 🙂

    @hustbaer: könntest du mir evtl erklären was dein Quellcode macht? Wie ich schon geschrieben hab bin ich ziemlich Ahnungslos in dem Bereich ^^
    er scheint aber "falsch" zu sein, wenn ich mir "last_byte" und "this_byte" ausgeben lasse bekomme ich folgendes:

    11111111 //<--last_byte
    11011111 //<--this_byte
    

    Da sind nur zehn und nicht elf einsen hintereinander, ich versteh allerdings nicht wie genau dein Code feststellt ob elf oder mehr Einsen hintereinander vorkommen und kann mir deshalb nicht vorstellen woran das liegt..

    evtl mach ich ja bei der Ausgabe auch einen Fehler(Quelltext wie gehabt, nur dass der Index jetzt bei 0 beginnt und bei 7 aufhört)
    Gruß, tXX



  • Die 11 Bits zählen von Bit Nr. 7 bis 0, nicht von 0 bis 7 😉
    Und wenn du so rum zählst, dann passt es.

    Was der Code macht sollte IMO nicht so schwer zu verstehen sein.
    Er sucht nach einer Folge aus zwei Byte, wo im "aktuellen" Byte (this_byte) alle Bits gesetzt sind (0xFF), und im Byte davor (last_byte) die höchstwertigen 3 Bits gesetzt sind (0xE0).

    Das ist ausreichend, da AFAIK der MPEG Sync immer an einer Byte-Grenze anfangen muss.

    p.S.: zumindest hat es für eine Sammlung aus > 100k MP3 Files (aus verschiedensten Quellen) gereicht, die MPEG Sync so zu suchen. Waren allerdings alles MP3 Files *ohne* ID3v2 Tag.

    Und...

    * wenn du "uint8_t" oder "unsigned char" statt "char" verwenden würdest, dann wären die doofen casts nicht nötig.

    * es wird vermutlich trotzdem nicht reichen die Header des 1. Frames so zu suchen, da viele ID3v2 nicht "unsynchronized" sind, und daher eine Bitfolge enthalten könnten, die wie ein MPEG Sync aussieht.

    p.p.S.: du könntest das Programm schön vereinfachen, indem du einfach das ganze MP3 File in einen Puffer lädst (oder in den Speicher mappst), und dann im Speicher direkt suchst. Würde auch garantiert deutlich schneller laufen.



  • Aber ist gesagt, dass beim zweiten Byte die drei höchsten Bits gesetzt sind, dann das nächste nicht und der Rest dann wieder gesetzt ist? Wenn der Rest des Bytes einen beliebigen Inhalt haben kann funktioniert deine Überprüfung nicht.



  • O.o schrieb:

    Aber ist gesagt, dass beim zweiten Byte die drei höchsten Bits gesetzt sind, dann das nächste nicht und der Rest dann wieder gesetzt ist? Wenn der Rest des Bytes einen beliebigen Inhalt haben kann funktioniert deine Überprüfung nicht.

    Doch, sie funktioniert.
    Beschreib mir einen (konkreten) Fall, wo du denkst, dass es nicht hinhauen könnte, dann kann ich dir vermutlich sagen, wo dein Denkfehler ist.



  • Ich denke, ich weiß was er meint: mit

    (static_cast<int>(this_byte) & 0xE0
    

    überprüfst du soweit ich das verstanden hab doch nicht, ob "this_byte" drei ode mehr gesetzte Bits am Anfang hat, sondern ob "this_byte" "0xE0" entspricht. Wenn "this_byte" mit drei oder mehr Einsen anfängt, sonst aber einen Unterschied zu dem überprüften aufweist, wird der Header fälschlicherweise nicht gefunden - so hab ich das zumindest verstanden, könnte durchaus ein Denkfehler drin sein^^



  • Da steht aber nicht static_cast<int>(this_byte) & 0xE0 sondern (static_cast<int>(this_byte) & 0xE0) == 0xE0 .

    Mal ganz davon abgesehen dass man mit "&" nicht auf "Gleichheit" oder "Entsprechung" (was auch immer das sein soll) prüft, sondern dass man damit zwei Zahlen "binär und" (AND) verknüpft. Das Ergebnis ist wieder eine Zahl, und diese muss dann gleich 0xE0 sein (==).

    Beispiel:

    sagen wir [i]this_byte[/i] wäre 0xE5, als char
    
    [i]static_cast<int>(this_byte)[/i] wäre dann bei "default char signed" 0xFFFFFFE5 (Sign-Extension, da signed)
    bzw. bei "default char unsigned" 0x000000E5 (Zero-Extension, da unsigned)
    
    [i](static_cast<int>(this_byte) & 0xE0)[/i] ist dann dieser wert, bit-für-bit AND verknüpft mit 0xE0, ...
    
    ...
    also 11111111111111111111111111100101b
    AND  00000000000000000000000011100000b
    =
         00000000000000000000000011100000b
    
    ...
    
    bzw. 00000000000000000000000011100101b
    AND  00000000000000000000000011100000b
    =
         00000000000000000000000011100000b
    
    Und 00000000000000000000000011100000b == 0xE0.
    

    Klar jetzt?
    Achja...
    Der Code setzt voraus dass entweder die Maschine two's complement verwendet (was "die üblichen" CPUs tun), ODER default char unsigned ist (beides ist natürlich auch OK).



  • Achsooo, Danke...
    Ich hab aber noch eine Frage: Der Frame-Header existiert ja für jeden Frame, die Angaben für die Bitrate sind aber in jedem Header gleich, wenn man nicht eine Variable Bitrate hat, oder?
    Dann könnte man einfach z.B. ab der Hälfte der Datei nach dem Header suchen und hätte so die lästigen id3v2 Tags vermieden, oder seh ich das falsch? Das würde nur in dem (sehr unwahrscheinlichen) Fall nicht funktionieren, dass der id3 Tag mehr als die Hälfte der Datei belegt...



  • Keine Ahnung ehrlich gesagt.
    Ich weiss nämlich nicht, ob die Frame-Daten selbst nicht auch 11 gesetzte Bits in Folge enthalten dürfen.

    EDIT: -> ausprobieren 🙂

    EDIT2:

    Der Frame-Header existiert ja für jeden Frame, die Angaben für die Bitrate sind aber in jedem Header gleich, wenn man nicht eine Variable Bitrate hat, oder?

    Ja, das müsste stimmen.
    Mit VBR kenne ich mich aber garnicht aus.
    Meine Kenntnisse über das MP3 Format beschränken sich auch ziemlich auf die Info, wie man Frames finden kann (vorausgesetzt es gibt keinen ID3v2 Tag)...



  • Okay, vielen Dank 👍


Log in to reply