Added an option to receive ATSC 3.0 STLTP.
All checks were successful
🚀 Pack skyscraper8 / make-zip (push) Successful in 48s
All checks were successful
🚀 Pack skyscraper8 / make-zip (push) Successful in 48s
This commit is contained in:
parent
f7aa6ecf67
commit
d33a0003ef
140
skyscraper8/Atsc/A324/StltpReceiver.cs
Normal file
140
skyscraper8/Atsc/A324/StltpReceiver.cs
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
using skyscraper5.Ietf.Rfc768;
|
||||||
|
using skyscraper5.Ietf.Rfc971;
|
||||||
|
using skyscraper5.Skyscraper.IO;
|
||||||
|
using skyscraper5.Skyscraper.Plugins;
|
||||||
|
using skyscraper5.Skyscraper.Scraper;
|
||||||
|
using skyscraper8.Ietf.Rfc3550_RTP;
|
||||||
|
using skyscraper8.Skyscraper.Net;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace skyscraper8.Atsc.A324
|
||||||
|
{
|
||||||
|
[SkyscraperPlugin]
|
||||||
|
internal class StltpReceiver : ISkyscraperMpePlugin
|
||||||
|
{
|
||||||
|
public bool CanHandlePacket(InternetHeader internetHeader, byte[] ipv4Packet)
|
||||||
|
{
|
||||||
|
if (internetHeader.Protocol != 0x11)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!internetHeader.IsDestinationMulticast)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (ipv4Packet[8] != 0x80)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if ((ipv4Packet[9] & 0x7f) != 97)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ConnectToStorage(object[] connector)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool processed;
|
||||||
|
private ushort expectedRtpSequenceNumber;
|
||||||
|
private int currentIpPacketBufferOffset;
|
||||||
|
private byte[] currentIpPacketBuffer;
|
||||||
|
private int bytesNeeded;
|
||||||
|
private int stateMachineState;
|
||||||
|
public void HandlePacket(InternetHeader internetHeader, byte[] ipv4Packet)
|
||||||
|
{
|
||||||
|
processed = false;
|
||||||
|
UserDatagram udpPacket = new UserDatagram(ipv4Packet);
|
||||||
|
RtpPacket rtpPacket = new RtpPacket(udpPacket.Payload);
|
||||||
|
if (rtpPacket.RtpPayloadType != MediaFormatEnum.DynamicRtpType97)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (expectedRtpSequenceNumber != rtpPacket.RtpSequenceNumber)
|
||||||
|
{
|
||||||
|
//sync loss
|
||||||
|
currentIpPacketBufferOffset = 0;
|
||||||
|
if (currentIpPacketBuffer != null)
|
||||||
|
Array.Clear(currentIpPacketBuffer);
|
||||||
|
bytesNeeded = 0;
|
||||||
|
stateMachineState = 0;
|
||||||
|
}
|
||||||
|
expectedRtpSequenceNumber = rtpPacket.RtpSequenceNumber;
|
||||||
|
expectedRtpSequenceNumber++;
|
||||||
|
|
||||||
|
MemoryStream ms = new MemoryStream(rtpPacket.RtpPayload);
|
||||||
|
while (ms.GetAvailableBytes() > 0)
|
||||||
|
{
|
||||||
|
switch (stateMachineState)
|
||||||
|
{
|
||||||
|
case 0: //Initial state, nothing processed yet and not yet synced either.
|
||||||
|
if (rtpPacket.RtpMarker)
|
||||||
|
{
|
||||||
|
uint packetOffset = rtpPacket.RtpSynchronizationSourceIdentifier & 0x0000ffff;
|
||||||
|
ms.Position = packetOffset;
|
||||||
|
stateMachineState = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//The packet can not be used for sync, so we can drop it.
|
||||||
|
processed = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case 1: //Prepare begin reading of the first four bytes of next IP packet.
|
||||||
|
if (currentIpPacketBuffer == null)
|
||||||
|
currentIpPacketBuffer = new byte[ushort.MaxValue];
|
||||||
|
Array.Clear(currentIpPacketBuffer);
|
||||||
|
bytesNeeded = 4;
|
||||||
|
currentIpPacketBufferOffset = 0;
|
||||||
|
stateMachineState = 2;
|
||||||
|
break;
|
||||||
|
case 2: //Read the first four byte of the next IP packet.
|
||||||
|
int readAmount = ms.Read(currentIpPacketBuffer, currentIpPacketBufferOffset, bytesNeeded);
|
||||||
|
bytesNeeded -= readAmount;
|
||||||
|
currentIpPacketBufferOffset += readAmount;
|
||||||
|
if (bytesNeeded == 0)
|
||||||
|
{
|
||||||
|
//Initial four bytes completed, now we know how long the packet is.
|
||||||
|
bytesNeeded = currentIpPacketBuffer[2] << 8 | currentIpPacketBuffer[3];
|
||||||
|
bytesNeeded -= 4;
|
||||||
|
stateMachineState = 3;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 3: //Read the IP packet until it's complete
|
||||||
|
int readAmount2 = ms.Read(currentIpPacketBuffer, currentIpPacketBufferOffset, bytesNeeded);
|
||||||
|
bytesNeeded -= readAmount2;
|
||||||
|
currentIpPacketBufferOffset += readAmount2;
|
||||||
|
if (bytesNeeded == 0)
|
||||||
|
{
|
||||||
|
//Packet complete!
|
||||||
|
stateMachineState = 4;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 4: //Deliver the packet
|
||||||
|
currentIpPacketBuffer[20] = 0x69;
|
||||||
|
currentIpPacketBuffer[21] = 0x69;
|
||||||
|
context.OnIpDatagram(0x1fff, currentIpPacketBuffer.AsSpan().Slice(0, currentIpPacketBufferOffset).ToArray());
|
||||||
|
stateMachineState = 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new NotImplementedException(string.Format("Unknown state in STLTP Receiver finite automata: {0}", stateMachineState));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
processed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private SkyscraperContext context;
|
||||||
|
public void SetContext(DateTime? currentTime, object skyscraperContext)
|
||||||
|
{
|
||||||
|
this.context = skyscraperContext as SkyscraperContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool StopProcessingAfterThis()
|
||||||
|
{
|
||||||
|
return processed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
32
skyscraper8/Ietf/Rfc3550_RTP/MediaFormatEnum.cs
Normal file
32
skyscraper8/Ietf/Rfc3550_RTP/MediaFormatEnum.cs
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
namespace skyscraper8.Ietf.Rfc3550_RTP
|
||||||
|
{
|
||||||
|
public enum MediaFormatEnum
|
||||||
|
{
|
||||||
|
PCMU = 0,
|
||||||
|
GSM = 3,
|
||||||
|
G723 = 4,
|
||||||
|
DVI4_32kbit = 5,
|
||||||
|
DVI4_64kbit = 6,
|
||||||
|
LPC = 7,
|
||||||
|
PCMA = 8,
|
||||||
|
G722 = 9,
|
||||||
|
L16Stereo = 10,
|
||||||
|
L16Mono = 11,
|
||||||
|
QCELP = 12,
|
||||||
|
ComfortNoise = 13,
|
||||||
|
MPA = 14,
|
||||||
|
G728 = 15,
|
||||||
|
DVI4_44kbit = 16,
|
||||||
|
DVI4_88kbit = 17,
|
||||||
|
G729 = 18,
|
||||||
|
CellB = 25,
|
||||||
|
Jpeg = 26,
|
||||||
|
NV = 28,
|
||||||
|
H261 = 31,
|
||||||
|
MPV = 32,
|
||||||
|
M2T = 33,
|
||||||
|
H263 = 34,
|
||||||
|
DynamicRtpType97 = 97
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
64
skyscraper8/Ietf/Rfc3550_RTP/RtpPacket.cs
Normal file
64
skyscraper8/Ietf/Rfc3550_RTP/RtpPacket.cs
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
using skyscraper5.Skyscraper.IO;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace skyscraper8.Ietf.Rfc3550_RTP
|
||||||
|
{
|
||||||
|
internal class RtpPacket
|
||||||
|
{
|
||||||
|
public RtpPacket(byte[] payload)
|
||||||
|
{
|
||||||
|
MemoryStream ms = new MemoryStream(payload, false);
|
||||||
|
byte byte1 = ms.ReadUInt8();
|
||||||
|
|
||||||
|
this.RtpVersion = new Version((byte1 & 0xc0) >> 6, 0);
|
||||||
|
this.RtpPadding = (byte1 & 0x20) != 0;
|
||||||
|
bool rtpExtension = (byte1 & 0x10) != 0;
|
||||||
|
int csrcCount = (byte1 & 0x0f);
|
||||||
|
|
||||||
|
byte byte2 = ms.ReadUInt8();
|
||||||
|
this.RtpMarker = (byte2 & 0x80) != 0;
|
||||||
|
this.RtpPayloadType = (MediaFormatEnum)(byte2 & 0x7f);
|
||||||
|
|
||||||
|
this.RtpSequenceNumber = ms.ReadUInt16BE();
|
||||||
|
|
||||||
|
this.RtpTimestamp = ms.ReadUInt32BE();
|
||||||
|
|
||||||
|
this.RtpSynchronizationSourceIdentifier = ms.ReadUInt32BE();
|
||||||
|
|
||||||
|
if (csrcCount != 0)
|
||||||
|
{
|
||||||
|
CsrcIdentifiers = new uint[csrcCount];
|
||||||
|
for (int i = 0; i < csrcCount; i++)
|
||||||
|
{
|
||||||
|
CsrcIdentifiers[i] = ms.ReadUInt32BE();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rtpExtension)
|
||||||
|
{
|
||||||
|
this.ExtensionHeaderId = ms.ReadUInt16BE();
|
||||||
|
ushort extensionHeaderLength = ms.ReadUInt16BE();
|
||||||
|
extensionHeaderLength *= 4;
|
||||||
|
this.ExtensionHeaderData = ms.ReadBytes(extensionHeaderLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.RtpPayload = ms.ReadBytes(ms.GetAvailableBytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Version RtpVersion { get; }
|
||||||
|
public bool RtpPadding { get; }
|
||||||
|
public bool RtpMarker { get; }
|
||||||
|
public MediaFormatEnum RtpPayloadType { get; }
|
||||||
|
public ushort RtpSequenceNumber { get; }
|
||||||
|
public uint RtpTimestamp { get; }
|
||||||
|
public uint RtpSynchronizationSourceIdentifier { get; }
|
||||||
|
public uint[] CsrcIdentifiers { get; private set; }
|
||||||
|
public ushort? ExtensionHeaderId { get; }
|
||||||
|
public byte[] ExtensionHeaderData { get; }
|
||||||
|
public byte[] RtpPayload { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -2,7 +2,7 @@
|
|||||||
"profiles": {
|
"profiles": {
|
||||||
"skyscraper8": {
|
"skyscraper8": {
|
||||||
"commandName": "Project",
|
"commandName": "Project",
|
||||||
"commandLineArgs": "salvage strat1 \"C:\\Users\\Sascha Schiemann\\Downloads\\65.0W_11304.256_V_30000_(2026-05-17 09.44.02)_dump.ts\"",
|
"commandLineArgs": "salvage strat1 \"Z:\\Persönliches\\Satellitescommunity\\Skyscraper Test Fixture\\65.0W_11304.256_V_30000_(2026-05-17 09.44.02)_dump.ts\"",
|
||||||
"remoteDebugEnabled": false
|
"remoteDebugEnabled": false
|
||||||
},
|
},
|
||||||
"Container (Dockerfile)": {
|
"Container (Dockerfile)": {
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using skyscraper8.Ietf.Rfc3550_RTP;
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
@ -118,32 +119,4 @@ namespace skyscraper8.SatIp
|
|||||||
Video,
|
Video,
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum MediaFormatEnum
|
|
||||||
{
|
|
||||||
PCMU = 0,
|
|
||||||
GSM = 3,
|
|
||||||
G723 = 4,
|
|
||||||
DVI4_32kbit = 5,
|
|
||||||
DVI4_64kbit = 6,
|
|
||||||
LPC = 7,
|
|
||||||
PCMA = 8,
|
|
||||||
G722 = 9,
|
|
||||||
L16Stereo = 10,
|
|
||||||
L16Mono = 11,
|
|
||||||
QCELP = 12,
|
|
||||||
ComfortNoise = 13,
|
|
||||||
MPA = 14,
|
|
||||||
G728 = 15,
|
|
||||||
DVI4_44kbit = 16,
|
|
||||||
DVI4_88kbit = 17,
|
|
||||||
G729 = 18,
|
|
||||||
CellB = 25,
|
|
||||||
Jpeg = 26,
|
|
||||||
NV = 28,
|
|
||||||
H261 = 31,
|
|
||||||
MPV = 32,
|
|
||||||
M2T = 33,
|
|
||||||
H263 = 34
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,7 +27,7 @@ namespace skyscraper8.Skyscraper.Net.Pcap
|
|||||||
{
|
{
|
||||||
if (pcapWritersIp == null)
|
if (pcapWritersIp == null)
|
||||||
{
|
{
|
||||||
pcapWritersIp = new PcapWriter[0x1fff];
|
pcapWritersIp = new PcapWriter[0x2000];
|
||||||
EnsureOutputdirExists();
|
EnsureOutputdirExists();
|
||||||
}
|
}
|
||||||
if (pcapWritersIp[pid] == null)
|
if (pcapWritersIp[pid] == null)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user