Implemented ATSC A/330 Link Layer Signalling detection.
All checks were successful
🚀 Pack skyscraper8 / make-zip (push) Successful in 45s

This commit is contained in:
feyris-tan 2026-06-02 23:00:31 +02:00
parent 1843d914ed
commit 51353a8014
3 changed files with 119 additions and 141 deletions

View File

@ -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<byte> payload)
{
byte a = payload[0];
@ -66,6 +73,11 @@ namespace skyscraper8.Atsc.A322
}
Span<byte> 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<AlpPacket> packets;
private void DeliverPacket(AlpPacket newPacket)
{
Console.WriteLine(newPacket.ToString());
delivered_alp_total++;
if (packets == null)
packets = new List<AlpPacket>();
packets.Add(newPacket);
}
private void HandleExtension(bool isLongExtension, int extType, Span<byte> 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()
{

View File

@ -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
}
}

View File

@ -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());
}
}