Began working on Threemedia OTT.
All checks were successful
🚀 Pack skyscraper8 / make-zip (push) Successful in 2m29s
All checks were successful
🚀 Pack skyscraper8 / make-zip (push) Successful in 2m29s
This commit is contained in:
parent
57c45c215e
commit
6e5bf2d2db
@ -134,7 +134,7 @@ namespace skyscraper5.Dvb.DataBroadcasting
|
|||||||
{
|
{
|
||||||
if (!announcedLlcTraffic)
|
if (!announcedLlcTraffic)
|
||||||
{
|
{
|
||||||
logger.InfoFormat("Detected LLC/SNAP traffic on PID {0:x4}", sourcePid);
|
//logger.InfoFormat("Detected LLC/SNAP traffic on PID {0:x4}", sourcePid);
|
||||||
announcedLlcTraffic = true;
|
announcedLlcTraffic = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
51
skyscraper8/Ietf/FLUTE/CdsCompletionPollRequestExtension.cs
Normal file
51
skyscraper8/Ietf/FLUTE/CdsCompletionPollRequestExtension.cs
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using skyscraper5.Skyscraper;
|
||||||
|
using skyscraper5.Skyscraper.IO;
|
||||||
|
|
||||||
|
namespace skyscraper8.Ietf.FLUTE
|
||||||
|
{
|
||||||
|
internal class CdsCompletionPollRequestExtension : Validatable
|
||||||
|
{
|
||||||
|
public CdsCompletionPollRequestExtension(byte[] extensionBuffer)
|
||||||
|
{
|
||||||
|
MemoryStream ms = new MemoryStream(extensionBuffer, false);
|
||||||
|
if (ms.GetAvailableBytes() < 1)
|
||||||
|
{
|
||||||
|
Valid = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte byteA = ms.ReadUInt8();
|
||||||
|
this.Version = (byteA & 0xf0) >> 4;
|
||||||
|
|
||||||
|
if (ms.GetAvailableBytes() < 1)
|
||||||
|
{
|
||||||
|
Valid = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte byteB = ms.ReadUInt8();
|
||||||
|
PollSequence = (byteA & 0x0f) << 8;
|
||||||
|
PollSequence += byteB;
|
||||||
|
|
||||||
|
if (ms.GetAvailableBytes() < 4)
|
||||||
|
{
|
||||||
|
Valid = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PollMask = ms.ReadUInt32BE();
|
||||||
|
Valid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public uint PollMask { get; set; }
|
||||||
|
|
||||||
|
public int PollSequence { get; set; }
|
||||||
|
|
||||||
|
public int Version { get; private set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -9,13 +9,28 @@ namespace skyscraper8.Ietf.FLUTE
|
|||||||
{
|
{
|
||||||
internal class FecHeader
|
internal class FecHeader
|
||||||
{
|
{
|
||||||
public FecHeader(MemoryStream ms)
|
public FecHeader(MemoryStream ms, byte codepoint)
|
||||||
{
|
{
|
||||||
|
switch (codepoint)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
SourceBlockNumber = ms.ReadUInt16BE();
|
SourceBlockNumber = ms.ReadUInt16BE();
|
||||||
EncodingSymbolId = ms.ReadUInt16BE();
|
EncodingSymbolId = ms.ReadUInt16BE();
|
||||||
|
break;
|
||||||
|
case 129:
|
||||||
|
SourceBlockNumber = ms.ReadUInt32BE();
|
||||||
|
SourceBlockLength = ms.ReadUInt16BE();
|
||||||
|
EncodingSymbolId = ms.ReadUInt16BE();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new NotImplementedException(String.Format("Unknown ALC CodePoint value {0}", codepoint));
|
||||||
}
|
}
|
||||||
|
|
||||||
public ushort SourceBlockNumber { get; }
|
}
|
||||||
|
|
||||||
|
public ushort? SourceBlockLength { get; set; }
|
||||||
|
|
||||||
|
public uint SourceBlockNumber { get; }
|
||||||
public ushort EncodingSymbolId { get; }
|
public ushort EncodingSymbolId { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -72,13 +72,13 @@ namespace skyscraper8.Ietf.FLUTE
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (blocks == null)
|
if (blocks == null)
|
||||||
blocks = new Dictionary<Tuple<ushort, ushort>, FluteBlock>();
|
blocks = new Dictionary<Tuple<uint, ushort>, FluteBlock>();
|
||||||
|
|
||||||
if (lctFrame.Atsc3Compliant)
|
if (lctFrame.Atsc3Compliant)
|
||||||
{
|
{
|
||||||
if (blocks.Count == 0)
|
if (blocks.Count == 0)
|
||||||
{
|
{
|
||||||
Tuple<ushort, ushort> fakeKey = new Tuple<ushort, ushort>(12345, 6789);
|
Tuple<uint, ushort> fakeKey = new Tuple<uint, ushort>(12345, 6789);
|
||||||
FluteBlock fakeBlock = new FluteBlock(0, 0, new byte[transferLength]);
|
FluteBlock fakeBlock = new FluteBlock(0, 0, new byte[transferLength]);
|
||||||
blocks.Add(fakeKey, fakeBlock);
|
blocks.Add(fakeKey, fakeBlock);
|
||||||
}
|
}
|
||||||
@ -92,7 +92,7 @@ namespace skyscraper8.Ietf.FLUTE
|
|||||||
if (lctFrame.FecHeader == null)
|
if (lctFrame.FecHeader == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ushort sbn = lctFrame.FecHeader.SourceBlockNumber;
|
uint sbn = lctFrame.FecHeader.SourceBlockNumber;
|
||||||
ushort esi = lctFrame.FecHeader.EncodingSymbolId;
|
ushort esi = lctFrame.FecHeader.EncodingSymbolId;
|
||||||
|
|
||||||
//FluteBlock? fluteBlock = blocks.Find(x =>
|
//FluteBlock? fluteBlock = blocks.Find(x =>
|
||||||
@ -122,7 +122,7 @@ namespace skyscraper8.Ietf.FLUTE
|
|||||||
blocksDeduplicated++;
|
blocksDeduplicated++;
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
Tuple<ushort, ushort> coordinate = new Tuple<ushort, ushort>(sbn, esi);
|
Tuple<uint, ushort> coordinate = new Tuple<uint, ushort>(sbn, esi);
|
||||||
if (blocks.ContainsKey(coordinate))
|
if (blocks.ContainsKey(coordinate))
|
||||||
{
|
{
|
||||||
blocksDeduplicated++;
|
blocksDeduplicated++;
|
||||||
@ -222,19 +222,19 @@ namespace skyscraper8.Ietf.FLUTE
|
|||||||
}
|
}
|
||||||
|
|
||||||
private long transferLength;
|
private long transferLength;
|
||||||
private Dictionary<Tuple<ushort,ushort>,FluteBlock> blocks;
|
private Dictionary<Tuple<uint,ushort>,FluteBlock> blocks;
|
||||||
private uint blocksDeduplicated;
|
private uint blocksDeduplicated;
|
||||||
|
|
||||||
public class FluteBlock
|
public class FluteBlock
|
||||||
{
|
{
|
||||||
public FluteBlock(ushort sourceBlockNumer, ushort encodingSymbolId, byte[] payload)
|
public FluteBlock(uint sourceBlockNumer, ushort encodingSymbolId, byte[] payload)
|
||||||
{
|
{
|
||||||
SourceBlockNumer = sourceBlockNumer;
|
SourceBlockNumer = sourceBlockNumer;
|
||||||
EncodingSymbolId = encodingSymbolId;
|
EncodingSymbolId = encodingSymbolId;
|
||||||
Payload = payload;
|
Payload = payload;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ushort SourceBlockNumer;
|
public uint SourceBlockNumer;
|
||||||
public ushort EncodingSymbolId;
|
public ushort EncodingSymbolId;
|
||||||
public byte[] Payload;
|
public byte[] Payload;
|
||||||
|
|
||||||
|
|||||||
@ -53,7 +53,7 @@ namespace skyscraper8.Ietf.FLUTE
|
|||||||
//DVB-NIP sample: "Z:\Persönliches\Satellitescommunity\Skyscraper Test Fixture\astra1_11141h_gse_nip.ts"
|
//DVB-NIP sample: "Z:\Persönliches\Satellitescommunity\Skyscraper Test Fixture\astra1_11141h_gse_nip.ts"
|
||||||
if (!isAtsc3)
|
if (!isAtsc3)
|
||||||
{
|
{
|
||||||
this.FecHeader = new FecHeader(ms);
|
this.FecHeader = new FecHeader(ms, this.Codepoint);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@ -83,6 +83,9 @@ namespace skyscraper8.Ietf.FLUTE
|
|||||||
case 64:
|
case 64:
|
||||||
this.FecObjectTransmissionInformation = new FecObjectTransmissionInformation(extensionBuffer);
|
this.FecObjectTransmissionInformation = new FecObjectTransmissionInformation(extensionBuffer);
|
||||||
break;
|
break;
|
||||||
|
case 65:
|
||||||
|
this.CdsCompletionPollRequest = new CdsCompletionPollRequestExtension(extensionBuffer);
|
||||||
|
break;
|
||||||
case 68:
|
case 68:
|
||||||
this.NipActualCarrierInformation = new NipActualCarrierInformation(extensionBuffer);
|
this.NipActualCarrierInformation = new NipActualCarrierInformation(extensionBuffer);
|
||||||
break;
|
break;
|
||||||
@ -98,10 +101,14 @@ namespace skyscraper8.Ietf.FLUTE
|
|||||||
Valid = true;
|
Valid = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CdsCompletionPollRequestExtension CdsCompletionPollRequest { get; set; }
|
||||||
|
|
||||||
private ulong ReadField(Stream stream, int bits)
|
private ulong ReadField(Stream stream, int bits)
|
||||||
{
|
{
|
||||||
switch(bits)
|
switch(bits)
|
||||||
{
|
{
|
||||||
|
case 0:
|
||||||
|
return 0;
|
||||||
case 16:
|
case 16:
|
||||||
return stream.ReadUInt16BE();
|
return stream.ReadUInt16BE();
|
||||||
case 32:
|
case 32:
|
||||||
|
|||||||
56
skyscraper8/ThreemediaOtt/ThreemediaContentFragment.cs
Normal file
56
skyscraper8/ThreemediaOtt/ThreemediaContentFragment.cs
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using skyscraper8.Ietf.FLUTE;
|
||||||
|
|
||||||
|
namespace skyscraper8.ThreemediaOtt
|
||||||
|
{
|
||||||
|
internal class ThreemediaContentFragment : IDisposable
|
||||||
|
{
|
||||||
|
public void IngestPacket(LctFrame lctFrame)
|
||||||
|
{
|
||||||
|
int totalBlocks = lctFrame.FecHeader.SourceBlockLength.Value;
|
||||||
|
if (blocks == null)
|
||||||
|
blocks = new byte[totalBlocks][];
|
||||||
|
|
||||||
|
int targetBlock = lctFrame.FecHeader.EncodingSymbolId;
|
||||||
|
if (targetBlock >= blocks.Length)
|
||||||
|
return;
|
||||||
|
if (blocks[targetBlock] != null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
blocks[targetBlock] = lctFrame.Payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[][] blocks;
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (blocks != null)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < blocks.Length; i++)
|
||||||
|
{
|
||||||
|
blocks[i] = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
blocks = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsComplete()
|
||||||
|
{
|
||||||
|
if (blocks == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (int i = 0; i < blocks.Length; i++)
|
||||||
|
{
|
||||||
|
if (blocks[i] == null)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
118
skyscraper8/ThreemediaOtt/ThreemediaOttHandler.cs
Normal file
118
skyscraper8/ThreemediaOtt/ThreemediaOttHandler.cs
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using log4net;
|
||||||
|
using skyscraper5.Ietf.Rfc768;
|
||||||
|
using skyscraper5.Ietf.Rfc971;
|
||||||
|
using skyscraper5.Skyscraper.Plugins;
|
||||||
|
using skyscraper5.Skyscraper.Scraper;
|
||||||
|
using skyscraper8.Ietf.FLUTE;
|
||||||
|
|
||||||
|
namespace skyscraper8.ThreemediaOtt
|
||||||
|
{
|
||||||
|
[SkyscraperPlugin]
|
||||||
|
internal class ThreemediaOttHandler : ISkyscraperMpePlugin
|
||||||
|
{
|
||||||
|
private static readonly IPAddress THREEMDIA_DESTINATION_IP = IPAddress.Parse("224.1.2.1");
|
||||||
|
private static readonly ILog logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name);
|
||||||
|
|
||||||
|
public void ConnectToStorage(object[] connector)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
private SkyscraperContext context;
|
||||||
|
public void SetContext(DateTime? currentTime, object skyscraperContext)
|
||||||
|
{
|
||||||
|
if (context != null)
|
||||||
|
{
|
||||||
|
context = skyscraperContext as SkyscraperContext;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool CanHandlePacket(InternetHeader internetHeader, byte[] ipv4Packet)
|
||||||
|
{
|
||||||
|
if (!internetHeader.IsDestinationMulticast)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!internetHeader.DestinationAddress.Equals(THREEMDIA_DESTINATION_IP))
|
||||||
|
return false;
|
||||||
|
if (internetHeader.Protocol != 17)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (ipv4Packet[2] != 0x04)
|
||||||
|
return false;
|
||||||
|
if (ipv4Packet[3] != 0x61)
|
||||||
|
return false;
|
||||||
|
if (ipv4Packet[8] != 0x10)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Dictionary<ushort, ThreemediaSession> sessions;
|
||||||
|
public void HandlePacket(InternetHeader internetHeader, byte[] ipv4Packet)
|
||||||
|
{
|
||||||
|
UserDatagram udpPacket = new UserDatagram(ipv4Packet);
|
||||||
|
LctFrame lctFrame = new LctFrame(udpPacket.Payload, false);
|
||||||
|
if (lctFrame.CloseSessionFlag)
|
||||||
|
{
|
||||||
|
AssembleSegment(udpPacket.DestinationPort);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
IngestPacket(udpPacket.DestinationPort, lctFrame);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void IngestPacket(ushort udpPacketDestinationPort, LctFrame lctFrame)
|
||||||
|
{
|
||||||
|
if (sessions == null)
|
||||||
|
sessions = new Dictionary<ushort, ThreemediaSession>();
|
||||||
|
|
||||||
|
ThreemediaSession selectedSession = null;
|
||||||
|
if (sessions.ContainsKey(udpPacketDestinationPort))
|
||||||
|
{
|
||||||
|
selectedSession = sessions[udpPacketDestinationPort];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
selectedSession = new ThreemediaSession();
|
||||||
|
sessions[udpPacketDestinationPort] = selectedSession;
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedSession.IngestPacket(lctFrame);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AssembleSegment(ushort destinationPort)
|
||||||
|
{
|
||||||
|
if (sessions == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!sessions.ContainsKey(destinationPort))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ThreemediaSession selectedSession = sessions[destinationPort];
|
||||||
|
if (!selectedSession.IsComplete())
|
||||||
|
{
|
||||||
|
logger.WarnFormat(String.Format("The session on Port {0} is incomplete and can not be recovered.", destinationPort));
|
||||||
|
selectedSession.Dispose();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool StopProcessingAfterThis()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
59
skyscraper8/ThreemediaOtt/ThreemediaSession.cs
Normal file
59
skyscraper8/ThreemediaOtt/ThreemediaSession.cs
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using skyscraper8.Ietf.FLUTE;
|
||||||
|
|
||||||
|
namespace skyscraper8.ThreemediaOtt
|
||||||
|
{
|
||||||
|
internal class ThreemediaSession : IDisposable
|
||||||
|
{
|
||||||
|
public void IngestPacket(LctFrame lctFrame)
|
||||||
|
{
|
||||||
|
int numFragments = lctFrame.LctHeader.CdsCompletionPollRequest.PollSequence;
|
||||||
|
if (fragments == null)
|
||||||
|
fragments = new ThreemediaContentFragment[numFragments];
|
||||||
|
|
||||||
|
int currentFragment = (int)lctFrame.LctHeader.TransportObjectIdentifier;
|
||||||
|
currentFragment--;
|
||||||
|
if (fragments[currentFragment] == null)
|
||||||
|
fragments[currentFragment] = new ThreemediaContentFragment();
|
||||||
|
|
||||||
|
fragments[currentFragment].IngestPacket(lctFrame);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ThreemediaContentFragment[] fragments;
|
||||||
|
|
||||||
|
public bool IsComplete()
|
||||||
|
{
|
||||||
|
if (fragments == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (int i = 0; i < fragments.Length; i++)
|
||||||
|
{
|
||||||
|
if (fragments[i] == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!fragments[i].IsComplete())
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (fragments != null)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < fragments.Length; i++)
|
||||||
|
{
|
||||||
|
fragments[i].Dispose();
|
||||||
|
fragments[i] = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fragments = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user