naudio / NLayer

MPEG 1 & 2 Decoder for Layers 1, 2, & 3
MIT License
124 stars 30 forks source link

Wrong Frame Size for MPEG-2 #10

Open kmcclellan opened 6 years ago

kmcclellan commented 6 years ago

I was getting an exception when decoding MPEG Version 2. Figured out it was because the frame lengths were being calculated incorrectly. I believe the issue is here (in MpegFrame.cs):

// calculate the frame's length
int frameSize;
if (BitRateIndex > 0)
{
    if (Layer == MpegLayer.LayerI)
    {
        frameSize = (12 * BitRate / SampleRate + Padding) * 4;
    }
    else
    {
        frameSize = 144 * BitRate / SampleRate + Padding;
    }
}

Compare to the length calculation used by NAudio:

int coefficient = frame.SampleCount/8;
if (frame.MpegLayer == MpegLayer.Layer1)
{
    frame.FrameLength = (coefficient*frame.BitRate/frame.SampleRate + nPadding)*4;
}
else
{
    frame.FrameLength = (coefficient*frame.BitRate)/frame.SampleRate + nPadding;
}

I don't have access to the specs, but it looks like the hard-coded 144 is only going to be correct for MPEG-1 (when sample count is 1152). Appears to have solved my error switching to use SampleCount / 8.

markheath commented 6 years ago

Thanks for reporting, if you have a test file you can share that would be helpful.

kmcclellan commented 6 years ago

I can't share the file I first ran into it with, but I found one online that is enough to reproduce: Sample

Test method NLayerTests.UnitTest1.TestMethod1 threw exception: 
System.IndexOutOfRangeException: Index was outside the bounds of the array.
    at NLayer.Decoder.LayerIIDecoderBase.GetCRC(MpegFrame frame, Int32[] rateTable, Int32[][] allocLookupTable, Boolean readScfsiBits, UInt32& crc)
   at NLayer.Decoder.MpegFrame.ValidateCRC()
   at NLayer.Decoder.MpegFrame.Validate()
   at NLayer.Decoder.MpegStreamReader.FindNextFrame()
   at NLayer.Decoder.MpegStreamReader.ReadToEnd()
   at NLayer.Decoder.MpegStreamReader.get_SampleCount()
   at NLayer.MpegFile.get_Length()
   at NLayerTests.UnitTest1.TestMethod1()

I believe any file using MPEG Version 2 or MPEG Version 2.5 will use the wrong frame length and not decode properly. Once it gets out of sync it ends up reading some invalid frame headers and invoking the wrong decoders (i.e. Layer I or II), causing an exception like the above.

markheath commented 5 years ago

I'm not sure NAudio is correctly parsing the frames on that file either - I can't load it through the NLayer.NAudioSupport

wei-kris commented 1 year ago

NAudio correctly parsed , But NAudio does not support android.


IndexOutOfRangeException: Index was outside the bounds of the array.
NLayer.Decoder.LayerIIIDecoder.Dequantize (System.Int32 idx, System.Single val, System.Int32 gr, System.Int32 ch) (at Assets/NLayer/Decoder/LayerIIIDecoder.cs:1427)
NLayer.Decoder.LayerIIIDecoder.ReadSamples (System.Int32 sfBits, System.Int32 gr, System.Int32 ch) (at Assets/NLayer/Decoder/LayerIIIDecoder.cs:1393)
NLayer.Decoder.LayerIIIDecoder.DecodeFrame (NLayer.IMpegFrame frame, System.Single[] ch0, System.Single[] ch1) (at Assets/NLayer/Decoder/LayerIIIDecoder.cs:562)
NLayer.MpegFrameDecoder.DecodeFrameImpl (NLayer.IMpegFrame frame, System.Array dest, System.Int32 destOffset) (at Assets/NLayer/MpegFrameDecoder.cs:143)
NLayer.MpegFrameDecoder.DecodeFrame (NLayer.IMpegFrame frame, System.Single[] dest, System.Int32 destOffset) (at Assets/NLayer/MpegFrameDecoder.cs:105)
NLayer.MpegFile.ReadSamplesImpl (System.Array buffer, System.Int32 index, System.Int32 count) (at Assets/NLayer/MpegFile.cs:263)
NLayer.MpegFile.ReadSamples (System.Single[] buffer, System.Int32 index, System.Int32 count) (at Assets/NLayer/MpegFile.cs:205)