skyscraper8/skyscraper8/Ietf/Rfc4236_ULE/UlePacketProcessor.cs

181 lines
4.7 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.NetworkInformation;
using System.Text;
using System.Threading.Tasks;
using skyscraper5.Mpeg2;
using skyscraper5.Skyscraper.IO;
namespace skyscraper8.Ietf.Rfc4236_ULE
{
internal class UlePacketProcessor : ITsPacketProcessor, IPayloadUnitDecoder
{
private readonly int _pid;
private readonly UleEventHandler _eventHandler;
private bool reassemblyMode;
private int reassemblyRemainingBytes;
private MemoryStream reassemblyStream;
private long offset;
public UlePacketProcessor(int pid, UleEventHandler eventHandler)
{
_pid = pid;
_eventHandler = eventHandler;
}
public void PushPacket(TsPacket packet)
{
if (packet.AdaptionFieldControl != 1)
{
_eventHandler.OnUleError(_pid, 2);
return;
}
if (packet.PayloadUnitStart)
{
PushPusiPacket(packet);
}
else
{
if (reassemblyMode)
{
int availableBytes = Math.Min(184, reassemblyRemainingBytes);
reassemblyStream.Write(packet.RawPacket, 4, availableBytes);
reassemblyRemainingBytes -= availableBytes;
if (reassemblyRemainingBytes == 0)
{
reassemblyMode = false;
reassemblyStream.Position = 0;
OnReassembledPacket(reassemblyStream);
reassemblyStream = null;
}
}
else
{
//Es gibt keine neue Payload Unit, und wir bauen uns auch keine zusammen, also diese wegwerfen.
return;
}
}
offset += 188;
}
private void PushPusiPacket(TsPacket packet)
{
bool multipleSndusInPacket = false;
MemoryStream payload = new MemoryStream(packet.RawPacket, false);
payload.Position += 4;
byte nextPacketOffset = payload.ReadUInt8();
if (!reassemblyMode)
payload.Position += nextPacketOffset;
while (payload.GetAvailableBytes() > 0)
{
if (reassemblyMode)
{
int availableBytes = Math.Min((int)payload.GetAvailableBytes(), reassemblyRemainingBytes);
byte[] stepBuffer = new byte[availableBytes];
int sucessfullyCopiedBytes = payload.Read(stepBuffer, 0, availableBytes);
if (sucessfullyCopiedBytes != availableBytes)
throw new UleException("Byte copy operation failed during reassembly.");
reassemblyStream.Write(stepBuffer, 0, sucessfullyCopiedBytes);
reassemblyRemainingBytes -= sucessfullyCopiedBytes;
if (reassemblyRemainingBytes == 0)
{
reassemblyMode = false;
reassemblyStream.Position = 0;
OnReassembledPacket(reassemblyStream);
reassemblyStream = null;
multipleSndusInPacket = true;
}
}
else
{
if (payload.GetAvailableBytes() == 1)
{
/*"If only one byte remains unprocessed in the TS Packet payload
after completion of the Current SNDU, the Receiver MUST discard this
final byte of TS Packet payload."*/
break;
}
byte lengthByteA = payload.ReadUInt8();
bool destinationAddressAbsent = (lengthByteA & 0x80) != 0;
byte lengthByteB = payload.ReadUInt8();
int length = lengthByteA & 0x7f;
length <<= 8;
length += lengthByteB;
length += 2;
if (length == 0x8001)
{
break;
}
reassemblyMode = true;
reassemblyRemainingBytes = length;
reassemblyStream = new MemoryStream();
reassemblyStream.WriteUInt8(lengthByteA);
reassemblyStream.WriteUInt8(lengthByteB);
}
}
}
private void OnReassembledPacket(MemoryStream ms)
{
bool crc = DvbCrc32.ValidateCrc(ms, 0, (int)ms.Length);
if (!crc)
{
_eventHandler.OnUleError(_pid, 1);
return;
}
byte lengthByteA = ms.ReadUInt8();
bool destinationAddressAbsent = (lengthByteA & 0x80) != 0;
byte lengthBytesB = ms.ReadUInt8();
ushort typeField = ms.ReadUInt16BE();
PhysicalAddress destAddress = null;
if (!destinationAddressAbsent)
destAddress = new PhysicalAddress(ms.ReadBytes(6));
long packetSize = ms.GetAvailableBytes() - 4;
byte[] pdu = ms.ReadBytes(packetSize);
uint crc32 = ms.ReadUInt32BE();
switch (typeField)
{
case 1:
OnBridgedFrame(pdu);
break;
default:
throw new NotImplementedException(String.Format("ULE Type 0x{0:X4}", typeField));
}
}
private void OnBridgedFrame(byte[] pdu)
{
MemoryStream ms = new MemoryStream(pdu, false);
PhysicalAddress destination = new PhysicalAddress(ms.ReadBytes(6));
PhysicalAddress source = new PhysicalAddress(ms.ReadBytes(6));
ushort etherType = ms.ReadUInt16BE();
byte[] contents = ms.ReadBytes(ms.GetAvailableBytes());
_eventHandler.OnEthernetFrame(_pid, destination, source, etherType, contents);
}
public void PacketLoss()
{
reassemblyMode = false;
reassemblyRemainingBytes = 0;
reassemblyStream = null;
}
}
}