From 8f2c31f10dff324efa6dc18d7a4dfa2467d968bb Mon Sep 17 00:00:00 2001
From: feyris-tan <4116042+feyris-tan@users.noreply.github.com>
Date: Sat, 8 Nov 2025 21:16:43 +0100
Subject: [PATCH] Preparations for BFBS Navy TV.
---
Documentation/sophia-net-signalling.md | 5 +-
skyscraper8.sln.DotSettings.user | 4 +-
skyscraper8/GS/BBframeDeencapsulator3.cs | 17 +-
skyscraper8/GS/GSE-BFBS/BfbsGseReader.cs | 166 ++++++++++++++----
skyscraper8/GS/GSE-BFBS/GseL2SHandler.cs | 31 ++++
skyscraper8/GS/GSE-HEM/GseHemReader.cs | 16 +-
skyscraper8/GS/GSE/GseFragmentation.cs | 21 ++-
skyscraper8/GS/GSE/GseLabel.cs | 7 +
skyscraper8/GS/GSE/GseReader.cs | 9 +-
skyscraper8/GS/GsContextDto.cs | 53 ++++++
skyscraper8/GS/GsException.cs | 23 +++
skyscraper8/GS/GsTypeDetector.cs | 24 +--
skyscraper8/GS/IMisHandler.cs | 6 +-
skyscraper8/GS/POC/Pts2Bbf.cs | 2 +-
skyscraper8/GS/POC/Pts2Bbf2.cs | 2 +-
skyscraper8/GS/POC/Stid135Test.cs | 4 +-
.../SiminnRadiomidunReader.cs | 12 +-
skyscraper8/GS/Stid135BbFrameReader.cs | 5 +-
.../InteractionChannel/GseTableStructure.cs | 38 ++++
.../InteractionChannelPsiGatherer.cs | 7 +-
skyscraper8/Mpeg2/PsiSection.cs | 7 +
skyscraper8/Properties/launchSettings.json | 2 +-
skyscraper8/Skyscraper/MpeEject.cs | 8 +
.../Skyscraper/Scraper/SkyscraperContext.cs | 29 ++-
24 files changed, 410 insertions(+), 88 deletions(-)
create mode 100644 skyscraper8/GS/GSE-BFBS/GseL2SHandler.cs
create mode 100644 skyscraper8/GS/GsContextDto.cs
create mode 100644 skyscraper8/GS/GsException.cs
create mode 100644 skyscraper8/InteractionChannel/GseTableStructure.cs
diff --git a/Documentation/sophia-net-signalling.md b/Documentation/sophia-net-signalling.md
index 3b3f2a0..775c3d0 100644
--- a/Documentation/sophia-net-signalling.md
+++ b/Documentation/sophia-net-signalling.md
@@ -7,12 +7,13 @@ the following Signalling applies:
|Stream Type|Interpretation |
|-----------|-----------------------------------|
-|0x80 |TBS 6903-X GSE Packets |
+|0x80 |STiD135 GS/GSE Packets |
|0x81 |AC-3 Audio |
|0x82 |PID only used for PCR |
|0x83 |AC-3 True HD Audio |
|0x84 |AC-3+ Audio |
-|0x85 |ULE (RFC 4326) |
+|0x85 |DTS Audio |
+|0x91 |ULE (RFC 4326) |
## Descriptors:
diff --git a/skyscraper8.sln.DotSettings.user b/skyscraper8.sln.DotSettings.user
index ef9d365..ad53eeb 100644
--- a/skyscraper8.sln.DotSettings.user
+++ b/skyscraper8.sln.DotSettings.user
@@ -13,8 +13,8 @@
ForceIncluded
ForceIncluded
/home/schiemas/.cache/JetBrains/Rider2025.1/resharper-host/temp/Rider/vAny/CoverageData/_skyscraper8.1808907683/Snapshot/snapshot.utdcvr
- <SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from <skyscraper8.Tests>" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session">
- <Project Location="/home/schiemas/RiderProjects/skyscraper8/skyscraper8.Tests" Presentation="<skyscraper8.Tests>" />
+ <SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from <skyscraper8.Tests>" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session">
+ <Project Location="\home\schiemas\RiderProjects\skyscraper8\skyscraper8.Tests" Presentation="<skyscraper8.Tests>" />
</SessionState>
diff --git a/skyscraper8/GS/BBframeDeencapsulator3.cs b/skyscraper8/GS/BBframeDeencapsulator3.cs
index 4031a3b..094202e 100644
--- a/skyscraper8/GS/BBframeDeencapsulator3.cs
+++ b/skyscraper8/GS/BBframeDeencapsulator3.cs
@@ -1,19 +1,18 @@
using log4net;
using skyscraper5.Dvb.DataBroadcasting;
+using skyscraper8.GS;
using skyscraper8.Skyscraper.Scraper;
namespace skyscraper8.GSE;
-public class BbframeDeencapsulator3 : IBbframeDeencapsulator
+class BbframeDeencapsulator3 : IBbframeDeencapsulator
{
- public BbframeDeencapsulator3(IMultiprotocolEncapsulationEventHandler mpeEventHandler, ISubTsHandler subTsHandler)
+ public BbframeDeencapsulator3(GsContextDto context)
{
- _mpeEventHandler = mpeEventHandler;
- _subTsHandler = subTsHandler;
+ this.context = context;
}
- private readonly ISubTsHandler _subTsHandler;
- private readonly IMultiprotocolEncapsulationEventHandler _mpeEventHandler;
+ private readonly GsContextDto context;
private static readonly ILog logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name);
private long numPushed;
public void PushPacket(byte[] bbframe)
@@ -38,11 +37,7 @@ public class BbframeDeencapsulator3 : IBbframeDeencapsulator
if (mis[bbHeader.Matype2] == null)
{
logger.InfoFormat("Found a stream on MIS {0}",bbHeader.Matype2);
- mis[bbHeader.Matype2] = new GsTypeDetector(bbHeader.Matype2)
- {
- mpeEventHandler = this._mpeEventHandler,
- subTsHandler = this._subTsHandler
- };
+ mis[bbHeader.Matype2] = new GsTypeDetector(context.MisClone(bbHeader.Matype2));
}
mis[bbHeader.Matype2].PushFrame(bbHeader, new ReadOnlySpan(bbframe, 11, bbframe.Length - 11));
diff --git a/skyscraper8/GS/GSE-BFBS/BfbsGseReader.cs b/skyscraper8/GS/GSE-BFBS/BfbsGseReader.cs
index 0405003..01a6bb7 100644
--- a/skyscraper8/GS/GSE-BFBS/BfbsGseReader.cs
+++ b/skyscraper8/GS/GSE-BFBS/BfbsGseReader.cs
@@ -1,30 +1,31 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+using skyscraper5.Dvb.DataBroadcasting;
using skyscraper5.Mpeg2;
+using skyscraper5.Skyscraper.IO;
using skyscraper8.GSE;
using skyscraper8.GSE.GSE;
using skyscraper8.Skyscraper.IO;
using skyscraper8.Skyscraper.Scraper;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using skyscraper5.src.InteractionChannel;
namespace skyscraper8.GS.GSE_BFBS
{
internal class BfbsGseReader : IMisHandler
{
- private readonly byte _misId;
- private readonly ISubTsHandler _subTsHandler;
-
private GseLabel lastLabel;
private int frameNo;
+ public GsContextDto Context { get; set; }
- public BfbsGseReader(byte misId, ISubTsHandler subTsHandler)
+ public BfbsGseReader(GsContextDto context)
{
- _misId = misId;
- _subTsHandler = subTsHandler;
+ this.Context = context;
}
-
+
public void PushFrame(BBHeader bbHeader, ReadOnlySpan readOnlySpan)
{
frameNo++;
@@ -51,68 +52,171 @@ namespace skyscraper8.GS.GSE_BFBS
}
else
{
+ GsePacket gsePacket = new GsePacket(startIndicator, endIndicator, labelTypeIndicator);
int gseLength = (byteA & 0x0f) << 8;
gseLength += span.ReadUInt8();
- byte? fragId = null;
+ gsePacket.FragmentId = null;
if (!startIndicator || !endIndicator)
{
- fragId = span.ReadUInt8();
+ gsePacket.FragmentId = span.ReadUInt8();
gseLength -= 1;
}
- ushort? totalLength = null;
if ((startIndicator) && !endIndicator)
{
- totalLength = span.ReadUInt16BE();
+ gsePacket.TotalLength = span.ReadUInt16BE();
gseLength -= 2;
}
-
- GseLabel label = null;
- ushort? protocolType = null;
+
if (startIndicator)
{
- protocolType = span.ReadUInt16BE();
+ gsePacket.ProtocolType = span.ReadUInt16BE();
gseLength -= 2;
if (labelTypeIndicator == 0)
{
- label = new _6byteLabel(span.ReadBytes(6));
+ gsePacket.Label = new _6byteLabel(span.ReadBytes(6));
gseLength -= 6;
}
else if (labelTypeIndicator == 1)
{
- label = new _3byteLabel(span.ReadBytes(3));
+ gsePacket.Label = new _3byteLabel(span.ReadBytes(3));
gseLength -= 3;
}
else if (labelTypeIndicator == 2)
{
- label = BroadcastLabel.GetInstance();
+ gsePacket.Label = BroadcastLabel.GetInstance();
}
else if (labelTypeIndicator == 3)
{
- label = lastLabel;
+ gsePacket.Label = lastLabel;
}
+ lastLabel = gsePacket.Label;
}
if (!startIndicator && endIndicator)
gseLength -= 4;
- ReadOnlySpan gseDataBytes = span.ReadBytes(gseLength);
-
- uint? crc32 = null;
+ gsePacket.GseDataBytes = span.ReadBytes(gseLength);
+
if (!startIndicator && endIndicator)
{
- crc32 = span.ReadUInt32BE();
+ gsePacket.Crc32 = span.ReadUInt32BE();
}
- HandleGseFrame(startIndicator, endIndicator, fragId, totalLength, label, protocolType, gseDataBytes, crc32);
+ HandleGseFrame(gsePacket);
}
}
}
- private void HandleGseFrame(bool startIndicator, bool endIndicator, byte? fragId, ushort? totalLength, GseLabel label, ushort? protocolType, ReadOnlySpan gseDataBytes, uint? crc32)
+ private GseFragmentation[] fragmentations;
+ private void HandleGseFrame(GsePacket gsePacket)
{
- //throw new NotImplementedException();
+ if (gsePacket.StartIndicator & gsePacket.EndIndicator)
+ {
+ HandleAssembledFrame(gsePacket.ProtocolType.Value, gsePacket.GseDataBytes);
+ return;
+ }
+
+ if (!gsePacket.StartIndicator && gsePacket.EndIndicator)
+ {
+ if (fragmentations == null)
+ {
+ //This stream had no fragmentations yet, so we cannot reassemble this packet.
+ return;
+ }
+
+ if (fragmentations[gsePacket.FragmentId.Value] == null)
+ {
+ //The previous fragments are missing, so we cannot reassemble this packet.
+ return;
+ }
+
+ fragmentations[gsePacket.FragmentId.Value].AddFragement(gsePacket);
+ if (fragmentations[gsePacket.FragmentId.Value].Validate())
+ {
+ byte[] gseDataBytes = fragmentations[gsePacket.FragmentId.Value].GetGseDataBytes();
+ ushort protocolType = fragmentations[gsePacket.FragmentId.Value].ProtocolType;
+ HandleAssembledFrame(protocolType, gseDataBytes);
+ fragmentations[gsePacket.FragmentId.Value] = null;
+ return;
+ }
+ else
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+ if (gsePacket.StartIndicator && !gsePacket.EndIndicator)
+ {
+ fragmentations = new GseFragmentation[256];
+ fragmentations[gsePacket.FragmentId.Value] = new GseFragmentation(gsePacket);
+ return;
+ }
+
+ if (!gsePacket.StartIndicator && !gsePacket.EndIndicator)
+ {
+ if (fragmentations == null)
+ {
+ //This stream had no fragmentations yet, so we cannot reassemble this packet.
+ return;
+ }
+
+ if (fragmentations[gsePacket.FragmentId.Value] == null)
+ {
+ //The previous fragments are missing, so we cannot reassemble this packet.
+ return;
+ }
+
+ throw new NotImplementedException("Frames without start and end indicators are not supported yet. Please consider submitting a sample.");
+ }
+ throw new NotImplementedException();
+ }
+
+ private void HandleAssembledFrame(ushort protocolType, byte[] buffer)
+ {
+ switch (protocolType)
+ {
+ case 0x0081:
+ //Network clock reference
+ HandleNetworkClockReference(buffer);
+ return;
+ case 0x0082:
+ //Lower Layer Signalling, see en_30154502v010401p.pdf, page 49
+ HandleLowerLayerSignalling(buffer);
+ return;
+ case 0x0091:
+ //according to https://www.iana.org/assignments/ule-next-headers/ule-next-headers.xhtml it's private
+ return;
+ case 0x0800:
+ Context.IpOutput.OnIpDatagram(0x010e, buffer);
+ return;
+ default:
+ throw new NotImplementedException(protocolType.ToString());
+ }
+ }
+
+ private GseL2SHandler rcs2;
+ private void HandleLowerLayerSignalling(byte[] buffer)
+ {
+ if (rcs2 == null)
+ {
+ rcs2 = new GseL2SHandler(Context);
+ }
+
+ rcs2.PushPacket(buffer);
+
+ }
+
+ private void HandleNetworkClockReference(byte[] buffer)
+ {
+ MemoryStream binaryReader = new MemoryStream(buffer);
+ uint pcrA = binaryReader.ReadUInt32BE();
+ ushort pcrB = binaryReader.ReadUInt16BE();
+ ulong pcr_base = ((ulong)pcrA << 1) | ((ulong)pcrB >> 15);
+ ulong pcr_ext = (ulong)pcrB & 0x01ff;
+ ulong PCR = pcr_base * 300 + pcr_ext;
+ ulong seconds = PCR / 27000000;
}
}
}
diff --git a/skyscraper8/GS/GSE-BFBS/GseL2SHandler.cs b/skyscraper8/GS/GSE-BFBS/GseL2SHandler.cs
new file mode 100644
index 0000000..310e612
--- /dev/null
+++ b/skyscraper8/GS/GSE-BFBS/GseL2SHandler.cs
@@ -0,0 +1,31 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using skyscraper8.InteractionChannel;
+
+namespace skyscraper8.GS.GSE_BFBS
+{
+ internal class GseL2SHandler
+ {
+ public GsContextDto Context { get; }
+
+ public GseL2SHandler(GsContextDto context)
+ {
+ Context = context;
+ }
+
+ public void PushPacket(byte[] buffer)
+ {
+ GseTableStructure gseTableStructure = new GseTableStructure(buffer);
+ switch (gseTableStructure.TableId)
+ {
+ default:
+ throw new NotImplementedException(String.Format(
+ "Unknown DVB-RCS2 Table Id: 0x{0:X2}\nIf this is reproducible on this stream, please consider submitting me a sample.",
+ gseTableStructure.TableId));
+ }
+ }
+ }
+}
diff --git a/skyscraper8/GS/GSE-HEM/GseHemReader.cs b/skyscraper8/GS/GSE-HEM/GseHemReader.cs
index 7f6b9b8..028571f 100644
--- a/skyscraper8/GS/GSE-HEM/GseHemReader.cs
+++ b/skyscraper8/GS/GSE-HEM/GseHemReader.cs
@@ -1,24 +1,26 @@
using log4net;
using skyscraper5.Dvb.DataBroadcasting;
using skyscraper5.Skyscraper.IO;
+using skyscraper8.GS;
using skyscraper8.GSE.GSE;
namespace skyscraper8.GSE.GSE_HEM;
-public class GseHemReader : IMisHandler
+class GseHemReader : IMisHandler
{
- public GseHemReader(IMultiprotocolEncapsulationEventHandler mpeEventHandler)
+ public GseHemReader(GsContextDto context)
{
rayBuffer = new RayBuffer();
- this.mpeEventHandler = mpeEventHandler;
+ this.Context = context;
}
-
- private IMultiprotocolEncapsulationEventHandler mpeEventHandler;
+
private GseLabel lastLabel;
private RayBuffer rayBuffer;
private const int BUFFER_THRESHOLD = 65536 * 2; //Twice the maximum PDU size of GSE. That way we can guarantee we have one full PDU in buffer.
private static readonly ILog logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name);
-
+
+ public GsContextDto Context { get; set; }
+
public void PushFrame(BBHeader bbHeader, ReadOnlySpan readOnlySpan)
{
MemoryStream ms = new MemoryStream(readOnlySpan.ToArray());
@@ -101,7 +103,7 @@ public class GseHemReader : IMisHandler
switch (packet.ProtocolType)
{
case 0x0800:
- mpeEventHandler.OnIpDatagram(0x010e,packet.GseDataBytes);
+ Context.IpOutput.OnIpDatagram(0x010e,packet.GseDataBytes);
return;
default:
logger.WarnFormat("This GSE-HEM stream contains traffic other than IP. IP is type 0x0800, but we got 0x{0:X4} here.", packet.ProtocolType);
diff --git a/skyscraper8/GS/GSE/GseFragmentation.cs b/skyscraper8/GS/GSE/GseFragmentation.cs
index 838a65a..9c56720 100644
--- a/skyscraper8/GS/GSE/GseFragmentation.cs
+++ b/skyscraper8/GS/GSE/GseFragmentation.cs
@@ -7,10 +7,22 @@ public class GseFragmentation
this.ProtocolType = child.ProtocolType.Value;
this.Label = child.Label;
AddFragement(child);
+ this.TotalLength = child.TotalLength.Value;
+ this.Crc32 = child.Crc32;
}
+ public uint Crc32 { get; set; }
+
+ public ushort TotalLength { get; set; }
+
private List fragments;
+ public bool Validate()
+ {
+ //TODO: Implement proper CRC-32 checking here
+ return true;
+ }
+
public void AddFragement(GsePacket packet)
{
if (fragments == null)
@@ -23,12 +35,11 @@ public class GseFragmentation
public ushort ProtocolType { get; private set; }
- public bool Validate()
- {
- //To be implemented...
- return true;
- }
+ ///
+ /// Assembles the payload
+ ///
+ /// The concatenated GSE Data Bytes of each packet.
public byte[] GetGseDataBytes()
{
int totalLength = fragments.Select(x => x.Length).Sum();
diff --git a/skyscraper8/GS/GSE/GseLabel.cs b/skyscraper8/GS/GSE/GseLabel.cs
index 799f5d6..ec0d2ce 100644
--- a/skyscraper8/GS/GSE/GseLabel.cs
+++ b/skyscraper8/GS/GSE/GseLabel.cs
@@ -6,6 +6,8 @@ public abstract class GseLabel
{
public abstract int Length { get; }
public abstract string ToString();
+
+ public abstract int LabelTypeIndicator { get; }
}
public class _6byteLabel : GseLabel
@@ -23,6 +25,7 @@ public class _6byteLabel : GseLabel
}
public override int Length => 6;
+ public override int LabelTypeIndicator => 0;
}
public class _3byteLabel : GseLabel
@@ -53,6 +56,8 @@ public class _3byteLabel : GseLabel
}
override public int Length => 3;
+
+ public override int LabelTypeIndicator => 1;
}
public class BroadcastLabel : GseLabel
@@ -75,4 +80,6 @@ public class BroadcastLabel : GseLabel
}
return _instance;
}
+
+ public override int LabelTypeIndicator => 2;
}
\ No newline at end of file
diff --git a/skyscraper8/GS/GSE/GseReader.cs b/skyscraper8/GS/GSE/GseReader.cs
index 015b052..8a7f6c8 100644
--- a/skyscraper8/GS/GSE/GseReader.cs
+++ b/skyscraper8/GS/GSE/GseReader.cs
@@ -1,14 +1,17 @@
using log4net;
using skyscraper5.Dvb.DataBroadcasting;
using skyscraper5.Skyscraper.IO;
+using skyscraper8.GS;
namespace skyscraper8.GSE.GSE;
internal class GseReader : IMisHandler
{
+
private GseLabel lastLabel;
- public IMultiprotocolEncapsulationEventHandler mpeEventHandler { get; set; }
private static readonly ILog logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name);
+ public GsContextDto Context { get; set; }
+
public void PushFrame(BBHeader bbHeader, ReadOnlySpan readOnlySpan)
{
MemoryStream ms = new MemoryStream(readOnlySpan.ToArray());
@@ -115,7 +118,7 @@ internal class GseReader : IMisHandler
currentFragmentation.AddFragement(child);
if (currentFragmentation.Validate())
{
- mpeEventHandler.OnIpDatagram(0x010e,currentFragmentation.GetGseDataBytes());
+ Context.IpOutput.OnIpDatagram(0x010e,currentFragmentation.GetGseDataBytes());
}
fragmentations[child.FragmentId.Value] = null;
@@ -150,7 +153,7 @@ internal class GseReader : IMisHandler
switch (child.ProtocolType)
{
case 0x0800:
- mpeEventHandler.OnIpDatagram(0x010e, child.GseDataBytes);
+ Context.IpOutput.OnIpDatagram(0x010e, child.GseDataBytes);
break;
default:
if (warnedEthertypes == null)
diff --git a/skyscraper8/GS/GsContextDto.cs b/skyscraper8/GS/GsContextDto.cs
new file mode 100644
index 0000000..dd8434b
--- /dev/null
+++ b/skyscraper8/GS/GsContextDto.cs
@@ -0,0 +1,53 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+using skyscraper5.Dvb.DataBroadcasting;
+using skyscraper5.src.InteractionChannel;
+using skyscraper8.Skyscraper.Scraper;
+
+namespace skyscraper8.GS
+{
+ internal class GsContextDto
+ {
+ public GsContextMisDto MisClone(byte mis)
+ {
+ GsContextMisDto misClone = new GsContextMisDto(mis);
+
+ Type type = this.GetType();
+ PropertyInfo[] propertyInfos = type.GetProperties();
+ foreach (PropertyInfo propertyInfo in propertyInfos)
+ {
+ object? value = propertyInfo.GetValue(this);
+ propertyInfo.SetValue(misClone, value);
+ }
+
+ return misClone;
+ }
+
+ public byte GetMisId()
+ {
+ GsContextMisDto misDto = this as GsContextMisDto;
+ if (misDto == null)
+ return 0;
+ return misDto.Mis;
+ }
+
+ public ISubTsHandler TsOutput { get; set; }
+ public IMultiprotocolEncapsulationEventHandler IpOutput { get; set; }
+
+ public InteractionChannelHandler Rcs2Output { get; set; }
+ }
+
+ internal class GsContextMisDto : GsContextDto
+ {
+ public byte Mis { get; }
+
+ public GsContextMisDto(byte mis)
+ {
+ Mis = mis;
+ }
+ }
+}
diff --git a/skyscraper8/GS/GsException.cs b/skyscraper8/GS/GsException.cs
new file mode 100644
index 0000000..a037857
--- /dev/null
+++ b/skyscraper8/GS/GsException.cs
@@ -0,0 +1,23 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace skyscraper8.GS
+{
+ public class GsException : Exception
+ {
+ public GsException()
+ {
+ }
+
+ public GsException(string message) : base(message)
+ {
+ }
+
+ public GsException(string message, Exception inner) : base(message, inner)
+ {
+ }
+ }
+}
diff --git a/skyscraper8/GS/GsTypeDetector.cs b/skyscraper8/GS/GsTypeDetector.cs
index 4db7f7a..a8280a0 100644
--- a/skyscraper8/GS/GsTypeDetector.cs
+++ b/skyscraper8/GS/GsTypeDetector.cs
@@ -1,5 +1,6 @@
using log4net;
using skyscraper5.Dvb.DataBroadcasting;
+using skyscraper8.GS;
using skyscraper8.GS.GSE_BFBS;
using skyscraper8.GS.SiminnRadiomidun;
using skyscraper8.GSE.GSE_HEM;
@@ -8,17 +9,16 @@ using skyscraper8.Skyscraper.Scraper;
namespace skyscraper8.GSE;
-public class GsTypeDetector : IMisHandler
+class GsTypeDetector : IMisHandler
{
- public IMultiprotocolEncapsulationEventHandler mpeEventHandler { get; set; }
- private readonly byte _misId;
- private readonly ILog logger;
+ public GsContextDto Context { get; set; }
+ private readonly ILog logger;
private HashSet> _postedStreamTypes;
- public ISubTsHandler subTsHandler;
- public GsTypeDetector(byte misId)
+
+ public GsTypeDetector(GsContextMisDto context)
{
- _misId = misId;
- logger = LogManager.GetLogger(String.Format("{0} MIS {1}",System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name,misId));
+ this.logger = LogManager.GetLogger(String.Format("{0} MIS {1}",System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name,context.Mis));
+ this.Context = context;
}
private GseReader gseReader;
@@ -41,7 +41,7 @@ public class GsTypeDetector : IMisHandler
if (gseReader == null)
{
gseReader = new GseReader();
- gseReader.mpeEventHandler = mpeEventHandler;
+ gseReader.Context = Context;
}
gseReader.PushFrame(bbHeader, readOnlySpan);
@@ -53,7 +53,7 @@ public class GsTypeDetector : IMisHandler
//Looks like GSE-HEM
if (gseHemReader == null)
{
- gseHemReader = new GseHemReader(mpeEventHandler);
+ gseHemReader = new GseHemReader(Context);
}
gseHemReader.PushFrame(bbHeader, readOnlySpan);
return;
@@ -68,7 +68,7 @@ public class GsTypeDetector : IMisHandler
if (siminnRadiomidunReader == null)
{
//These behave similar to T2-MI. Interesting.
- siminnRadiomidunReader = new SiminnRadiomidunReader(_misId,subTsHandler);
+ siminnRadiomidunReader = new SiminnRadiomidunReader(Context);
}
siminnRadiomidunReader.PushFrame(bbHeader, readOnlySpan);
@@ -89,7 +89,7 @@ public class GsTypeDetector : IMisHandler
{
if (bfbsGseReader == null)
{
- bfbsGseReader = new BfbsGseReader(_misId, subTsHandler);
+ bfbsGseReader = new BfbsGseReader(Context);
}
bfbsGseReader.PushFrame(bbHeader, readOnlySpan);
diff --git a/skyscraper8/GS/IMisHandler.cs b/skyscraper8/GS/IMisHandler.cs
index 93e7c51..52b06b8 100644
--- a/skyscraper8/GS/IMisHandler.cs
+++ b/skyscraper8/GS/IMisHandler.cs
@@ -1,6 +1,10 @@
+using skyscraper8.GS;
+
namespace skyscraper8.GSE;
-public interface IMisHandler
+interface IMisHandler
{
+ public GsContextDto Context { get; set; }
+
void PushFrame(BBHeader bbHeader, ReadOnlySpan readOnlySpan);
}
\ No newline at end of file
diff --git a/skyscraper8/GS/POC/Pts2Bbf.cs b/skyscraper8/GS/POC/Pts2Bbf.cs
index 97009e8..8ebb6d8 100644
--- a/skyscraper8/GS/POC/Pts2Bbf.cs
+++ b/skyscraper8/GS/POC/Pts2Bbf.cs
@@ -44,7 +44,7 @@ public class Pts2Bbf
FileStream fileStream = file.OpenRead();
TsContext mpeg2 = new TsContext();
- mpeg2.RegisterPacketProcessor(0x010e, new Stid135BbFrameReader(null, null, dumper));
+ mpeg2.RegisterPacketProcessor(0x010e, new Stid135BbFrameReader(null, dumper));
DataStorage dataStorage = new InMemoryScraperStorage();
ObjectStorage objectStorage = new NullObjectStorage();
SkyscraperContext skyscraper = new SkyscraperContext(mpeg2, dataStorage, objectStorage);
diff --git a/skyscraper8/GS/POC/Pts2Bbf2.cs b/skyscraper8/GS/POC/Pts2Bbf2.cs
index 3d40ced..f4939e1 100644
--- a/skyscraper8/GS/POC/Pts2Bbf2.cs
+++ b/skyscraper8/GS/POC/Pts2Bbf2.cs
@@ -25,7 +25,7 @@ public class Pts2Bbf2 : IBbframeDeencapsulator
public void Run()
{
TsContext mpeg2 = new TsContext();
- Stid135BbFrameReader bbFrameReader = new Stid135BbFrameReader(null, null, this);
+ Stid135BbFrameReader bbFrameReader = new Stid135BbFrameReader(null, this);
mpeg2.RegisterPacketProcessor(0x010e, bbFrameReader);
SkyscraperContext context = new SkyscraperContext(mpeg2, new InMemoryScraperStorage(), new NullObjectStorage());
diff --git a/skyscraper8/GS/POC/Stid135Test.cs b/skyscraper8/GS/POC/Stid135Test.cs
index 41c667b..68944ed 100644
--- a/skyscraper8/GS/POC/Stid135Test.cs
+++ b/skyscraper8/GS/POC/Stid135Test.cs
@@ -3,6 +3,7 @@ using skyscraper5.Mpeg2;
using skyscraper5.Skyscraper.Scraper;
using skyscraper5.Skyscraper.Scraper.Storage.Filesystem;
using skyscraper5.Skyscraper.Scraper.Storage.InMemory;
+using skyscraper8.GS;
using skyscraper8.Skyscraper.Scraper.Storage;
namespace skyscraper8.GSE;
@@ -19,7 +20,8 @@ public class Stid135Test
ObjectStorage objectStorage = new FilesystemStorage(new DirectoryInfo("nip"));
SkyscraperContext skyscraper = new SkyscraperContext(mpeg2, dataStorage, objectStorage);
- mpeg2.RegisterPacketProcessor(0x010e, new Stid135BbFrameReader(skyscraper,skyscraper));
+ GsContextDto context = new GsContextDto();
+ mpeg2.RegisterPacketProcessor(0x010e, new Stid135BbFrameReader(context));
skyscraper.InitalizeFilterChain();
skyscraper.IngestFromStream(fileStream);
diff --git a/skyscraper8/GS/SiminnRadiomidun/SiminnRadiomidunReader.cs b/skyscraper8/GS/SiminnRadiomidun/SiminnRadiomidunReader.cs
index 3d4824c..2bb6fd9 100644
--- a/skyscraper8/GS/SiminnRadiomidun/SiminnRadiomidunReader.cs
+++ b/skyscraper8/GS/SiminnRadiomidun/SiminnRadiomidunReader.cs
@@ -13,19 +13,18 @@ namespace skyscraper8.GS.SiminnRadiomidun
{
internal class SiminnRadiomidunReader : IMisHandler
{
- public SiminnRadiomidunReader(byte mis, ISubTsHandler tsOutput)
+ public SiminnRadiomidunReader(GsContextDto context)
{
this.packetQueue = new Queue>();
this.isInSync = false;
- this.subTsKey = new SiminnRadiomidunSubTsIdentifier(mis);
- this.tsOutput = tsOutput;
+ this.subTsKey = new SiminnRadiomidunSubTsIdentifier(context.GetMisId());
+ this.Context = context;
}
private bool isInSync;
private Queue> packetQueue;
private MemoryStream currentItem;
private SiminnRadiomidunSubTsIdentifier subTsKey;
- private ISubTsHandler tsOutput;
public long PacketQueueSize
{
@@ -37,6 +36,9 @@ namespace skyscraper8.GS.SiminnRadiomidun
return packetQueue.Select(x => x.Item2.Length).Sum();
}
}
+
+ public GsContextDto Context { get; set; }
+
public void PushFrame(BBHeader bbHeader, ReadOnlySpan readOnlySpan)
{
if (packetQueue == null)
@@ -87,7 +89,7 @@ namespace skyscraper8.GS.SiminnRadiomidun
private void OutputPacket(byte[] buffer)
{
buffer[0] = 0x47;
- tsOutput.OnSubTsPacket(subTsKey, buffer);
+ Context.TsOutput.OnSubTsPacket(subTsKey, buffer);
}
}
}
diff --git a/skyscraper8/GS/Stid135BbFrameReader.cs b/skyscraper8/GS/Stid135BbFrameReader.cs
index 99cd8e2..e8e8e09 100644
--- a/skyscraper8/GS/Stid135BbFrameReader.cs
+++ b/skyscraper8/GS/Stid135BbFrameReader.cs
@@ -7,6 +7,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using skyscraper5.Dvb.DataBroadcasting;
+using skyscraper8.GS;
using skyscraper8.Skyscraper.Scraper;
namespace skyscraper8.GSE
@@ -15,10 +16,10 @@ namespace skyscraper8.GSE
{
private IBbframeDeencapsulator deencapsulator;
- public Stid135BbFrameReader(IMultiprotocolEncapsulationEventHandler mpeEventHandler, ISubTsHandler subTsHandler, IBbframeDeencapsulator deencapsulator = null)
+ public Stid135BbFrameReader(GsContextDto context, IBbframeDeencapsulator deencapsulator = null)
{
if (deencapsulator == null)
- deencapsulator = new BbframeDeencapsulator3(mpeEventHandler, subTsHandler);
+ deencapsulator = new BbframeDeencapsulator3(context);
this.deencapsulator = deencapsulator;
}
diff --git a/skyscraper8/InteractionChannel/GseTableStructure.cs b/skyscraper8/InteractionChannel/GseTableStructure.cs
new file mode 100644
index 0000000..22c3f5c
--- /dev/null
+++ b/skyscraper8/InteractionChannel/GseTableStructure.cs
@@ -0,0 +1,38 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using skyscraper5.Skyscraper.IO;
+
+namespace skyscraper8.InteractionChannel
+{
+ ///
+ /// Represents the structure in ETSI EN 301 545-2 V1.4.1, clause 6.4.3.1.1
+ ///
+ internal class GseTableStructure
+ {
+ public GseTableStructure(byte[] buffer)
+ {
+ MemoryStream ms = new MemoryStream(buffer, false);
+ TableId = ms.ReadUInt8();
+ InteractiveNetworkId = ms.ReadUInt16BE();
+
+ byte byteA = ms.ReadUInt8();
+ VersionNumber = (byteA & 0x3e) >> 1;
+ CurrentNextIndicator = (byteA & 0x01) != 0;
+
+ TableContent = ms.ReadBytes(ms.GetAvailableBytes());
+ }
+
+ public byte[] TableContent { get; private set; }
+
+ public bool CurrentNextIndicator { get; private set; }
+
+ public int VersionNumber { get; private set; }
+
+ public ushort InteractiveNetworkId { get; private set; }
+
+ public byte TableId { get; private set; }
+ }
+}
diff --git a/skyscraper8/InteractionChannel/InteractionChannelPsiGatherer.cs b/skyscraper8/InteractionChannel/InteractionChannelPsiGatherer.cs
index 11fb9a2..cc5e5d6 100644
--- a/skyscraper8/InteractionChannel/InteractionChannelPsiGatherer.cs
+++ b/skyscraper8/InteractionChannel/InteractionChannelPsiGatherer.cs
@@ -62,7 +62,12 @@ namespace skyscraper5.src.InteractionChannel
switch (buffer[0])
{
case 0x40: //NIT
- //see ETSI EN 301 545-2
+ InteractionChannelSiSectionHeader nitHeader = new InteractionChannelSiSectionHeader(ms);
+ if (!nitHeader.Valid)
+ {
+ _handler.OnInteractionChannelError(InteractionChannelErrorState.HeaderInvalid);
+ return;
+ }
throw new NotImplementedException("NIT");
case 0x41: //RMT
Rmt rmt = new Rmt(ms);
diff --git a/skyscraper8/Mpeg2/PsiSection.cs b/skyscraper8/Mpeg2/PsiSection.cs
index 3ce58c4..e507a2a 100644
--- a/skyscraper8/Mpeg2/PsiSection.cs
+++ b/skyscraper8/Mpeg2/PsiSection.cs
@@ -100,5 +100,12 @@ namespace skyscraper5.Mpeg2
}
public int BytesContained => receivedBytes;
+
+ internal static PsiSection FromByteArray(byte[] alreadyGathered)
+ {
+ PsiSection child = new PsiSection();
+ child.data = new MemoryStream(alreadyGathered, false);
+ return child;
+ }
}
}
diff --git a/skyscraper8/Properties/launchSettings.json b/skyscraper8/Properties/launchSettings.json
index ee4f78d..84087f8 100644
--- a/skyscraper8/Properties/launchSettings.json
+++ b/skyscraper8/Properties/launchSettings.json
@@ -2,7 +2,7 @@
"profiles": {
"skyscraper8": {
"commandName": "Project",
- "commandLineArgs": "E:\\638951188146456482.ts",
+ "commandLineArgs": "\"E:\\638951188146456482.ts\"",
"remoteDebugEnabled": false
},
"Container (Dockerfile)": {
diff --git a/skyscraper8/Skyscraper/MpeEject.cs b/skyscraper8/Skyscraper/MpeEject.cs
index 881b9cb..34d0951 100644
--- a/skyscraper8/Skyscraper/MpeEject.cs
+++ b/skyscraper8/Skyscraper/MpeEject.cs
@@ -119,6 +119,11 @@ public class MpeEject : ISkyscraperMpePlugin
byte[] packetBuffer = new byte[188];
for (int i = 0; i < userDatagram.Payload.Length; i += 188)
{
+ if (!session.PrintedNotice)
+ {
+ logger.InfoFormat("Found a Transport Stream in IP packets: {0} -> {1}", sourceEndpoint, targetEndpoint);
+ session.PrintedNotice = true;
+ }
Array.Copy(userDatagram.Payload, i, packetBuffer, 0, 188);
subTsHandler.OnSubTsPacket(session, packetBuffer);
lastOutputSucessful = true;
@@ -134,6 +139,8 @@ public class MpeEject : ISkyscraperMpePlugin
{
return lastOutputSucessful;
}
+
+
}
public class MpeEjectSession
@@ -155,4 +162,5 @@ public class MpeEjectSession
}
public int ValidDatagrams { get; set; }
+ public bool PrintedNotice { get; set; }
}
\ No newline at end of file
diff --git a/skyscraper8/Skyscraper/Scraper/SkyscraperContext.cs b/skyscraper8/Skyscraper/Scraper/SkyscraperContext.cs
index 46a3a2f..f342f2b 100644
--- a/skyscraper8/Skyscraper/Scraper/SkyscraperContext.cs
+++ b/skyscraper8/Skyscraper/Scraper/SkyscraperContext.cs
@@ -74,12 +74,14 @@ using System.IO;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
+using System.Reflection;
using System.Security.Policy;
using System.Text;
using skyscraper5.src.InteractionChannel.Model2;
using skyscraper8.Abertis;
using skyscraper8.Experimentals.NdsSsu;
using skyscraper8.Experimentals.OtvSsu;
+using skyscraper8.GS;
using skyscraper8.GSE;
using skyscraper8.Ieee802_1AB;
using skyscraper8.InteractionChannel.Model2;
@@ -306,7 +308,7 @@ namespace skyscraper5.Skyscraper.Scraper
{
if (!DvbContext.IsPidProcessorPresent(0x010e))
{
- DvbContext.RegisterPacketProcessor(0x010e, new Stid135BbFrameReader(this, this));
+ DvbContext.RegisterPacketProcessor(0x010e, new Stid135BbFrameReader(CreateGsContext()));
UiJunction?.SetGseMode();
LogEvent(SkyscraperContextEvent.SpecialTsMode, "STiD135 encapsulated GS detected.");
SpecialTsType = 3;
@@ -322,6 +324,29 @@ namespace skyscraper5.Skyscraper.Scraper
}
}
+ private GsContextDto CreateGsContext()
+ {
+ //Build the object
+ GsContextDto child = new GsContextDto();
+ child.IpOutput = this;
+ child.TsOutput = this;
+ child.Rcs2Output = this;
+
+ //for futureproofing
+ PropertyInfo[] properties = typeof(GsContextDto).GetProperties();
+ foreach (PropertyInfo propertyInfo in properties)
+ {
+ object? value = propertyInfo.GetValue(child);
+ if (value == null)
+ {
+ throw new SkyscraperException(String.Format("While creating the {0}, the {1} was not properly set. This is a bug, tell Fey.", nameof(GsContextDto), propertyInfo.Name));
+ }
+ }
+
+ //actually return the object
+ return child;
+ }
+
private DocsisPacketProcessor docsisPacketProcessor;
public int SpecialTsType { get; private set; }
private void CheckSpecialTs()
@@ -361,7 +386,7 @@ namespace skyscraper5.Skyscraper.Scraper
{
if (!DvbContext.IsPidProcessorPresent(0x010e))
{
- DvbContext.RegisterPacketProcessor(0x010e, new Stid135BbFrameReader(this, this));
+ DvbContext.RegisterPacketProcessor(0x010e, new Stid135BbFrameReader(CreateGsContext()));
UiJunction?.SetGseMode();
LogEvent(SkyscraperContextEvent.SpecialTsMode, "STiD135 encapsulated GS detected.");
SpecialTsType = 3;