From 3c6b5cf98ef305871f2c94d4e7f7f9bc416f0fc8 Mon Sep 17 00:00:00 2001 From: feyris-tan <4116042+feyris-tan@users.noreply.github.com> Date: Wed, 3 Jun 2026 22:59:24 +0200 Subject: [PATCH] Can now parse ATSC3 PLPs. --- .../Atsc/A322/AtscPlpBasebandParser.cs | 59 +++++++++++++-- skyscraper8/Atsc/A322/IAtscPlpEventHandler.cs | 16 +++++ .../A324/BasebandPacketDataStreamReceiver.cs | 2 +- ...AdditionalHeaderForSignalingInformation.cs | 2 +- skyscraper8/Atsc/A330/LinkMappingTable.cs | 72 +++++++++++++++++++ .../Skyscraper/Scraper/SkyscraperContext.cs | 10 ++- 6 files changed, 151 insertions(+), 10 deletions(-) create mode 100644 skyscraper8/Atsc/A322/IAtscPlpEventHandler.cs create mode 100644 skyscraper8/Atsc/A330/LinkMappingTable.cs diff --git a/skyscraper8/Atsc/A322/AtscPlpBasebandParser.cs b/skyscraper8/Atsc/A322/AtscPlpBasebandParser.cs index 243f15a..527a2da 100644 --- a/skyscraper8/Atsc/A322/AtscPlpBasebandParser.cs +++ b/skyscraper8/Atsc/A322/AtscPlpBasebandParser.cs @@ -1,4 +1,5 @@ -using skyscraper5.Ietf.Rfc971; +using log4net; +using skyscraper5.Ietf.Rfc971; using skyscraper5.Skyscraper.IO; using skyscraper8.Atsc.A330; using skyscraper8.GSE.GSE_HEM; @@ -14,6 +15,16 @@ namespace skyscraper8.Atsc.A322 { internal class AtscPlpBasebandParser { + private readonly ushort _plpId; + private readonly IAtscPlpEventHandler _eventHandler; + + public AtscPlpBasebandParser(ushort plpId, IAtscPlpEventHandler eventHandler) + { + _plpId = plpId; + _eventHandler = eventHandler; + } + + private static readonly ILog logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name); private RayBuffer rayBuffer; private const int MINIMUM_REQUIRED_RAY_LENGTH = 65535 * 2; private const int MINIMUM_REQUIRED_RAY_PACKETS = 2; @@ -80,14 +91,14 @@ namespace skyscraper8.Atsc.A322 rayBuffer.Enqueue(new MemoryStream(bbHeaderStripped.ToArray(), false), pointer); delivered_bbframes_total++; - if (pointer + 4 > bbHeaderStripped.Length) + /*if (pointer + 4 > bbHeaderStripped.Length) { Console.WriteLine("BBFRame, offset: {0}, less than four bytes left.", pointer); } else { 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 (rayBuffer.AvailableBytes >= MINIMUM_REQUIRED_RAY_LENGTH && rayBuffer.QueuedItems >= MINIMUM_REQUIRED_RAY_PACKETS) { @@ -128,11 +139,45 @@ namespace skyscraper8.Atsc.A322 private List packets; private void DeliverPacket(AlpPacket newPacket) { - Console.WriteLine(newPacket.ToString()); + //Console.WriteLine(newPacket.ToString()); delivered_alp_total++; - if (packets == null) - packets = new List(); - packets.Add(newPacket); + + switch (newPacket.PacketType) + { + case AlpPacketType.Ipv4: + HandleIpv4(newPacket.Payload); + break; + case AlpPacketType.LinkLayerSignalling: + HandleLinkLayerSignalling(newPacket.AdditionalHeaderForSignalingInformation, newPacket.Payload); + break; + default: + throw new NotImplementedException(String.Format("ALP Packet Type {0}", newPacket.PacketType)); + } + } + + private void HandleIpv4(byte[] newPacketPayload) + { + _eventHandler?.OnIpDatagram(0x1ffd, newPacketPayload); + } + + private void HandleLinkLayerSignalling(AdditionalHeaderForSignalingInformation signalingInformation, byte[] payload) + { + if (signalingInformation.SignalingType == 1 && signalingInformation.SignalingTypeExtension == 0xffff && + signalingInformation.SignalingFormat == 0 && signalingInformation.SignalingEncoding == 0) + { + LinkMappingTable lmt = new LinkMappingTable(payload); + if (lmt.ShouldRaiseEvent()) + { + _eventHandler?.OnAtsc3LinkMappingTable(_plpId, signalingInformation, lmt); + } + } + else + { + logger.WarnFormat( + "Encountered unknown Link-Layer Signaling: Type = 0x{0:X2}, Type Extension = 0x{1:X4}, Format = 0x{2:X2}, Encoding = 0x{3:X2}", + signalingInformation.SignalingType, signalingInformation.SignalingTypeExtension, + signalingInformation.SignalingFormat, signalingInformation.SignalingEncoding); + } } private void HandleExtension(bool isLongExtension, int extType, Span extensionField) diff --git a/skyscraper8/Atsc/A322/IAtscPlpEventHandler.cs b/skyscraper8/Atsc/A322/IAtscPlpEventHandler.cs new file mode 100644 index 0000000..764955c --- /dev/null +++ b/skyscraper8/Atsc/A322/IAtscPlpEventHandler.cs @@ -0,0 +1,16 @@ +using skyscraper5.Ietf.Rfc971; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using skyscraper8.Atsc.A330; + +namespace skyscraper8.Atsc.A322 +{ + internal interface IAtscPlpEventHandler + { + void OnIpDatagram(int pid, byte[] payload); + void OnAtsc3LinkMappingTable(ushort plp, AdditionalHeaderForSignalingInformation signalingInformation, LinkMappingTable lmt); + } +} diff --git a/skyscraper8/Atsc/A324/BasebandPacketDataStreamReceiver.cs b/skyscraper8/Atsc/A324/BasebandPacketDataStreamReceiver.cs index 53cdb25..1ec44e8 100644 --- a/skyscraper8/Atsc/A324/BasebandPacketDataStreamReceiver.cs +++ b/skyscraper8/Atsc/A324/BasebandPacketDataStreamReceiver.cs @@ -158,7 +158,7 @@ namespace skyscraper8.Atsc.A324 } if (plps[plp] == null) { - plps[plp] = new AtscPlpBasebandParser(); + plps[plp] = new AtscPlpBasebandParser(plp, context); } plps[plp].PushBbframe(rtpPayload); } diff --git a/skyscraper8/Atsc/A330/AdditionalHeaderForSignalingInformation.cs b/skyscraper8/Atsc/A330/AdditionalHeaderForSignalingInformation.cs index ad79e5e..5fa9d13 100644 --- a/skyscraper8/Atsc/A330/AdditionalHeaderForSignalingInformation.cs +++ b/skyscraper8/Atsc/A330/AdditionalHeaderForSignalingInformation.cs @@ -8,7 +8,7 @@ using skyscraper5.Skyscraper.IO; namespace skyscraper8.Atsc.A330 { - internal class AdditionalHeaderForSignalingInformation : Validatable + public class AdditionalHeaderForSignalingInformation : Validatable { public AdditionalHeaderForSignalingInformation(byte[] readBytes) { diff --git a/skyscraper8/Atsc/A330/LinkMappingTable.cs b/skyscraper8/Atsc/A330/LinkMappingTable.cs new file mode 100644 index 0000000..41d606c --- /dev/null +++ b/skyscraper8/Atsc/A330/LinkMappingTable.cs @@ -0,0 +1,72 @@ +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 +{ + public class LinkMappingTable : Validatable + { + public LinkMappingTable(byte[] buffer) + { + MemoryStream ms = new MemoryStream(buffer, false); + byte firstByte = ms.ReadUInt8(); + + int numPlps = (firstByte & 0xfc) >> 2; + numPlps++; + + int reserved = (firstByte & 0x03); + + LinkMappings = new LinkMappingTableEntry[numPlps]; + + for (int i = 0; i < numPlps; i++) + { + LinkMappings[i] = new LinkMappingTableEntry(); + + byte secondByte = ms.ReadUInt8(); + LinkMappings[i].PlpId = (secondByte & 0xfc) >> 2; + reserved = (secondByte & 0x03); + + byte numMulticasts = ms.ReadUInt8(); + LinkMappings[i].Multicasts = new LinkMappingTableEntryMulticast[numMulticasts]; + + for (int j = 0; j < numMulticasts; j++) + { + throw new NotImplementedException("Link Mapping Table Multicast not supported yet."); + } + } + + Valid = true; + } + + public LinkMappingTableEntry[] LinkMappings; + + public bool ShouldRaiseEvent() + { + foreach (LinkMappingTableEntry entry in LinkMappings) + { + foreach (LinkMappingTableEntryMulticast multicast in entry.Multicasts) + { + return true; + } + } + + return false; + } + } + + public class LinkMappingTableEntry + { + public int PlpId { get; set; } + + public LinkMappingTableEntryMulticast[] Multicasts { get; set; } + } + + public class LinkMappingTableEntryMulticast + { + + } +} diff --git a/skyscraper8/Skyscraper/Scraper/SkyscraperContext.cs b/skyscraper8/Skyscraper/Scraper/SkyscraperContext.cs index dc87961..c775247 100644 --- a/skyscraper8/Skyscraper/Scraper/SkyscraperContext.cs +++ b/skyscraper8/Skyscraper/Scraper/SkyscraperContext.cs @@ -88,6 +88,8 @@ using System.Net.NetworkInformation; using System.Reflection; using System.Text; using System.Threading; +using skyscraper8.Atsc.A322; +using skyscraper8.Atsc.A330; using Tsubasa.IO; using Platform = skyscraper5.Dvb.SystemSoftwareUpdate.Model.Platform; using RntParser = skyscraper5.Dvb.TvAnytime.RntParser; @@ -100,7 +102,7 @@ namespace skyscraper5.Skyscraper.Scraper UpdateNotificationEventHandler, DataCarouselEventHandler, RdsEventHandler, IScte35EventHandler, IAutodetectionEventHandler, IRstEventHandler, IRntEventHandler, IMultiprotocolEncapsulationEventHandler, ObjectCarouselEventHandler, T2MIEventHandler, IDisposable, IFrameGrabberEventHandler, IntEventHandler, IRctEventHandler, ISkyscraperContext, IDocsisEventHandler, AbertisDecoderEventHandler, Id3Handler, - InteractionChannelHandler, SgtEventHandler, IDvbNipEventHandler, UleEventHandler, OtvSsuHandler, NdsSsuHandler, ISubTsHandler, ILldpFrameHandler, SisHandler, IWneHandler + InteractionChannelHandler, SgtEventHandler, IDvbNipEventHandler, UleEventHandler, OtvSsuHandler, NdsSsuHandler, ISubTsHandler, ILldpFrameHandler, SisHandler, IWneHandler, IAtscPlpEventHandler { public const bool ALLOW_STREAM_TYPE_AUTODETECTION = true; public const bool ALLOW_FFMPEG_FRAMEGRABBER = true; @@ -1811,6 +1813,12 @@ namespace skyscraper5.Skyscraper.Scraper } } + public void OnAtsc3LinkMappingTable(ushort plp, AdditionalHeaderForSignalingInformation signalingInformation, + LinkMappingTable lmt) + { + throw new NotImplementedException(); + } + public void GsIpTrafficDetected() { LogEvent(SkyscraperContextEvent.SpecialTsMode, "Valid IP Traffic detected");