From a46e4ff5568b025e98bdb73a538a452630cc555e Mon Sep 17 00:00:00 2001 From: feyris-tan <4116042+feyris-tan@users.noreply.github.com> Date: Tue, 14 Oct 2025 07:08:27 +0200 Subject: [PATCH] Detect broken GS encapsulation. --- .gitignore | 1 + skyscraper8/GSE/BbframeDeencapsulator.cs | 60 ++++++++++++++++++++-- skyscraper8/GSE/GsePacket.cs | 13 ++++- skyscraper8/Mpeg2/TsContext.cs | 50 ++++++++++++++++-- skyscraper8/Properties/launchSettings.json | 2 +- 5 files changed, 117 insertions(+), 9 deletions(-) diff --git a/.gitignore b/.gitignore index 7196d81..833cb57 100644 --- a/.gitignore +++ b/.gitignore @@ -129,3 +129,4 @@ imgui.ini /skyscraper8.Manual/skyscraper8.Manual.out /skyscraper8.Manual/skyscraper8.Manual.pdf /skyscraper8.Manual/skyscraper8.Manual.synctex.gz +/.vs/skyscraper8/CopilotIndices/17.14.1231.31060 diff --git a/skyscraper8/GSE/BbframeDeencapsulator.cs b/skyscraper8/GSE/BbframeDeencapsulator.cs index e933f9e..ab2f788 100644 --- a/skyscraper8/GSE/BbframeDeencapsulator.cs +++ b/skyscraper8/GSE/BbframeDeencapsulator.cs @@ -90,6 +90,11 @@ namespace skyscraper8.GSE break; } + if (!packet.Valid) + { + break; + } + if (packet.IsCompletePacket) { if (ValidateEthertype(packet.ProtocolType.Value)) @@ -113,6 +118,25 @@ namespace skyscraper8.GSE continue; } + if (!packet.StartIndicator && packet.EndIndicator) + { + if (gseFragmentations == null) + continue; + if (gseFragmentations[packet.FragmentId.Value] == null) + continue; + + gseFragmentations[packet.FragmentId.Value].AddPacket(packet); + continue; + } + + if (!packet.StartIndicator && !packet.EndIndicator) + { + if (gseFragmentations == null) + continue; + + throw new NotImplementedException(); + } + throw new NotImplementedException(); } } @@ -129,8 +153,8 @@ namespace skyscraper8.GSE long availableBytes = ms.GetAvailableBytes(); byte[] readBytes = ms.ReadBytes(availableBytes); - - GseAssemblyState assemblyState = ValidateGse(gseAssembler); + + GseAssemblyState assemblyState = ValidateGse(gseAssembler, true); if (assemblyState == GseAssemblyState.NOT_YET_BEGUN) { if ((syncByte & 0xc0) == 0xc0) @@ -195,7 +219,7 @@ namespace skyscraper8.GSE VALID_NULL_PACKET, //somehow these only show up on TBS6903x, not DD Max SX8? } - private GseAssemblyState ValidateGse(MemoryStream ms) + private GseAssemblyState ValidateGse(MemoryStream ms, bool tryFix = false) { if (ms == null) return GseAssemblyState.NOT_YET_BEGUN; @@ -204,6 +228,8 @@ namespace skyscraper8.GSE ms.Position = 0; while (ms.GetAvailableBytes() > 2) { + long fixPosition = ms.Position; + //GSE-Header byte byteA = ms.ReadUInt8(); bool startIndicator = (byteA & 0x80) != 0; @@ -224,6 +250,12 @@ namespace skyscraper8.GSE //Console.WriteLine("GSE Length: {0}", gseLength); if (gseLength > 9000) { + if (tryFix) + { + ms.Position = fixPosition; + ms.WriteUInt8Repeat(0, (int)ms.GetAvailableBytes()); + break; + } ms.Position = backupPosition; return GseAssemblyState.BROKEN_SUSPECT_LENGTH; } @@ -236,6 +268,11 @@ namespace skyscraper8.GSE if (startIndicator && !endIndicator) { + if (2 > ms.GetAvailableBytes()) + { + ms.Position = backupPosition; + return GseAssemblyState.NEED_MORE_DATA; + } ushort totalLength = ms.ReadUInt16BE(); gseLength -= 2; } @@ -250,6 +287,12 @@ namespace skyscraper8.GSE ushort protocolType = ms.ReadUInt16BE(); if (!ValidateEthertype(protocolType)) { + if (tryFix) + { + ms.Position = fixPosition; + ms.WriteUInt8Repeat(0, (int)ms.GetAvailableBytes()); + break; + } ms.Position = backupPosition; return GseAssemblyState.BROKEN_SUSPECT_ETHERTYPE; } @@ -278,6 +321,12 @@ namespace skyscraper8.GSE if (gseLength < 0) { + if (tryFix) + { + ms.Position = fixPosition; + ms.WriteUInt8Repeat(0, (int)ms.GetAvailableBytes()); + break; + } ms.Position = backupPosition; return GseAssemblyState.BROKEN_NEGATIVE_LENGTH; } @@ -291,6 +340,11 @@ namespace skyscraper8.GSE ms.Position += gseLength; if (!startIndicator && endIndicator) { + if (4 > ms.GetAvailableBytes()) + { + ms.Position = backupPosition; + return GseAssemblyState.NEED_MORE_DATA; + } uint crc32 = ms.ReadUInt32BE(); int endCrc32 = (int)ms.Position; DvbCrc32.ValidateCrc(ms, startCrc32, endCrc32); diff --git a/skyscraper8/GSE/GsePacket.cs b/skyscraper8/GSE/GsePacket.cs index da4f130..d3ec9d3 100644 --- a/skyscraper8/GSE/GsePacket.cs +++ b/skyscraper8/GSE/GsePacket.cs @@ -9,7 +9,7 @@ using skyscraper5.Skyscraper; namespace skyscraper8.GSE { - internal class GsePacket + internal class GsePacket : Validatable { public static GsePacket Read(MemoryStream ms) { @@ -62,12 +62,23 @@ namespace skyscraper8.GSE if (!packet.StartIndicator && packet.EndIndicator) gseLength -= 4; + if (gseLength < 0) + { + packet.Valid = false; + return packet; + } + if (gseLength > ms.GetAvailableBytes()) + { + packet.Valid = false; + return packet; + } packet.GseData = ms.ReadBytes(gseLength); if (!packet.StartIndicator && packet.EndIndicator) packet.Crc32 = ms.ReadUInt32BE(); } + packet.Valid = true; return packet; } diff --git a/skyscraper8/Mpeg2/TsContext.cs b/skyscraper8/Mpeg2/TsContext.cs index 7bbaa52..f94245e 100644 --- a/skyscraper8/Mpeg2/TsContext.cs +++ b/skyscraper8/Mpeg2/TsContext.cs @@ -1,15 +1,17 @@ -using System; +using log4net; +using skyscraper5.Skyscraper; +using skyscraper5.src.Mpeg2.PacketFilter; +using System; using System.Collections.Generic; using System.Net; using System.Runtime.CompilerServices; -using skyscraper5.Skyscraper; -using skyscraper5.src.Mpeg2.PacketFilter; namespace skyscraper5.Mpeg2 { public class TsContext : ITsPacketProcessor { - private ITsPacketProcessor[] processors; + private static readonly ILog logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name); + private ITsPacketProcessor[] processors; private uint[] continuities; private ulong[] pidPackets; @@ -42,6 +44,8 @@ namespace skyscraper5.Mpeg2 if (FilterChain == null || FilterChain.Count == 0) throw new InvalidOperationException("The filter chain has not been initialized."); + CheckBrokenEncapsulation(packet); + for (int i = 0; i < FilterChain.Count; i++) { if (FilterChain[i] == null) @@ -61,6 +65,44 @@ namespace skyscraper5.Mpeg2 processors[packet.PID].PushPacket(packet); } + private bool encapsulationBrokenConfirmed; + private bool CheckBrokenEncapsulation(TsPacket packet) + { + if (encapsulationBrokenConfirmed) + return true; + + switch (packet.PID) + { + case 0x0000: + case 0x0001: + break; + default: + return false; + } + + int occupiedPids = CountOccupiedPids(); + if (occupiedPids < 1000) + return false; + + if (packet.TSC == 0) + return false; + + logger.WarnFormat("It looks like you're either having bad reception or this is a GS with broken encapsulation. This means that you either have FrameMode enabled, or are not using a STiD135."); + encapsulationBrokenConfirmed = true; + return true; + } + + private int CountOccupiedPids() + { + int result = 0; + for (int i = 0; i < pidPackets.Length; i++) + { + if (pidPackets[i] > 0) + result++; + } + return result; + } + private bool EnsureContinuity(TsPacket packet) { //Found in ISO 13818-1.pdf, page 38 diff --git a/skyscraper8/Properties/launchSettings.json b/skyscraper8/Properties/launchSettings.json index d7ff207..4075ac8 100644 --- a/skyscraper8/Properties/launchSettings.json +++ b/skyscraper8/Properties/launchSettings.json @@ -2,7 +2,7 @@ "profiles": { "skyscraper8": { "commandName": "Project", - "commandLineArgs": "\"\\\\utena\\mergerfs\\Skyscraper\\ipProto253_eutelsat7_10803h-max-sx8.ts\"", + "commandLineArgs": "cscan tcp://172.20.20.203:6969", "remoteDebugEnabled": false }, "Container (Dockerfile)": {