MP3-Header auslesen gelingt nicht



  • 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