From 8c76f9a42109a86f6f48584ae003d55f36840d4f Mon Sep 17 00:00:00 2001 From: feyris-tan <4116042+feyris-tan@users.noreply.github.com> Date: Sat, 19 Jul 2025 22:44:49 +0200 Subject: [PATCH] Added a rudimentary ULE deframer. --- skyscraper8/Ietf/IetfException.cs | 24 +++ skyscraper8/Ietf/Rfc4236_ULE/UleContestant.cs | 46 +++++ .../Ietf/Rfc4236_ULE/UleEventHandler.cs | 24 +++ skyscraper8/Ietf/Rfc4236_ULE/UleException.cs | 23 +++ .../Ietf/Rfc4236_ULE/UlePacketProcessor.cs | 176 ++++++++++++++++++ 5 files changed, 293 insertions(+) create mode 100644 skyscraper8/Ietf/IetfException.cs create mode 100644 skyscraper8/Ietf/Rfc4236_ULE/UleContestant.cs create mode 100644 skyscraper8/Ietf/Rfc4236_ULE/UleEventHandler.cs create mode 100644 skyscraper8/Ietf/Rfc4236_ULE/UleException.cs create mode 100644 skyscraper8/Ietf/Rfc4236_ULE/UlePacketProcessor.cs diff --git a/skyscraper8/Ietf/IetfException.cs b/skyscraper8/Ietf/IetfException.cs new file mode 100644 index 0000000..b53e269 --- /dev/null +++ b/skyscraper8/Ietf/IetfException.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using skyscraper5; + +namespace skyscraper8.Ietf +{ + public class IetfException : SkyscraperException + { + public IetfException() + { + } + + public IetfException(string message) : base(message) + { + } + + public IetfException(string message, Exception inner) : base(message, inner) + { + } + } +} diff --git a/skyscraper8/Ietf/Rfc4236_ULE/UleContestant.cs b/skyscraper8/Ietf/Rfc4236_ULE/UleContestant.cs new file mode 100644 index 0000000..5418d86 --- /dev/null +++ b/skyscraper8/Ietf/Rfc4236_ULE/UleContestant.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.NetworkInformation; +using System.Text; +using System.Threading.Tasks; +using skyscraper5.Skyscraper.Plugins; +using skyscraper5.Skyscraper.Scraper; +using skyscraper5.Skyscraper.Scraper.StreamAutodetection; + +namespace skyscraper8.Ietf.Rfc4236_ULE +{ + [SkyscraperPlugin] + internal class UleContestant : Contestant, UleEventHandler + { + public UleContestant(int pid) : base("ULE", pid) + { + PacketProcessor = new UlePacketProcessor(pid, this); + } + + public override void Dispose() + { + throw new NotImplementedException(); + } + + public override void DeclareWinner(SkyscraperContext skyscraperContext, int pid, ProgramContext programContext) + { + throw new NotImplementedException(); + } + + public override void Introduce(ProgramContext programContext) + { + throw new NotImplementedException(); + } + + public void OnEthernetFrame(int pid, PhysicalAddress destination, PhysicalAddress source, ushort etherType, byte[] contents) + { + Score++; + } + + public void OnUleError(int pid, int errorNo) + { + Score--; + } + } +} diff --git a/skyscraper8/Ietf/Rfc4236_ULE/UleEventHandler.cs b/skyscraper8/Ietf/Rfc4236_ULE/UleEventHandler.cs new file mode 100644 index 0000000..b774498 --- /dev/null +++ b/skyscraper8/Ietf/Rfc4236_ULE/UleEventHandler.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.NetworkInformation; +using System.Text; +using System.Threading.Tasks; + +namespace skyscraper8.Ietf.Rfc4236_ULE +{ + internal interface UleEventHandler + { + void OnEthernetFrame(int pid, PhysicalAddress destination, PhysicalAddress source, ushort etherType, byte[] contents); + + /// + /// The UlePacketProcessor is supposed to call this whenever an Error occurs during deframing. + /// + /// The source PID of the ULE ES + /// + /// Set this to: + /// 1* for a CRC error. + /// + void OnUleError(int pid, int errorNo); + } +} diff --git a/skyscraper8/Ietf/Rfc4236_ULE/UleException.cs b/skyscraper8/Ietf/Rfc4236_ULE/UleException.cs new file mode 100644 index 0000000..44682f8 --- /dev/null +++ b/skyscraper8/Ietf/Rfc4236_ULE/UleException.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace skyscraper8.Ietf.Rfc4236_ULE +{ + public class UleException : IetfException + { + public UleException() + { + } + + public UleException(string message) : base(message) + { + } + + public UleException(string message, Exception inner) : base(message, inner) + { + } + } +} diff --git a/skyscraper8/Ietf/Rfc4236_ULE/UlePacketProcessor.cs b/skyscraper8/Ietf/Rfc4236_ULE/UlePacketProcessor.cs new file mode 100644 index 0000000..6e627e6 --- /dev/null +++ b/skyscraper8/Ietf/Rfc4236_ULE/UlePacketProcessor.cs @@ -0,0 +1,176 @@ +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.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; + } + + + } +}