145 lines
4.2 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|