skyscraper8/skyscraper8/Atsc/A324/StltpReceiver.cs
feyris-tan 1b01ed8553
All checks were successful
🚀 Pack skyscraper8 / make-zip (push) Successful in 52s
Discriminate between multiple virtual networks.
2026-06-14 21:29:10 +02:00

145 lines
4.2 KiB
C#

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]
[PluginPriority(1)]
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
if (networkIdentifier == null)
{
networkIdentifier = new StltpNetworkIdentifier();
}
context.OnIpDatagram(networkIdentifier, 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 StltpNetworkIdentifier networkIdentifier;
private SkyscraperContext context;
public void SetContext(DateTime? currentTime, object skyscraperContext)
{
this.context = skyscraperContext as SkyscraperContext;
}
public bool StopProcessingAfterThis()
{
return processed;
}
}
}