private void ParseMP3Frames(byte[] buff) { 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 }; var offset = 0; var length = buff.Length; while (length >= 4) { ulong header; int mpegVersion, layer, bitRate, sampleRate, padding, channelMode; int frameLen; header = (ulong)BitConverterBE.ToUInt32(buff, offset) << 32; if (BitHelper.Read(ref header, 11) != 0x7FF) { break; } mpegVersion = BitHelper.Read(ref header, 2); layer = BitHelper.Read(ref header, 2); BitHelper.Read(ref header, 1); bitRate = BitHelper.Read(ref header, 4); sampleRate = BitHelper.Read(ref header, 2); 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; if (mpegVersion == 3) { sampleRate = MPEG1SampleRate[sampleRate]; } else if (mpegVersion == 2) { sampleRate = MPEG20SampleRate[sampleRate]; } else { sampleRate = MPEG25SampleRate[sampleRate]; } frameLen = GetFrameLength(mpegVersion, bitRate, sampleRate, padding); if (frameLen > length) { break; } var isVBRHeaderFrame = false; if (_frameOffsets.Count == 0) { // Check for an existing VBR header just to be safe (I haven't seen any in FLVs) var o = offset + GetFrameDataOffset(mpegVersion, channelMode); if (BitConverterBE.ToUInt32(buff, o) == 0x58696E67) { // "Xing" isVBRHeaderFrame = true; _delayWrite = false; _hasVBRHeader = true; } } if (isVBRHeaderFrame) { } else if (_firstBitRate == 0) { _firstBitRate = bitRate; _mpegVersion = mpegVersion; _sampleRate = sampleRate; _channelMode = channelMode; _firstFrameHeader = BitConverterBE.ToUInt32(buff, offset); } else if (!_isVBR && bitRate != _firstBitRate) { _isVBR = true; if (_hasVBRHeader) { } else if (_delayWrite) { WriteVBRHeader(true); _writeVBRHeader = true; _delayWrite = false; } else { _warnings.Add("Detected VBR too late, cannot add VBR header."); } } _frameOffsets.Add(_totalFrameLength + (uint)offset); offset += frameLen; length -= frameLen; } _totalFrameLength += (uint)buff.Length; }
public void WriteChunk(byte[] chunk, uint timeStamp) { if (chunk.Length < 1) { return; } if (chunk[0] == 0) { // Header if (chunk.Length < 3) { return; } var bits = (ulong)BitConverterBE.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 == 4) // HE-AAC { _aacProfile = 1; // Uses LC profile + SBR } if (_aacProfile < 0 || _aacProfile > 3) { throw new Exception("Unsupported AAC profile."); } if (_sampleRateIndex > 12) { throw new Exception("Invalid AAC sample rate index."); } if (_channelConfig > 6) { throw new Exception("Invalid AAC channel configuration."); } } else { // Audio data var 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); _fs.Write(BitConverterBE.GetBytes(bits), 1, 7); _fs.Write(chunk, 1, dataSize); } }
public void WriteChunk(byte[] chunk, uint timeStamp) { var subModeSizes = new[] { 0, 43, 119, 160, 220, 300, 364, 492, 79 }; var wideBandSizes = new[] { 0, 36, 112, 192, 352 }; var inBandSignalSizes = new[] { 1, 1, 4, 4, 4, 4, 4, 4, 8, 8, 16, 16, 32, 32, 64, 64 }; var frameStart = -1; var frameEnd = 0; var offset = 0; var length = chunk.Length * 8; int x; while (length - offset >= 5) { x = BitHelper.Read(chunk, ref offset, 1); if (x != 0) { // wideband frame x = BitHelper.Read(chunk, ref offset, 3); if (x < 1 || x > 4) { goto Error; } offset += wideBandSizes[x] - 4; } else { x = BitHelper.Read(chunk, ref offset, 4); if (x >= 1 && x <= 8) { // narrowband frame if (frameStart != -1) { WriteFramePacket(chunk, frameStart, frameEnd); } frameStart = frameEnd; offset += subModeSizes[x] - 5; } else if (x == 15) { // terminator break; } else if (x == 14) { // in-band signal if (length - offset < 4) { goto Error; } x = BitHelper.Read(chunk, ref offset, 4); offset += inBandSignalSizes[x]; } else if (x == 13) { // custom in-band signal if (length - offset < 5) { goto Error; } x = BitHelper.Read(chunk, ref offset, 5); offset += x * 8; } else { goto Error; } } frameEnd = offset; } if (offset > length) { goto Error; } if (frameStart != -1) { WriteFramePacket(chunk, frameStart, frameEnd); } return; Error: throw new Exception("Invalid Speex data."); }
private void GetFrameSize(byte[] chunk) { if (_codecID == 2) { // Reference: flv_h263_decode_picture_header from libavcodec's h263.c if (chunk.Length < 10) { return; } if (chunk[0] != 0 || chunk[1] != 0) { return; } var x = BitConverterBE.ToUInt64(chunk, 2); int format; if (BitHelper.Read(ref x, 1) != 1) { return; } BitHelper.Read(ref x, 5); BitHelper.Read(ref x, 8); format = BitHelper.Read(ref x, 3); switch (format) { case 0: _width = BitHelper.Read(ref x, 8); _height = BitHelper.Read(ref x, 8); break; case 1: _width = BitHelper.Read(ref x, 16); _height = BitHelper.Read(ref x, 16); break; case 2: _width = 352; _height = 288; break; case 3: _width = 176; _height = 144; break; case 4: _width = 128; _height = 96; break; case 5: _width = 320; _height = 240; break; case 6: _width = 160; _height = 120; break; default: return; } } else if (_codecID == 4 || _codecID == 5) { // Reference: vp6_parse_header from libavcodec's vp6.c var skip = _codecID == 4 ? 1 : 4; if (chunk.Length < skip + 8) { return; } var x = BitConverterBE.ToUInt64(chunk, skip); var deltaFrameFlag = BitHelper.Read(ref x, 1); var quant = BitHelper.Read(ref x, 6); var separatedCoeffFlag = BitHelper.Read(ref x, 1); var subVersion = BitHelper.Read(ref x, 5); var filterHeader = BitHelper.Read(ref x, 2); var interlacedFlag = BitHelper.Read(ref x, 1); if (deltaFrameFlag != 0) { return; } if (separatedCoeffFlag != 0 || filterHeader == 0) { BitHelper.Read(ref x, 16); } _height = BitHelper.Read(ref x, 8) * 16; _width = BitHelper.Read(ref x, 8) * 16; // chunk[0] contains the width and height (4 bits each, respectively) that should // be cropped off during playback, which will be non-zero if the encoder padded // the frames to a macroblock boundary. But if you use this adjusted size in the // AVI header, DirectShow seems to ignore it, and it can cause stride or chroma // alignment problems with VFW if the width/height aren't multiples of 4. if (!_isAlphaWriter) { var cropX = chunk[0] >> 4; var cropY = chunk[0] & 0x0F; if ((cropX != 0 || cropY != 0) && !_isAlphaWriter) { _warnings.Add(String.Format("Suggested cropping: {0} pixels from right, {1} pixels from bottom.", cropX, cropY)); } } } }