14 May 2016

MP3 - Step 1 - Find the first header

An MP3 frame header starts with 11 set bits, and the 12th should also always be set. But finding 12 set bits doesn't garantee you found a frame header, so to make sure, you should verify several succeeding headers as well.
const int HEADERS_TO_VERIFY = 5;
var headers = new List(HEADERS_TO_VERIFY);
MP3Header header = null; //MP3Header is a class for the headers' properties.

while (headers.Count < HEADERS_TO_VERIFY)
{
    header = ParseHeader();

    if (header == null)
    {
        headers.Clear();
        Source.Position = ++pos;
        continue;
    }

    header.Position = Source.Position - 4; //Source is the MP3 bitstream.
    headers.Add(header);
    Debug.WriteLine($"Found MPEG {header.Version} layer {header.Layer} at {Source.Position}.");
    Source.Position += header.SizeData - 4;
}

private MP3Header ParseHeader()
{
    //A frame starts with 11 set bits. The 12th bit should also always be set.
    if (Source.Read(12).Any(bit => !bit)) return null; //Read 12 bits from the bitstream.

    //Parse the four bytes into a frame header.
    var header = new MP3Header();
    header.Version = Source.Read() ? 1 : 2; //Read 1 bit from the bitstream.
    header.Layer = 4 - Source.Read(2).ToInt();
    header.IsCRCProtected = Source.Read();

    if (!header.Layer.Between(1, 3)) return null;

    header.SetKilobitsSecond(Source.Read(4).ToInt());
    header.SetSamplesSecond(Source.Read(2).ToInt());
    header.IsPadded = Source.Read();
    Source.Read(); //Private bit

    header.Channeling = (MP3Channeling)Source.Read(2).ToInt();
    header.IsMSStereo = Source.Read();
    header.IsIntensityStereo = Source.Read();
    Source.Read(2); //Copyright bit, is original bit
    header.Emphasis = (MP3Emphasis)Source.Read(2).ToInt();

    //Verify the header is good.
    if (header.Emphasis == MP3Emphasis.Reserved ||
        header.SamplesSecond <= 0 || 
        header.KilobitsSecond <= 0) return null;
    return header;
}

No comments:

Post a Comment