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 &lt;skyscraper8.Tests&gt;" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> - <Project Location="/home/schiemas/RiderProjects/skyscraper8/skyscraper8.Tests" Presentation="&lt;skyscraper8.Tests&gt;" /> + <SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from &lt;skyscraper8.Tests&gt;" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> + <Project Location="\home\schiemas\RiderProjects\skyscraper8\skyscraper8.Tests" Presentation="&lt;skyscraper8.Tests&gt;" /> </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;