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": {
|
||||
"skyscraper8": {
|
||||
"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
|
||||
},
|
||||
"Container (Dockerfile)": {
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using skyscraper8.Ietf.Rfc3550_RTP;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
@ -118,32 +119,4 @@ namespace skyscraper8.SatIp
|
||||
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)
|
||||
{
|
||||
pcapWritersIp = new PcapWriter[0x1fff];
|
||||
pcapWritersIp = new PcapWriter[0x2000];
|
||||
EnsureOutputdirExists();
|
||||
}
|
||||
if (pcapWritersIp[pid] == null)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user