private void ParseMp3Frames(byte[] buffer) { var mpeg1BitRate = new[] { 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0 }; var mpeg2XBitRate = new[] { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0 }; var mpeg1SampleRate = new[] { 44100, 48000, 32000, 0 }; var mpeg20SampleRate = new[] { 22050, 24000, 16000, 0 }; var mpeg25SampleRate = new[] { 11025, 12000, 8000, 0 }; int offset = 0; int length = buffer.Length; while (length >= 4) { int mpegVersion, sampleRate, channelMode; ulong header = (ulong)BigEndianBitConverter.ToUInt32(buffer, offset) << 32; if (BitHelper.Read(ref header, 11) != 0x7FF) { break; } mpegVersion = BitHelper.Read(ref header, 2); int layer = BitHelper.Read(ref header, 2); BitHelper.Read(ref header, 1); int bitRate = BitHelper.Read(ref header, 4); sampleRate = BitHelper.Read(ref header, 2); int padding = BitHelper.Read(ref header, 1); BitHelper.Read(ref header, 1); channelMode = BitHelper.Read(ref header, 2); if (mpegVersion == 1 || layer != 1 || bitRate == 0 || bitRate == 15 || sampleRate == 3) { break; } bitRate = (mpegVersion == 3 ? mpeg1BitRate[bitRate] : mpeg2XBitRate[bitRate]) * 1000; switch (mpegVersion) { case 2: sampleRate = mpeg20SampleRate[sampleRate]; break; case 3: sampleRate = mpeg1SampleRate[sampleRate]; break; default: sampleRate = mpeg25SampleRate[sampleRate]; break; } int frameLenght = GetFrameLength(mpegVersion, bitRate, sampleRate, padding); if (frameLenght > length) { break; } bool isVbrHeaderFrame = false; if (frameOffsets.Count == 0) { // Check for an existing VBR header just to be safe (I haven't seen any in FLVs) int o = offset + GetFrameDataOffset(mpegVersion, channelMode); if (BigEndianBitConverter.ToUInt32(buffer, o) == 0x58696E67) { // "Xing" isVbrHeaderFrame = true; this.delayWrite = false; this.hasVbrHeader = true; } } if (!isVbrHeaderFrame) { if (this.firstBitRate == 0) { this.firstBitRate = bitRate; this.mpegVersion = mpegVersion; this.sampleRate = sampleRate; this.channelMode = channelMode; this.firstFrameHeader = BigEndianBitConverter.ToUInt32(buffer, offset); } else if (!this.isVbr && bitRate != this.firstBitRate) { this.isVbr = true; if (!this.hasVbrHeader) { if (this.delayWrite) { this.WriteVbrHeader(true); this.writeVbrHeader = true; this.delayWrite = false; } else { this.warnings.Add("Detected VBR too late, cannot add VBR header."); } } } } this.frameOffsets.Add(this.totalFrameLength + (uint)offset); offset += frameLenght; length -= frameLenght; } this.totalFrameLength += (uint)buffer.Length; }
public void WriteChunk(byte[] chunk, uint timeStamp) { if (chunk.Length < 1) { return; } if (chunk[0] == 0) { // Header if (chunk.Length < 3) { return; } ulong bits = (ulong)BigEndianBitConverter.ToUInt16(chunk, 1) << 48; aacProfile = BitHelper.Read(ref bits, 5) - 1; sampleRateIndex = BitHelper.Read(ref bits, 4); channelConfig = BitHelper.Read(ref bits, 4); if (aacProfile < 0 || aacProfile > 3) { throw new AudioExtractionException("Unsupported AAC profile."); } if (sampleRateIndex > 12) { throw new AudioExtractionException("Invalid AAC sample rate index."); } if (channelConfig > 6) { throw new AudioExtractionException("Invalid AAC channel configuration."); } } else { // Audio data int dataSize = chunk.Length - 1; ulong bits = 0; // Reference: WriteADTSHeader from FAAC's bitstream.c BitHelper.Write(ref bits, 12, 0xFFF); BitHelper.Write(ref bits, 1, 0); BitHelper.Write(ref bits, 2, 0); BitHelper.Write(ref bits, 1, 1); BitHelper.Write(ref bits, 2, aacProfile); BitHelper.Write(ref bits, 4, sampleRateIndex); BitHelper.Write(ref bits, 1, 0); BitHelper.Write(ref bits, 3, channelConfig); BitHelper.Write(ref bits, 1, 0); BitHelper.Write(ref bits, 1, 0); BitHelper.Write(ref bits, 1, 0); BitHelper.Write(ref bits, 1, 0); BitHelper.Write(ref bits, 13, 7 + dataSize); BitHelper.Write(ref bits, 11, 0x7FF); BitHelper.Write(ref bits, 2, 0); fileStream.Write(BigEndianBitConverter.GetBytes(bits), 1, 7); fileStream.Write(chunk, 1, dataSize); } }