From 51353a8014064c01b1e6577ed25e2b1e48a4d4e1 Mon Sep 17 00:00:00 2001 From: feyris-tan <4116042+feyris-tan@users.noreply.github.com> Date: Tue, 2 Jun 2026 23:00:31 +0200 Subject: [PATCH] Implemented ATSC A/330 Link Layer Signalling detection. --- .../Atsc/A322/AtscPlpBasebandParser.cs | 168 ++++++------------ ...AdditionalHeaderForSignalingInformation.cs | 50 ++++++ skyscraper8/Atsc/A330/AlpPacket.cs | 42 ++--- 3 files changed, 119 insertions(+), 141 deletions(-) create mode 100644 skyscraper8/Atsc/A330/AdditionalHeaderForSignalingInformation.cs diff --git a/skyscraper8/Atsc/A322/AtscPlpBasebandParser.cs b/skyscraper8/Atsc/A322/AtscPlpBasebandParser.cs index 263a4a2..0cd174c 100644 --- a/skyscraper8/Atsc/A322/AtscPlpBasebandParser.cs +++ b/skyscraper8/Atsc/A322/AtscPlpBasebandParser.cs @@ -1,6 +1,7 @@ using skyscraper5.Ietf.Rfc971; using skyscraper5.Skyscraper.IO; using skyscraper8.Atsc.A330; +using skyscraper8.GSE.GSE_HEM; using System; using System.Collections.Generic; using System.Linq; @@ -13,6 +14,12 @@ namespace skyscraper8.Atsc.A322 { internal class AtscPlpBasebandParser { + private RayBuffer rayBuffer; + private const int MINIMUM_REQUIRED_RAY_LENGTH = 65535 * 2; + private const int MINIMUM_REQUIRED_RAY_PACKETS = 2; + private uint delivered_bbframes_total; + private uint delivered_alp_total; + internal void PushBbframe(Span payload) { byte a = payload[0]; @@ -66,6 +73,11 @@ namespace skyscraper8.Atsc.A322 } Span bbHeaderStripped = payload.Slice(addToPointer); + if (rayBuffer == null) + rayBuffer = new RayBuffer(); + rayBuffer.Enqueue(new MemoryStream(bbHeaderStripped.ToArray(), false), pointer); + delivered_bbframes_total++; + if (pointer + 4 > bbHeaderStripped.Length) { Console.WriteLine("BBFRame, offset: {0}, less than four bytes left.", pointer); @@ -74,117 +86,51 @@ namespace skyscraper8.Atsc.A322 { Console.WriteLine("BBFRame, offset: {0}, first bytes: 0x{1:X2}{2:X2}{3:X2}{4:X2}", pointer, bbHeaderStripped[pointer], bbHeaderStripped[pointer + 1], bbHeaderStripped[pointer + 2], bbHeaderStripped[pointer + 3]); } - /*while (ms.GetAvailableBytes() > 0) + + while (rayBuffer.AvailableBytes >= MINIMUM_REQUIRED_RAY_LENGTH && rayBuffer.QueuedItems >= MINIMUM_REQUIRED_RAY_PACKETS) { - switch(stateMachineState) + AlpPacket newPacket = new AlpPacket(); + ushort syncWord = rayBuffer.ReadUInt16BE(); + newPacket.PacketType = (AlpPacketType)((syncWord & 0xe000) >> 13); + newPacket.PayloadConfiguration = (syncWord & 0x1000) != 0; + if (!newPacket.PayloadConfiguration) { - case 0: //Initial state, nothing parsed yet. - if (pointer == 8191) - { - return; //no new packet to be found in this BBFRAME. - } - ms.Position = pointer + addToPointer; - neededBuffer = null; - neededBufferOffset = 0; - currentAlpPacket = null; - stateMachineState = 1; - break; - case 1: //Prepare get Base Header for ALP packet encapsulation - neededBuffer = new byte[2]; - stateMachineState = 2; - neededBufferOffset = 0; - break; - case 2: //Get Base Header for ALP packet encapsulation - int readSucessful = ms.Read(neededBuffer, neededBufferOffset, neededBuffer.Length - neededBufferOffset); - neededBufferOffset += readSucessful; - if (neededBufferOffset == neededBuffer.Length) - { - stateMachineState = 3; - } - break; - case 3: //Parse current ALP packet - currentAlpPacket = new AlpPacket(neededBuffer); - if (!currentAlpPacket.PayloadConfiguration) - { - if (currentAlpPacket.HeaderMode.Value) - { - throw new NotImplementedException("Single Packet Header not yet implemented."); - } - } - else - { - if (!currentAlpPacket.SegmentationConcatenation.Value) - { - throw new NotImplementedException("Segmentation Header not yet implemented."); - } - else - { - stateMachineState = 7; - break; - } - } - stateMachineState = 4; - break; - case 4: //Prepate get packet payload - neededBufferOffset = 0; - neededBuffer = new byte[currentAlpPacket.Length]; - stateMachineState = 5; - break; - case 5: //Get packet payload - int readSuccesful2 = ms.Read(neededBuffer, neededBufferOffset, neededBuffer.Length - neededBufferOffset); - neededBufferOffset += readSuccesful2; - if (neededBufferOffset == neededBuffer.Length) - { - stateMachineState = 6; - } - break; - case 6: //Deliver packet - DeliverPacket(currentAlpPacket, neededBuffer); - stateMachineState = 1; - break; - case 7: //Read Concatenation Header first byte. - byte[] concatHeaderFirstByteBuffer = new byte[1]; - int readResult = ms.Read(concatHeaderFirstByteBuffer, 0, 1); - if (readResult == 0) - break; - - currentAlpPacket.ConcatLengthMsb = (concatHeaderFirstByteBuffer[0] & 0xf0) >> 4; - currentAlpPacket.Count = (concatHeaderFirstByteBuffer[0] & 0x0e) >> 1; - currentAlpPacket.SIF = (concatHeaderFirstByteBuffer[0] & 0x01) != 0; - - int neededLength = 12 * currentAlpPacket.Count; - if ((currentAlpPacket.Count & 1) == 0) - neededLength += 4; - neededLength /= 8; - - neededBufferOffset = 0; - stateMachineState = 8; - - break; - case 8: //Get Concatenation Header Lengths - int readSuccesful3 = ms.Read(neededBuffer, neededBufferOffset, neededBuffer.Length - neededBufferOffset); - neededBufferOffset += readSuccesful3; - if (neededBufferOffset == neededBuffer.Length) - { - stateMachineState = 9; - } - break; - case 9: //Parse Concatenation Header Lengths - currentAlpPacket.ParseConcatenationHeaderLengths(neededBuffer); - if (currentAlpPacket.PacketType == AlpPacketType.LinkLayerSignalling) - { - throw new NotImplementedException("Concatenated Link Layer Signalling Packets in ATSC 3.0 not yet supported."); - } - - currentAlpPacket.Length2 = (currentAlpPacket.ConcatLengthMsb << 11) | currentAlpPacket.Length; - neededBufferOffset = 0; - neededBuffer = new byte[currentAlpPacket.Length2]; - stateMachineState = 5; - break; - default: - throw new NotImplementedException(String.Format("ATSC PLP Baseband Parser State machine state #{0}", stateMachineState)); + newPacket.HeaderMode = (syncWord & 0x0800) != 0; + newPacket.Length = (syncWord & 0x07ff); + if (newPacket.HeaderMode) + { + throw new NotImplementedException("single packet header"); + } } - }*/ + else + { + throw new NotImplementedException(("yes payload configuration")); + } + + if (newPacket.PacketType == AlpPacketType.Ipv4 && newPacket.Length < 20) + { + rayBuffer.Resync(); + continue; + } + + if (newPacket.PacketType == AlpPacketType.LinkLayerSignalling) + { + newPacket.AdditionalHeaderForSignalingInformation = new AdditionalHeaderForSignalingInformation(rayBuffer.ReadBytes(5)); + } + + newPacket.Payload = rayBuffer.ReadBytes(newPacket.Length); + DeliverPacket(newPacket); + } + } + + private List packets; + private void DeliverPacket(AlpPacket newPacket) + { + Console.WriteLine(newPacket.ToString()); + delivered_alp_total++; + if (packets == null) + packets = new List(); + packets.Add(newPacket); } private void HandleExtension(bool isLongExtension, int extType, Span extensionField) @@ -196,11 +142,7 @@ namespace skyscraper8.Atsc.A322 } throw new NotImplementedException(); } - - private void DeliverPacket(AlpPacket? currentAlpPacket, byte[]? neededBuffer) - { - Console.WriteLine("PacketType = {0} \t Length = {1} (first bytes = {2:X2},{3:X2},{4:X2})", currentAlpPacket.PacketType, neededBuffer.Length, neededBuffer[0], neededBuffer[1], neededBuffer[2]); - } + internal void OnSyncLoss() { diff --git a/skyscraper8/Atsc/A330/AdditionalHeaderForSignalingInformation.cs b/skyscraper8/Atsc/A330/AdditionalHeaderForSignalingInformation.cs new file mode 100644 index 0000000..ad79e5e --- /dev/null +++ b/skyscraper8/Atsc/A330/AdditionalHeaderForSignalingInformation.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using skyscraper5.Skyscraper; +using skyscraper5.Skyscraper.IO; + +namespace skyscraper8.Atsc.A330 +{ + internal class AdditionalHeaderForSignalingInformation : Validatable + { + public AdditionalHeaderForSignalingInformation(byte[] readBytes) + { + MemoryStream ms = new MemoryStream(readBytes, false); + SignalingType = ms.ReadUInt8(); + SignalingTypeExtension = ms.ReadUInt16BE(); + SignalingVersion = ms.ReadUInt8(); + + byte a = ms.ReadUInt8(); + SignalingFormat = (CodeValuesForSignalingFormat)((a & 0xb0) >> 6); + SignalingEncoding = (CodeValuesForSignalingEncoding)((a & 0x30) >> 4); + + Valid = true; + } + + public CodeValuesForSignalingEncoding SignalingEncoding { get; set; } + + public CodeValuesForSignalingFormat SignalingFormat { get; set; } + + public byte SignalingVersion { get; set; } + + public ushort SignalingTypeExtension { get; set; } + + public byte SignalingType { get; set; } + } + + public enum CodeValuesForSignalingFormat : int + { + Binary = 0, + Xml = 1, + Json = 2, + } + + public enum CodeValuesForSignalingEncoding : int + { + Uncompressed = 0, + Deflate = 1 + } +} diff --git a/skyscraper8/Atsc/A330/AlpPacket.cs b/skyscraper8/Atsc/A330/AlpPacket.cs index 89a1abf..4527e06 100644 --- a/skyscraper8/Atsc/A330/AlpPacket.cs +++ b/skyscraper8/Atsc/A330/AlpPacket.cs @@ -9,37 +9,23 @@ namespace skyscraper8.Atsc.A330 { internal class AlpPacket { - public AlpPacket(byte[] buffer) - { - MemoryStream ms = new MemoryStream(buffer, false); - ushort first16bits = ms.ReadUInt16BE(); - PacketType = (AlpPacketType)((first16bits & 0xe000) >> 13); - PayloadConfiguration = (first16bits & 0x1000) != 0; - if (!PayloadConfiguration) - { - HeaderMode = (first16bits & 0x0800) != 0; - Length = (first16bits & 0x07ff); - } - else - { - SegmentationConcatenation = (first16bits & 0x0800) != 0; - Length = (first16bits & 0x07ff); - } - } - public AlpPacketType PacketType { get; private set; } - public bool PayloadConfiguration { get; private set; } - public bool? HeaderMode { get; } - public bool? SegmentationConcatenation { get; } - public int Length { get; } - public int ConcatLengthMsb { get; internal set; } - public int Count { get; internal set; } - public bool SIF { get; internal set; } - public int Length2 { get; internal set; } + public AlpPacketType PacketType { get; internal set; } + public bool PayloadConfiguration { get; set; } + public bool HeaderMode { get; set; } + public int Length { get; set; } + public byte[] Payload { get; set; } + public AdditionalHeaderForSignalingInformation AdditionalHeaderForSignalingInformation { get; set; } - internal void ParseConcatenationHeaderLengths(byte[]? neededBuffer) + public override string ToString() { - throw new NotImplementedException(); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < Math.Min(4, Length); i++) + { + sb.AppendFormat("{0:X2}", Payload[i]); + } + + return String.Format("ALP Packet Type = {0}, PayloadConf = {1}, HeaderMode = {2}, Length = {3}, First bytes = 0x{4}", PacketType, PayloadConfiguration, HeaderMode, Length, sb.ToString()); } }