AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Multimedia Delphi Wirkliche Position eines mp3headers...nicht mal jedi kanns!
Thema durchsuchen
Ansicht
Themen-Optionen

Wirkliche Position eines mp3headers...nicht mal jedi kanns!

Ein Thema von dadu · begonnen am 30. Aug 2005 · letzter Beitrag vom 30. Aug 2005
Antwort Antwort
dadu

Registriert seit: 17. Apr 2003
Ort: Dattenberg
102 Beiträge
 
Delphi 2005 Personal
 
#1

Wirkliche Position eines mp3headers...nicht mal jedi kanns!

  Alt 30. Aug 2005, 20:12
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...
DaDu
  Mit Zitat antworten Zitat
Benutzerbild von Flocke
Flocke

Registriert seit: 9. Jun 2005
Ort: Unna
1.172 Beiträge
 
Delphi 10.2 Tokyo Professional
 
#2

Re: Wirkliche Position eines mp3headers...nicht mal jedi kan

  Alt 30. Aug 2005, 20:34
Also ich habe das in einem Framescanner mal so gemacht (C-Code)
Code:
//======================================================================
//
// 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))
Ich teste also auf (1111 1111 1111 .aa. bbbb cc..).
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:
ok := ((syncword and $0000f0ff) = $0000f0ff) and
      ((syncword and $00000600) <> $00000000) and
      ((syncword and $00f00000) <> $00f00000) and
      ((syncword and $000c0000) <> $000c0000);
Ein bisschen aufwendiger ist der Test doch noch gewesen (hab gerade noch mal durch die Sourcen geschaut):

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).
Angehängte Dateien
Dateityp: cpp layer3_912.cpp (17,5 KB, 13x aufgerufen)
Volker
Besucht meine Garage
Aktuell: RtfLabel 1.3d, PrintToFile 1.4
  Mit Zitat antworten Zitat
dadu

Registriert seit: 17. Apr 2003
Ort: Dattenberg
102 Beiträge
 
Delphi 2005 Personal
 
#3

Re: Wirkliche Position eines mp3headers...nicht mal jedi kan

  Alt 30. Aug 2005, 20:52
Zitat von Flocke:
Ich teste also auf (1111 1111 1111 .aa. bbbb cc..).
aa muss ungleich "00" sein
bbbb muss ungleich "1111" sein
cc muss ungleich "11" sein
(binär)
Das mache ich auch...hilft aber nicht immer, denn es kann ja passieren das scheinbar ein richtiger Header darsteht und slebst die Länge muss nicht utopisch sein, hatte schon einen header mit der Spielzeit 300sekunden! das lied hatte aber nur 200 und der echte header war laut winamp 200bytes weiter unten.

Zitat von Flocke:
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.
Das ist interessant, leider werde ich aus dem C Code nicht schlau. Was meinst du mit "du prüfst ob an den erwarteten Stellen noch Syncwords sind". Ich hatte gedacht der einzige Sync ist am Anfang des Headers...Wäre dir sehr dankbar, wenn du das ein wenig präziser beschreiben könntest.
DaDu
  Mit Zitat antworten Zitat
Benutzerbild von Flocke
Flocke

Registriert seit: 9. Jun 2005
Ort: Unna
1.172 Beiträge
 
Delphi 10.2 Tokyo Professional
 
#4

Re: Wirkliche Position eines mp3headers...nicht mal jedi kan

  Alt 30. Aug 2005, 21:04
Zitat:
Das ist interessant, leider werde ich aus dem C Code nicht schlau. Was meinst du mit "du prüfst ob an den erwarteten Stellen noch Syncwords sind". Ich hatte gedacht der einzige Sync ist am Anfang des Headers...Wäre dir sehr dankbar, wenn du das ein wenig präziser beschreiben könntest.
Vorhin vergessen: Jeder MP3-Frame beginnt mit einem Syncword, darum gibt's auch so viele Matches für dein Pattern

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:
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;
}
"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.

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
// === ===+==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ====
Volker
Besucht meine Garage
Aktuell: RtfLabel 1.3d, PrintToFile 1.4
  Mit Zitat antworten Zitat
dadu

Registriert seit: 17. Apr 2003
Ort: Dattenberg
102 Beiträge
 
Delphi 2005 Personal
 
#5

Re: Wirkliche Position eines mp3headers...nicht mal jedi kan

  Alt 30. Aug 2005, 21:12
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.
DaDu
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 10:58 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz