Can now parse NIT, TDT, SCT, FCT2 and BCT from GSE.
Some checks failed
🚀 Pack skyscraper8 / make-zip (push) Failing after 2m40s

This commit is contained in:
feyris-tan 2025-11-09 16:55:05 +01:00
parent c64a0f5c46
commit a1125fbb2d
18 changed files with 352 additions and 31 deletions

View File

@ -11,6 +11,7 @@ using System.Diagnostics;
using System.Linq;
using System.Net.NetworkInformation;
using System.Security.Cryptography;
using skyscraper8.InteractionChannel.Model;
namespace skyscraper5.Data.PostgreSql
{
@ -949,6 +950,21 @@ namespace skyscraper5.Data.PostgreSql
return false;
}
public bool TestForRcs2Nit(RcsNit nit)
{
throw new NotImplementedException();
}
public void InsertRcs2Nit(RcsNit nit)
{
throw new NotImplementedException();
}
public bool UpdateRcs2Tdt(ushort interactiveNetworkId, DateTime tdtTimestamp)
{
throw new NotImplementedException();
}
private bool AreArraysEqual(byte[] l, byte[] r)
{
if (l.Length != r.Length)
@ -972,29 +988,6 @@ namespace skyscraper5.Data.PostgreSql
command.ExecuteNonQuery();
}
public bool TestForTerminalBurstTimePlan2(ushort interactiveNetworkId, byte tbtp2GroupId, byte frameFrameNumber)
{
throw new NotImplementedException();
}
public void StoreTerminalBurstTimePlan2(ushort interactiveNetworkId, byte tbtp2GroupId, Tbtp2.Frame frame)
{
throw new NotImplementedException();
}
public bool TestForFrameComposition2(ushort networkId, Fct2.Frame fct2)
{
throw new NotImplementedException();
}
public void InsertFct2Frame(ushort networkId, Fct2.Frame frame)
{
throw new NotImplementedException();
}
public bool TestForBroadcastConfiguration(ushort networkId, byte txTypeTxType)
{
throw new NotImplementedException();
}
public void InsertBroadcastConfiguration(ushort networkId, Bct.BroadcastConfiguration txType)
{
throw new NotImplementedException();
}
}
}
}

View File

@ -17,6 +17,7 @@ namespace skyscraper5.Dvb.Descriptors
public NetworkNameDescriptor(byte[] buffer)
{
NetworkName = En300468AnnexATextDecoder.GetInstance().Decode(buffer);
Valid = true;
}
public string NetworkName { get; private set; }

View File

@ -50,6 +50,8 @@ namespace skyscraper5.Dvb.Descriptors
break;
}
}
Valid = true;
}
public int ModulationType { get; private set; }
@ -100,5 +102,20 @@ namespace skyscraper5.Dvb.Descriptors
NineOfTen = 9,
NoConventionalCoding = 15
}
public override string ToString()
{
StringBuilder sb = new StringBuilder();
sb.Append(this.OrbitalPosition);
sb.Append("°");
sb.Append(this.East ? 'E' : 'W');
sb.Append("/");
sb.Append(Frequency / 100);
sb.Append("/");
sb.Append(this.Polarization.ToString()[0]);
sb.Append("/");
sb.Append(this.SymbolRate / 10);
return sb.ToString();
}
}
}

View File

@ -351,5 +351,10 @@ namespace skyscraper5.Dvb.Descriptors
{
return HashCode.Combine((int)LinkageType, ServiceId, OriginalNetworkId, TransportStreamId);
}
public override string ToString()
{
return String.Format("{0} on (0x{1:X4},0x{2:X4},0x{3:X4})", LinkageType, OriginalNetworkId, TransportStreamId, ServiceId);
}
}
}

View File

@ -7,6 +7,8 @@ using skyscraper5.src.InteractionChannel;
using skyscraper5.src.InteractionChannel.Model;
using skyscraper5.src.InteractionChannel.Model2;
using skyscraper8.InteractionChannel;
using skyscraper8.InteractionChannel.Model;
using skyscraper8.InteractionChannel.Model2;
namespace skyscraper8.GS.GSE_BFBS
{
@ -31,6 +33,16 @@ namespace skyscraper8.GS.GSE_BFBS
MemoryStream ms = new MemoryStream(gseTableStructure.TableContent, false);
switch (gseTableStructure.TableId)
{
case 0x40:
RcsNit nit = new RcsNit(ms);
if (!nit.Valid)
{
Context.Rcs2Output.OnInteractionChannelError(InteractionChannelErrorState.NitInvalid);
return;
}
Context.Rcs2Output.OnRcs2Nit(gseTableStructure.InteractiveNetworkId, nit);
return;
case 0x41:
Rmt rmt = new Rmt(ms, true);
if (!rmt.Valid)
@ -41,6 +53,26 @@ namespace skyscraper8.GS.GSE_BFBS
Context.Rcs2Output.OnRcsMap(rmt);
return;
case 0x70:
Rcs2Tdt tdt = new Rcs2Tdt(ms);
if (!tdt.Valid)
{
Context.Rcs2Output.OnInteractionChannelError(InteractionChannelErrorState.TdtInvalid);
return;
}
Context.Rcs2Output.OnRcs2Tdt(gseTableStructure.InteractiveNetworkId, tdt);
return;
case 0xa0:
Sct sct = new Sct(ms);
if (!sct.Valid)
{
Context.Rcs2Output.OnInteractionChannelError(InteractionChannelErrorState.SctInvalid);
return;
}
Context.Rcs2Output.OnSuperframeComposition(gseTableStructure.InteractiveNetworkId, sct);
return;
case 0xa4:
Cmt cmt = new Cmt(ms);
if (!cmt.Valid)
@ -50,6 +82,24 @@ namespace skyscraper8.GS.GSE_BFBS
}
Context.Rcs2Output.OnCorrectionMessage(gseTableStructure.InteractiveNetworkId, cmt);
return;
case 0xab:
Fct2 fct2 = new Fct2(ms);
if (!fct2.Valid)
{
Context.Rcs2Output.OnInteractionChannelError(InteractionChannelErrorState.Fct2Invalid);
return;
}
Context.Rcs2Output.OnFrameComposition2(gseTableStructure.InteractiveNetworkId, fct2);
return;
case 0xac:
Bct bct = new Bct(ms);
if (!bct.Valid)
{
Context.Rcs2Output.OnInteractionChannelError(InteractionChannelErrorState.BctInvalid);
return;
}
Context.Rcs2Output.OnBroadcastConfiguration(gseTableStructure.InteractiveNetworkId, bct);
return;
case 0xad:
Tbtp2 tbtp2 = new Tbtp2(ms);
if (!tbtp2.Valid)

View File

@ -23,6 +23,8 @@ namespace skyscraper5.src.InteractionChannel
Tbtp2Invalid,
Tmst2Invalid,
BctInvalid,
GseTableStructureInvalid
GseTableStructureInvalid,
NitInvalid,
TdtInvalid
}
}

View File

@ -8,6 +8,7 @@ using System.Net.NetworkInformation;
using System.Text;
using System.Threading.Tasks;
using skyscraper5.src.InteractionChannel.Model2;
using skyscraper8.InteractionChannel.Model;
using skyscraper8.InteractionChannel.Model2;
namespace skyscraper5.src.InteractionChannel
@ -33,5 +34,7 @@ namespace skyscraper5.src.InteractionChannel
void OnTerminalBurstTimePlan2(ushort interactiveNetworkId, Tbtp2 tbtp2);
void OnFrameComposition2(ushort networkId, Fct2 fct2);
void OnBroadcastConfiguration(ushort networkId, Bct bct);
void OnRcs2Nit(ushort interactiveNetworkId, RcsNit nit);
void OnRcs2Tdt(ushort interactiveNetworkId, Rcs2Tdt tdt);
}
}

View File

@ -0,0 +1,127 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using skyscraper5.Dvb.Descriptors;
using skyscraper5.Mpeg2;
using skyscraper5.Skyscraper;
using skyscraper5.Skyscraper.IO;
namespace skyscraper8.InteractionChannel.Model
{
public class RcsNit : Validatable
{
private static TsDescriptorUnpacker unpacker;
public RcsNit(MemoryStream ms)
{
byte byteA = ms.ReadUInt8();
byteA &= 0x0f;
int networkDescriptorsLength = byteA << 8;
networkDescriptorsLength += ms.ReadUInt8();
byte[] networkDescriptorsBytes = ms.ReadBytes(networkDescriptorsLength);
if (unpacker == null)
unpacker = TsDescriptorUnpacker.GetInstance();
if (!ParseNetworkDescriptors(unpacker.UnpackDescriptors(networkDescriptorsBytes, "NIT")))
{
Valid = false;
return;
}
byteA = ms.ReadUInt8();
byteA &= 0x0f;
int multiplexStreamsSpecLength = byteA << 8;
multiplexStreamsSpecLength += ms.ReadUInt8();
byte[] multiplexStreamsSpecBytes = ms.ReadBytes(multiplexStreamsSpecLength);
Valid = ParseMultiplexStreamsSpec(multiplexStreamsSpecBytes);
}
private bool ParseNetworkDescriptors(IEnumerable<TsDescriptor> descriptors)
{
foreach (TsDescriptor descriptor in descriptors)
{
if (!descriptor.Valid)
continue;
switch (descriptor)
{
case NetworkNameDescriptor nnd:
this.NetworkName = nnd.NetworkName;
break;
case LinkageDescriptor ld:
if (Linkages == null)
Linkages = new List<LinkageDescriptor>();
Linkages.Add(ld);
break;
default:
throw new NotImplementedException(String.Format(
"The RCS2-NIT Parser doesn't understand descriptors of type 0x{0:X2} yet. Please consider sharing a sample of this stream.",
descriptor.GetDescriptorId()));
}
}
return true;
}
public string NetworkName { get; set; }
public List<LinkageDescriptor> Linkages { get; private set; }
private bool ParseMultiplexStreamsSpec(byte[] buffer)
{
MemoryStream ms = new MemoryStream(buffer, false);
ForwardMultiplex = ms.ReadUInt16BE();
OriginalNetworkId = ms.ReadUInt16BE();
byte byteA = ms.ReadUInt8();
byteA &= 0x0f;
int transportDescriptorsLength = byteA << 8;
transportDescriptorsLength += ms.ReadUInt8();
IEnumerable<TsDescriptor> descriptors = unpacker.UnpackDescriptors(ms.ReadBytes(transportDescriptorsLength), "NIT");
foreach (TsDescriptor descriptor in descriptors)
{
if (!descriptor.Valid)
continue;
switch (descriptor)
{
case SatelliteDeliverySystemDescriptor sdsd:
this.SatelliteDeliverySystem = sdsd;
break;
default:
throw new NotImplementedException(String.Format(
"The RCS2-NIT Muliplex-Stream-Spec Parser doesn't understand descriptors of type 0x{0:X2} yet. Please consider sharing a sample of this stream.",
descriptor.GetDescriptorId()));
}
}
return true;
}
public SatelliteDeliverySystemDescriptor SatelliteDeliverySystem { get; set; }
public ushort OriginalNetworkId { get; set; }
public ushort ForwardMultiplex { get; set; }
protected bool Equals(RcsNit other)
{
return OriginalNetworkId == other.OriginalNetworkId && ForwardMultiplex == other.ForwardMultiplex;
}
public override bool Equals(object? obj)
{
if (ReferenceEquals(null, obj))
return false;
if (ReferenceEquals(this, obj))
return true;
if (obj.GetType() != this.GetType())
return false;
return Equals((RcsNit)obj);
}
public override int GetHashCode()
{
return HashCode.Combine(OriginalNetworkId, ForwardMultiplex);
}
}
}

View File

@ -14,7 +14,7 @@ namespace skyscraper5.src.InteractionChannel.Model2
public Fct2(MemoryStream ms)
{
byte frameTypeLoopCount = ms.ReadUInt8();
frameTypeLoopCount--;
frameTypeLoopCount++;
FrameTypes = new Frame[frameTypeLoopCount];
for (int i = 0; i < frameTypeLoopCount; i++)
{
@ -40,9 +40,11 @@ namespace skyscraper5.src.InteractionChannel.Model2
}
byte sectionLoopCount = ms.ReadUInt8();
sectionLoopCount++;
FrameTypes[i].SectionLoop = new SectionLoop[sectionLoopCount];
for (int j = 0; j < sectionLoopCount; j++)
{
FrameTypes[i].SectionLoop[j] = new SectionLoop();
FrameTypes[i].SectionLoop[j].DefaultTxType = ms.ReadUInt8();
FrameTypes[i].SectionLoop[j].FixedAccessMethod = (ms.ReadUInt8() & 0x0f);
FrameTypes[i].SectionLoop[j].RepeatCount = ms.ReadUInt16BE();

View File

@ -0,0 +1,28 @@
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.InteractionChannel.Model2
{
public class Rcs2Tdt : Validatable
{
public Rcs2Tdt(MemoryStream ms)
{
DateTime? dateTime = ms.ReadEtsiEn300468AnnexCDateTime();
if (!dateTime.HasValue)
{
Valid = false;
return;
}
Timestamp = dateTime.Value;
Valid = true;
}
public DateTime Timestamp { get; set; }
}
}

View File

@ -8,6 +8,7 @@ using System.Net.NetworkInformation;
using System.Text;
using System.Threading.Tasks;
using skyscraper5.src.InteractionChannel.Model2;
using skyscraper8.InteractionChannel.Model;
using skyscraper8.InteractionChannel.Model2;
namespace skyscraper5.src.InteractionChannel
@ -37,6 +38,16 @@ namespace skyscraper5.src.InteractionChannel
{
}
public void OnRcs2Nit(ushort interactiveNetworkId, RcsNit nit)
{
}
public void OnRcs2Tdt(ushort interactiveNetworkId, Rcs2Tdt tdt)
{
}
public void OnFrameComposition2(ushort? networkId, Fct2 fct2)
{

View File

@ -84,6 +84,7 @@ using skyscraper8.Experimentals.OtvSsu;
using skyscraper8.GS;
using skyscraper8.GSE;
using skyscraper8.Ieee802_1AB;
using skyscraper8.InteractionChannel.Model;
using skyscraper8.InteractionChannel.Model2;
using skyscraper8.Skyscraper.Net;
using skyscraper8.Skyscraper.Scraper;
@ -429,7 +430,7 @@ namespace skyscraper5.Skyscraper.Scraper
{
string loggerName = String.Format("{0}{1}", IsChild ? ChildName + ", " : "", eventType.ToString());
LogManager.GetLogger(loggerName).Info(name);
if (eventType != SkyscraperContextEvent.TdtTime && eventType != SkyscraperContextEvent.TotTime && eventType != SkyscraperContextEvent.Scte35TimeSignal)
if (eventType != SkyscraperContextEvent.TdtTime && eventType != SkyscraperContextEvent.TotTime && eventType != SkyscraperContextEvent.Scte35TimeSignal && eventType != SkyscraperContextEvent.Rcs2TdtTime)
{
lastEventTimestamp = DateTime.Now;
}
@ -2488,6 +2489,21 @@ namespace skyscraper5.Skyscraper.Scraper
}
}
public void OnRcs2Nit(ushort interactiveNetworkId, RcsNit nit)
{
if (!DataStorage.TestForRcs2Nit(nit))
{
LogEvent(SkyscraperContextEvent.Rcs2Network, String.Format("Network ID #{0}, ({1}) on {2}", nit.OriginalNetworkId, nit.NetworkName, nit.SatelliteDeliverySystem.ToString()));
DataStorage.InsertRcs2Nit(nit);
}
}
public void OnRcs2Tdt(ushort interactiveNetworkId, Rcs2Tdt tdt)
{
LogEvent(SkyscraperContextEvent.Rcs2TdtTime, String.Format("Network ID #{0}, {1}", interactiveNetworkId, tdt.Timestamp));
DataStorage.UpdateRcs2Tdt(interactiveNetworkId, tdt.Timestamp);
}
void InteractionChannelHandler.OnTimeslotComposition(ushort interactiveNetworkId, Tct tct)
{
throw new NotImplementedException();

View File

@ -89,6 +89,8 @@
TerminalBurstTimePlan2,
FrameComposition2,
BroadcastConfiguration,
EthernetLinkLayerDiscovery
EthernetLinkLayerDiscovery,
Rcs2Network,
Rcs2TdtTime
}
}

View File

@ -28,6 +28,7 @@ using skyscraper8.Ses;
using System.Net;
using System.Net.NetworkInformation;
using skyscraper5.src.InteractionChannel.Model2;
using skyscraper8.InteractionChannel.Model;
using skyscraper8.InteractionChannel.Model2;
using Platform = skyscraper5.Dvb.SystemSoftwareUpdate.Model.Platform;
@ -201,5 +202,8 @@ namespace skyscraper8.Skyscraper.Scraper.Storage
void InsertFct2Frame(ushort networkId, Fct2.Frame frame);
bool TestForBroadcastConfiguration(ushort networkId, byte txTypeTxType);
void InsertBroadcastConfiguration(ushort networkId, Bct.BroadcastConfiguration txType);
bool TestForRcs2Nit(RcsNit nit);
void InsertRcs2Nit(RcsNit nit);
bool UpdateRcs2Tdt(ushort interactiveNetworkId, DateTime tdtTimestamp);
}
}

View File

@ -38,6 +38,7 @@ using skyscraper8.DvbI;
using skyscraper8.DvbNip;
using skyscraper8.Experimentals.NdsSsu;
using skyscraper8.Ietf.FLUTE;
using skyscraper8.InteractionChannel.Model;
using skyscraper8.InteractionChannel.Model2;
using skyscraper8.Ses;
using skyscraper8.SimpleServiceDiscoveryProtocol;
@ -1187,6 +1188,21 @@ namespace skyscraper5.Skyscraper.Scraper.Storage.Filesystem
throw new NotImplementedException();
}
public bool TestForRcs2Nit(RcsNit nit)
{
throw new NotImplementedException();
}
public void InsertRcs2Nit(RcsNit nit)
{
throw new NotImplementedException();
}
public bool UpdateRcs2Tdt(ushort interactiveNetworkId, DateTime tdtTimestamp)
{
throw new NotImplementedException();
}
public IEnumerable<Tuple<int, int, ProgramMapping>> SelectAllPmt()
{
throw new NotImplementedException();

View File

@ -38,6 +38,7 @@ using System.Net;
using System.Net.NetworkInformation;
using skyscraper5.src.InteractionChannel.Model2;
using skyscraper8.Ietf.FLUTE;
using skyscraper8.InteractionChannel.Model;
using skyscraper8.InteractionChannel.Model2;
using skyscraper8.Skyscraper.Scraper.Storage;
using skyscraper8.Skyscraper.Scraper.Storage.Utilities;
@ -1025,6 +1026,38 @@ namespace skyscraper5.Skyscraper.Scraper.Storage.InMemory
broadcastConfigurations.Add(key, txType);
}
private HashSet<RcsNit> _rcs2Nits;
public bool TestForRcs2Nit(RcsNit nit)
{
if (_rcs2Nits == null)
return false;
return _rcs2Nits.Contains(nit);
}
public void InsertRcs2Nit(RcsNit nit)
{
if (_rcs2Nits == null)
_rcs2Nits = new HashSet<RcsNit>();
_rcs2Nits.Add(nit);
}
private DateTime[] _rcs2Timestamps;
public bool UpdateRcs2Tdt(ushort interactiveNetworkId, DateTime tdtTimestamp)
{
if (_rcs2Timestamps == null)
_rcs2Timestamps = new DateTime[ushort.MaxValue + 1];
if (_rcs2Timestamps[interactiveNetworkId] < tdtTimestamp)
{
_rcs2Timestamps[interactiveNetworkId] = tdtTimestamp;
return true;
}
return false;
}
public IEnumerable<Tuple<int, int, ProgramMapping>> SelectAllPmt()
{
for (int x = 0; x < pmtEntries.Length; x++)

View File

@ -12,6 +12,7 @@ using System.Net.NetworkInformation;
using System.Text;
using System.Threading.Tasks;
using skyscraper5.src.InteractionChannel.Model2;
using skyscraper8.InteractionChannel.Model;
using skyscraper8.InteractionChannel.Model2;
namespace skyscraper5.src.Skyscraper.Scraper.StreamAutodetection.Contestants
@ -210,7 +211,17 @@ namespace skyscraper5.src.Skyscraper.Scraper.StreamAutodetection.Contestants
}
}
public void OnFrameComposition2(ushort? networkId, Fct2 fct2)
public void OnRcs2Nit(ushort interactiveNetworkId, RcsNit nit)
{
//TODO: put validation logic in here, important!
}
public void OnRcs2Tdt(ushort interactiveNetworkId, Rcs2Tdt tdt)
{
//TODO: put validation logic in here, important!
}
public void OnFrameComposition2(ushort? networkId, Fct2 fct2)
{
if (fct2.FrameTypes.Length > 0)
{

View File

@ -18,7 +18,7 @@ namespace skyscraper5.Skyscraper
{
if (!_valid.HasValue)
{
throw new InvalidOperationException(String.Format("{0} doesn't know whether it's valid.", this.GetType().FullName));
throw new InvalidOperationException(String.Format("{0} doesn't know whether it's valid. This is a bug, please share a sample of a stream that reproduces this.", this.GetType().FullName));
}
return _valid.Value;