Added support for LLDP in ULE's bridged frame mode.
Some checks failed
🚀 Pack skyscraper8 / make-zip (push) Failing after 1m20s
Some checks failed
🚀 Pack skyscraper8 / make-zip (push) Failing after 1m20s
This commit is contained in:
parent
aa21725182
commit
e8893f85ce
1
.gitignore
vendored
1
.gitignore
vendored
@ -131,3 +131,4 @@ imgui.ini
|
||||
/skyscraper8.Manual/skyscraper8.Manual.synctex.gz
|
||||
/.vs/skyscraper8/CopilotIndices/17.14.1231.31060
|
||||
/.vs/skyscraper8/CopilotIndices/17.14.1290.42047
|
||||
/Documentation/TSDuck-Samples/experiment2/*.ts
|
||||
|
||||
22
Documentation/TSDuck-Samples/experiment2/run.sh
Normal file
22
Documentation/TSDuck-Samples/experiment2/run.sh
Normal file
@ -0,0 +1,22 @@
|
||||
BITRATE=6000000
|
||||
PCR_DISTANCE=6000
|
||||
PCR_PID=1001
|
||||
|
||||
tsp -v -b $BITRATE \
|
||||
-I null \
|
||||
-P regulate --packet-burst 14 \
|
||||
-P filter --every $PCR_DISTANCE --set-label 1 \
|
||||
-P craft --only-label 1 --pid $PCR_PID --no-payload --pcr 0 \
|
||||
-P continuity --pid $PCR_PID --fix \
|
||||
-P pcradjust --pid $PCR_PID \
|
||||
-P pat -c -a 1/1000 \
|
||||
-P pmt -c -i 1 --pcr-pid $PCR_PID -p 1000 -a 1002/0x0d -a 1003/0x0d -a 1004/0x0d \
|
||||
--add-stream-identifier --set-data-broadcast-id 1002/5 \
|
||||
--set-data-broadcast-id 1003/5 --set-data-broadcast-id 1004/5 \
|
||||
-P inject tdt.xml --pid 0x14 --bitrate 2000 --stuffing \
|
||||
-P timeref --system-synchronous \
|
||||
-P mpeinject -p 1002 -l 127.0.0.2 2000 --max-queue 8192 \
|
||||
-P mpeinject -p 1003 -l 127.0.0.2 3000 --max-queue 8192 \
|
||||
-P history \
|
||||
-P until -s 60 \
|
||||
-O file --max-size 100000000 experiment2-clean-60s.ts
|
||||
4
Documentation/TSDuck-Samples/experiment2/tdt.xml
Normal file
4
Documentation/TSDuck-Samples/experiment2/tdt.xml
Normal file
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<tsduck>
|
||||
<TDT UTC_time="1984-01-01 00:13:37"/>
|
||||
</tsduck>
|
||||
@ -6,6 +6,7 @@
|
||||
\usepackage{amssymb}
|
||||
\usepackage{xcolor}
|
||||
\usepackage{pifont,mdframed}
|
||||
\usepackage{verbatim}
|
||||
|
||||
|
||||
\usepackage{geometry}
|
||||
@ -186,12 +187,55 @@ If you are using Microsoft Windows, you can also Drag’n’Drop a TS file onto
|
||||
\section{How skyscraper8 was made}
|
||||
skyscraper8 is developed in the C\# programming language, using Microsoft Visual Studio 2022. Therefore it requires a Microsoft .NET runtime, which is included in Microsoft Windows and freely available for most Linux distributions and Apple's macOS.
|
||||
|
||||
The .NET assembly of skyscraper8 is not obfuscated or protected in any way. This is on purpose. If you want to study how skyscraper8 works under the hood, I hereby allow you to use tools like RedGate's Reflector or JetBrains' dotPeek to inspect the skyscraper8.dll file. I plan to release the clear source code at a later date.
|
||||
The .NET assembly of skyscraper8 is not obfuscated or protected in any way. This is on purpose. If you want to study how skyscraper8 works under the hood, I hereby allow you to use tools like RedGate's Reflector or JetBrains' dotPeek to inspect the skyscraper8.dll file. I plan to release the clear source code at a later date. \\
|
||||
|
||||
Although I can't and won't force you to give credit, if you find some of my source code inspiring or helpful and want to use it in your own projects, it would be great if you give credit to where you got it from. \\
|
||||
|
||||
This document was typeset in \LaTeX{}, using the TeX Live distribution, and while I wrote it myself, I did use GPT-5 for proofreading the initial version of this document. The proofreading of this document is the only part of skyscraper8 for which an LLM was used. No part of the actual skyscraper8 codebase itself was written by an LLM. \\
|
||||
|
||||
For generating artificial TS recordings to test on, TSDuck was used. \\
|
||||
|
||||
\subsection{Experiments conducted using TSDuck}
|
||||
|
||||
\subsubsection {Creating a TS that carried multiple sub-TS using MPE}
|
||||
|
||||
Since skyscraper8 public release 12, it's possible to have skyscraper8 extract one or more TS carried via Multiprotocol Encapsulation. Unfortunately the dish I have at home is not aimed at satellites carrying such a stream. So I used TSDuck to build one artificially. \\
|
||||
|
||||
The following script was used to to run \codeword{tsp}:
|
||||
|
||||
\verbatiminput{../Documentation/TSDuck-Samples/experiment2/run.sh}
|
||||
|
||||
Contents of tdt.xml:
|
||||
|
||||
\verbatiminput{../Documentation/TSDuck-Samples/experiment2/tdt.xml}
|
||||
|
||||
Before executing that script, run this beforehand, to get a stream of Stingray Spa, of course you can use any other IPTV station you'd like:
|
||||
|
||||
\begin{verbatim}
|
||||
tsp -v -I hls https://lotus.stingray.com/manifest/ose-122ads-montreal/samsungtvplus/master.m3u8 \
|
||||
-P regulate -P history -O ip 127.0.0.2:3000
|
||||
\end{verbatim}
|
||||
|
||||
For the other stream, I used Offener Kanal Berlin, like so:
|
||||
|
||||
\begin{verbatim}
|
||||
tsp -v -I hls https://alex-stream.rosebud-media.de/bounce/alexlivetv50.smil/index.m3u8 \
|
||||
-P regulate -P history -O ip 127.0.0.2:2000
|
||||
\end{verbatim}
|
||||
|
||||
Finally, run the script above to get a clean TS containing two other TS encapsulated in MPE.
|
||||
|
||||
This document was typeset in \LaTeX{}, using the TeX Live distribution, and while I wrote it myself, I did use GPT-5 for proofreading the initial version of this document. The proofreading of this document is the only part of skyscraper8 for which an LLM was used. No part of the actual skyscraper8 codebase itself was written by an LLM.
|
||||
|
||||
\subsection{Personal remarks and some useless bonus information}
|
||||
|
||||
\subsubsection{Credits}
|
||||
skyscraper8 uses some external libraries:
|
||||
\begin{itemize}
|
||||
\item One of the external libraries is Ionic.Zlib.Core.dll, which is part of the \href{https://github.com/DinoChiesa/DotNetZip-2025}{DotNetZip} tool set by Dino Chiesa.
|
||||
\item Also used is Newtonsoft.Json.dll a.k.a. \href{https://github.com/JamesNK/Newtonsoft.Json}{Json.NET} by James Newton-King.
|
||||
\item Some of the GS handling code got inspired from the \href{https://github.com/newspaperman/bbframe-tools}{bbframe-tools} written by newspaperman.
|
||||
\end{itemize}
|
||||
|
||||
\subsubsection{How and why skyscraper8 began: A young man's dream}
|
||||
Ever since I was a child, I was fascinated by TV. But not actually watching it, rather understanding how it works. I grew up in the 90s, in an area where Cable TV was not really available, and terristial reception (yeah, those TVs with bunny ears!) only worked when it felt like it. So satellite was the best way to get my childish fix of cartoons! We can say the dish on my roof has always been a companion. Far more interesting than actually watching TV was scanning through the printed TV guides. I was curious. How did they make these? And how would I make one myself? I wondered. Of course my parents wouldn't know - they're not techies. \\
|
||||
|
||||
|
||||
13
skyscraper8/Ieee802_1AB/ILldpFrameHandler.cs
Normal file
13
skyscraper8/Ieee802_1AB/ILldpFrameHandler.cs
Normal file
@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace skyscraper8.Ieee802_1AB
|
||||
{
|
||||
internal interface ILldpFrameHandler
|
||||
{
|
||||
void OnLldpFrame(LldpFrame frame);
|
||||
}
|
||||
}
|
||||
154
skyscraper8/Ieee802_1AB/LldpFrame.cs
Normal file
154
skyscraper8/Ieee802_1AB/LldpFrame.cs
Normal file
@ -0,0 +1,154 @@
|
||||
using System.Text;
|
||||
using skyscraper5.Skyscraper.IO;
|
||||
using skyscraper8.Ieee802_1AB.Model;
|
||||
|
||||
namespace skyscraper8.Ieee802_1AB;
|
||||
|
||||
public class LldpFrame
|
||||
{
|
||||
public LldpFrame(byte[] contents)
|
||||
{
|
||||
MemoryStream ms = new MemoryStream(contents, false);
|
||||
while (ms.Position < ms.Length)
|
||||
{
|
||||
ushort tlv = ms.ReadUInt16BE();
|
||||
int type = (tlv & 0xfe00) >> 9;
|
||||
int length = (tlv & 0x01ff);
|
||||
byte[] value = ms.ReadBytes(length);
|
||||
switch (type)
|
||||
{
|
||||
case 1: //Chassis
|
||||
this.Chassis = new LldpChassis(value);
|
||||
break;
|
||||
case 2: //Port
|
||||
this.Port = new LldpPort(value);
|
||||
break;
|
||||
case 3: //Time to Live
|
||||
this.TimeToLive = (value[0] << 8) | value[1];
|
||||
this.TimeToLive--;
|
||||
break;
|
||||
case 4:
|
||||
this.PortDescription = Encoding.UTF8.GetString(value);
|
||||
break;
|
||||
case 5:
|
||||
this.SystemName = Encoding.UTF8.GetString(value);
|
||||
break;
|
||||
case 6:
|
||||
this.SystemDescription = Encoding.UTF8.GetString(value);
|
||||
break;
|
||||
case 7:
|
||||
this.Capabilities = new LldpCapabilities(value);
|
||||
break;
|
||||
case 8:
|
||||
this.ManagementAddress = new LldpManagementAddress(value);
|
||||
break;
|
||||
case 127:
|
||||
string OrganizationUniqueCode = BitConverter.ToString(value, 0, 3);
|
||||
byte OrganizationallyDefinedSubtype = value[3];
|
||||
MemoryStream payload = new MemoryStream(value, 4, value.Length - 4);
|
||||
switch (OrganizationUniqueCode)
|
||||
{
|
||||
case "00-80-C2": //IEEE 802.1 Chair
|
||||
switch (OrganizationallyDefinedSubtype)
|
||||
{
|
||||
case 6: //VLAN ID
|
||||
this.PortVlanIdentifier = ms.ReadUInt16BE();
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException(String.Format("Unknown IEEE 802.1 Subtype {0}! Please share a sample of this stream.",OrganizationallyDefinedSubtype));
|
||||
}
|
||||
break;
|
||||
case "00-12-0F": //IEEE 802.3 Chair
|
||||
switch (OrganizationallyDefinedSubtype)
|
||||
{
|
||||
//see 802.1AB-2009.pdf, Annex F
|
||||
case 1: //MAC/PHY configuration/status TLV format
|
||||
this.MacPhyConfigurationStatus = new LldpMacPhyConfigurationStatus(payload);
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException(String.Format("Unknown IEEE 802.3 Subtype {0}! Please share a sample of this stream.", OrganizationallyDefinedSubtype));
|
||||
}
|
||||
break;
|
||||
case "00-12-BB":
|
||||
switch (OrganizationallyDefinedSubtype) //TR-41. Don't know which document yet.
|
||||
{
|
||||
case 1: //Media Capabilities
|
||||
this.MediaCapabilities = new LldpMediaCapabilities(payload);
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException(String.Format("Unknown TR-41 Subtype {0}! Please share a sample of this stream.", OrganizationallyDefinedSubtype));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException(String.Format("Unknown Organization Unique Code {0}. Please share a sample of this stream.",OrganizationUniqueCode));
|
||||
}
|
||||
|
||||
break;
|
||||
case 0: //End of LLDP PDU
|
||||
return;
|
||||
default:
|
||||
throw new NotImplementedException( String.Format("LLDP Option {0} not yet supported. Please share a sample of this stream.",type));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public LldpMediaCapabilities MediaCapabilities { get; set; }
|
||||
|
||||
public LldpMacPhyConfigurationStatus MacPhyConfigurationStatus { get; set; }
|
||||
|
||||
public ushort? PortVlanIdentifier { get; set; }
|
||||
|
||||
public LldpManagementAddress ManagementAddress { get; private set; }
|
||||
|
||||
public LldpCapabilities Capabilities { get; set; }
|
||||
|
||||
public string SystemDescription { get; set; }
|
||||
|
||||
public string SystemName { get; set; }
|
||||
|
||||
public string PortDescription { get; set; }
|
||||
|
||||
public int? TimeToLive { get; set; }
|
||||
|
||||
public LldpPort Port { get; set; }
|
||||
|
||||
public LldpChassis Chassis { get; private set; }
|
||||
|
||||
protected bool Equals(LldpFrame other)
|
||||
{
|
||||
return Port.Equals(other.Port) && Chassis.Equals(other.Chassis);
|
||||
}
|
||||
|
||||
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((LldpFrame)obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(Port, Chassis);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (!string.IsNullOrEmpty(SystemName))
|
||||
{
|
||||
sb.Append(SystemName);
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append(Chassis.ToString());
|
||||
}
|
||||
|
||||
sb.Append(", ");
|
||||
sb.Append(Port.ToString());
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
36
skyscraper8/Ieee802_1AB/Model/LldpCapabilities.cs
Normal file
36
skyscraper8/Ieee802_1AB/Model/LldpCapabilities.cs
Normal file
@ -0,0 +1,36 @@
|
||||
using skyscraper5.Skyscraper.IO;
|
||||
|
||||
namespace skyscraper8.Ieee802_1AB.Model;
|
||||
|
||||
public class LldpCapabilities
|
||||
{
|
||||
private readonly ushort capabilities;
|
||||
private readonly ushort enabledCapabilities;
|
||||
|
||||
public LldpCapabilities(byte[] buffer)
|
||||
{
|
||||
MemoryStream ms = new MemoryStream(buffer);
|
||||
this.capabilities = ms.ReadUInt16BE();
|
||||
this.enabledCapabilities = ms.ReadUInt16BE();
|
||||
}
|
||||
|
||||
public bool HasOther => (capabilities & 0x0001) != 0;
|
||||
public bool HasRepeater => (capabilities & 0x0002) != 0;
|
||||
public bool HasBridge => (capabilities & 0x0004) != 0;
|
||||
public bool HasWlanAccessPoint => (capabilities & 0x0008) != 0;
|
||||
public bool HasRouter => (capabilities & 0x0010) != 0;
|
||||
public bool HasTelephone => (capabilities & 0x0020) != 0;
|
||||
public bool HasDocsis => (capabilities & 0x0040) != 0;
|
||||
public bool IsStation => (capabilities & 0x0080) != 0;
|
||||
public bool HasCVLan => (capabilities & 0x0100) != 0;
|
||||
public bool HasSVLan => (capabilities & 0x0200) != 0;
|
||||
public bool HasTpmr => (capabilities & 0x0400) != 0;
|
||||
|
||||
public bool EnabledOther => (enabledCapabilities & 0x0001) != 0;
|
||||
public bool EnabledRepeater => (enabledCapabilities & 0x0002) != 0;
|
||||
public bool EnabledBridge => (enabledCapabilities & 0x0004) != 0;
|
||||
public bool EnabledWlanAccessPoint => (enabledCapabilities & 0x0008) != 0;
|
||||
public bool EnabledRouter => (enabledCapabilities & 0x0010) != 0;
|
||||
public bool EnabledTelephone => (enabledCapabilities & 0x0020) != 0;
|
||||
public bool EnabledDocsis => (enabledCapabilities & 0x0040) != 0;
|
||||
}
|
||||
57
skyscraper8/Ieee802_1AB/Model/LldpChassis.cs
Normal file
57
skyscraper8/Ieee802_1AB/Model/LldpChassis.cs
Normal file
@ -0,0 +1,57 @@
|
||||
using System.Net.NetworkInformation;
|
||||
using skyscraper5.Skyscraper.IO;
|
||||
|
||||
namespace skyscraper8.Ieee802_1AB.Model;
|
||||
|
||||
public class LldpChassis
|
||||
{
|
||||
public LldpChassis(byte[] value)
|
||||
{
|
||||
MemoryStream ms = new MemoryStream(value);
|
||||
ChassisSubtype = ms.ReadUInt8();
|
||||
switch (ChassisSubtype)
|
||||
{
|
||||
case 4:
|
||||
MacAddress = new PhysicalAddress(ms.ReadBytes(6));
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException(string.Format("Chassis Subtype {0} not yet supported. Please share a sample of this stream.", ChassisSubtype));
|
||||
}
|
||||
}
|
||||
|
||||
public PhysicalAddress MacAddress { get; set; }
|
||||
|
||||
public byte ChassisSubtype { get; private set; }
|
||||
|
||||
protected bool Equals(LldpChassis other)
|
||||
{
|
||||
return MacAddress.Equals(other.MacAddress) && ChassisSubtype == other.ChassisSubtype;
|
||||
}
|
||||
|
||||
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((LldpChassis)obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(MacAddress, ChassisSubtype);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
switch (ChassisSubtype)
|
||||
{
|
||||
case 4:
|
||||
return MacAddress.ToString();
|
||||
default:
|
||||
return String.Format("Chassis Subtype {0}", ChassisSubtype);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using skyscraper5.Skyscraper.IO;
|
||||
|
||||
namespace skyscraper8.Ieee802_1AB.Model
|
||||
{
|
||||
public class LldpMacPhyConfigurationStatus
|
||||
{
|
||||
private readonly byte autoNegotioationStatus;
|
||||
private readonly ushort pmdAutoNegotiation;
|
||||
|
||||
public LldpMacPhyConfigurationStatus(MemoryStream ms)
|
||||
{
|
||||
autoNegotioationStatus = ms.ReadUInt8();
|
||||
pmdAutoNegotiation = ms.ReadUInt16BE();
|
||||
OperationalMauType = ms.ReadUInt16BE();
|
||||
}
|
||||
|
||||
public ushort OperationalMauType { get; private set; }
|
||||
|
||||
public bool AutonegotiationSupported => (autoNegotioationStatus & 0x01) != 0;
|
||||
public bool AutonegotiationStatus => (autoNegotioationStatus & 0x02) != 0;
|
||||
}
|
||||
}
|
||||
37
skyscraper8/Ieee802_1AB/Model/LldpManagementAddress.cs
Normal file
37
skyscraper8/Ieee802_1AB/Model/LldpManagementAddress.cs
Normal file
@ -0,0 +1,37 @@
|
||||
using System.Net;
|
||||
using skyscraper5.Skyscraper.IO;
|
||||
|
||||
namespace skyscraper8.Ieee802_1AB.Model;
|
||||
|
||||
public class LldpManagementAddress
|
||||
{
|
||||
public LldpManagementAddress(byte[] buffer)
|
||||
{
|
||||
MemoryStream ms = new MemoryStream(buffer, false);
|
||||
byte managementAddressStringLength = ms.ReadUInt8();
|
||||
ManagementAddressSubtype = ms.ReadUInt8();
|
||||
managementAddressStringLength--;
|
||||
|
||||
if (ManagementAddressSubtype != 1)
|
||||
{
|
||||
throw new NotImplementedException("Unknown management address subtype. Please share a sample of this stream.");
|
||||
}
|
||||
|
||||
ManagementAddress = new IPAddress(ms.ReadBytes(managementAddressStringLength));
|
||||
InterfaceNumberingSubtype = ms.ReadUInt8();
|
||||
InterfaceNumber = ms.ReadUInt32BE();
|
||||
|
||||
byte oidStringLength = ms.ReadUInt8();
|
||||
ObjectIdentifier = ms.ReadBytes(oidStringLength);
|
||||
}
|
||||
|
||||
public byte[] ObjectIdentifier { get; set; }
|
||||
|
||||
public uint InterfaceNumber { get; set; }
|
||||
|
||||
public byte InterfaceNumberingSubtype { get; set; }
|
||||
|
||||
public byte ManagementAddressSubtype { get; set; }
|
||||
|
||||
public IPAddress ManagementAddress { get; set; }
|
||||
}
|
||||
28
skyscraper8/Ieee802_1AB/Model/LldpMediaCapabilities.cs
Normal file
28
skyscraper8/Ieee802_1AB/Model/LldpMediaCapabilities.cs
Normal file
@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using skyscraper5.Skyscraper.IO;
|
||||
|
||||
namespace skyscraper8.Ieee802_1AB.Model
|
||||
{
|
||||
public class LldpMediaCapabilities
|
||||
{
|
||||
private readonly ushort capabilities;
|
||||
public byte NetworkConnectivity { get; set; }
|
||||
|
||||
public LldpMediaCapabilities(MemoryStream ms)
|
||||
{
|
||||
capabilities = ms.ReadUInt16BE();
|
||||
NetworkConnectivity = ms.ReadUInt8();
|
||||
}
|
||||
|
||||
public bool LldpMedCapable => (capabilities & 0x0001) != 0;
|
||||
public bool NetworkPolicyCapable => (capabilities & 0x0002) != 0;
|
||||
public bool LocationIdentificationCapable => (capabilities & 0x0004) != 0;
|
||||
public bool ExtendedPowerViaMdiPse => (capabilities & 0x0008) != 0;
|
||||
public bool ExtendedPowerViaMdiPd => (capabilities & 0x0010) != 0;
|
||||
public bool InventoryCapable => (capabilities & 0x0020) != 0;
|
||||
}
|
||||
}
|
||||
79
skyscraper8/Ieee802_1AB/Model/LldpPort.cs
Normal file
79
skyscraper8/Ieee802_1AB/Model/LldpPort.cs
Normal file
@ -0,0 +1,79 @@
|
||||
using System.Net.NetworkInformation;
|
||||
using skyscraper5.Skyscraper.IO;
|
||||
|
||||
namespace skyscraper8.Ieee802_1AB.Model;
|
||||
|
||||
public class LldpPort
|
||||
{
|
||||
public LldpPort(byte[] buffer)
|
||||
{
|
||||
MemoryStream ms = new MemoryStream(buffer);
|
||||
Subtype = ms.ReadUInt8();
|
||||
switch (Subtype)
|
||||
{
|
||||
case 3:
|
||||
MacAddress = new PhysicalAddress(ms.ReadBytes(6));
|
||||
break;
|
||||
case 5:
|
||||
PortId = ms.ReadUTF8FixedLength((int)ms.GetAvailableBytes());
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException(String.Format("Unknown Port Subtype {0}. Please share a sample of this stream.", Subtype));
|
||||
}
|
||||
}
|
||||
|
||||
public PhysicalAddress MacAddress { get; set; }
|
||||
public string PortId { get; set; }
|
||||
public byte Subtype { get; private set; }
|
||||
|
||||
protected bool Equals(LldpPort other)
|
||||
{
|
||||
switch (Subtype)
|
||||
{
|
||||
case 3:
|
||||
return MacAddress.Equals(other.MacAddress) && Subtype == other.Subtype;
|
||||
case 5:
|
||||
return PortId == other.PortId && Subtype == other.Subtype;
|
||||
default:
|
||||
throw new NotImplementedException(String.Format("Unknown Port Subtype {0}. Please share a sample of this stream.", Subtype));
|
||||
}
|
||||
}
|
||||
|
||||
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((LldpPort)obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
switch (Subtype)
|
||||
{
|
||||
case 3:
|
||||
return HashCode.Combine(PortId, MacAddress);
|
||||
case 5:
|
||||
return HashCode.Combine(PortId, Subtype);
|
||||
default:
|
||||
throw new NotImplementedException(String.Format("Unknown Port Subtype {0}. Please share a sample of this stream.", Subtype));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
switch (Subtype)
|
||||
{
|
||||
case 3:
|
||||
return MacAddress.ToString();
|
||||
case 5:
|
||||
return PortId;
|
||||
default:
|
||||
return String.Format("Port Subtype {0}", Subtype);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -41,7 +41,7 @@ namespace skyscraper5
|
||||
class Program
|
||||
{
|
||||
private static readonly ILog logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name);
|
||||
private const int PUBLIC_RELEASE = 11;
|
||||
private const int PUBLIC_RELEASE = 12;
|
||||
private static void IntegrationTest()
|
||||
{
|
||||
/*List<SsdpDevice> ssdpDevices = SsdpClient.GetSsdpDevices(1000).ToList();
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
"profiles": {
|
||||
"skyscraper8": {
|
||||
"commandName": "Project",
|
||||
"commandLineArgs": "subts-on",
|
||||
"commandLineArgs": "\"D:\\sorglos-iww-rww-aca-oca.ts\"",
|
||||
"remoteDebugEnabled": false
|
||||
},
|
||||
"Container (Dockerfile)": {
|
||||
|
||||
158
skyscraper8/Skyscraper/MpeEject.cs
Normal file
158
skyscraper8/Skyscraper/MpeEject.cs
Normal file
@ -0,0 +1,158 @@
|
||||
using System.Net;
|
||||
using log4net;
|
||||
using skyscraper5.Ietf.Rfc768;
|
||||
using skyscraper5.Ietf.Rfc971;
|
||||
using skyscraper5.Skyscraper;
|
||||
using skyscraper5.Skyscraper.Plugins;
|
||||
using skyscraper8.Skyscraper.Net;
|
||||
using skyscraper8.Skyscraper.Scraper;
|
||||
using skyscraper8.Skyscraper.Scraper.Storage;
|
||||
|
||||
namespace skyscraper8.Skyscraper;
|
||||
|
||||
/// <summary>
|
||||
/// This plugin does the opposite of what TSDuck's MpeInject does.
|
||||
/// </summary>
|
||||
[SkyscraperPlugin]
|
||||
public class MpeEject : ISkyscraperMpePlugin
|
||||
{
|
||||
private static readonly ILog logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name);
|
||||
|
||||
public void ConnectToStorage(object[] connector)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private ISubTsHandler subTsHandler;
|
||||
public void SetContext(DateTime? currentTime, object skyscraperContext)
|
||||
{
|
||||
subTsHandler = skyscraperContext as ISubTsHandler;
|
||||
if (subTsHandler == null)
|
||||
{
|
||||
logger.ErrorFormat("Couldn't get a handle for the SubTsHandler. Deencapsulation will not work.");
|
||||
}
|
||||
}
|
||||
|
||||
public bool CanHandlePacket(InternetHeader internetHeader, byte[] ipv4Packet)
|
||||
{
|
||||
if (internetHeader.Protocol != 0x11)
|
||||
return false;
|
||||
|
||||
int payloadLength = ipv4Packet.Length - 8;
|
||||
int tsCheck = payloadLength % 188;
|
||||
return tsCheck == 0;
|
||||
}
|
||||
|
||||
private Dictionary<Tuple<IPEndPoint, IPEndPoint>, MpeEjectSession> sessions;
|
||||
private bool lastOutputSucessful;
|
||||
public void HandlePacket(InternetHeader internetHeader, byte[] ipv4Packet)
|
||||
{
|
||||
if (subTsHandler == null)
|
||||
{
|
||||
lastOutputSucessful = false;
|
||||
return;
|
||||
}
|
||||
|
||||
UserDatagram userDatagram = new UserDatagram(ipv4Packet);
|
||||
IPEndPoint sourceEndpoint = new IPEndPoint(internetHeader.SourceAddress, userDatagram.SourcePort);
|
||||
IPEndPoint targetEndpoint = new IPEndPoint(internetHeader.DestinationAddress, userDatagram.DestinationPort);
|
||||
Tuple<IPEndPoint, IPEndPoint> sessionPointer = new Tuple<IPEndPoint, IPEndPoint>(sourceEndpoint, targetEndpoint);
|
||||
|
||||
if (sessions == null)
|
||||
sessions = new Dictionary<Tuple<IPEndPoint, IPEndPoint>, MpeEjectSession>();
|
||||
|
||||
MpeEjectSession session = null;
|
||||
if (sessions.ContainsKey(sessionPointer))
|
||||
{
|
||||
session = sessions[sessionPointer];
|
||||
if (session.Bootstrapped && !session.Valid)
|
||||
{
|
||||
lastOutputSucessful = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
session = new MpeEjectSession(sourceEndpoint, targetEndpoint);
|
||||
sessions.Add(sessionPointer, session);
|
||||
}
|
||||
|
||||
if (userDatagram.Payload.Length == 188)
|
||||
{
|
||||
if (!session.Bootstrapped)
|
||||
{
|
||||
lastOutputSucessful = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!session.Valid)
|
||||
{
|
||||
lastOutputSucessful = false;
|
||||
return;
|
||||
}
|
||||
|
||||
subTsHandler.OnSubTsPacket(session,userDatagram.Payload);
|
||||
lastOutputSucessful = true;
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < userDatagram.Payload.Length; i += 188)
|
||||
{
|
||||
if (userDatagram.Payload[i] != 0x47)
|
||||
{
|
||||
if (session.Valid)
|
||||
{
|
||||
logger.WarnFormat("Lost TS sync on stream for: {0} -> {1}", sourceEndpoint, targetEndpoint);
|
||||
}
|
||||
session.Bootstrapped = true;
|
||||
session.Valid = false;
|
||||
lastOutputSucessful = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
session.ValidDatagrams++;
|
||||
if (session.ValidDatagrams >= 10)
|
||||
{
|
||||
session.Bootstrapped = true;
|
||||
session.Valid = true;
|
||||
byte[] packetBuffer = new byte[188];
|
||||
for (int i = 0; i < userDatagram.Payload.Length; i += 188)
|
||||
{
|
||||
Array.Copy(userDatagram.Payload, i, packetBuffer, 0, 188);
|
||||
subTsHandler.OnSubTsPacket(session, packetBuffer);
|
||||
lastOutputSucessful = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
lastOutputSucessful = false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool StopProcessingAfterThis()
|
||||
{
|
||||
return lastOutputSucessful;
|
||||
}
|
||||
}
|
||||
|
||||
public class MpeEjectSession
|
||||
{
|
||||
public IPEndPoint SourceEndpoint { get; }
|
||||
public IPEndPoint DestinationEndpoint { get; }
|
||||
|
||||
public MpeEjectSession(IPEndPoint sourceEndpoint, IPEndPoint destinationEndpoint)
|
||||
{
|
||||
SourceEndpoint = sourceEndpoint;
|
||||
DestinationEndpoint = destinationEndpoint;
|
||||
}
|
||||
public bool Bootstrapped { get; set; }
|
||||
public bool Valid { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return String.Format("{0} -> {1}", SourceEndpoint, DestinationEndpoint).SanitizeFileName();
|
||||
}
|
||||
|
||||
public int ValidDatagrams { get; set; }
|
||||
}
|
||||
@ -81,6 +81,7 @@ using skyscraper8.Abertis;
|
||||
using skyscraper8.Experimentals.NdsSsu;
|
||||
using skyscraper8.Experimentals.OtvSsu;
|
||||
using skyscraper8.GSE;
|
||||
using skyscraper8.Ieee802_1AB;
|
||||
using skyscraper8.InteractionChannel.Model2;
|
||||
using skyscraper8.Skyscraper.Net;
|
||||
using skyscraper8.Skyscraper.Scraper;
|
||||
@ -97,7 +98,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
|
||||
InteractionChannelHandler, SgtEventHandler, IDvbNipEventHandler, UleEventHandler, OtvSsuHandler, NdsSsuHandler, ISubTsHandler, ILldpFrameHandler
|
||||
{
|
||||
public const bool ALLOW_STREAM_TYPE_AUTODETECTION = true;
|
||||
public const bool ALLOW_FFMPEG_FRAMEGRABBER = true;
|
||||
@ -2930,6 +2931,11 @@ namespace skyscraper5.Skyscraper.Scraper
|
||||
return;
|
||||
case 0x3e30:
|
||||
//Something proprietary. No idea.
|
||||
return;
|
||||
case 0x88cc:
|
||||
//Link Layer Discovery Protocol
|
||||
LldpFrame lldpFrame = new LldpFrame(contents);
|
||||
OnLldpFrame(lldpFrame);
|
||||
return;
|
||||
default:
|
||||
throw new NotImplementedException(String.Format("EtherType: 0x{0:X4}", etherType));
|
||||
@ -3076,6 +3082,17 @@ namespace skyscraper5.Skyscraper.Scraper
|
||||
//TODO: Implement this.
|
||||
}
|
||||
|
||||
|
||||
|
||||
private HashSet<LldpFrame> lldpFrames;
|
||||
public void OnLldpFrame(LldpFrame frame)
|
||||
{
|
||||
if (lldpFrames == null)
|
||||
lldpFrames = new HashSet<LldpFrame>();
|
||||
|
||||
if (lldpFrames.Add(frame))
|
||||
{
|
||||
LogEvent(SkyscraperContextEvent.EthernetLinkLayerDiscovery, frame.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -88,6 +88,7 @@
|
||||
NdsSsuProgress,
|
||||
TerminalBurstTimePlan2,
|
||||
FrameComposition2,
|
||||
BroadcastConfiguration
|
||||
BroadcastConfiguration,
|
||||
EthernetLinkLayerDiscovery
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user