![]() |
Wirkliche Position eines mp3headers...nicht mal jedi kanns!
Hi Leute!
Ich habe heute ein Programm geschrieben um den MP3 Header auszulesen auch den Xing Header für Variable Bitraten. Ziel war es die Spiellänge von mp3s zu bestimmen, auch von denen, mit VBR. Das funktioniert auch alles ganz gut, doch bei einigen läuft da ganzschön was schief: Normale Dokumentationen meinen immer man solle nach dem Sync '1111 1111 111X XXX' suchen, denn diese besetzten Bits weisen auf den Anfang des MPEG Headers hin. Selbst die Jedis machen das so. Doch oft, wie ich feststellen musste, ist das nicht der Header, sondern einfach nur zufällig besetzte Bits; dabei steht der echte Header weiter unten in der Datei. Als erster Gedanke kam mir, den Header an dieser Stelle auszulesen, und sobald die Bitrate, die MPEGVersion, die Samplingrate oder die LayerVersion in einem ungültigen Bereich landen, die Suche nach dem Header fortzuführen, da es sich ja offensichtlich bei dem erkannten Snyc um Zufall handelt. Gesagt getan, es werden durch dieses Verfahren auch schon viele mp3s die vorher falsch waren, richtig erkannt, nur leider noch immer nicht alle. Das Interessante: "WinAMP" erkennt alle Header richtig...es muss also noch ein Kriterium für die Richtigkeit eines Headers geben..hoffe ihr wisst es! PS: Allen Code zu diesem Thema den ich bis jetzt gefunden habe, inkl. Jedis suchen nur nach dem "1111 1111 111X XXX" Sync... |
Re: Wirkliche Position eines mp3headers...nicht mal jedi kan
Liste der Anhänge anzeigen (Anzahl: 1)
Also ich habe das in einem Framescanner mal so gemacht (C-Code)
Code:
Ich teste also auf (1111 1111 1111 .aa. bbbb cc..).
//======================================================================
// // Aufbau des MPEG Audio Synchronisationswort // // ========0======== ========1======== ========2======== ========3======== Byte // 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 Bit // // 1 1 1 1 1 1 1 1 1 1 1 1 ~0-0 ~1-1-1-1 1-1~ Valid // X x x x x x x X X 1-1 Compatible // // +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ // |1 1 1 1 1 1 1 1| |1 1 1 1| | | | | | | | | | | | | | | // +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ // | | | | | | | | | | | | // | | | | | | | | | | | +-- emphasis // | | | | | | | | | | +----- original // | | | | | | | | | +------- copyright // | | | | | | | | +---------- mode extension // | | | | | | | +-------------- mode // | | | | | | +------------------- extension // | | | | | +--------------------- padding // | | | | +------------------------ frequency (0,1,2) // | | | +------------------------------ bitrate (1-14) // | | +------------------------------------- no crc check // | +---------------------------------------- layer (1,2,3) // +------------------------------------------- version (0,1) // //====================================================================== union l3f_syncword { unsigned char sw_b[4]; unsigned long sw_l; }; #define L3F_SYNC_VALID(sw) \ ( (sw).sw_b[0] == 0xff \ && ((sw).sw_b[1] & 0xf0) == 0xf0 \ && ((sw).sw_b[1] & 0x06) != 0x00 \ && ((sw).sw_b[2] & 0xf0) != 0xf0 \ && ((sw).sw_b[2] & 0x0c) != 0x0c) #define L3F_SYNC_COMPATIBLE(s1, s2) \ ( ((s1).sw_b[1] & 0x0e) == ((s2).sw_b[1] & 0x0e) \ && ((s1).sw_b[2] & 0xfc) == ((s2).sw_b[2] & 0xfc) \ && (((s1).sw_b[3] & 0xc0) == 0xc0) == (((s2).sw_b[3] & 0xc0) == 0xc0)) aa muss ungleich "00" sein bbbb muss ungleich "1111" sein cc muss ungleich "11" sein (binär) Damit habe ich noch nie einen falschen Frame erwischt. [Nachtrag] Also in Pascal (falls Syncword ein Cardinal ist)
Delphi-Quellcode:
Ein bisschen aufwendiger ist der Test doch noch gewesen (hab gerade noch mal durch die Sourcen geschaut):
ok := ((syncword and $0000f0ff) = $0000f0ff) and
((syncword and $00000600) <> $00000000) and ((syncword and $00f00000) <> $00f00000) and ((syncword and $000c0000) <> $000c0000); Nachdem ich ein gültiges Syncword gefunden habe prüfe ich dann außerdem noch ob an den erwarteten Stellen (anhand der Framegröße berechnet) noch mindestens 7 weitere gültige Syncwords folgen und mit dem Makro L3F_SYNC_COMPATIBLE zusätzlich noch, ob diese zum ersten gefundenen kompatibel sind. Ich hab' die C++-Datei mal angehängt falls du was damit anfangen kannst (die drei Header-Dateien braucht man nicht). |
Re: Wirkliche Position eines mp3headers...nicht mal jedi kan
Zitat:
Zitat:
|
Re: Wirkliche Position eines mp3headers...nicht mal jedi kan
Zitat:
Jeder MP3-Frame hat eine bestimmte Länge, die man auf's Byte genau ausrechnen kann (aus den Daten im Syncword). Das macht die folgende Routine (sorry, wieder C):
Code:
"L3F_Header" ist eine selbstdefinierte Struktur, in der ich die einzelnen Bitfelder aus dem Syncword ablege. Mit Hilfe der Arrays l3f_spframe, l3f_bpslot, l3f_frequency und l3f_kbps errechne ich dann die Länge eines Frames in Samples und in Bytes.
int
l3f_header_set_syncword (L3F_Header *self, long off, l3f_syncword sw) { int sb, spf, bps, freq, kbps; self->offset = off; self->syncword.sw_l = sw.sw_l; sb = sw.sw_b[1]; self->i_version = (sb >> 3) & 1; self->i_layer = (sb >> 1) & 3; self->i_no_crc = (char)((sb ) & 1); sb = sw.sw_b[2]; self->i_bitrate = (sb >> 4) & 15; self->i_freq = (sb >> 2) & 3; self->i_padding = (sb >> 1) & 1; self->i_extension = (char)((sb ) & 1); sb = sw.sw_b[3]; self->i_mode = (sb >> 6) & 3; self->i_mode_ext = (sb >> 4) & 3; self->i_copyright = (char)((sb >> 3) & 1); self->i_original = (char)((sb >> 2) & 1); self->i_emphasis = (sb ) & 3; // Framelänge berechnen spf = l3f_spframe[self->i_version][self->i_layer]; bps = l3f_bpslot[self->i_layer]; freq = l3f_frequency[self->i_version][self->i_freq]; kbps = 125 * l3f_kbps[self->i_version][self->i_layer][self->i_bitrate]; // Auf irgendwelche ungültigen Indizes testen if (spf <= 0 || bps <= 0 || freq <= 0 || kbps <= 0) return 0; self->length_in_bytes = l3f_umuldiv_trunc(spf, kbps, freq) + bps * self->i_padding; self->length_in_samples = spf; return 1; } Sofern die MP3-Datei korrekt ist findest du exakt "length_in_bytes" Bytes weiter (ab Beginn des Syncwords) das nächste Syncword - und das muss ja kompatibel sein, also müssen z.B. Layer und Frequenz übereinstimmen. C/C++ ist für jemanden der Delphi kann gar nicht sooo schwer zu lesen. Alternativ kannst du dir die vier Felder "version", "layer", "frequency" und "bitrate" aus dem Syncword extrahieren und mit der folgenden Tabelle (die auch in der C++-Datei steht) in die Framelänge umrechnen (ggf. plus 1 Byte "padding", auch aus dem Syncword). V/L ist version/layer F ist frequency die Spalten sind die Bitrate (-> jeweils die Werte aus dem Syncword)
Code:
// Hier ist eine Tabelle mit den Basislängen (ohne Padding) für alle
// möglichen Werte von h.version, h.layer, h.frequency und h.bitrate. // // V/L F | 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 // === ===+==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== // 0-0 0 | -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 // 1 | -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 // 2 | -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 // 3 | -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 // MPEG 2 Layer 3 ------------------------------------------------------------------------ // 0-1 0 | 0 26 52 78 104 130 156 182 208 261 313 365 417 470 522 -1 // 1 | 0 24 48 72 96 120 144 168 192 240 288 336 384 432 480 -1 // 2 | 0 36 72 108 144 180 216 252 288 360 432 504 576 648 720 -1 // 3 | -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 // MPEG 2 Layer 2 ------------------------------------------------------------------------ // 0-2 0 | 0 52 104 156 208 261 313 365 417 522 626 731 835 940 1044 -1 // 1 | 0 48 96 144 192 240 288 336 384 480 576 672 768 864 960 -1 // 2 | 0 72 144 216 288 360 432 504 576 720 864 1008 1152 1296 1440 -1 // 3 | -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 // MPEG 2 Layer 1 ------------------------------------------------------------------------ // 0-3 0 | 0 69 104 121 139 174 208 243 278 313 348 383 417 487 557 -1 // 1 | 0 64 96 112 128 160 192 224 256 288 320 352 384 448 512 -1 // 2 | 0 96 144 168 192 240 288 336 384 432 480 528 576 672 768 -1 // 3 | -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 // === ===+==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== // 1-0 0 | -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 // 1 | -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 // 2 | -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 // 3 | -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 // MPEG 1 Layer 3 ------------------------------------------------------------------------ // 1-1 0 | 0 104 130 156 182 208 261 313 365 417 522 626 731 835 1044 -1 // 1 | 0 96 120 144 168 192 240 288 336 384 480 576 672 768 960 -1 // 2 | 0 144 180 216 252 288 360 432 504 576 720 864 1008 1152 1440 -1 // 3 | -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 // MPEG 1 Layer 2 ------------------------------------------------------------------------ // 1-2 0 | 0 104 156 182 208 261 313 365 417 522 626 731 835 1044 1253 -1 // 1 | 0 96 144 168 192 240 288 336 384 480 576 672 768 960 1152 -1 // 2 | 0 144 216 252 288 360 432 504 576 720 864 1008 1152 1440 1728 -1 // 3 | -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 // MPEG 1 Layer 1 ------------------------------------------------------------------------ // 1-3 0 | 0 34 69 104 139 174 208 243 278 313 348 383 417 452 487 -1 // 1 | 0 32 64 96 128 160 192 224 256 288 320 352 384 416 448 -1 // 2 | 0 48 96 144 192 240 288 336 384 432 480 528 576 624 672 -1 // 3 | -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 // === ===+==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== |
Re: Wirkliche Position eines mp3headers...nicht mal jedi kan
Cool...ich glaub ich habs verstanden...werd ich gleich mal ausprobieren!..das wusste ich nämlich noch nicht, dass JEDER frame einen eigenen header hat.
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 10:25 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz