3245 lines
133 KiB
C#
3245 lines
133 KiB
C#
using log4net;
|
|
using moe.yo3explorer.skyscraper8.DVBI.Model;
|
|
using skyscraper5.Abertis;
|
|
using skyscraper5.Docsis;
|
|
using skyscraper5.Docsis.MacManagement;
|
|
using skyscraper5.DsmCc.Descriptors;
|
|
using skyscraper5.Dvb.DataBroadcasting;
|
|
using skyscraper5.Dvb.DataBroadcasting.Biop;
|
|
using skyscraper5.Dvb.DataBroadcasting.Dsm.Event;
|
|
using skyscraper5.Dvb.DataBroadcasting.Dsm.Stream;
|
|
using skyscraper5.Dvb.DataBroadcasting.IntModel;
|
|
using skyscraper5.Dvb.DataBroadcasting.SkyscraperVfs;
|
|
using skyscraper5.Dvb.Descriptors;
|
|
using skyscraper5.Dvb.Psi;
|
|
using skyscraper5.Dvb.Psi.Model;
|
|
using skyscraper5.Dvb.Subtitling;
|
|
using skyscraper5.Dvb.Subtitling.Model;
|
|
using skyscraper5.Dvb.SystemSoftwareUpdate;
|
|
using skyscraper5.Dvb.SystemSoftwareUpdate.Model;
|
|
using skyscraper5.Dvb.TvAnytime;
|
|
using skyscraper5.Ietf.Rfc768;
|
|
using skyscraper5.Ietf.Rfc971;
|
|
using skyscraper5.Mheg5;
|
|
using skyscraper5.Mhp.Descriptors;
|
|
using skyscraper5.Mhp.Descriptors.InteractionTransportSelectors;
|
|
using skyscraper5.Mhp.Si;
|
|
using skyscraper5.Mhp.Si.Model;
|
|
using skyscraper5.Mpeg2;
|
|
using skyscraper5.Mpeg2.Descriptors;
|
|
using skyscraper5.Mpeg2.Psi;
|
|
using skyscraper5.Mpeg2.Psi.Model;
|
|
using skyscraper5.Rds;
|
|
using skyscraper5.Rds.Messages;
|
|
using skyscraper5.Scte35;
|
|
using skyscraper5.Skyscraper.IO;
|
|
using skyscraper5.Skyscraper.Net;
|
|
using skyscraper5.Skyscraper.Net.Pcap;
|
|
using skyscraper5.Skyscraper.Plugins;
|
|
using skyscraper5.Skyscraper.Scraper.FrameGrabber;
|
|
using skyscraper5.Skyscraper.Scraper.Storage;
|
|
using skyscraper5.Skyscraper.Scraper.Storage.InMemory;
|
|
using skyscraper5.Skyscraper.Scraper.Storage.Utilities;
|
|
using skyscraper5.Skyscraper.Scraper.StreamAutodetection;
|
|
using skyscraper5.Skyscraper.Scraper.Utils;
|
|
using skyscraper5.src.Id3;
|
|
using skyscraper5.src.InteractionChannel;
|
|
using skyscraper5.src.InteractionChannel.Model;
|
|
using skyscraper5.src.InteractionChannel.Model.Descriptors;
|
|
using skyscraper5.src.Mpeg2;
|
|
using skyscraper5.src.Mpeg2.PacketFilter;
|
|
using skyscraper5.src.Privates;
|
|
using skyscraper5.src.Skyscraper;
|
|
using skyscraper5.src.Skyscraper.Scraper.Dns;
|
|
using skyscraper5.src.Teletext;
|
|
using skyscraper5.T2MI;
|
|
using skyscraper5.T2MI.Packets;
|
|
using skyscraper5.T2MI.Packets.AdressingFunctions;
|
|
using skyscraper5.Teletext;
|
|
using skyscraper5.Teletext.Vps;
|
|
using skyscraper5.Teletext.Wss;
|
|
using skyscraper8.DvbI;
|
|
using skyscraper8.DvbNip;
|
|
using skyscraper8.Ietf.FLUTE;
|
|
using skyscraper8.Ietf.Rfc4236_ULE;
|
|
using skyscraper8.Ses;
|
|
using skyscraper8.Skyscraper.Scraper.Storage;
|
|
using skyscraper8.yo3explorer;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
using System.ComponentModel;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Net;
|
|
using System.Net.NetworkInformation;
|
|
using System.Reflection;
|
|
using System.Security.Policy;
|
|
using System.Text;
|
|
using skyscraper5.src.InteractionChannel.Model2;
|
|
using skyscraper8.Abertis;
|
|
using skyscraper8.Experimentals.NdsSsu;
|
|
using skyscraper8.Experimentals.OtvSsu;
|
|
using skyscraper8.GS;
|
|
using skyscraper8.GSE;
|
|
using skyscraper8.GSE.GSE;
|
|
using skyscraper8.Ieee802_1AB;
|
|
using skyscraper8.InteractionChannel.Model;
|
|
using skyscraper8.InteractionChannel.Model2;
|
|
using skyscraper8.InteractionChannel.Model2.Descriptors;
|
|
using skyscraper8.Skyscraper.Net;
|
|
using skyscraper8.Skyscraper.Scraper;
|
|
using skyscraper8.T2MI;
|
|
using Tsubasa.IO;
|
|
using Platform = skyscraper5.Dvb.SystemSoftwareUpdate.Model.Platform;
|
|
using RntParser = skyscraper5.Dvb.TvAnytime.RntParser;
|
|
|
|
namespace skyscraper5.Skyscraper.Scraper
|
|
{
|
|
public class SkyscraperContext : IPatEventHandler, IPmtEventHandler, INitEventHandler, ITeletextPageHandler,
|
|
IBatEventHandler, ISdtEventHandler, ICatEventHandler, ITdtEventHandler, ITotEventHandler, ITsdtEventHandler,
|
|
IEitEventHandler, IAitEventHandler, ISubtitleEventHandler,
|
|
UpdateNotificationEventHandler, DataCarouselEventHandler, RdsEventHandler, IScte35EventHandler,
|
|
IAutodetectionEventHandler, IRstEventHandler, IRntEventHandler, IMultiprotocolEncapsulationEventHandler, ObjectCarouselEventHandler, T2MIEventHandler,
|
|
IDisposable, IFrameGrabberEventHandler, IntEventHandler, IRctEventHandler, ISkyscraperContext, IDocsisEventHandler, AbertisDecoderEventHandler, Id3Handler,
|
|
InteractionChannelHandler, SgtEventHandler, IDvbNipEventHandler, UleEventHandler, OtvSsuHandler, NdsSsuHandler, ISubTsHandler, ILldpFrameHandler
|
|
{
|
|
public const bool ALLOW_STREAM_TYPE_AUTODETECTION = true;
|
|
public const bool ALLOW_FFMPEG_FRAMEGRABBER = true;
|
|
|
|
private static readonly ILog logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name);
|
|
|
|
public TsContext DvbContext { get; }
|
|
public DataStorage DataStorage { get; }
|
|
public ObjectStorage ObjectStorage { get; }
|
|
|
|
|
|
public bool SourceIsDisk { get; set; }
|
|
|
|
public PsiDecoder PatDecoder { get; private set; }
|
|
public PsiDecoder CatDecoder { get; private set; }
|
|
public PsiDecoder NitDecoder { get; private set; }
|
|
public PsiDecoder Pid0x11Decoder { get; private set; }
|
|
public PsiDecoder Pid0x12Decoder { get; private set; }
|
|
public IPsiDecoderTransformer PmtDecoderTransformer { get; set; }
|
|
public SkyscraperContext(TsContext dvbContext, DataStorage dataStorage = null, ObjectStorage objectStorage = null, int skipAtStart = 0, int? currentNetworkId = null, int? currentTransportStreamId = null)
|
|
{
|
|
TsDescriptorUnpacker descriptorUnpacker = TsDescriptorUnpacker.GetInstance();
|
|
for (byte i = 0x80; i < 0xfe; i++)
|
|
{
|
|
descriptorUnpacker.SetUserDefined(i, true);
|
|
}
|
|
|
|
descriptorUnpacker.SetUserDefined(0xfe, true);
|
|
|
|
DvbContext = dvbContext;
|
|
|
|
|
|
//PAT
|
|
PatDecoder = new PsiDecoder(0, new PatParser(this));
|
|
dvbContext.RegisterPacketProcessor(0, PatDecoder);
|
|
//PMT autodetect from PAT
|
|
|
|
//CAT
|
|
CatDecoder = new PsiDecoder(1, new CatParser(this));
|
|
dvbContext.RegisterPacketProcessor(1, CatDecoder);
|
|
|
|
//TSDT (experimental)
|
|
dvbContext.RegisterPacketProcessor(2, new PsiDecoder(2, new TsdtParser(this)));
|
|
|
|
//IPMP (experimental)
|
|
dvbContext.RegisterPacketProcessor(3, new PsiDecoder(3, new IpmpParser(this)));
|
|
|
|
//NIT
|
|
NitDecoder = new PsiDecoder(0x10, new NitParser(this));
|
|
dvbContext.RegisterPacketProcessor(0x10, NitDecoder);
|
|
|
|
//SDT / BAT
|
|
Pid0x11Decoder = new PsiDecoder(0x11, new Pid0x11Decoder(this, this));
|
|
dvbContext.RegisterPacketProcessor(0x11, Pid0x11Decoder);
|
|
|
|
//EIT
|
|
//dvbContext.RegisterPacketProcessor(0x12, new PsiDecoder(0x12, new EitParser(this)));
|
|
Pid0x12Decoder = new PsiDecoder(0x12, new Pid0x12Decoder(this));
|
|
dvbContext.RegisterPacketProcessor(0x12, Pid0x12Decoder);
|
|
|
|
//RST
|
|
DvbContext.RegisterPacketProcessor(0x13, new PsiDecoder(0x13, new RstParser(this)));
|
|
|
|
//TDT
|
|
//TOT handled by TDT processor
|
|
dvbContext.RegisterPacketProcessor(0x14, new PsiDecoder(0x14, new TimetableParser(this, this)));
|
|
|
|
//RNT
|
|
dvbContext.RegisterPacketProcessor(0x16, new PsiDecoder(0x16, new RntParser(this)));
|
|
|
|
//DIT
|
|
//PID 0x1E
|
|
//won't need these. We operate on broadcasted stuff, so we shouldn't encounter partial TS
|
|
|
|
//DIT
|
|
//PID 0x1F
|
|
//won't need these. We operate on broadcasted stuff, so we shouldn't encounter partial TS
|
|
|
|
if (dataStorage == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(dataStorage));
|
|
}
|
|
DataStorage = dataStorage;
|
|
|
|
if (objectStorage == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(objectStorage));
|
|
}
|
|
ObjectStorage = objectStorage;
|
|
|
|
CurrentNetworkId = currentNetworkId;
|
|
CurrentTransportStreamId = currentTransportStreamId;
|
|
DsmCcsToLookFor = new Dictionary<ushort, byte>();
|
|
SsusToLookFor = new List<KeyValuePair<ushort, byte>>();
|
|
|
|
this.skipBufferRemain = skipAtStart;
|
|
}
|
|
|
|
public int? CurrentNetworkId { get; private set; }
|
|
public int? CurrentTransportStreamId { get; private set; }
|
|
|
|
public TeiFilter TeiFilter { get; private set; }
|
|
public ScrambleFilter ScrambleFilter { get; private set; }
|
|
public TeiOnOffFilter TeiOnOffFilter { get; private set; }
|
|
public void InitalizeFilterChain(params IPacketFilter[] args)
|
|
{
|
|
if (DvbContext.FilterChain != null)
|
|
throw new InvalidOperationException("The filter chain was already initalized.");
|
|
if (TeiFilter == null)
|
|
TeiFilter = new TeiFilter();
|
|
if (ScrambleFilter == null)
|
|
ScrambleFilter = new ScrambleFilter();
|
|
if (TeiOnOffFilter == null)
|
|
TeiOnOffFilter = new TeiOnOffFilter();
|
|
|
|
List<IPacketFilter> result = new List<IPacketFilter>();
|
|
result.AddRange(args);
|
|
result.Add(TeiFilter);
|
|
result.Add(ScrambleFilter);
|
|
result.Add(TeiOnOffFilter);
|
|
result.Sort(new PluginPrioritySorter());
|
|
DvbContext.FilterChain = result;
|
|
|
|
StringBuilder stringBuilder = new StringBuilder();
|
|
stringBuilder.Append("IN");
|
|
for (int i = 0; i < result.Count; i++)
|
|
{
|
|
stringBuilder.Append(" -> ");
|
|
if (result[i] != null)
|
|
{
|
|
stringBuilder.Append(result[i].GetType().Name);
|
|
}
|
|
else
|
|
{
|
|
stringBuilder.Append("(null)");
|
|
}
|
|
}
|
|
stringBuilder.Append(" -> OUT");
|
|
logger.DebugFormat("Built filter chain: {0}", stringBuilder.ToString());
|
|
}
|
|
|
|
public int GetIpCommunicationParties()
|
|
{
|
|
if (trafficInfos == null)
|
|
return 0;
|
|
else
|
|
return trafficInfos.Count;
|
|
}
|
|
|
|
public void IngestFromStream(Stream stream)
|
|
{
|
|
byte[] buffer = new byte[188];
|
|
while (true)
|
|
{
|
|
if (IsAbortConditionMet())
|
|
break;
|
|
|
|
if (stream.Read(buffer, 0, 188) != 188)
|
|
break;
|
|
|
|
try
|
|
{
|
|
IngestSinglePacket(buffer);
|
|
}
|
|
catch (InvalidTsPacketException)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
stream.Close();
|
|
runningEvents = null;
|
|
}
|
|
|
|
private bool skipBufferStarted;
|
|
private int skipBufferRemain;
|
|
private bool firstPacketDone = false;
|
|
private ulong totalPacketsPushed;
|
|
|
|
public void IngestSinglePacket(byte[] buffer)
|
|
{
|
|
if (buffer.Length != 188)
|
|
throw new ArgumentException(String.Format("{0}.{1} != {2}", nameof(buffer), nameof(buffer.Length),
|
|
188));
|
|
|
|
if (skipBufferRemain > 0)
|
|
{
|
|
if (!skipBufferStarted)
|
|
{
|
|
LogEvent(SkyscraperContextEvent.StartPacketProcessing, String.Format("Skipping {0} packets before begin.", skipBufferRemain));
|
|
skipBufferStarted = true;
|
|
}
|
|
skipBufferRemain--;
|
|
}
|
|
|
|
if (!firstPacketDone)
|
|
{
|
|
LogEvent(SkyscraperContextEvent.StartPacketProcessing);
|
|
if (buffer[0] == 0x47 && buffer[1] == 0x41 && buffer[2] == 0x18 && (buffer[3] & 0x10) != 0 && buffer[4] == 0x00 && buffer[5] == 0x80 && buffer[6] == 0x00)
|
|
{
|
|
DvbContext.RegisterPacketProcessor(0x0118, new OldStreamReaderDetector());
|
|
SpecialTsType = 2;
|
|
}
|
|
if (buffer[0] == 0x47 && buffer[1] == 0x41 && buffer[2] == 0x0e && (buffer[3] & 0x10) != 0 && buffer[4] == 0x00 && buffer[5] == 0x80 && buffer[6] == 0x00)
|
|
{
|
|
if (!DvbContext.IsPidProcessorPresent(0x010e))
|
|
{
|
|
DvbContext.RegisterPacketProcessor(0x010e, new Stid135BbFrameReader(CreateGsContext()));
|
|
UiJunction?.SetGseMode();
|
|
LogEvent(SkyscraperContextEvent.SpecialTsMode, "STiD135 encapsulated GS detected.");
|
|
SpecialTsType = 3;
|
|
}
|
|
}
|
|
firstPacketDone = true;
|
|
}
|
|
|
|
DvbContext.PushPacket(buffer);
|
|
if (totalPacketsPushed++ == 1000)
|
|
{
|
|
CheckSpecialTs();
|
|
}
|
|
}
|
|
|
|
private GsContextDto CreateGsContext()
|
|
{
|
|
//Build the object
|
|
GsContextDto child = new GsContextDto();
|
|
child.IpOutput = this;
|
|
child.TsOutput = this;
|
|
child.Rcs2Output = this;
|
|
|
|
//for futureproofing
|
|
PropertyInfo[] properties = typeof(GsContextDto).GetProperties();
|
|
foreach (PropertyInfo propertyInfo in properties)
|
|
{
|
|
object? value = propertyInfo.GetValue(child);
|
|
if (value == null)
|
|
{
|
|
throw new SkyscraperException(String.Format("While creating the {0}, the {1} was not properly set. This is a bug, tell Fey.", nameof(GsContextDto), propertyInfo.Name));
|
|
}
|
|
}
|
|
|
|
//actually return the object
|
|
return child;
|
|
}
|
|
|
|
private DocsisPacketProcessor docsisPacketProcessor;
|
|
public int SpecialTsType { get; private set; }
|
|
private void CheckSpecialTs()
|
|
{
|
|
ulong[] pidStatistics = DvbContext.GetPidStatistics();
|
|
if (pidStatistics == null)
|
|
{
|
|
totalPacketsPushed = 0;
|
|
return;
|
|
}
|
|
int occupiedPids = pidStatistics.Where(x => x > 0).Count();
|
|
int numTotal = (int)totalPacketsPushed;
|
|
int num1fff = (int)pidStatistics[0x1fff];
|
|
double percentage1fff = (double)num1fff * 100.0 / (double)numTotal;
|
|
if (true)
|
|
{
|
|
if (pidStatistics[0x0118] > 0)
|
|
{
|
|
int num118 = (int)pidStatistics[0x0118];
|
|
double p = (double)num118 * 100.0 / numTotal;
|
|
if (p > 58)
|
|
{
|
|
if (!DvbContext.IsPidProcessorPresent(0x0118))
|
|
{
|
|
DvbContext.RegisterPacketProcessor(0x0118, new OldStreamReaderDetector());
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
if (pidStatistics[0x010e] > 0)
|
|
{
|
|
int num10e = (int)pidStatistics[0x010e];
|
|
double p = (double)num10e * 100.0 / numTotal;
|
|
if (occupiedPids == 1)
|
|
{
|
|
if (p > 20)
|
|
{
|
|
if (!DvbContext.IsPidProcessorPresent(0x010e))
|
|
{
|
|
DvbContext.RegisterPacketProcessor(0x010e, new Stid135BbFrameReader(CreateGsContext()));
|
|
UiJunction?.SetGseMode();
|
|
LogEvent(SkyscraperContextEvent.SpecialTsMode, "STiD135 encapsulated GS detected.");
|
|
SpecialTsType = 3;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (occupiedPids <= 2)
|
|
{
|
|
if (pidStatistics[0x1ffe] > 10 && num1fff > 10)
|
|
{
|
|
docsisPacketProcessor = new DocsisPacketProcessor(this);
|
|
DvbContext.RegisterPacketProcessor(0x1ffe, docsisPacketProcessor);
|
|
UiJunction?.NotifyDocsisCarrier(docsisPacketProcessor.DocsisEnvironment);
|
|
LogEvent(SkyscraperContextEvent.SpecialTsMode, "DOCSIS Carrier TS detected.");
|
|
SpecialTsType = 4;
|
|
return;
|
|
}
|
|
|
|
int num20 = (int)pidStatistics[0x0020];
|
|
double percentage20 = (double)num20 * 100.0 / (double)numTotal;
|
|
if (percentage20 > 99.0 && percentage1fff < 1.0)
|
|
{
|
|
MultiprotocolEncapsulationDecoder blockstreamDecoder = new MultiprotocolEncapsulationDecoder(this);
|
|
DvbContext.RegisterPacketProcessor(0x0020, new PsiDecoder(0x0020,blockstreamDecoder));
|
|
UiJunction?.NotifyBlockstreamCarrier();
|
|
LogEvent(SkyscraperContextEvent.SpecialTsMode, "Blockstream Carrier TS detected.");
|
|
SpecialTsType = 5;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
[DebuggerStepThrough]
|
|
private void LogEvent(SkyscraperContextEvent eventType, string name = null)
|
|
{
|
|
string loggerName = String.Format("{0}{1}", IsChild ? ChildName + ", " : "", eventType.ToString());
|
|
LogManager.GetLogger(loggerName).Info(name);
|
|
if (eventType != SkyscraperContextEvent.TdtTime && eventType != SkyscraperContextEvent.TotTime && eventType != SkyscraperContextEvent.Scte35TimeSignal && eventType != SkyscraperContextEvent.Rcs2TdtTime)
|
|
{
|
|
lastEventTimestamp = DateTime.Now;
|
|
}
|
|
EventsLogged++;
|
|
}
|
|
|
|
public ulong EventsLogged { get; private set; }
|
|
private DateTime lastEventTimestamp;
|
|
|
|
public void NetworkPidFromPat(int networkPid)
|
|
{
|
|
if (DvbContext.IsPidProcessorPresent(networkPid))
|
|
return;
|
|
|
|
DvbContext.RegisterPacketProcessor(networkPid, new PsiDecoder(networkPid, new NitParser(this)));
|
|
LogEvent(SkyscraperContextEvent.NetworkPidFromPat);
|
|
}
|
|
|
|
private PmtTracker pmtTracker;
|
|
|
|
public void ProgramMapPidFromPat(int pmtPid, ushort programId)
|
|
{
|
|
SpecialTsType = 1;
|
|
if (CurrentNetworkId.HasValue && CurrentTransportStreamId.HasValue)
|
|
{
|
|
DataStorage.StorePatEntry(CurrentNetworkId.Value, CurrentTransportStreamId.Value, pmtPid, programId);
|
|
}
|
|
|
|
if (DvbContext.IsPidProcessorPresent(pmtPid))
|
|
return;
|
|
|
|
PsiDecoder pmtParser = new PsiDecoder(pmtPid, new PmtParser(this));
|
|
if (PmtDecoderTransformer != null)
|
|
{
|
|
PmtDecoderTransformer.Transform(pmtParser);
|
|
}
|
|
|
|
DvbContext.RegisterPacketProcessor(pmtPid, pmtParser);
|
|
LogEvent(SkyscraperContextEvent.ProgramMapPidFromPat);
|
|
UiJunction?.NotifyPatProgram(pmtPid, programId);
|
|
|
|
if (pmtTracker == null)
|
|
pmtTracker = new PmtTracker();
|
|
pmtTracker.AddPmtPid(pmtPid);
|
|
}
|
|
|
|
public void SetTransportStreamId(ushort transportStreamId)
|
|
{
|
|
if (!CurrentTransportStreamId.HasValue)
|
|
{
|
|
CurrentTransportStreamId = transportStreamId;
|
|
return;
|
|
}
|
|
}
|
|
|
|
private List<KeyValuePair<ushort, byte>> SsusToLookFor;
|
|
private Dictionary<ushort, byte> DsmCcsToLookFor;
|
|
|
|
public void PmtEvent(ProgramMapping result, int pmtPid)
|
|
{
|
|
bool logworthy = false;
|
|
if (CurrentNetworkId.HasValue && CurrentTransportStreamId.HasValue)
|
|
{
|
|
|
|
if (!DataStorage.TestForPmtEvent(CurrentNetworkId.Value, CurrentTransportStreamId.Value, result))
|
|
{
|
|
if (DataStorage.StorePmtEvent(CurrentNetworkId.Value, CurrentTransportStreamId.Value, result))
|
|
logworthy = true;
|
|
}
|
|
|
|
if (result.CaSystemId.HasValue && result.CaPid.HasValue)
|
|
{
|
|
CaDescriptor caDescriptor = new CaDescriptor(result.CaSystemId.Value, result.CaPid.Value, result.CaPrivateData);
|
|
NotifyOfCaSystem(caDescriptor, true);
|
|
}
|
|
}
|
|
|
|
bool allPrivateStreams = true;
|
|
foreach (ProgramMappingStream mappingStream in result.Streams)
|
|
{
|
|
if (!mappingStream.IsUserPrivateStream())
|
|
{
|
|
allPrivateStreams = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
byte madeUpComponentTags = 1;
|
|
foreach (ProgramMappingStream mappingStream in result.Streams)
|
|
{
|
|
if (mappingStream.CaPid.HasValue)
|
|
{
|
|
if (!DvbContext.IsPidProcessorPresent(mappingStream.CaPid.Value))
|
|
{
|
|
NotifyOfCaSystem(new CaDescriptor(mappingStream.CaSystemId.Value, mappingStream.CaPid.Value, mappingStream.CaPrivateData),true);
|
|
}
|
|
}
|
|
|
|
if (!DvbContext.IsPidProcessorPresent(mappingStream.ElementaryPid))
|
|
{
|
|
List<KeyValuePair<ushort, byte>> ssuKvs = SsusToLookFor.FindAll(x => x.Key == result.ProgramNumber);
|
|
bool foundSsu = false;
|
|
foreach (KeyValuePair<ushort, byte> keyValuePair in ssuKvs)
|
|
{
|
|
byte ssuComponentTag = keyValuePair.Value;
|
|
if (ssuComponentTag == mappingStream.ComponentTag)
|
|
{
|
|
DvbContext.RegisterPacketProcessor(mappingStream.ElementaryPid, new PsiDecoder(mappingStream.ElementaryPid, new DataCarouselDecoder(mappingStream, result, DataCarouselIntention.SoftwareUpdate, this)));
|
|
SsusToLookFor.Remove(keyValuePair);
|
|
foundSsu = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (foundSsu)
|
|
continue;
|
|
|
|
|
|
StreamType guessedStreamType = GuessStreamType(mappingStream, result.Streams.Count, result.RegistrationFormatIdentifier, allPrivateStreams);
|
|
switch (guessedStreamType)
|
|
{
|
|
case StreamType.Video:
|
|
ITsPacketProcessor videoPacketProcessor = null;
|
|
if (ALLOW_FFMPEG_FRAMEGRABBER)
|
|
{
|
|
if (TcpProxyEnabled)
|
|
{
|
|
if (!result.CaPid.HasValue)
|
|
{
|
|
if (!mappingStream.CaPid.HasValue)
|
|
{
|
|
if (CurrentNetworkId.HasValue && CurrentTransportStreamId.HasValue)
|
|
{
|
|
if (!ObjectStorage.TestForFramegrab(CurrentNetworkId.Value, CurrentTransportStreamId.Value, result.ProgramNumber, mappingStream.ElementaryPid))
|
|
{
|
|
ffmpegFrameGrabber ffmfg = new ffmpegFrameGrabber(CurrentNetworkId.Value, CurrentTransportStreamId.Value, result, mappingStream, this);
|
|
ffmfg.TsProxyEndPoint = DvbContext.GetTcpProxyEndPoint();
|
|
videoPacketProcessor = ffmfg;
|
|
}
|
|
else
|
|
{
|
|
videoPacketProcessor = new PacketDiscarder();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (videoPacketProcessor != null)
|
|
{
|
|
DvbContext.RegisterPacketProcessor(mappingStream.ElementaryPid, videoPacketProcessor);
|
|
}
|
|
|
|
break;
|
|
case StreamType.Teletext:
|
|
if (!CurrentNetworkId.HasValue)
|
|
break;
|
|
if (!CurrentTransportStreamId.HasValue)
|
|
break;
|
|
TeletextPesProcessor ttp = new TeletextPesProcessor(this, CurrentNetworkId.Value, CurrentTransportStreamId.Value, result.ProgramNumber);
|
|
if (result.PrivateDataSpecifier.HasValue)
|
|
ttp.PrivateDataSpecifier = result.PrivateDataSpecifier;
|
|
if (mappingStream.PrivateDataSpecifier.HasValue)
|
|
ttp.PrivateDataSpecifier = mappingStream.PrivateDataSpecifier;
|
|
DvbContext.RegisterPacketProcessor(mappingStream.ElementaryPid, new PesDecoder(ttp));
|
|
break;
|
|
case StreamType.Audio:
|
|
DvbContext.RegisterPacketProcessor(mappingStream.ElementaryPid, new PacketDiscarder());
|
|
break;
|
|
case StreamType.Application:
|
|
DvbContext.RegisterPacketProcessor(mappingStream.ElementaryPid,
|
|
new PsiDecoder(mappingStream.ElementaryPid, new AitParser(this, result.ProgramNumber)));
|
|
break;
|
|
case StreamType.Ignorable:
|
|
break;
|
|
case StreamType.HbbTv:
|
|
DvbContext.RegisterPacketProcessor(mappingStream.ElementaryPid,
|
|
new PsiDecoder(mappingStream.ElementaryPid,
|
|
new DataCarouselDecoder(mappingStream, result, DataCarouselIntention.NoIntention, this)));
|
|
break;
|
|
case StreamType.SystemSoftwareUpdate:
|
|
SystemSoftwareUpdateInfo ssuInfo =
|
|
new SystemSoftwareUpdateInfo(mappingStream.DataBroadcastSelector);
|
|
if (ssuInfo.Valid)
|
|
TryRegisterSystemSoftwareUpdateInfo(ssuInfo, mappingStream, result);
|
|
break;
|
|
case StreamType.Subtitles:
|
|
PesDecoder pesDecoder = new PesDecoder(new SubtitleDecoder(this));
|
|
pesDecoder.Tag = "subs";
|
|
DvbContext.RegisterPacketProcessor(mappingStream.ElementaryPid, pesDecoder);
|
|
//DvbContext.RegisterPacketProcessor(mappingStream.ElementaryPid, new PacketDumper(new FileInfo("subtitle.ts")));
|
|
break;
|
|
case StreamType.DvbMhp:
|
|
//Test and make up component tags.
|
|
if (!mappingStream.ComponentTag.HasValue)
|
|
MakeUpComponentTags(result);
|
|
DvbContext.RegisterPacketProcessor(mappingStream.ElementaryPid,
|
|
new PsiDecoder(mappingStream.ElementaryPid,
|
|
new DataCarouselDecoder(mappingStream, result, DataCarouselIntention.NoIntention, this)));
|
|
break;
|
|
case StreamType.Scte35:
|
|
DvbContext.RegisterPacketProcessor(mappingStream.ElementaryPid,
|
|
new PsiDecoder(mappingStream.ElementaryPid,
|
|
new Scte35SiDecoder(result.ProgramNumber, this)));
|
|
break;
|
|
case StreamType.RDS:
|
|
RdsPesProcessor rdsPesProcessor = new RdsPesProcessor(mappingStream.ElementaryPid,
|
|
result.ProgramNumber, this);
|
|
DvbContext.RegisterPacketProcessor(mappingStream.ElementaryPid,
|
|
new PesDecoder(rdsPesProcessor));
|
|
break;
|
|
case StreamType.MultiprotocolEncapsulation:
|
|
MultiprotocolEncapsulationDecoder mpeDec = new MultiprotocolEncapsulationDecoder(this);
|
|
DvbContext.RegisterPacketProcessor(mappingStream.ElementaryPid,
|
|
new PsiDecoder(mappingStream.ElementaryPid, mpeDec));
|
|
break;
|
|
case StreamType.Mheg5:
|
|
Mheg5DataBroadcastIdSelector selector =
|
|
new Mheg5DataBroadcastIdSelector(mappingStream.DataBroadcastSelector);
|
|
//These looked like Object Carousels to me
|
|
if (!mappingStream.ComponentTag.HasValue)
|
|
MakeUpComponentTags(result);
|
|
//ObjectCarouselDecoder ocd = new ObjectCarouselDecoder(mappingStream.ElementaryPid, this, result, mappingStream.ComponentTag.Value);
|
|
DataCarouselDecoder dcd = new DataCarouselDecoder(mappingStream, result, DataCarouselIntention.NoIntention, this);
|
|
//ocd.Tag = StreamType.Mheg5;
|
|
DvbContext.RegisterPacketProcessor(mappingStream.ElementaryPid, new PsiDecoder(mappingStream.ElementaryPid, dcd));
|
|
break;
|
|
case StreamType.TryAutodetect:
|
|
StreamTypeAutodetection sta = new StreamTypeAutodetection(mappingStream.ElementaryPid, this);
|
|
sta.ProgramContext.Program = result;
|
|
sta.ProgramContext.Stream = mappingStream;
|
|
sta.IntroduceStreamToContestants();
|
|
if (!sta.IsWinnerDetermined)
|
|
{
|
|
DvbContext.RegisterPacketProcessor(mappingStream.ElementaryPid, sta);
|
|
}
|
|
break;
|
|
case StreamType.IpMacNotification:
|
|
IntParser intParser = new IntParser(this);
|
|
DvbContext.RegisterPacketProcessor(mappingStream.ElementaryPid, new PsiDecoder(mappingStream.ElementaryPid, intParser));
|
|
break;
|
|
case StreamType.RelatedContentTable:
|
|
RctParser rctParser = new RctParser(this, result.ProgramNumber);
|
|
DvbContext.RegisterPacketProcessor(mappingStream.ElementaryPid, new PsiDecoder(mappingStream.ElementaryPid, rctParser));
|
|
break;
|
|
case StreamType.T2Mi:
|
|
T2MIDecoder t2miDecoder = new T2MIDecoder(mappingStream.ElementaryPid, this);
|
|
DvbContext.RegisterPacketProcessor(mappingStream.ElementaryPid, t2miDecoder);
|
|
break;
|
|
case StreamType.InteractionChannelForSatellite:
|
|
InteractionChannelPsiGatherer interactionChannelDecoder = new InteractionChannelPsiGatherer();
|
|
interactionChannelDecoder.ExpectedTables = mappingStream.UnknownUserDefines[0];
|
|
interactionChannelDecoder.Handler = this;
|
|
DvbContext.RegisterPacketProcessor(mappingStream.ElementaryPid, new PsiDecoder(mappingStream.ElementaryPid, interactionChannelDecoder));
|
|
break;
|
|
|
|
case StreamType.Id3:
|
|
DvbContext.RegisterPacketProcessor(mappingStream.ElementaryPid, new PesDecoder(new Id3PesProcessor(this)));
|
|
break;
|
|
default:
|
|
throw new NotImplementedException(String.Format(
|
|
"Detection of Stream type for PID 0x{0:X4} in Service {1}", pmtPid,
|
|
result.ProgramNumber));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (logworthy)
|
|
LogEvent(SkyscraperContextEvent.PmtEvent, String.Format("#{0}", result.ProgramNumber));
|
|
|
|
UiJunction?.NotifyPmtProgram(result, pmtPid);
|
|
|
|
pmtTracker?.MarkAsProcessed(pmtPid);
|
|
if (pmtTracker.ShouldFireAutodetection())
|
|
{
|
|
CheckForHiddenMpes();
|
|
}
|
|
}
|
|
|
|
public void MakeUpComponentTags(ProgramMapping target)
|
|
{
|
|
bool[] usedTags = new bool[byte.MaxValue];
|
|
foreach (ProgramMappingStream programMappingStream in target.Streams)
|
|
{
|
|
if (programMappingStream.ComponentTag.HasValue)
|
|
{
|
|
usedTags[programMappingStream.ComponentTag.Value] = true;
|
|
}
|
|
}
|
|
|
|
usedTags[0] = true;
|
|
|
|
foreach (ProgramMappingStream mappingStream in target.Streams)
|
|
{
|
|
if (!mappingStream.ComponentTag.HasValue)
|
|
{
|
|
for (byte b = 0; b < Byte.MaxValue; b++)
|
|
{
|
|
if (!usedTags[b])
|
|
{
|
|
usedTags[b] = true;
|
|
mappingStream.ComponentTag = b;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void TryRegisterSystemSoftwareUpdateInfo(SystemSoftwareUpdateInfo info,
|
|
ProgramMappingStream mappingStream, ProgramMapping mapping)
|
|
{
|
|
if (DvbContext.IsPidProcessorPresent(mappingStream.ElementaryPid))
|
|
return;
|
|
|
|
//TODO: Scrape this once we have an SSU Event handler.
|
|
foreach (SystemSoftwareUpdateInfo.Oui infoOui in info.Ouis)
|
|
{
|
|
switch (infoOui.UpdateType)
|
|
{
|
|
//Found on ETSI TS 102 006, Page 13
|
|
case 0: //proprietary update solution
|
|
if (ALLOW_STREAM_TYPE_AUTODETECTION)
|
|
{
|
|
StreamTypeAutodetection sta = new StreamTypeAutodetection(mappingStream.ElementaryPid, this);
|
|
sta.ProgramContext.Stream = mappingStream;
|
|
sta.ProgramContext.Program = mapping;
|
|
DvbContext.RegisterPacketProcessor(mappingStream.ElementaryPid, sta);
|
|
}
|
|
else
|
|
DvbContext.RegisterPacketProcessor(mappingStream.ElementaryPid, new PacketDiscarder());
|
|
break;
|
|
case 1: //standard update carousel (i.e. without notification table) via broadcast
|
|
if (!mappingStream.ComponentTag.HasValue)
|
|
{
|
|
MakeUpComponentTags(mapping);
|
|
}
|
|
|
|
DvbContext.RegisterPacketProcessor(mappingStream.ElementaryPid, new PsiDecoder(mappingStream.ElementaryPid, new DataCarouselDecoder(mappingStream, mapping, DataCarouselIntention.SoftwareUpdate, this)));
|
|
break;
|
|
case 2: //system software update carousel with notification table (UNT) both available via broadcast
|
|
case 3: //system software update signalled via broadcast UNT, update available from the return channel
|
|
case 4: //system software update signalled via broadcast UNT, update available from the internet
|
|
DvbContext.RegisterPacketProcessor(mappingStream.ElementaryPid, new PsiDecoder(mappingStream.ElementaryPid, new UntDecoder(this, mapping)));
|
|
break;
|
|
case 5:
|
|
case 6:
|
|
case 7:
|
|
case 8:
|
|
case 9:
|
|
case 10:
|
|
case 11:
|
|
case 12:
|
|
case 13:
|
|
case 14:
|
|
case 15:
|
|
//reserved for future use as of 2015-06
|
|
//ignore it.
|
|
break;
|
|
default:
|
|
throw new NotImplementedException(String.Format("{0} = {1:X2}", nameof(infoOui.UpdateType),
|
|
infoOui.UpdateType));
|
|
}
|
|
}
|
|
}
|
|
|
|
private StreamType GuessStreamType(ProgramMappingStream stream, int totalStreams,
|
|
uint? parentRegistrationFormatIdentifier, bool allPrivateStreams)
|
|
{
|
|
if (stream.StreamType == PmtStreamType.H262)
|
|
return StreamType.Video;
|
|
if (stream.AvcStillPresent.HasValue)
|
|
return StreamType.Video;
|
|
if (stream.AudioType.HasValue)
|
|
return StreamType.Audio;
|
|
if (stream.StreamType == PmtStreamType.AvcVideoStream)
|
|
return StreamType.Video;
|
|
if (stream.StreamType == PmtStreamType.Iso11172Audio)
|
|
return StreamType.Audio;
|
|
if (stream.StreamType == PmtStreamType.Iso13818_3Audio)
|
|
return StreamType.Audio;
|
|
if (stream.StreamType == PmtStreamType.Iso13818_7AudioADTS)
|
|
return StreamType.Audio;
|
|
if (stream.StreamType == PmtStreamType.HevcVideoStream)
|
|
return StreamType.Video;
|
|
if (stream.StreamType == PmtStreamType.Iso13818_1PesPackets && stream.Ac4ChannelMode.HasValue)
|
|
return StreamType.Audio; //AC-4 Audio
|
|
if (stream.StreamType == PmtStreamType.Iso13818_1PesPackets && stream.BSID.HasValue)
|
|
return StreamType.Audio;
|
|
if (stream.StreamType == PmtStreamType.Iso14496_3Audio && stream.AacProfileAndLevel.HasValue)
|
|
return StreamType.Audio;
|
|
if ((int)stream.StreamType == 0x81 && stream.ComponentType.HasValue)
|
|
return StreamType.Audio; //AC-3 Audio, but weird.
|
|
if (stream.Teletexts != null)
|
|
{
|
|
for (int i = 0; i < stream.Teletexts.Length; i++)
|
|
{
|
|
if (stream.Teletexts[i] != null)
|
|
{
|
|
return StreamType.Teletext;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (stream.Applications != null)
|
|
{
|
|
return StreamType.Application;
|
|
}
|
|
|
|
if (stream.DataBroadcastId == 0x0123)
|
|
{
|
|
return StreamType.HbbTv;
|
|
}
|
|
|
|
if (stream.DataBroadcastId == 0x000a)
|
|
{
|
|
return StreamType.SystemSoftwareUpdate;
|
|
}
|
|
|
|
if (!ALLOW_STREAM_TYPE_AUTODETECTION)
|
|
{
|
|
if ((int)stream.StreamType == 0xc0 || (int)stream.StreamType == 0xc1)
|
|
{
|
|
if (stream.PrivateDataSpecifier.HasValue)
|
|
throw new NotImplementedException(String.Format("{0}", nameof(stream.PrivateDataSpecifier)));
|
|
else
|
|
return StreamType.Ignorable;
|
|
}
|
|
}
|
|
|
|
if (stream.StreamType == PmtStreamType.Iso13818_1PesPackets && stream.Subtitlings != null &&
|
|
stream.Subtitlings.Length > 0)
|
|
{
|
|
return StreamType.Subtitles;
|
|
}
|
|
|
|
if (stream.DataBroadcastId == 0xf0)
|
|
{
|
|
return StreamType.DvbMhp;
|
|
}
|
|
|
|
if (!ALLOW_STREAM_TYPE_AUTODETECTION)
|
|
{
|
|
if (!stream.DataBroadcastId.HasValue && stream.StreamType == PmtStreamType.Iso13818_1PrivateSections &&
|
|
totalStreams == 1)
|
|
{
|
|
return StreamType.Ignorable;
|
|
}
|
|
}
|
|
|
|
if (stream.FormatIdentifier.HasValue && stream.FormatIdentifier == 0x43554549 &&
|
|
(int)stream.StreamType == 0x86)
|
|
{
|
|
return StreamType.Scte35;
|
|
}
|
|
|
|
if ((int)stream.StreamType == 0x86 && parentRegistrationFormatIdentifier.HasValue &&
|
|
parentRegistrationFormatIdentifier.Value == 0x43554549)
|
|
{
|
|
return StreamType.Scte35;
|
|
}
|
|
|
|
if (stream.DataBroadcastId == 0x0007) //According to dvbservices.com, this is an Object Carousel
|
|
{
|
|
return StreamType.DvbMhp;
|
|
}
|
|
|
|
if (stream.StreamType == PmtStreamType.Iso13818_1PesPackets && stream.VbiData != null)
|
|
{
|
|
return StreamType.Teletext;
|
|
}
|
|
|
|
if ((byte)stream.StreamType == 0x89 && stream.AncillaryDataDescriptor != null &&
|
|
stream.AncillaryDataDescriptor.RdsOnly)
|
|
return StreamType.RDS;
|
|
|
|
if (!ALLOW_STREAM_TYPE_AUTODETECTION)
|
|
{
|
|
if (stream.StreamType == PmtStreamType.Iso13818_6TypeB && !stream.DataBroadcastId.HasValue)
|
|
return StreamType.Ignorable;
|
|
|
|
if (stream.StreamType == PmtStreamType.Iso13818_6TypeC && !stream.DataBroadcastId.HasValue)
|
|
return StreamType.Ignorable;
|
|
|
|
if (stream.DataBroadcastId.HasValue && stream.DataBroadcastId.Value == 0x0140)
|
|
{
|
|
//Proprietary by CANAL+, does not seem to be documented.
|
|
return StreamType.Ignorable;
|
|
}
|
|
}
|
|
|
|
if (stream.DataBroadcastId.HasValue && stream.DataBroadcastId.Value == 0x0005 /*&& stream.StreamType == PmtStreamType.Iso13818_6TypeD*/)
|
|
{
|
|
return StreamType.MultiprotocolEncapsulation;
|
|
}
|
|
|
|
if (!ALLOW_STREAM_TYPE_AUTODETECTION)
|
|
{
|
|
if (allPrivateStreams && !stream.DataBroadcastId.HasValue && !stream.PrivateDataSpecifier.HasValue &&
|
|
!parentRegistrationFormatIdentifier.HasValue)
|
|
return StreamType.Ignorable;
|
|
|
|
if (stream.DataBroadcastId == 0x010b)
|
|
{
|
|
//NDS France Technologies system software download
|
|
//sadly undocumented.
|
|
return StreamType.Ignorable;
|
|
}
|
|
|
|
if (stream.StreamType == PmtStreamType.Iso13818_1PesPackets && totalStreams == 1 &&
|
|
!stream.DataBroadcastId.HasValue && !stream.PrivateDataSpecifier.HasValue)
|
|
{
|
|
//A single private stream. We have no clue how to deal with it, at all.
|
|
return StreamType.Ignorable;
|
|
}
|
|
}
|
|
|
|
if (stream.DataBroadcastId == 0x0106)
|
|
{
|
|
//Defined in ETSI ES 202 184 V2.4.1
|
|
return StreamType.Mheg5;
|
|
}
|
|
|
|
if (stream.DataBroadcastId == 0x000b)
|
|
{
|
|
return StreamType.IpMacNotification;
|
|
}
|
|
|
|
if (stream.RelatedContentDescriptorPresent.HasValue)
|
|
{
|
|
if (stream.RelatedContentDescriptorPresent.Value && stream.StreamType == PmtStreamType.Iso13818_1PrivateSections)
|
|
{
|
|
return StreamType.RelatedContentTable;
|
|
}
|
|
}
|
|
|
|
if (stream.NumT2MiStreams.HasValue && stream.StreamType == PmtStreamType.Iso13818_1PesPackets)
|
|
{
|
|
return StreamType.T2Mi;
|
|
}
|
|
|
|
if (stream.UnknownUserDefines != null)
|
|
{
|
|
if (stream.UnknownUserDefines.Count == 1)
|
|
{
|
|
if (stream.UnknownUserDefines[0].DescriptorTag == 0xa7)
|
|
{
|
|
bool interactionChannelPossible = stream.UnknownUserDefines[0].Data.All(x => x == 0x40 || x == 0x41 || x == 0x4d || x == 0x70 || x >= 0xA0);
|
|
if (interactionChannelPossible)
|
|
{
|
|
|
|
return StreamType.InteractionChannelForSatellite;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if (stream.MetadataApplicationFormat == 0xffff && stream.MetadataApplicationFormatIdentifier == 0x49443320 && stream.MetadataFormat == 0xff && stream.MetadataFormatIdentifier == 0x49443320)
|
|
{
|
|
return StreamType.Id3;
|
|
}
|
|
|
|
if (ALLOW_STREAM_TYPE_AUTODETECTION)
|
|
{
|
|
//Abandon all hope, ye who enter here...
|
|
return StreamType.TryAutodetect;
|
|
}
|
|
|
|
return StreamType.Unknown;
|
|
}
|
|
|
|
private int onidHits, onidMisses;
|
|
|
|
public void SetNetworkId(ushort networkId)
|
|
{
|
|
SetNetworkId(networkId, false);
|
|
}
|
|
|
|
public void SetNetworkId(ushort networkId, bool forceOverwrite = false)
|
|
{
|
|
if (CurrentNetworkId == null)
|
|
{
|
|
CurrentNetworkId = networkId;
|
|
return;
|
|
}
|
|
|
|
if (CurrentNetworkId != networkId)
|
|
{
|
|
onidMisses++;
|
|
if (forceOverwrite)
|
|
{
|
|
CurrentNetworkId = networkId;
|
|
}
|
|
}
|
|
else
|
|
onidHits++;
|
|
}
|
|
|
|
private EitEvent[] runningEvents;
|
|
public void OnEitEvent(EitEvent eitEvent)
|
|
{
|
|
if (DataStorage.StoreEitEvent(eitEvent))
|
|
LogEvent(SkyscraperContextEvent.EitEvent, eitEvent.EventName);
|
|
|
|
UiJunction?.NotifyEvent(eitEvent);
|
|
|
|
if (eitEvent.RunningStatus == RunningStatus.Running)
|
|
{
|
|
if (runningEvents == null)
|
|
runningEvents = new EitEvent[UInt16.MaxValue];
|
|
runningEvents[eitEvent.ServiceId] = eitEvent;
|
|
}
|
|
}
|
|
|
|
public void OnNitTransportStream(ushort networkId, NitTransportStream transportStream)
|
|
{
|
|
UiJunction?.NotifyNit(transportStream);
|
|
|
|
string name;
|
|
switch (transportStream.DeliveryMethod)
|
|
{
|
|
case NitTransportStream.TransportMedium.DVB_S:
|
|
case NitTransportStream.TransportMedium.DVB_S2:
|
|
name = String.Format("{3}°{4}/{0}/{1}/{2}", transportStream.Frequency / 100,
|
|
transportStream.Polarization.ToString().Substring(0, 1),
|
|
(float)transportStream.SymbolRate / 10.0f, transportStream.OrbitalPosition,
|
|
transportStream.East.Value ? "E" : "W");
|
|
break;
|
|
case NitTransportStream.TransportMedium.DVB_T2:
|
|
if (transportStream.Frequency != null)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
if (transportStream.CellInfos == null)
|
|
return;
|
|
if (transportStream.CellInfos.Count == 0)
|
|
return;
|
|
name = String.Format("Center Frequency: {0}", transportStream.GetT2Frequency());
|
|
break;
|
|
case NitTransportStream.TransportMedium.Unknown:
|
|
return;
|
|
case NitTransportStream.TransportMedium.DVB_C:
|
|
name = String.Format("{0}/{1}/{2}", transportStream.Frequency / 100000, transportStream.SymbolRate,
|
|
transportStream.RenderModulation());
|
|
break;
|
|
default:
|
|
throw new NotImplementedException(transportStream.DeliveryMethod.ToString());
|
|
}
|
|
|
|
if (!DataStorage.TestForNitTransportStream(networkId, transportStream))
|
|
{
|
|
DataStorage.StoreNitTransportStream(networkId, transportStream);
|
|
LogEvent(SkyscraperContextEvent.OnNitTransportStream, name);
|
|
return;
|
|
}
|
|
|
|
if (DataStorage.UpdateNitTransportStream(networkId, transportStream))
|
|
{
|
|
LogEvent(SkyscraperContextEvent.OnNitTransportStreamUpdate, name);
|
|
return;
|
|
}
|
|
}
|
|
|
|
public void OnNitNetwork(NitNetwork nitNetwork)
|
|
{
|
|
if (!string.IsNullOrEmpty(nitNetwork.Name))
|
|
{
|
|
|
|
}
|
|
if (nitNetwork.XaitPid.HasValue && CurrentNetworkId.HasValue)
|
|
{
|
|
ushort nitNetworkXaitPid = nitNetwork.XaitPid.Value;
|
|
if (nitNetwork.NetworkId == CurrentNetworkId.Value)
|
|
{
|
|
if (!DvbContext.IsPidProcessorPresent(nitNetworkXaitPid))
|
|
{
|
|
AitParser aitParser = new AitParser(this, 0x00);
|
|
aitParser.Tag = "XAIT";
|
|
DvbContext.RegisterPacketProcessor(nitNetworkXaitPid,
|
|
new PsiDecoder(nitNetworkXaitPid, aitParser));
|
|
LogEvent(SkyscraperContextEvent.XaitDetected, String.Format("PID {0}", nitNetworkXaitPid));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!DataStorage.TestForNitNetwork(nitNetwork))
|
|
{
|
|
DataStorage.StoreNitNetwork(nitNetwork);
|
|
LogEvent(SkyscraperContextEvent.OnNitNetwork, nitNetwork.Name);
|
|
return;
|
|
}
|
|
|
|
if (DataStorage.UpdateNitNetwork(nitNetwork))
|
|
{
|
|
LogEvent(SkyscraperContextEvent.OnNitNetworkUpdate, nitNetwork.Name);
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
private HashSet<VpsCoordinate> _vpsCoordinates;
|
|
|
|
public void OnVpsData(int networkId, int transportStreamId, ushort programNumber, VpsDataBlock vpsDataField)
|
|
{
|
|
//We don't really need VPS data, but we'll use it as another way of making sure we
|
|
//got everything.
|
|
if (_vpsCoordinates == null)
|
|
{
|
|
_vpsCoordinates = new HashSet<VpsCoordinate>();
|
|
}
|
|
|
|
VpsCoordinate vpsCoordinate = new VpsCoordinate(programNumber, vpsDataField.Month, vpsDataField.Day,
|
|
vpsDataField.Hour, vpsDataField.Minute);
|
|
if (_vpsCoordinates.Add(vpsCoordinate))
|
|
{
|
|
LogEvent(SkyscraperContextEvent.VpsData,
|
|
String.Format("{0:D2}.{1:D2} {2:D2}:{3:D2} {4}", vpsCoordinate.Day, vpsCoordinate.Month,
|
|
vpsCoordinate.Hour, vpsCoordinate.Minute, vpsCoordinate.ProgramNumber));
|
|
}
|
|
}
|
|
|
|
|
|
private Dictionary<ushort, WssDataBlock> _wssDataBlocks;
|
|
|
|
public void OnWssData(int networkId, int transportStreamId, ushort programNumber, WssDataBlock wssDataBlock)
|
|
{
|
|
if (_wssDataBlocks == null)
|
|
{
|
|
_wssDataBlocks = new Dictionary<ushort, WssDataBlock>();
|
|
}
|
|
|
|
if (!_wssDataBlocks.ContainsKey(programNumber))
|
|
{
|
|
_wssDataBlocks.Add(programNumber, wssDataBlock);
|
|
LogEvent(SkyscraperContextEvent.WssData, programNumber.ToString());
|
|
UiJunction?.NotifyWss(programNumber, wssDataBlock);
|
|
}
|
|
}
|
|
|
|
public void OnTeletextPage(int networkId, int transportStreamId, ushort programNumber,
|
|
TeletextMagazine magazine)
|
|
{
|
|
if (!currentTime.HasValue)
|
|
return;
|
|
|
|
DataStorage.StoreTeletextPage(networkId, transportStreamId, programNumber, magazine, currentTime.Value);
|
|
}
|
|
|
|
public void OnBatBouquet(BatBouquet batBouquet)
|
|
{
|
|
UiJunction?.NotifyBat(batBouquet);
|
|
if (DataStorage.TestForBatBouquet(batBouquet))
|
|
{
|
|
if (DataStorage.UpdateBatBouquet(batBouquet))
|
|
{
|
|
LogEvent(SkyscraperContextEvent.BatBouquetUpdate, batBouquet.BouquetName);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DataStorage.StoreBatBouquet(batBouquet);
|
|
LogEvent(SkyscraperContextEvent.BatBouquet, batBouquet.BouquetName);
|
|
}
|
|
}
|
|
|
|
public void OnBatTransportStream(BatBouquet batBouquet, BatTransportStream child)
|
|
{
|
|
UiJunction?.NotifyBatTs(batBouquet.BouquetId, child);
|
|
string name = String.Format("{0},{1}", batBouquet.BouquetId, child.OriginalNetworkId);
|
|
if (DataStorage.TestForBatTransportStream(batBouquet.BouquetId, child))
|
|
{
|
|
if (DataStorage.UpdateBatTransportStream(batBouquet.BouquetId, child))
|
|
{
|
|
LogEvent(SkyscraperContextEvent.BatTransportStreamUpdate, name);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DataStorage.StoreBatTransportStream(batBouquet.BouquetId, child);
|
|
LogEvent(SkyscraperContextEvent.BatTransportStream, name);
|
|
}
|
|
}
|
|
|
|
public void OnSdtService(ushort transportStreamId, ushort originalNetworkId, SdtService sdtService)
|
|
{
|
|
if (DataStorage.TestForSdtService(transportStreamId, originalNetworkId, sdtService))
|
|
{
|
|
if (!IsChild)
|
|
{
|
|
if (DataStorage.UpdateSdtService(transportStreamId, originalNetworkId, sdtService))
|
|
{
|
|
LogEvent(SkyscraperContextEvent.OnSdtServiceUpdate, sdtService.ServiceName);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DataStorage.StoreSdtService(transportStreamId, originalNetworkId, sdtService);
|
|
LogEvent(SkyscraperContextEvent.OnSdtService, sdtService.ServiceName);
|
|
}
|
|
|
|
UiJunction?.NotifySdtService(sdtService);
|
|
}
|
|
|
|
private DateTime? currentTime;
|
|
private DateTime? currentTimeForTim;
|
|
|
|
public void SetCurrentTime(DateTime currentTime)
|
|
{
|
|
if (!this.currentTime.HasValue)
|
|
{
|
|
this.currentTime = currentTime;
|
|
}
|
|
currentTimeForTim = currentTime;
|
|
}
|
|
|
|
public void SetCurrentProgrammeType(int programNumber, PTY.ProgrammeTypeCodes pty)
|
|
{
|
|
if (!CurrentNetworkId.HasValue)
|
|
return;
|
|
|
|
if (!CurrentTransportStreamId.HasValue)
|
|
return;
|
|
|
|
if (DataStorage.UpdateRdsPty(CurrentNetworkId.Value, CurrentTransportStreamId.Value, programNumber, pty))
|
|
{
|
|
LogEvent(SkyscraperContextEvent.RdsPty, String.Format("{0} -> {1}", programNumber, pty.ToString()));
|
|
}
|
|
}
|
|
|
|
public void MarkAsRdsTrafficInformationProgramme(int programNumber)
|
|
{
|
|
if (!CurrentNetworkId.HasValue)
|
|
return;
|
|
|
|
if (!CurrentTransportStreamId.HasValue)
|
|
return;
|
|
|
|
if (DataStorage.MarkAsRdsTrafficInformationProgramme(CurrentNetworkId.Value, CurrentTransportStreamId.Value, programNumber))
|
|
{
|
|
LogEvent(SkyscraperContextEvent.RdsTrafficInfoDetected, String.Format("{0}", programNumber));
|
|
}
|
|
}
|
|
|
|
public void OnTotTime(DateTime utcTime, LocalTimeOffsetDescriptor ltod)
|
|
{
|
|
UiJunction?.NotifyTot(utcTime, ltod);
|
|
SetCurrentTime(utcTime);
|
|
|
|
if (!CurrentNetworkId.HasValue)
|
|
return;
|
|
|
|
if (!CurrentTransportStreamId.HasValue)
|
|
return;
|
|
|
|
if (DataStorage.UpdateTimeOffsetTable(CurrentNetworkId.Value, CurrentTransportStreamId.Value, utcTime,
|
|
ltod))
|
|
LogEvent(SkyscraperContextEvent.TotTime, utcTime.ToString());
|
|
}
|
|
|
|
public void OnTdtTime(DateTime utcTime)
|
|
{
|
|
UiJunction?.NotifyTdt(utcTime);
|
|
SetCurrentTime(utcTime);
|
|
|
|
if (!CurrentNetworkId.HasValue)
|
|
return;
|
|
|
|
if (!CurrentTransportStreamId.HasValue)
|
|
return;
|
|
|
|
if (DataStorage.UpdateTimeAndDate(CurrentNetworkId.Value, CurrentTransportStreamId.Value, utcTime))
|
|
LogEvent(SkyscraperContextEvent.TdtTime, utcTime.ToString());
|
|
}
|
|
|
|
public void OnAitApplication(AitApplication aitApplication, ushort programNumber)
|
|
{
|
|
UiJunction?.NotifyAit(aitApplication);
|
|
foreach (TransportProtocolDescriptor transportProtocol in aitApplication.TransportProtocols)
|
|
{
|
|
switch (transportProtocol.ProtocolId)
|
|
{
|
|
case 0x01:
|
|
ObjectCarouselTransportSelector octs =
|
|
(ObjectCarouselTransportSelector)transportProtocol.Selector;
|
|
DsmCcsToLookFor[programNumber] = octs.ComponentTag;
|
|
break;
|
|
case 0x03:
|
|
//The interwebs!
|
|
break;
|
|
default:
|
|
if (transportProtocol.Selector == null)
|
|
continue;
|
|
throw new NotImplementedException(String.Format("{0} {1:x2}",
|
|
nameof(transportProtocol.ProtocolId), transportProtocol.ProtocolId));
|
|
}
|
|
}
|
|
|
|
if (!DataStorage.TestForAitApplication(aitApplication.ApplicationIdentifier))
|
|
{
|
|
DataStorage.StoreAitApplication(aitApplication);
|
|
LogEvent(SkyscraperContextEvent.AitApplication, aitApplication.TryGetName());
|
|
}
|
|
}
|
|
|
|
public void NotifyFileArrival(VfsFile vfsFile)
|
|
{
|
|
UiJunction?.DsmCcVfs(vfsFile);
|
|
|
|
if (!CurrentTransportStreamId.HasValue)
|
|
return;
|
|
if (!CurrentNetworkId.HasValue)
|
|
return;
|
|
|
|
if (ObjectStorage.ObjectCarouselFileArrival(vfsFile, CurrentTransportStreamId.Value, CurrentNetworkId.Value))
|
|
{
|
|
LogEvent(SkyscraperContextEvent.FileArrival, String.Format("PID {0:X4}, Path {1}", vfsFile.SourcePid, vfsFile.ToString()));
|
|
}
|
|
|
|
}
|
|
|
|
public void NotifySubStream(ProgramMapping programMapping, Tap tap, ushort[] eventIds, Dictionary<uint, string> context, EventList_T eventListT, Info_T infoT)
|
|
{
|
|
foreach (ProgramMappingStream mappingStream in programMapping.Streams)
|
|
{
|
|
if (mappingStream.ComponentTag.HasValue)
|
|
{
|
|
ushort l = mappingStream.ComponentTag.Value;
|
|
ushort r = tap.AssociationTag;
|
|
if (l == r)
|
|
{
|
|
if (!DvbContext.IsPidProcessorPresent(mappingStream.ElementaryPid))
|
|
{
|
|
//DvbContext.RegisterPacketProcessor(mappingStream.ElementaryPid, new PsiDecoder(mappingStream.ElementaryPid, new ObjectCarouselDecoder(mappingStream.ElementaryPid, this, programMapping, mappingStream.ComponentTag.Value)));
|
|
DvbContext.RegisterPacketProcessor(mappingStream.ElementaryPid, new PsiDecoder(mappingStream.ElementaryPid, new DataCarouselDecoder(mappingStream, programMapping, DataCarouselIntention.NoIntention, this)));
|
|
LogEvent(SkyscraperContextEvent.SubStreamFromObjectCarousel, String.Format("PID {0:X4}", mappingStream.ElementaryPid));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void NotifyOfCaSystem(CaDescriptor caDescriptor, bool fromPmt = false)
|
|
{
|
|
if (caDescriptor.CaSystemId == 0x0000)
|
|
return; //Invalid CAID
|
|
|
|
if (!CurrentNetworkId.HasValue)
|
|
return;
|
|
if (!CurrentTransportStreamId.HasValue)
|
|
return;
|
|
|
|
if (!fromPmt)
|
|
{
|
|
UiJunction?.NotifyCat(caDescriptor);
|
|
if (!DataStorage.TestForCaSystem(CurrentNetworkId.Value, CurrentTransportStreamId.Value, caDescriptor.CaPid))
|
|
{
|
|
DataStorage.StoreCaSystem(CurrentNetworkId.Value, CurrentTransportStreamId.Value, caDescriptor);
|
|
LogEvent(SkyscraperContextEvent.CaSystem, String.Format("{0} on PID {1:X4}", CaSystemNames.GetHumanReadableName(caDescriptor.CaSystemId), caDescriptor.CaPid));
|
|
}
|
|
}
|
|
|
|
if (!DvbContext.IsPidProcessorPresent(caDescriptor.CaPid))
|
|
{
|
|
//These PIDs for CA Systems contain ECM and EMM messages.
|
|
//They are specific for *every* CA System.
|
|
//It is probably not worth reverse-engineering every single one of them.
|
|
//So we just discard these packages.
|
|
DvbContext.RegisterPacketProcessor(caDescriptor.CaPid, new PacketDiscarder());
|
|
}
|
|
}
|
|
|
|
public void NotifyOfSubtitleLine(Page page)
|
|
{
|
|
|
|
}
|
|
|
|
public void UpdateNotification(UpdateNotificationGroup common, UpdateNotificationTarget target, ushort ProgramNumber)
|
|
{
|
|
/*int hashCode = target.GetHashCode();
|
|
if (!ScraperStorage.TestForUpdateNotification(hashCode, common))
|
|
{
|
|
foreach (Compatibility compatibility in target.Compatibilities)
|
|
{
|
|
foreach (Platform platform in target.Platforms)
|
|
{
|
|
ScraperStorage.StoreUpdateNotification(hashCode, common, compatibility, platform);
|
|
LogEvent(SkyscraperContextEvent.UpdateNotification,
|
|
String.Format("{0} -> {1}", compatibility, platform));
|
|
}
|
|
}
|
|
}*/
|
|
foreach (Compatibility compatibility in target.Compatibilities)
|
|
{
|
|
foreach (Platform platform in target.Platforms)
|
|
{
|
|
DataStorage.StoreUpdateNotification(0, common, compatibility, platform);
|
|
}
|
|
}
|
|
|
|
|
|
if (common.AssociationTag.HasValue)
|
|
{
|
|
Platform platform = new Platform();
|
|
platform.DataBroadcastId = common.DataBroadcastId;
|
|
platform.PrivateData = common.PrivateData;
|
|
platform.AssociationTag = common.AssociationTag;
|
|
platform.UpdateFlag = common.UpdateFlag;
|
|
platform.UpdateMethod = common.UpdateMethod;
|
|
platform.UpdatePriority = common.UpdatePriority;
|
|
target.Platforms.Add(platform);
|
|
}
|
|
|
|
foreach (Platform targetPlatform in target.Platforms)
|
|
{
|
|
if (!targetPlatform.AssociationTag.HasValue)
|
|
continue;
|
|
if (!SsusToLookFor.Any(x => x.Key == ProgramNumber && x.Value == (byte)targetPlatform.AssociationTag))
|
|
{
|
|
SsusToLookFor.Add(new KeyValuePair<ushort, byte>(ProgramNumber,
|
|
(byte)targetPlatform.AssociationTag.Value));
|
|
}
|
|
}
|
|
}
|
|
|
|
private Dictionary<DatabaseKeyDsmCcModule, double> _dataCarouselProgresses;
|
|
|
|
public bool IsModuleWanted(int elementaryPid, ushort moduleId, byte moduleVersion)
|
|
{
|
|
if (!CurrentNetworkId.HasValue)
|
|
return false;
|
|
|
|
if (!CurrentTransportStreamId.HasValue)
|
|
return false;
|
|
|
|
if (DataStorage.IsDsmCcModuleBlacklisted(CurrentNetworkId.Value, CurrentTransportStreamId.Value, elementaryPid, moduleId, moduleVersion))
|
|
return false;
|
|
|
|
if (ObjectStorage.IsDsmCcModuleWanted(CurrentNetworkId.Value, CurrentTransportStreamId.Value, elementaryPid, moduleId, moduleVersion))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
public void NotifyOfNewModule(int elementaryPid, ushort moduleInfoModuleId, byte moduleInfoModuleVersion)
|
|
{
|
|
if (!CurrentNetworkId.HasValue)
|
|
return;
|
|
|
|
if (!CurrentTransportStreamId.HasValue)
|
|
return;
|
|
|
|
if (_dataCarouselProgresses == null)
|
|
_dataCarouselProgresses = new Dictionary<DatabaseKeyDsmCcModule, double>();
|
|
|
|
DatabaseKeyDsmCcModule key = new DatabaseKeyDsmCcModule(CurrentNetworkId.Value, CurrentTransportStreamId.Value, elementaryPid, moduleInfoModuleId, moduleInfoModuleVersion);
|
|
|
|
if (dedupModules.Contains(key))
|
|
return;
|
|
|
|
if (ObjectStorage.IsDsmCcModuleWanted(CurrentNetworkId.Value, CurrentTransportStreamId.Value, elementaryPid, moduleInfoModuleId, moduleInfoModuleVersion))
|
|
{
|
|
if (!_dataCarouselProgresses.ContainsKey(key))
|
|
{
|
|
_dataCarouselProgresses.Add(key, 0.0);
|
|
}
|
|
LogEvent(SkyscraperContextEvent.NotifyOfNewModule, String.Format("PID {2:X4}, Module #{0:X4}, Version {1}", moduleInfoModuleId, moduleInfoModuleVersion, elementaryPid));
|
|
UiJunction?.DsmCcModuleAdd(elementaryPid, moduleInfoModuleId, moduleInfoModuleVersion);
|
|
}
|
|
}
|
|
|
|
public void NotifyModuleDownloadProgress(int elementaryPid, ushort moduleInfoModuleId, byte moduleInfoModuleVersion, double moduleInfoDownloadProgress)
|
|
{
|
|
if (!CurrentNetworkId.HasValue)
|
|
return;
|
|
|
|
if (!CurrentTransportStreamId.HasValue)
|
|
return;
|
|
|
|
if (_dataCarouselProgresses == null)
|
|
return;
|
|
|
|
DatabaseKeyDsmCcModule key = new DatabaseKeyDsmCcModule(CurrentNetworkId.Value, CurrentTransportStreamId.Value, elementaryPid, moduleInfoModuleId, moduleInfoModuleVersion);
|
|
if (_dataCarouselProgresses.ContainsKey(key))
|
|
{
|
|
_dataCarouselProgresses[key] = moduleInfoDownloadProgress;
|
|
LogEvent(SkyscraperContextEvent.ModuleDownloadProgress, String.Format("PID {3:X4}, Module #{0}, Version {1}, {2}% done", moduleInfoModuleId, moduleInfoModuleVersion, moduleInfoDownloadProgress, elementaryPid));
|
|
UiJunction?.DsmCcModuleProgress(elementaryPid, moduleInfoModuleId, moduleInfoModuleVersion, moduleInfoDownloadProgress);
|
|
}
|
|
}
|
|
|
|
public ObjectCarouselEventHandler GetObjectCarouselEventHandler()
|
|
{
|
|
return this;
|
|
}
|
|
|
|
public void NotifyDownloadComplete(int elementaryPid, ushort moduleModuleId, byte moduleModuleVersion, Stream result, bool isObjectCarousel)
|
|
{
|
|
if (!CurrentNetworkId.HasValue)
|
|
return;
|
|
|
|
if (!CurrentTransportStreamId.HasValue)
|
|
return;
|
|
|
|
if (_dataCarouselProgresses == null)
|
|
return;
|
|
|
|
if (isObjectCarousel)
|
|
throw new NotImplementedException();
|
|
|
|
UiJunction?.SetMemorySaverMode(true);
|
|
ObjectStorage.DataCarouselModuleArrival(CurrentNetworkId.Value, CurrentTransportStreamId.Value, elementaryPid, moduleModuleId, moduleModuleVersion, result);
|
|
UiJunction?.SetMemorySaverMode(false);
|
|
|
|
LogEvent(SkyscraperContextEvent.ModuleDownloadComplete, String.Format("Module {0}, Version {1}", moduleModuleId, moduleModuleVersion));
|
|
|
|
DatabaseKeyDsmCcModule key = new DatabaseKeyDsmCcModule(CurrentNetworkId.Value, CurrentTransportStreamId.Value, elementaryPid, moduleModuleId, moduleModuleVersion);
|
|
_dataCarouselProgresses.Remove(key);
|
|
|
|
UiJunction?.DsmCcModuleComplete(elementaryPid, moduleModuleId, moduleModuleVersion);
|
|
}
|
|
|
|
public void DsmCcDoItNowEvent(ProgramMapping programMapping, StreamEventDescriptor descriptorListStreamEventDescriptor, int pid)
|
|
{
|
|
if (!CurrentTransportStreamId.HasValue)
|
|
return;
|
|
if (!CurrentNetworkId.HasValue)
|
|
return;
|
|
if (!currentTime.HasValue)
|
|
return;
|
|
DataStorage.StoreDsmCcDoItNowEvent(currentTime.Value, CurrentNetworkId.Value, CurrentNetworkId.Value, programMapping.ProgramNumber, descriptorListStreamEventDescriptor, pid);
|
|
|
|
//These "do-it-now" Events are specific for _EVERY_ application and probably not
|
|
//worth extending the scraping time. They are most likely subtitles or commands
|
|
//for game-show play-along apps. Therefore we won't create a log entry for these, as it
|
|
//would increase scraping duration.
|
|
}
|
|
|
|
private List<DatabaseKeyDsmCcModule> dedupModules = new List<DatabaseKeyDsmCcModule>();
|
|
public void PreventDsmCcModuleRepetition(int elementaryPid, ushort moduleModuleId, byte moduleModuleVersion)
|
|
{
|
|
dedupModules.Add(new DatabaseKeyDsmCcModule(CurrentNetworkId.Value, CurrentTransportStreamId.Value, elementaryPid, moduleModuleId, moduleModuleVersion));
|
|
}
|
|
|
|
public void SetRdsProgrammeServiceName(int programNumber, string programmeService2)
|
|
{
|
|
if (DataStorage.UpdateRdsProgrammeServiceName(CurrentNetworkId.Value, CurrentTransportStreamId.Value,
|
|
programNumber, programmeService2))
|
|
{
|
|
LogEvent(SkyscraperContextEvent.RdsProgrammeServiceName,
|
|
String.Format("{0:X4}, {1:X4}", programNumber, programmeService2));
|
|
}
|
|
}
|
|
|
|
public bool TestCanHandleRdsMessages(int programNumber)
|
|
{
|
|
if (!CurrentNetworkId.HasValue)
|
|
return false;
|
|
|
|
if (!CurrentTransportStreamId.HasValue)
|
|
return false;
|
|
|
|
if (!DataStorage.TestForKnownRdsData(CurrentNetworkId.Value, CurrentTransportStreamId.Value,
|
|
programNumber))
|
|
{
|
|
DataStorage.EnableRdsCollection(CurrentNetworkId.Value, CurrentTransportStreamId.Value,
|
|
programNumber);
|
|
LogEvent(SkyscraperContextEvent.BeginRdsRecording,
|
|
String.Format("{0:X4},{1:X4},{2:X4}", CurrentNetworkId.Value, CurrentTransportStreamId.Value,
|
|
programNumber));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public void SetRdsRadioText(int programNumber, string text)
|
|
{
|
|
if (DataStorage.UpdateRdsRadioText(CurrentNetworkId.Value, CurrentTransportStreamId.Value, programNumber,
|
|
text))
|
|
{
|
|
LogEvent(SkyscraperContextEvent.RdsText, String.Format("{0:X4} [{1}]", programNumber, text));
|
|
}
|
|
}
|
|
|
|
public void NotifySpliceInsert(ushort ProgramNumber, SpliceInsert spliceInsert)
|
|
{
|
|
UiJunction?.NotifyScte35(ProgramNumber, spliceInsert);
|
|
if (!CurrentNetworkId.HasValue)
|
|
return;
|
|
|
|
if (!CurrentTransportStreamId.HasValue)
|
|
return;
|
|
|
|
if (!DataStorage.TestForScte35SpliceInsert(CurrentNetworkId.Value, CurrentTransportStreamId.Value,
|
|
ProgramNumber, spliceInsert))
|
|
{
|
|
DataStorage.StoreScte35SpliceInsert(CurrentNetworkId.Value, CurrentTransportStreamId.Value, ProgramNumber, spliceInsert);
|
|
LogEvent(SkyscraperContextEvent.Scte35Splice,
|
|
String.Format("Program {0:X4}, Splice at {1:X16}", ProgramNumber, spliceInsert.SpliceTime));
|
|
}
|
|
}
|
|
|
|
public void NotifyTimeSignal(ushort programNumber, TimeSignal timeSignal)
|
|
{
|
|
UiJunction?.NotifyScte35(programNumber, timeSignal);
|
|
if (!CurrentNetworkId.HasValue)
|
|
return;
|
|
|
|
if (!CurrentTransportStreamId.HasValue)
|
|
return;
|
|
|
|
if (!currentTime.HasValue)
|
|
return;
|
|
|
|
DataStorage.SetScte35TimeSignal(CurrentNetworkId.Value, CurrentTransportStreamId.Value, currentTime.Value, programNumber, timeSignal);
|
|
LogEvent(SkyscraperContextEvent.Scte35TimeSignal, String.Format("Program {0:X4}, Time Signal {1:X16}", programNumber, timeSignal.Value));
|
|
}
|
|
|
|
public void NotifyOfCompliance(string compliance)
|
|
{
|
|
if (!CurrentNetworkId.HasValue)
|
|
return;
|
|
|
|
if (!CurrentTransportStreamId.HasValue)
|
|
return;
|
|
|
|
if (!DataStorage.IsCompliant(CurrentNetworkId.Value, CurrentTransportStreamId.Value, compliance))
|
|
{
|
|
DataStorage.MarkAsCompliant(CurrentNetworkId.Value, CurrentTransportStreamId.Value, compliance);
|
|
LogEvent(SkyscraperContextEvent.Compliance, compliance);
|
|
}
|
|
}
|
|
|
|
public void NotifiyStationIdentification(string stationIdentification)
|
|
{
|
|
if (!CurrentNetworkId.HasValue)
|
|
return;
|
|
|
|
if (!CurrentTransportStreamId.HasValue)
|
|
return;
|
|
|
|
if (DataStorage.SetStationIdentification(CurrentNetworkId.Value, CurrentTransportStreamId.Value,
|
|
stationIdentification))
|
|
{
|
|
LogEvent(SkyscraperContextEvent.StationIdentification, stationIdentification);
|
|
}
|
|
}
|
|
|
|
public void AutodetectionSucessful(int pid, Contestant contestant, ProgramContext programContext)
|
|
{
|
|
UiJunction?.NotifyStreamTypeDetection(contestant.Tag, pid);
|
|
LogEvent(SkyscraperContextEvent.StreamTypeAutodetection, String.Format("{0} on PID {1:X4}", contestant.Tag, pid));
|
|
contestant.DeclareWinner(this, pid, programContext);
|
|
return;
|
|
}
|
|
|
|
|
|
private IpTrafficHandler ipTrafficHandler;
|
|
public void OnIpDatagram(int pid, byte[] payload)
|
|
{
|
|
if (ipTrafficHandler == null)
|
|
{
|
|
StorageConnectionManager storageConnectionManager = StorageConnectionManager.GetInstance();
|
|
ipTrafficHandler = storageConnectionManager.GetDefaultIpTrafficHandler();
|
|
}
|
|
if (payload.Length == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
ipTrafficHandler?.HandlePacket(pid, payload);
|
|
|
|
int ipVersion = (payload[0] & 0xf0) >> 4;
|
|
if (ipVersion == 4)
|
|
{
|
|
if (Array.TrueForAll(payload, x => x == 0))
|
|
return;
|
|
int ihl = (payload[0] & 0x0f);
|
|
ihl *= 32;
|
|
ihl /= 8;
|
|
byte[] ipv4Header = new byte[ihl];
|
|
if (payload.Length < ihl)
|
|
return;
|
|
Array.Copy(payload, 0, ipv4Header, 0, ihl);
|
|
InternetHeader internetHeader = new InternetHeader(ipv4Header);
|
|
if (!internetHeader.ChecksumValid)
|
|
return;
|
|
byte[] ipv4Packet = new byte[payload.Length - ihl];
|
|
Array.Copy(payload, ihl, ipv4Packet, 0, payload.Length - ihl);
|
|
OnIpv4PacketArrival(internetHeader, ipv4Packet);
|
|
}
|
|
else if (ipVersion == 6)
|
|
{
|
|
if (payload.Length < 40)
|
|
{
|
|
return;
|
|
}
|
|
MemoryStream ipv6Stream = new MemoryStream(payload, false);
|
|
byte byteA = ipv6Stream.ReadUInt8();
|
|
byte byteB = ipv6Stream.ReadUInt8();
|
|
|
|
int trafficClass = (byteA & 0x0f) << 4;
|
|
trafficClass = ((byteB & 0xf0) >> 4);
|
|
|
|
int flowLabel = (byteB & 0x0f) << 16;
|
|
flowLabel += ipv6Stream.ReadUInt16BE();
|
|
|
|
ushort payloadLength = ipv6Stream.ReadUInt16BE();
|
|
byte nextHeader = ipv6Stream.ReadUInt8();
|
|
byte hopLimit = ipv6Stream.ReadUInt8();
|
|
|
|
IPAddress sourceAddress = new IPAddress(ipv6Stream.ReadBytes(16));
|
|
IPAddress destinationAddress = new IPAddress(ipv6Stream.ReadBytes(16));
|
|
InternetHeader ipv6Header = new InternetHeader(trafficClass, flowLabel, payloadLength, nextHeader, hopLimit, sourceAddress, destinationAddress);
|
|
if (ipv6Stream.GetAvailableBytes() >= payloadLength)
|
|
{
|
|
OnIpv4PacketArrival(ipv6Header, ipv6Stream.ReadBytes(payloadLength));
|
|
}
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
//throw new Exception("invalid ip packet");
|
|
}
|
|
}
|
|
|
|
public void GsIpTrafficDetected()
|
|
{
|
|
LogEvent(SkyscraperContextEvent.SpecialTsMode, "Valid IP Traffic detected");
|
|
}
|
|
|
|
private HashSet<IpTrafficInfo> trafficInfos;
|
|
private ReadOnlyCollection<ISkyscraperMpePlugin> mpePlugins;
|
|
public SkyscraperDnsCache DnsCache { get; private set; }
|
|
|
|
private bool ProcessDns(IpTrafficInfo trafficInfo, byte[] ipv4Packet)
|
|
{
|
|
if (DnsCache == null)
|
|
DnsCache = new SkyscraperDnsCache(DataStorage);
|
|
//Handle DNS
|
|
bool result = false;
|
|
if (trafficInfo.Protocol == 0x11)
|
|
{
|
|
UserDatagram udpPacket = new UserDatagram(ipv4Packet);
|
|
if (udpPacket.Valid)
|
|
{
|
|
if (udpPacket.SourcePort == 53)
|
|
{
|
|
result = DnsCache.ProcessDnsPacket(udpPacket.Payload);
|
|
}
|
|
}
|
|
}
|
|
trafficInfo.SourceName = DnsCache.GetDnsName(trafficInfo.Source);
|
|
trafficInfo.TargetName = DnsCache.GetDnsName(trafficInfo.Target);
|
|
return result;
|
|
}
|
|
|
|
public void OnIpv4PacketArrival(InternetHeader internetHeader, byte[] ipv4Packet)
|
|
{
|
|
//There are as many use cases as there are internet applications in these.
|
|
//Not really required to decode all of them, if you ask me.
|
|
/*
|
|
* One known use is LX9SES on Astra 23.5, with the following arguments:
|
|
* -> Destination Address always 228.64.0.56
|
|
* -> Source Address always 10.225.49.1
|
|
* -> Protocol always 0x11 (UDP)
|
|
*/
|
|
if (trafficInfos == null)
|
|
trafficInfos = new HashSet<IpTrafficInfo>();
|
|
|
|
IpTrafficInfo iti = IpTrafficInfo.FromInternetHeader(internetHeader);
|
|
if (ProcessDns(iti, ipv4Packet))
|
|
{
|
|
LogEvent(SkyscraperContextEvent.LearnDns, String.Format("{0} = {1}", DnsCache.LastLearned.Value, DnsCache.LastLearned.Key));
|
|
}
|
|
UiJunction?.NotifyMpeTraffic(iti, ipv4Packet.Length);
|
|
|
|
if (trafficInfos.Add(iti))
|
|
{
|
|
LogEvent(SkyscraperContextEvent.IpTraffic, iti.ToString());
|
|
}
|
|
|
|
if (mpePlugins == null)
|
|
{
|
|
mpePlugins = PluginManager.GetInstance().GetMpePlugins();
|
|
object[] connector = StorageConnectionManager.GetPluginConnectors(DataStorage, ObjectStorage);
|
|
foreach (ISkyscraperMpePlugin plugin in mpePlugins)
|
|
{
|
|
try
|
|
{
|
|
plugin.ConnectToStorage(connector);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
foreach (ISkyscraperMpePlugin plugin in mpePlugins)
|
|
{
|
|
if (plugin.CanHandlePacket(internetHeader, ipv4Packet))
|
|
{
|
|
plugin.SetContext(currentTime,this);
|
|
plugin.HandlePacket(internetHeader, ipv4Packet);
|
|
if (plugin.StopProcessingAfterThis())
|
|
return;
|
|
}
|
|
}
|
|
/*
|
|
if (internetHeader.Protocol == 0x11)
|
|
{
|
|
UserDatagram userDatagram = new UserDatagram(ipv4Packet);
|
|
if (!userDatagram.Valid)
|
|
return;
|
|
|
|
if (internetHeader.SourceAddress.Equals(_fpUrmetSrc) && internetHeader.DestinationAddress.Equals(_fpUrmetDst))
|
|
{
|
|
//Found on Astra 19.2 12604/H - not the slightest idea what this does.
|
|
//Service name is FP URMET.
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
ipPackets++;
|
|
}
|
|
}
|
|
else if (internetHeader.Protocol == 0x02)
|
|
{
|
|
IgmpMessage igmpMessage = new IgmpMessage(ipv4Packet);
|
|
//Also Found on Astra 19.2 12604/H, I don't think we'll need these.
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
ipPackets++;
|
|
}*/
|
|
}
|
|
|
|
public void NotifyRunningStatus(uint transportStreamId, uint originalNetworkId, uint serviceId, uint eventId,
|
|
RunningStatus runningStatus)
|
|
{
|
|
if (!currentTime.HasValue)
|
|
return;
|
|
|
|
if (DataStorage.StoreRunningStatus(transportStreamId, originalNetworkId, serviceId, eventId, runningStatus, currentTime.Value))
|
|
{
|
|
LogEvent(SkyscraperContextEvent.RunningStatusChange, String.Format("({0:X4},{1:X4},{2:X4},{3:X4}) -> {4}", originalNetworkId, transportStreamId, serviceId, eventId, runningStatus));
|
|
}
|
|
}
|
|
|
|
public bool EnableTimeout { get; set; }
|
|
|
|
public int TimeoutSeconds { get; set; }
|
|
|
|
public bool CancelOnNextPacket { get; set; }
|
|
public ISkyscraperUiJunction UiJunction { get; set; }
|
|
|
|
public bool IsAbortConditionMet()
|
|
{
|
|
if (ffmpegFrameGrabber.BusyFrameGrabbers > 0)
|
|
return false;
|
|
|
|
if (EnableTimeout && TimeoutSeconds == 0)
|
|
TimeoutSeconds = 10;
|
|
|
|
if (!firstPacketDone)
|
|
return false;
|
|
|
|
if (CancelOnNextPacket)
|
|
{
|
|
LogEvent(SkyscraperContextEvent.AbortConditionMet, "Aborted from another thread.");
|
|
CancelOnNextPacket = false;
|
|
return true;
|
|
}
|
|
|
|
if (EnableTimeout)
|
|
{
|
|
TimeSpan sinceLastEvent = DateTime.Now - lastEventTimestamp;
|
|
if (sinceLastEvent.TotalSeconds > TimeoutSeconds)
|
|
{
|
|
LogEvent(SkyscraperContextEvent.AbortConditionMet, String.Format("Event Timeout after {0} seconds", TimeoutSeconds));
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public void OnT2MiPacketLoss(int pid, byte expectedPacket, T2MIHeader header)
|
|
{
|
|
}
|
|
|
|
public void OnT2MiPacket(int pid, byte basebandFramePlpId, byte[] basebandPacket)
|
|
{
|
|
if (subSkyscrapers == null)
|
|
subSkyscrapers = new Dictionary<object, SkyscraperContext>();
|
|
|
|
T2MiSubSkyscraperIdentifier subSkyscraperIdentifier = new T2MiSubSkyscraperIdentifier(pid, basebandFramePlpId);
|
|
if (!subSkyscrapers.ContainsKey(subSkyscraperIdentifier))
|
|
{
|
|
LogEvent(SkyscraperContextEvent.T2MiPlpDetected, String.Format("PID {0:X4}, PLP {1}", pid, basebandFramePlpId));
|
|
}
|
|
|
|
OnSubTsPacket(subSkyscraperIdentifier, basebandPacket);
|
|
}
|
|
|
|
private Dictionary<object, SkyscraperContext> subSkyscrapers;
|
|
public void OnSubTsPacket(object identifier, byte[] packet)
|
|
{
|
|
if (subSkyscrapers == null)
|
|
subSkyscrapers = new Dictionary<object, SkyscraperContext>();
|
|
|
|
if (!subSkyscrapers.ContainsKey(identifier))
|
|
{
|
|
SkyscraperContext child = new SkyscraperContext(new TsContext(), DataStorage, ObjectStorage);
|
|
child.IsChild = true;
|
|
child.ChildName = identifier.ToString();
|
|
child.ChildTimestamp = DateTime.Now.Ticks;
|
|
|
|
List<IPacketFilter> packetFilters = new List<IPacketFilter>();
|
|
StorageConnectionManager storageConnectionManager = StorageConnectionManager.GetInstance();
|
|
if (storageConnectionManager.IsSubTsDumpingEnabled())
|
|
{
|
|
string filename = String.Format("{0}_{1}.ts", child.ChildName.SanitizeFileName(), child.ChildTimestamp);
|
|
FileInfo fi = new FileInfo(filename);
|
|
TsRecorder packetDumper = new TsRecorder();
|
|
packetDumper.RecordingOutputDirectory = new DirectoryInfo(".");
|
|
packetDumper.SetNextFilename(filename);
|
|
packetDumper.CreateBufferedStream();
|
|
packetFilters.Add(packetDumper);
|
|
}
|
|
|
|
child.InitalizeFilterChain(packetFilters.ToArray());
|
|
subSkyscrapers.Add(identifier, child);
|
|
}
|
|
|
|
SkyscraperContext context = subSkyscrapers[identifier];
|
|
context.IngestSinglePacket(packet);
|
|
}
|
|
public long ChildTimestamp { get; private set; }
|
|
public string ChildName { get; private set; }
|
|
public bool IsChild { get; private set; }
|
|
|
|
public void OnT2MiTimestamp(int pid, _0x20_DvbT2Timestamp t2Timestamp)
|
|
{
|
|
if (!CurrentNetworkId.HasValue)
|
|
return;
|
|
|
|
if (!CurrentTransportStreamId.HasValue)
|
|
return;
|
|
|
|
if (subSkyscrapers == null)
|
|
return;
|
|
|
|
List<SkyscraperContext> contexts = new List<SkyscraperContext>();
|
|
foreach (KeyValuePair<object, SkyscraperContext> subSkyscraper in subSkyscrapers)
|
|
{
|
|
object subSkyscraperKey = subSkyscraper.Key;
|
|
if (!(subSkyscraperKey is T2MiSubSkyscraperIdentifier))
|
|
continue;
|
|
|
|
T2MiSubSkyscraperIdentifier identifier = subSkyscraperKey as T2MiSubSkyscraperIdentifier;
|
|
if (identifier.Pid != pid)
|
|
continue;
|
|
|
|
contexts.Add(subSkyscraper.Value);
|
|
}
|
|
|
|
if (contexts.Count == 0)
|
|
return;
|
|
|
|
long sum = contexts.Select(x => (long)x.EventsLogged).Sum();
|
|
if (sum < (contexts.Count * 2))
|
|
return;
|
|
|
|
DateTime resolveTime = t2Timestamp.ResolveTime();
|
|
DateTime knownTimestamp = DataStorage.T2MiGetTimestamp(CurrentNetworkId.Value, CurrentTransportStreamId.Value, pid);
|
|
if (resolveTime > knownTimestamp)
|
|
{
|
|
DataStorage.T2MiSetTimestamp(CurrentNetworkId.Value, CurrentTransportStreamId.Value, pid, resolveTime);
|
|
}
|
|
}
|
|
|
|
public void OnT2MiIqData(int relatedPid, _0x31_IqData iqData)
|
|
{
|
|
|
|
}
|
|
|
|
public void OnT2MiIqData(int relatedPid, _0x01_IqData iqData2)
|
|
{
|
|
|
|
}
|
|
|
|
public void OnT2MiError(int relatedPid, int skyscraperErrorCode)
|
|
{
|
|
|
|
}
|
|
|
|
public void OnT2MiL1Current(int relatedPid, _0x10_L1Current l1Current)
|
|
{
|
|
//No clue about this data. It's not really interesting.
|
|
}
|
|
|
|
public void OnT2MiArbitraryCellInsertion(int relatedPid, _0x02_ArbitraryCellInsertion arbitraryCellInsertion)
|
|
{
|
|
|
|
}
|
|
|
|
public void OnT2MiBalancingCells(int relatedPid, byte frameIndex, uint numActiveBiasCellsPerP2)
|
|
{
|
|
|
|
}
|
|
|
|
public void OnT2MiL1Future(int relatedPid, _0x11_L1Future l1Future)
|
|
{
|
|
if (l1Future.FutureData.L1DynNext.Length == 0)
|
|
return;
|
|
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public void OnT2MiIndividualAddressing(int relatedPid, _0x21_IndividualAddressing individualAddressing)
|
|
{
|
|
if (!CurrentNetworkId.HasValue)
|
|
return;
|
|
|
|
if (!CurrentTransportStreamId.HasValue)
|
|
return;
|
|
|
|
if (subSkyscrapers == null)
|
|
return;
|
|
|
|
bool any = subSkyscrapers.Where(x => x.Key is T2MiSubSkyscraperIdentifier)
|
|
.Select(x => (T2MiSubSkyscraperIdentifier)x.Key)
|
|
.Any();
|
|
if (!any)
|
|
return;
|
|
|
|
foreach (AddressingFunction function in individualAddressing.Commands)
|
|
{
|
|
if (!DataStorage.T2MiTestForTransmitter(CurrentNetworkId, CurrentTransportStreamId, relatedPid, function.TxIdentifier))
|
|
DataStorage.T2MiRememberTransmitter(CurrentNetworkId, CurrentTransportStreamId, relatedPid, function.TxIdentifier);
|
|
|
|
switch (function.Tag)
|
|
{
|
|
case 0x00:
|
|
_0x00_TransmitterTimeOffset tto = (_0x00_TransmitterTimeOffset)function;
|
|
DataStorage.T2MiSetTransmitterTimeOffset(CurrentNetworkId, CurrentTransportStreamId, relatedPid, function.TxIdentifier, tto.TimeOffset);
|
|
break;
|
|
default:
|
|
throw new NotImplementedException(String.Format("Individual Addressing function 0x{0:X2}", function.Tag));
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool TcpProxyEnabled
|
|
{
|
|
get => DvbContext.TcpProxyEnabled;
|
|
set => DvbContext.TcpProxyEnabled = value;
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
if (TcpProxyEnabled)
|
|
TcpProxyEnabled = false;
|
|
|
|
|
|
if (ipTrafficHandler != null)
|
|
{
|
|
ipTrafficHandler.Dispose();
|
|
}
|
|
|
|
|
|
if (DvbContext.TcpProxyEnabled)
|
|
{
|
|
DvbContext.TcpProxyEnabled = false;
|
|
}
|
|
|
|
if (docsisPacketProcessor != null)
|
|
{
|
|
docsisPacketProcessor.Dispose();
|
|
}
|
|
|
|
if (!SourceIsDisk)
|
|
{
|
|
if (_dataCarouselProgresses != null)
|
|
{
|
|
foreach (KeyValuePair<DatabaseKeyDsmCcModule, double> item in _dataCarouselProgresses)
|
|
{
|
|
if (item.Value == 0.0)
|
|
continue;
|
|
|
|
DataStorage.FailDsmCcDownload(item.Key, item.Value);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (_pluginContexts != null)
|
|
{
|
|
foreach (object plugin in _pluginContexts)
|
|
{
|
|
IDisposable disposable = plugin as IDisposable;
|
|
if (disposable != null)
|
|
disposable.Dispose();
|
|
}
|
|
_pluginContexts.Clear();
|
|
}
|
|
|
|
DataStorage.WaitForCompletion();
|
|
}
|
|
|
|
public bool TestForFrame(int currentNetworkId, int transportStreamId, ushort mappingProgramNumber, int mappingStreamElementaryPid)
|
|
{
|
|
return ObjectStorage.TestForFramegrab(currentNetworkId, transportStreamId, mappingProgramNumber, mappingStreamElementaryPid);
|
|
}
|
|
|
|
public void NotifyCompletedFrame(int currentNetworkId, int transportStreamId, ushort mappingProgramNumber,
|
|
int mappingStreamElementaryPid, byte[] imageData)
|
|
{
|
|
ObjectStorage.StoreFramegrab(currentNetworkId, transportStreamId, mappingProgramNumber, (ushort)mappingStreamElementaryPid, imageData);
|
|
LogEvent(SkyscraperContextEvent.GotFramegrab, String.Format("Program {0:X4}, PID {1:X4}", mappingProgramNumber, mappingStreamElementaryPid));
|
|
UiJunction?.ShowFramegrab(currentNetworkId, transportStreamId, mappingProgramNumber, mappingStreamElementaryPid, imageData);
|
|
}
|
|
|
|
public void OnIpMacNotification(int sourcePid, Dvb.DataBroadcasting.IntModel.Platform platform, Target target, Operational operational)
|
|
{
|
|
IEnumerable<IpMacNotification> ipMacNotifications = IpMacNotification.FlatMap(platform, target, operational);
|
|
foreach (IpMacNotification notification in ipMacNotifications)
|
|
{
|
|
if (!DataStorage.TestForIpMacNotification(notification))
|
|
{
|
|
DataStorage.StoreIpMacNotification(notification);
|
|
LogEvent(SkyscraperContextEvent.IpMacNotification, String.Format("{0} -> {1}", platform.Name, notification.TargetName));
|
|
}
|
|
}
|
|
}
|
|
|
|
public void OnRelatedContent(Rct result, ushort resultProgramNumber)
|
|
{
|
|
if (result.LinkInfo.Length == 0)
|
|
return;
|
|
if (runningEvents == null)
|
|
return;
|
|
if (runningEvents[resultProgramNumber] == null)
|
|
return;
|
|
|
|
EitEvent lEvent = runningEvents[resultProgramNumber];
|
|
|
|
foreach (RctLinkInfo rctLinkInfo in result.LinkInfo)
|
|
{
|
|
if (!DataStorage.TestForRelatedContent(lEvent, rctLinkInfo))
|
|
{
|
|
DataStorage.SetRelatedContent(lEvent, rctLinkInfo);
|
|
LogEvent(SkyscraperContextEvent.RelatedContent, String.Format("Program {0}, Event {1} -> {2}", resultProgramNumber, lEvent.EventId, rctLinkInfo.GetIdentifierString()));
|
|
}
|
|
}
|
|
}
|
|
|
|
private bool warnedMissingLocation;
|
|
|
|
private void WarnMissingLocation()
|
|
{
|
|
if (!warnedMissingLocation)
|
|
{
|
|
LogEvent(SkyscraperContextEvent.LocationMissing);
|
|
warnedMissingLocation = true;
|
|
}
|
|
}
|
|
|
|
public void OnParticipantDetected(PhysicalAddress pa)
|
|
{
|
|
int? currentLocation = DataStorage.GetCurrentLocationId();
|
|
if (currentLocation.HasValue)
|
|
{
|
|
LogEvent(SkyscraperContextEvent.DocsisParticipant, BitConverter.ToString(pa.GetAddressBytes()));
|
|
DataStorage.StoreDocsisParticipant(pa, currentLocation.Value);
|
|
}
|
|
else
|
|
{
|
|
WarnMissingLocation();
|
|
}
|
|
}
|
|
|
|
public void OnCmtsTimestamp(PhysicalAddress source, uint timing)
|
|
{
|
|
//DOCSIS Timestamps are not interesting to us.
|
|
}
|
|
|
|
public void OnUpstreamChannel(UpstreamChannelDescriptor mmm)
|
|
{
|
|
UiJunction?.NotifyDocsisFrequency(mmm.Frequency, true, mmm);
|
|
if (!mmm.Frequency.HasValue)
|
|
return;
|
|
|
|
int? currentLocation = DataStorage.GetCurrentLocationId();
|
|
if (!currentLocation.HasValue)
|
|
{
|
|
WarnMissingLocation();
|
|
return;
|
|
}
|
|
|
|
if (!DataStorage.TestForDocsisUpstreamChannel(mmm.Source, mmm.Frequency.Value,currentLocation.Value))
|
|
{
|
|
DataStorage.StoreDocsisUpstreamChannel(mmm, currentLocation.Value);
|
|
LogEvent(SkyscraperContextEvent.DocsisUpstreamChannel, String.Format("{0} -> {1}", mmm.Source.ToString(), mmm.Frequency.Value));
|
|
}
|
|
}
|
|
|
|
public void OnDownstreamChannel(PhysicalAddress physicalAddress, MacDomainDescriptor.DownstreamActiveChannel downstreamActiveChannel)
|
|
{
|
|
UiJunction?.NotifyDocsisFrequency(downstreamActiveChannel.Frequency, false, downstreamActiveChannel);
|
|
|
|
if (!downstreamActiveChannel.Frequency.HasValue)
|
|
return;
|
|
|
|
int? currentLocation = DataStorage.GetCurrentLocationId();
|
|
if (!currentLocation.HasValue)
|
|
{
|
|
WarnMissingLocation();
|
|
return;
|
|
}
|
|
|
|
if (!DataStorage.TestForDocsisDownstreamChannel(physicalAddress, downstreamActiveChannel,currentLocation.Value))
|
|
{
|
|
DataStorage.StoreDocsisDownstreamChannel(physicalAddress, downstreamActiveChannel,currentLocation.Value);
|
|
LogEvent(SkyscraperContextEvent.DocsisDownstreamChannel, String.Format("{0} -> {1}", physicalAddress.ToString(), downstreamActiveChannel.Frequency.Value));
|
|
}
|
|
}
|
|
|
|
public void OnLearnedIpFromMac(PhysicalAddress arpHeaderSenderHardwareAddress, IPAddress arpHeaderSenderProtocolAddress)
|
|
{
|
|
int? currentLocation = DataStorage.GetCurrentLocationId();
|
|
if (!currentLocation.HasValue)
|
|
{
|
|
WarnMissingLocation();
|
|
return;
|
|
}
|
|
|
|
if (DataStorage.SetCmtsIp(arpHeaderSenderHardwareAddress, arpHeaderSenderProtocolAddress))
|
|
{
|
|
LogEvent(SkyscraperContextEvent.LearnedCmtsIp,String.Format("{0} -> {1}",arpHeaderSenderHardwareAddress.ToString(),arpHeaderSenderProtocolAddress.ToString()));
|
|
}
|
|
}
|
|
|
|
public void OnAbertisPacket(int pid, byte[] outBuffer)
|
|
{
|
|
AbertisSubSkyscraperKey key = new AbertisSubSkyscraperKey(pid);
|
|
OnSubTsPacket(key, outBuffer);
|
|
}
|
|
|
|
public void OnAbertisSyncLoss(int pid, int oldPosition = -1, long newPosition = -1)
|
|
{
|
|
if (subSkyscrapers == null)
|
|
DvbContext.RegisterPacketProcessor(pid, new PacketDiscarder());
|
|
|
|
AbertisSubSkyscraperKey key = new AbertisSubSkyscraperKey(pid);
|
|
if (!subSkyscrapers.ContainsKey(key) == null)
|
|
DvbContext.RegisterPacketProcessor(pid, new PacketDiscarder());
|
|
|
|
if (!subSkyscrapers[key].firstPacketDone)
|
|
DvbContext.RegisterPacketProcessor(pid, new PacketDiscarder());
|
|
else
|
|
throw new NotImplementedException(
|
|
"I couldn't sync this Abertis-styled stream, and I couldn't figure out why. Could you please share a sample of this stream?");
|
|
}
|
|
|
|
public void OnMonochromeData(int networkId, int transportStreamId, ushort programNumber, MonochromeDataField result)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
void Id3Handler.OnId3Error(Id3ErrorState state)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
void Id3Handler.OnId3Tag(Id3Tag tag)
|
|
{
|
|
if (tag.IsAllPrivate())
|
|
return;
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
void InteractionChannelHandler.OnCorrectionMessage(ushort interactiveNetworkId, Cmt cmt)
|
|
{
|
|
foreach(Cmt.CmtEntry entry in cmt.Entries)
|
|
{
|
|
if (!DataStorage.TestForCmtEntry(interactiveNetworkId,entry))
|
|
{
|
|
LogEvent(SkyscraperContextEvent.CorrectionMessage, String.Format("Network ID = {0}, Group ID = {1}, Logon ID = {2}, Slot Type = {3}", interactiveNetworkId, entry.GroupId, entry.LoginId,entry.SlotType.ToString()));
|
|
DataStorage.InsertCmtEntry(interactiveNetworkId, entry);
|
|
}
|
|
}
|
|
}
|
|
|
|
void InteractionChannelHandler.OnFrameComposition(ushort interactiveNetworkId, Fct fct)
|
|
{
|
|
foreach(Fct.Frame frame in fct.Frames)
|
|
{
|
|
if (!DataStorage.TestForFrameComposition(interactiveNetworkId,frame))
|
|
{
|
|
LogEvent(SkyscraperContextEvent.FrameComposition, String.Format("Network ID = {0}, Frame = {1}", interactiveNetworkId, frame.FrameId));
|
|
DataStorage.InsertFctFrame(interactiveNetworkId, frame);
|
|
}
|
|
}
|
|
}
|
|
|
|
private bool[] interactionChannelErrors;
|
|
void InteractionChannelHandler.OnInteractionChannelError(InteractionChannelErrorState unexpectedTable)
|
|
{
|
|
if (interactionChannelErrors == null)
|
|
{
|
|
interactionChannelErrors = new bool[Enum.GetNames(typeof(InteractionChannelErrorState)).Length];
|
|
}
|
|
int offset = (int)unexpectedTable;
|
|
if (!interactionChannelErrors[offset])
|
|
{
|
|
LogEvent(SkyscraperContextEvent.InteractionChannelError, unexpectedTable.ToString());
|
|
interactionChannelErrors[offset] = true;
|
|
}
|
|
}
|
|
|
|
public void OnRcsMap(Rmt rmt)
|
|
{
|
|
if (rmt.Linkages != null)
|
|
{
|
|
foreach(_0x4a_LinkageDescriptor linkage in rmt.Linkages)
|
|
{
|
|
if (!DataStorage.TestForRmtLinkage(linkage))
|
|
{
|
|
LogEvent(SkyscraperContextEvent.RmtLinkage, String.Format("Interactive Network #{0} -> ONID {1}, TSID {2}", linkage.InteractiveNetworkId, linkage.OriginalNetworkId, linkage.TransportStreamId));
|
|
DataStorage.InsertRmtLinkage(linkage);
|
|
}
|
|
}
|
|
}
|
|
if (rmt.TransportStreams != null)
|
|
{
|
|
foreach(Rmt.TransportStream transportStream in rmt.TransportStreams)
|
|
{
|
|
if (!DataStorage.TestForRmtTransportStream(rmt.NetworkId, transportStream))
|
|
{
|
|
LogEvent(SkyscraperContextEvent.RmtTransportStream, String.Format("{0} -> {1},{2}", rmt.NetworkId, transportStream.OriginalNetworkId, transportStream.TransportStreamId));
|
|
DataStorage.InsertRmtTransportStream(rmt.NetworkId, transportStream);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void CheckForHiddenMpes()
|
|
{
|
|
uint threshold = 1000;
|
|
if (pmtTracker != null)
|
|
{
|
|
if (!pmtTracker.AllPmtsProcessed)
|
|
{
|
|
return;
|
|
}
|
|
|
|
threshold = 1;
|
|
}
|
|
int[] occupiedPids = DvbContext.GetOccupiedPids();
|
|
ulong[] pidStatistics = DvbContext.GetPidStatistics();
|
|
for (int i = 0; i < (pidStatistics.Length - 1); i++)
|
|
{
|
|
if (pidStatistics[i] > threshold)
|
|
{
|
|
if (!occupiedPids.Contains(i))
|
|
{
|
|
LogEvent(SkyscraperContextEvent.SpecialTsMode, String.Format("Attaching stream-type autodetector to PID 0x{0:X4}", i));
|
|
DvbContext.RegisterPacketProcessor(i, new StreamTypeAutodetection(i, this));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void OnSatellitePosition(ushort interactiveNetworkId, Spt spt)
|
|
{
|
|
foreach (Spt.Satellite satellite in spt.Satellites)
|
|
{
|
|
if (!DataStorage.TestForSatellitePosition(interactiveNetworkId,satellite))
|
|
{
|
|
LogEvent(SkyscraperContextEvent.SatellitePosition, String.Format("NID = {0}, Satellite ID = {1} (X = {2}, Y = {3}, Z = {4})", interactiveNetworkId, satellite.Id, satellite.X, satellite.Y, satellite.Z));
|
|
DataStorage.StoreSatellitePosition(interactiveNetworkId, satellite);
|
|
}
|
|
}
|
|
}
|
|
|
|
void InteractionChannelHandler.OnSuperframeComposition(ushort interactiveNetworkId, Sct sct)
|
|
{
|
|
foreach(Sct.Superframe superframe in sct.Superframes)
|
|
{
|
|
if (!DataStorage.TestForSuperframeComposition(interactiveNetworkId,superframe))
|
|
{
|
|
LogEvent(SkyscraperContextEvent.SuperframeComposition, String.Format("Network ID = {0}, Superframe ID = {1}, Frequency = {2}/{3}", interactiveNetworkId, superframe.SuperframeId, superframe.SuperframeCentreFrequency,superframe.UplinkPolarization));
|
|
DataStorage.StoreSuperframeComposition(interactiveNetworkId, superframe);
|
|
}
|
|
}
|
|
}
|
|
|
|
void InteractionChannelHandler.OnTerminalBurstTimePlan(ushort interactiveNetworkId, Tbtp tbtp)
|
|
{
|
|
foreach (Tbtp.TbtpFrame frame in tbtp.Frames)
|
|
{
|
|
foreach (Tbtp.TbtpFrame.BtpEntity btp in frame.BTP)
|
|
{
|
|
if (DataStorage.TestForTerminalBurstTimePlan(interactiveNetworkId,tbtp.GroupId,btp.LogonId))
|
|
{
|
|
LogEvent(SkyscraperContextEvent.TerminalBurstTimePlan, String.Format("Network ID = {0}, Group ID = {1}, Logon ID = {2}", interactiveNetworkId, tbtp.GroupId, btp.LogonId));
|
|
DataStorage.StoreTerminalBurstTimePlan(interactiveNetworkId, tbtp.GroupId, tbtp.SuperframeCount, frame.FrameNumber, btp);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void OnTimeslotComposition(ushort interactiveNetworkId, Tct tct)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public void OnTerminalBurstTimePlan2(ushort interactiveNetworkId, Tbtp2 tbtp2)
|
|
{
|
|
foreach (Tbtp2.Frame frame in tbtp2.Frames)
|
|
{
|
|
for (int assignmentOrdinal = 0; assignmentOrdinal < frame.Assignments.Length; assignmentOrdinal++)
|
|
{
|
|
Tbtp2.Assignment assignment = frame.Assignments[assignmentOrdinal];
|
|
if (!DataStorage.TestForTerminalBurstTimePlan2(interactiveNetworkId, tbtp2.GroupId, frame.FrameNumber))
|
|
{
|
|
LogEvent(SkyscraperContextEvent.TerminalBurstTimePlan2, String.Format("Network ID = {0}, Group ID = {1}, Frame Number = {2}",interactiveNetworkId,tbtp2.GroupId,frame.FrameNumber));
|
|
DataStorage.StoreTerminalBurstTimePlan2(interactiveNetworkId, tbtp2.GroupId, frame);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void OnFrameComposition2(ushort networkId, Fct2 fct2)
|
|
{
|
|
foreach (Fct2.Frame frame in fct2.FrameTypes)
|
|
{
|
|
if (!DataStorage.TestForFrameComposition2(networkId, frame))
|
|
{
|
|
LogEvent(SkyscraperContextEvent.FrameComposition2,String.Format("Network ID = {0}, Frame Type = {1}",networkId,frame.FrameType));
|
|
DataStorage.InsertFct2Frame(networkId, frame);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void OnBroadcastConfiguration(ushort networkId, Bct bct)
|
|
{
|
|
foreach (Bct.BroadcastConfiguration txType in bct.TxTypes)
|
|
{
|
|
if (!DataStorage.TestForBroadcastConfiguration(networkId, txType.TxType))
|
|
{
|
|
LogEvent(SkyscraperContextEvent.BroadcastConfiguration, String.Format("Network ID = {0}, Broadcast Configuration #{1}", networkId, txType.TxType));
|
|
DataStorage.InsertBroadcastConfiguration(networkId, txType);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void OnRcs2Nit(ushort interactiveNetworkId, RcsNit nit)
|
|
{
|
|
if (!DataStorage.TestForRcs2Nit(nit))
|
|
{
|
|
LogEvent(SkyscraperContextEvent.Rcs2Network, String.Format("Network ID #{0}, ({1}) on {2}", nit.OriginalNetworkId, nit.NetworkName, nit.SatelliteDeliverySystem.ToString()));
|
|
DataStorage.InsertRcs2Nit(nit);
|
|
}
|
|
}
|
|
|
|
public void OnRcs2Tdt(ushort interactiveNetworkId, Rcs2Tdt tdt)
|
|
{
|
|
LogEvent(SkyscraperContextEvent.Rcs2TdtTime, String.Format("Network ID #{0}, {1}", interactiveNetworkId, tdt.Timestamp));
|
|
DataStorage.UpdateRcs2Tdt(interactiveNetworkId, tdt.Timestamp);
|
|
}
|
|
|
|
public void OnTransmissionModeSupport2(ushort interactiveNetworkId, Tmst2 tmst2)
|
|
{
|
|
foreach (Tmst2.TransmissionMode mode in tmst2.TransmissionModes)
|
|
{
|
|
if (!DataStorage.TestForTmst2(interactiveNetworkId, mode))
|
|
{
|
|
LogEvent(SkyscraperContextEvent.TransmissionModeSupport2, String.Format("Network ID #{0}, MODCOD {1}", interactiveNetworkId, mode.Modcod));
|
|
DataStorage.InsertTmst2(interactiveNetworkId, mode);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void OnFramePayloadFormatAnnouncement(ushort networkId, _0xb7_FramePayloadFormatDescriptor descriptor)
|
|
{
|
|
foreach (_0xb7_FramePayloadFormatDescriptor.TransmissionContext transmissionContext in descriptor.Contexts)
|
|
{
|
|
if (!DataStorage.TestForTimFramePayloadFormat(networkId, transmissionContext))
|
|
{
|
|
LogEvent(SkyscraperContextEvent.TimFramePayloadFormat,String.Format("Network #{0}, Context ID #{1}",networkId,transmissionContext.TransmissionContextId));
|
|
DataStorage.InsertTimFramePayloadFormat(networkId, transmissionContext);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void OnCorrectionMessageExtension(PhysicalAddress macAddress, _0xb1_CorrectionMessageExtensionDescriptor descriptor)
|
|
{
|
|
if (!DataStorage.TestForTimCorrectionMessageExtension(macAddress))
|
|
{
|
|
LogEvent(SkyscraperContextEvent.TimCorrectionExtension, String.Format("For participant {0}", macAddress));
|
|
DataStorage.InsertTimCorrectionMessageExtension(macAddress, descriptor);
|
|
}
|
|
}
|
|
|
|
public void OnControlAssignment(PhysicalAddress macAddress, _0xa4_SyncAssignDescriptor descriptor)
|
|
{
|
|
if (!DataStorage.TestForTimControlAssignment(macAddress))
|
|
{
|
|
LogEvent(SkyscraperContextEvent.TimControlAssignment, String.Format("For participant {0}", macAddress));
|
|
DataStorage.InsertTimControlAssignment(macAddress, descriptor);
|
|
}
|
|
}
|
|
|
|
public void OnSatelliteReturnLink(PhysicalAddress macAddress, _0xa9_SatelliteReturnLinkDescriptor descriptor)
|
|
{
|
|
if (!DataStorage.TestForTimSatelliteReturnLink(macAddress))
|
|
{
|
|
LogEvent(SkyscraperContextEvent.TimSatelliteReturnLink, String.Format("For participant {0}: {1}", macAddress, descriptor));
|
|
DataStorage.InsertTimSatelliteReturnLink(macAddress, descriptor);
|
|
}
|
|
}
|
|
|
|
public void OnLowerLayerService(PhysicalAddress macAddress, _0xbb_LowerLayerServiceDescriptor descriptor)
|
|
{
|
|
if (!DataStorage.TestForLowerLayerService(macAddress))
|
|
{
|
|
LogEvent(SkyscraperContextEvent.TimLowerLayerService, String.Format("For participant {0}", macAddress));
|
|
DataStorage.InsertTimLowerLayerService(macAddress, descriptor);
|
|
}
|
|
}
|
|
|
|
public void OnHigherLayerInitalization(PhysicalAddress macAddress, _0xc4_HigherLayersInitializeDescriptor descriptor)
|
|
{
|
|
foreach (_0xc4_HigherLayersInitializeDescriptor.Layer2Interface layer2Interface in descriptor.Layer2Interfaces)
|
|
{
|
|
if (!DataStorage.TestForHigherLayerServiceInitalization(macAddress, layer2Interface))
|
|
{
|
|
LogEvent(SkyscraperContextEvent.TimHigherLayerInitalization, String.Format("For participant {0} -> {1}", macAddress, layer2Interface.Ipv4McAddress));
|
|
DataStorage.InsertHigherLayerServiceInitalization(macAddress, layer2Interface);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void LogonResponseDescriptor(PhysicalAddress macAddress, _0xb9_LogonResponseDescriptor descriptor)
|
|
{
|
|
if (!DataStorage.TestForTimLogonResponse(macAddress))
|
|
{
|
|
LogEvent(SkyscraperContextEvent.TimLogonResponse, String.Format("For participant {0}: {1}", macAddress, descriptor.ToString()));
|
|
DataStorage.InsertTimLogonResponse(macAddress, descriptor);
|
|
}
|
|
}
|
|
|
|
public void OnForwardInteractionPath(PhysicalAddress macAddress, _0xad_ForwardInteractionPathDescriptor descriptor)
|
|
{
|
|
foreach (_0xad_ForwardInteractionPathDescriptor.ForwardInteractionPath forwardInteractionPath in descriptor.Paths)
|
|
{
|
|
if (!DataStorage.TestForTimForwardInteractionPath(macAddress, forwardInteractionPath))
|
|
{
|
|
LogEvent(SkyscraperContextEvent.TimForwardInteractionPath, String.Format("For participant {0}: {1}", macAddress, forwardInteractionPath.ToString()));
|
|
DataStorage.InsertTimForwardInteractionPath(macAddress, forwardInteractionPath);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void OnCorrectionMessage(PhysicalAddress mac, _0xa1_CorrectionMessageDescriptor cmd)
|
|
{
|
|
if (!DataStorage.TestForTim(mac))
|
|
{
|
|
LogEvent(SkyscraperContextEvent.Tim, String.Format("for participant {0}", mac.ToString()));
|
|
DataStorage.CreateTim(mac);
|
|
}
|
|
if (DataStorage.CorrectTim(mac, cmd))
|
|
{
|
|
LogEvent(SkyscraperContextEvent.TimCorrection, String.Format("for participant {0}", mac.ToString()));
|
|
}
|
|
}
|
|
|
|
public void OnContentionControl(PhysicalAddress mac, _0xab_ContentionControlDescriptor ccdNew)
|
|
{
|
|
if (!DataStorage.TestForTim(mac))
|
|
{
|
|
LogEvent(SkyscraperContextEvent.Tim, String.Format("for participant {0}", mac.ToString()));
|
|
DataStorage.CreateTim(mac);
|
|
}
|
|
if (DataStorage.ContentionTim(mac,ccdNew))
|
|
{
|
|
LogEvent(SkyscraperContextEvent.TimContentionControl, String.Format("for participant {0}", mac.ToString()));
|
|
}
|
|
}
|
|
|
|
public void OnCorrectionControl(PhysicalAddress mac, _0xac_CorrectionControlDescriptor descriptor)
|
|
{
|
|
if (!DataStorage.TestForTim(mac))
|
|
{
|
|
LogEvent(SkyscraperContextEvent.Tim, String.Format("for participant {0}", mac.ToString()));
|
|
DataStorage.CreateTim(mac);
|
|
}
|
|
if (DataStorage.CorrectionControlTim(mac,descriptor))
|
|
{
|
|
LogEvent(SkyscraperContextEvent.TimCorrectionControl, String.Format("for participant {0}", mac.ToString()));
|
|
}
|
|
}
|
|
|
|
private List<PhysicalAddress> networkLayerInfos;
|
|
public void OnNetworkLayerInfo(PhysicalAddress mac, _0xa0_NetworkLayerInfoDescriptor nlid)
|
|
{
|
|
if (currentTimeForTim == null)
|
|
return;
|
|
if (!DataStorage.TestForTim(mac))
|
|
{
|
|
LogEvent(SkyscraperContextEvent.Tim, String.Format("for participant {0}", mac.ToString()));
|
|
DataStorage.CreateTim(mac);
|
|
}
|
|
if (DataStorage.NetworkLayerInfoTim(mac,nlid,currentTimeForTim.Value))
|
|
{
|
|
if (networkLayerInfos == null)
|
|
networkLayerInfos = new List<PhysicalAddress>();
|
|
if (!networkLayerInfos.Contains(mac))
|
|
{
|
|
LogEvent(SkyscraperContextEvent.TimNetworkLayerInfo, String.Format("for participant {0}", mac.ToString()));
|
|
networkLayerInfos.Add(mac);
|
|
}
|
|
}
|
|
}
|
|
|
|
void InteractionChannelHandler.OnTransmissionModeSupport(ushort interactiveNetworkId, Tmst tmst)
|
|
{
|
|
byte[] knownTmst = DataStorage.GetTmst(interactiveNetworkId);
|
|
if (knownTmst == null)
|
|
{
|
|
LogEvent(SkyscraperContextEvent.TransmissionModeSupport, String.Format("Network ID = {0}, Modes = {1}", interactiveNetworkId, BitConverter.ToString(tmst.Modes)));
|
|
DataStorage.InsertTmst(interactiveNetworkId, tmst.Modes);
|
|
}
|
|
else if (Array.Equals(knownTmst,tmst.Modes))
|
|
{
|
|
LogEvent(SkyscraperContextEvent.TransmissionModeUpdate,String.Format("Network ID = {0}, Old Modes = {2}, New Modes = {1}", interactiveNetworkId, BitConverter.ToString(tmst.Modes), BitConverter.ToString(knownTmst)));
|
|
DataStorage.UpdateTmst(interactiveNetworkId, tmst.Modes);
|
|
}
|
|
}
|
|
|
|
public int GetRmtTransmissionStandard(ushort networkId)
|
|
{
|
|
return DataStorage.GetRmtTransmissionStandard(networkId);
|
|
}
|
|
|
|
public void AutodetectionRuleOut(int pid, Contestant contestant, ProgramContext programContext)
|
|
{
|
|
//LogEvent(SkyscraperContextEvent.StreamTypeAutodetection, String.Format("PID 0x{0:X4} probably isn't a {1}", pid, contestant.Tag));
|
|
}
|
|
|
|
void SgtEventHandler.AnnounceSgtList(SgtList list)
|
|
{
|
|
if (!DataStorage.TestForSgtList(list))
|
|
{
|
|
LogEvent(SkyscraperContextEvent.SgtList, String.Format("List #{0} ({1})", list.ServiceListId, list.GetName()));
|
|
DataStorage.InsertSgtList(list);
|
|
}
|
|
}
|
|
|
|
void SgtEventHandler.OnSgtError(SgtError invalidTableId)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
void SgtEventHandler.OnSgtService(SgtService child)
|
|
{
|
|
if (!DataStorage.TestForSgtService(child))
|
|
{
|
|
LogEvent(SkyscraperContextEvent.SgtService, String.Format("LCN #{0} in List #{1} ({2})", child.Lcn, child.ServiceListId, child.GetName()));
|
|
DataStorage.InsertSgtService(child);
|
|
}
|
|
}
|
|
|
|
public void ProcessVirtualFilesystem(IFilesystem vfs, ProgramMappingStream programMappingStream)
|
|
{
|
|
yo3explorerToSkyscraperContextBridge context = new yo3explorerToSkyscraperContextBridge();
|
|
context.DataStorage = DataStorage;
|
|
if (!currentTime.HasValue)
|
|
return;
|
|
context.CurrentTime = currentTime.Value;
|
|
context.Source = ServiceListEntryPointSource.TransportStream;
|
|
context.OriginalNetworkId = CurrentNetworkId;
|
|
context.TransportStreamId = CurrentTransportStreamId;
|
|
context.PID = programMappingStream.ElementaryPid;
|
|
|
|
yo3explorerState state = yo3explorerState.GetInstance();
|
|
|
|
PluginManager pluginManager = PluginManager.GetInstance();
|
|
IReadOnlyList<skyscraper8.yo3explorer.FilesystemProcessorPlugin> processors = pluginManager.GetFilesystemProcessors();
|
|
|
|
foreach(FilesystemProcessorPlugin processor in processors)
|
|
{
|
|
processor.ProcessFilesystem(vfs, context, state);
|
|
}
|
|
}
|
|
|
|
public void FluteFileArrival(NipActualCarrierInformation carrier, FluteListener listener)
|
|
{
|
|
string extension = Path.GetExtension(listener.FileAssociation.ContentLocation).ToLowerInvariant();
|
|
if (!DvbNipUtilities.IsContinuousFileType(extension))
|
|
LogEvent(SkyscraperContextEvent.FluteFileArrival, listener.FileAssociation.ContentLocation);
|
|
else
|
|
{
|
|
IPEndPoint ep = new IPEndPoint(listener.DestinationAddress, listener.DestinationPort);
|
|
if (!knownNipSegments.ContainsKey(ep))
|
|
knownNipSegments[ep] = new List<string>();
|
|
knownNipSegments[ep].Add(listener.FileAssociation.ContentLocation);
|
|
}
|
|
ObjectStorage.DvbNipFileArrival(carrier, listener);
|
|
}
|
|
|
|
public void OnMulticastGatewayConfiguration(NipActualCarrierInformation carrier, MulticastGatewayConfigurationType multicastGatewayConfiguration)
|
|
{
|
|
if (multicastGatewayConfiguration.MulticastSession != null)
|
|
{
|
|
foreach (MulticastSessionType multicastSession in multicastGatewayConfiguration.MulticastSession)
|
|
{
|
|
if (!DataStorage.DvbNipTestForMulticastSession(multicastSession))
|
|
{
|
|
LogEvent(SkyscraperContextEvent.DvbNipMulticastSession, multicastSession.serviceIdentifier);
|
|
DataStorage.DvbNipInsertMulticastSession(multicastSession);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (multicastGatewayConfiguration.MulticastGatewayConfigurationTransportSession != null)
|
|
{
|
|
foreach (MulticastGatewayConfigurationTransportSessionType multicastGatewayConfigurationTransportSession in multicastGatewayConfiguration.MulticastGatewayConfigurationTransportSession)
|
|
{
|
|
if (multicastGatewayConfigurationTransportSession.EndpointAddress == null)
|
|
continue;
|
|
|
|
MulticastEndpointAddressType endpointAddress = multicastGatewayConfigurationTransportSession.EndpointAddress[0];
|
|
if (!DataStorage.DvbNipTestForMulticastGatewayConfigurationTransportSession(carrier, endpointAddress))
|
|
{
|
|
string name = String.Format("{0} -> {1}:{2}, TSI = {3}", endpointAddress.NetworkSourceAddress,
|
|
endpointAddress.NetworkDestinationGroupAddress, endpointAddress.TransportDestinationPort,
|
|
endpointAddress.MediaTransportSessionIdentifier);
|
|
LogEvent(SkyscraperContextEvent.DvbNipMulticastGatewayConfigurationTransportSession, name);
|
|
DataStorage.DvbNipInsertMulticastGatewayConfigurationTransportSession(carrier, endpointAddress);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (multicastGatewayConfiguration.MulticastGatewaySessionReporting != null)
|
|
{
|
|
throw new NotImplementedException(nameof(multicastGatewayConfiguration.MulticastGatewaySessionReporting));
|
|
}
|
|
}
|
|
|
|
public void OnNipCarrierDetected(NipActualCarrierInformation currentCarrierInformation)
|
|
{
|
|
LogEvent(SkyscraperContextEvent.NipCarrierDetected, String.Format("{0}", currentCarrierInformation.NipStreamProviderName));
|
|
if (!DataStorage.DvbNipTestForCarrier(currentCarrierInformation))
|
|
{
|
|
DataStorage.DvbNipInsertCarrier(currentCarrierInformation);
|
|
}
|
|
}
|
|
|
|
public bool IsFileNeeded(string announcedFileContentLocation)
|
|
{
|
|
if (announcedFileContentLocation.StartsWith("urn:dvb"))
|
|
return true;
|
|
|
|
return !ObjectStorage.DvbNipTestForFile(announcedFileContentLocation);
|
|
}
|
|
|
|
public void OnPrivateDataSignallingManifest(NipActualCarrierInformation currentCarrierInformation, PrivateDataSignallingManifestType privateDataSignallingManifest)
|
|
{
|
|
foreach (PrivateDataProviderType privateDataProvider in privateDataSignallingManifest.PrivateDataProvider)
|
|
{
|
|
privateDataProvider.privateDataProviderID.FlipEndian();
|
|
uint privateDataSpecifier = BitConverter.ToUInt32(privateDataProvider.privateDataProviderID);
|
|
List<string> privateDataSessions = privateDataProvider.PrivateDataSession.SelectMany(x => x.TagRef).ToList();
|
|
bool added = DataStorage.DvbNipPrivateDataSpecifier(currentCarrierInformation, privateDataSignallingManifest.VersionUpdate, privateDataSpecifier, privateDataSessions);
|
|
if (added)
|
|
{
|
|
LogEvent(SkyscraperContextEvent.NipPrivateDataSpecifier, String.Format("{0} -> {1}", currentCarrierInformation.NipStreamProviderName, privateDataSpecifier));
|
|
}
|
|
}
|
|
}
|
|
|
|
public void OnServiceListEntryPoints(NipActualCarrierInformation currentCarrierInformation, ServiceListEntryPoints serviceListEntryPoints, DateTime dvbNipTime, DvbNipServiceListNotifier serviceListNotifier)
|
|
{
|
|
long sourceHash = DvbIUtils.GetSourceHash(ServiceListEntryPointSource.DvbNip, currentCarrierInformation.NipNetworkId, currentCarrierInformation.NipCarrierId, currentCarrierInformation.NipLinkId);
|
|
serviceListNotifier.SetSourceHash(sourceHash);
|
|
DvbIDataStorage dataStorage = DataStorage;
|
|
|
|
if (dataStorage.TestForDvbiServiceListEntryPoints(sourceHash))
|
|
{
|
|
DateTime lastChecked = dataStorage.GetLastDvbiServiceListEntryPointUpdateDate(sourceHash);
|
|
TimeSpan sinceLastChecked = dvbNipTime - lastChecked;
|
|
if (sinceLastChecked.TotalDays < 1.0)
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
logger.InfoFormat("New DVB-I Service List Entry Point: {0}", currentCarrierInformation.NipStreamProviderName);
|
|
dataStorage.InsertDvbiServiceListEntryPoint(sourceHash);
|
|
}
|
|
|
|
IEnumerable<DvbiServiceList> enumerable = DvbIUtils.FlattenServiceListEntryPoints(serviceListEntryPoints);
|
|
foreach (DvbiServiceList serviceList in enumerable)
|
|
{
|
|
DateTime serviceListLastChecked;
|
|
if (dataStorage.TestForDvbiServiceList(serviceList.Id))
|
|
{
|
|
serviceListLastChecked = dataStorage.GetDvbiServiceListLastUpdateDate(serviceList.Id);
|
|
}
|
|
else
|
|
{
|
|
logger.InfoFormat("New DVB-I Service List: {0}", serviceList.Name);
|
|
dataStorage.InsertDvbiServiceList(serviceList);
|
|
dataStorage.AddDvbiServiceListToServiceListEntryPoint(serviceList, sourceHash);
|
|
serviceListLastChecked = new DateTime(1970, 1, 1);
|
|
}
|
|
|
|
TimeSpan sinceLastServiceListCheck = dvbNipTime - serviceListLastChecked;
|
|
if (sinceLastServiceListCheck.TotalDays < 1.0)
|
|
continue;
|
|
|
|
serviceListNotifier.AddServiceListUrl(serviceList.URI, serviceList.Id);
|
|
/*if (vfs.FileExists(serviceList.URI))
|
|
{
|
|
byte[] serviceListBytes = vfs.GetFile(serviceList.URI);
|
|
ServiceListType serviceListData = DvbIUtils.UnpackServiceList(serviceListBytes);
|
|
HandleServiceList(serviceListData, dataStorage, serviceList.Id);
|
|
dataStorage.UpdateDvbiServiceListLastCheckedDate(serviceList.Id, context.CurrentTime);
|
|
}*/
|
|
}
|
|
|
|
dataStorage.UpdateDvbiServiceListEntryPointUpdateDate(sourceHash, dvbNipTime);
|
|
}
|
|
|
|
public void OnServiceList(NipActualCarrierInformation currentCarrierInformation, string serviceListId,
|
|
ServiceListType serviceList)
|
|
{
|
|
List<DvbIService> services = DvbIUtils.FlattenServiceList(serviceList).ToList();
|
|
DvbIDataStorage dataStorage = DataStorage;
|
|
foreach (DvbIService service in services)
|
|
{
|
|
if (dataStorage.TestForDvbiService(service.Id))
|
|
{
|
|
int versionInDb = dataStorage.GetDvbiServiceVersion(service.Id);
|
|
if (service.Version > versionInDb)
|
|
{
|
|
dataStorage.UpdateDvbiService(service);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
logger.InfoFormat("New DVB-I Service: {0}", service.ServiceName);
|
|
dataStorage.InsertDvbiService(service);
|
|
dataStorage.AddDvbiServiceToServiceList(service.Id, serviceListId);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void OnTimeOffsetFile(NipActualCarrierInformation currentCarrierInformation, TimeOffsetFileType timeOffsetFile)
|
|
{
|
|
if (timeOffsetFile.time_of_change != DateTime.MinValue)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
}
|
|
|
|
public void OnNetworkInformationFile(NipActualCarrierInformation currentCarrierInformation, NetworkInformationFileType networkInformationFile)
|
|
{
|
|
List<BroadcastNetworkType> broadcastNetworks = new List<BroadcastNetworkType>();
|
|
if (networkInformationFile.ActualBroadcastNetwork != null)
|
|
broadcastNetworks.Add(networkInformationFile.ActualBroadcastNetwork);
|
|
if (networkInformationFile.OtherBroadcastNetwork != null)
|
|
broadcastNetworks.AddRange(networkInformationFile.OtherBroadcastNetwork);
|
|
|
|
foreach (BroadcastNetworkType network in broadcastNetworks)
|
|
{
|
|
if (!DataStorage.DvbNipTestForNetwork(network))
|
|
{
|
|
LogEvent(SkyscraperContextEvent.DvbNipNetwork, network.NetworkName);
|
|
DataStorage.DvbNipInsertNetwork(network);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void OnServiceInformationFile(NipActualCarrierInformation currentCarrierInformation, ServiceInformationFileType serviceInformationFile)
|
|
{
|
|
foreach (BroadcastMediaStreamType broadcastMediaStreamType in serviceInformationFile.BroadcastMediaStream)
|
|
{
|
|
if (!DataStorage.DvbNipTestForService(broadcastMediaStreamType))
|
|
{
|
|
string id = String.Format("{0},{1},{2},{3}", broadcastMediaStreamType.NIPNetworkID,
|
|
broadcastMediaStreamType.NIPCarrierID, broadcastMediaStreamType.NIPLinkID,
|
|
broadcastMediaStreamType.NIPServiceID);
|
|
LogEvent(SkyscraperContextEvent.DvbNipService, id);
|
|
DataStorage.DvbNipInsertService(broadcastMediaStreamType);
|
|
}
|
|
}
|
|
}
|
|
|
|
private Dictionary<IPEndPoint, List<string>> knownNipSegments;
|
|
private HashSet<string> knownUrls;
|
|
public void FluteFileAnnouncement(IPAddress ip, ushort port, FDTInstanceType flute)
|
|
{
|
|
if (knownUrls == null)
|
|
{
|
|
knownUrls = new HashSet<string>();
|
|
knownNipSegments = new Dictionary<IPEndPoint, List<string>>();
|
|
}
|
|
|
|
string url = flute.File[0].ContentLocation;
|
|
if (knownUrls.Contains(url))
|
|
return;
|
|
|
|
LogEvent(SkyscraperContextEvent.FluteFileAnnouncement, String.Format("{0}:{1} -> {2}", ip.ToString(), port, url));
|
|
knownUrls.Add(url);
|
|
|
|
IPEndPoint ep = new IPEndPoint(ip, port);
|
|
if (!knownNipSegments.ContainsKey(ep))
|
|
knownNipSegments[ep] = new List<string>();
|
|
knownNipSegments[ep].Add(url);
|
|
}
|
|
|
|
public void FluteFileDownloadProgress(NipActualCarrierInformation currentCarrierInformation, ulong destinationTsi, ulong destinationToi, double progress, FileType filetype)
|
|
{
|
|
if (filetype != null)
|
|
{
|
|
if (!filetype.ContentLocation.Equals("urn:dvb:metadata:cs:NativeIPMulticastTransportObjectTypeCS:2023:bootstrap"))
|
|
{
|
|
string extension = Path.GetExtension(filetype.ContentLocation).ToLowerInvariant();
|
|
if (!DvbNipUtilities.IsContinuousFileType(extension))
|
|
{
|
|
LogEvent(SkyscraperContextEvent.FluteFileProgress,
|
|
String.Format("Filename={0}, {1}%", filetype.ContentLocation, progress));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private List<object> _pluginContexts;
|
|
|
|
public List<object> PluginContext
|
|
{
|
|
get
|
|
{
|
|
if (_pluginContexts == null)
|
|
_pluginContexts = new List<object>();
|
|
|
|
return _pluginContexts;
|
|
}
|
|
}
|
|
|
|
public void OnPluginEvent()
|
|
{
|
|
lastEventTimestamp = DateTime.Now;
|
|
}
|
|
|
|
public void OnEthernetFrame(int pid, PhysicalAddress destination, PhysicalAddress source, ushort etherType, byte[] contents)
|
|
{
|
|
if (Array.TrueForAll(contents, x => x == 0))
|
|
return;
|
|
if (etherType <= 1500)
|
|
{
|
|
OnLlcFrame(etherType, contents);
|
|
return;
|
|
}
|
|
switch (etherType)
|
|
{
|
|
case 0x0800:
|
|
OnIpDatagram(pid, contents);
|
|
return;
|
|
case 0x22e3:
|
|
//This is related to G.9961, a PowerLine standard specified by ITU-T T-REC-G.9961
|
|
//I don't think we need this.
|
|
return;
|
|
case 0x2f00:
|
|
//This is something proprietary.
|
|
//Quite possibly related to Extron hardware?
|
|
return;
|
|
case 0x0806:
|
|
//This is an ARP Frame. We don't need it.
|
|
return;
|
|
case 0x8137:
|
|
//This is an IPX Frame. We don't need it.
|
|
return;
|
|
case 0x86dd:
|
|
OnIpDatagram(pid, contents);
|
|
return;
|
|
case 0x88e1:
|
|
//This is related to FRITZ!Powerline.
|
|
//Probably very proprietary and undocumented, so I guess we'll discard it.
|
|
return;
|
|
case 0x890d:
|
|
//IEEE Std 802.11 - Fast Roaming Remote Request (802.11r)
|
|
//These seem to happen when one moves from one Wi-Fi Access Point to another. We probably don't need that one.
|
|
return;
|
|
case 0x8912:
|
|
//Related to mediaxtream, see https://de.wikipedia.org/wiki/Mediaxtream
|
|
//Probably not very interesting.
|
|
return;
|
|
case 0x9001:
|
|
//3Com(Bridge) XNS Sys Mgmt
|
|
//Can't say anything about these. I don't have any 3Com equipment.
|
|
return;
|
|
case 0x94ce:
|
|
//Something proprietary. No idea.
|
|
return;
|
|
case 0xb69d:
|
|
//Something proprietary. No idea.
|
|
return;
|
|
case 0xf67f:
|
|
if (contents[20] == 0xaa && contents[21] == 0xaa && contents[22] == 0x03 && contents[23] == 0x00 && contents[24] == 0x00 && contents[25] == 0x00)
|
|
{
|
|
(contents[26], contents[27]) = (contents[27], contents[26]);
|
|
ushort newEtherType = BitConverter.ToUInt16(contents, 26);
|
|
byte[] newPacket = new byte[contents.Length - 28];
|
|
Array.Copy(contents, 28, newPacket, 0, newPacket.Length);
|
|
OnEthernetFrame(pid, destination, source, newEtherType, newPacket);
|
|
}
|
|
return;
|
|
case 0x94ad:
|
|
//Something proprietary. No idea.
|
|
return;
|
|
case 0x4957:
|
|
//Something proprietary. No idea.
|
|
return;
|
|
case 0x3e30:
|
|
//Something proprietary. No idea.
|
|
return;
|
|
case 0x88cc:
|
|
//Link Layer Discovery Protocol
|
|
LldpFrame lldpFrame = new LldpFrame(contents);
|
|
OnLldpFrame(lldpFrame);
|
|
return;
|
|
case 0x9417:
|
|
case 0xf95c:
|
|
//Unknown proprietary.
|
|
return;
|
|
default:
|
|
throw new NotImplementedException(String.Format("EtherType: 0x{0:X4}", etherType));
|
|
}
|
|
}
|
|
|
|
public void OnUleError(int pid, int errorNo)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public void OnLlcFrame(ushort etherType, byte[] contents)
|
|
{
|
|
MemoryStream llcStream = new MemoryStream(contents, true);
|
|
byte dsap = llcStream.ReadUInt8();
|
|
byte ssap = llcStream.ReadUInt8();
|
|
if (dsap == 0x00 || ssap == 0x00)
|
|
{
|
|
//This is a NULL frame. It can be discarded either way.
|
|
return;
|
|
}
|
|
if (dsap == 0x42 || ssap == 0x42)
|
|
{
|
|
//This is a Spanning Tree Frame. We probably don't need it.
|
|
return;
|
|
}
|
|
if (dsap == 0xe0 || ssap == 0xe0)
|
|
{
|
|
//This is a Novell Netware Frame. We don't need to bother with it.
|
|
return;
|
|
}
|
|
|
|
if (dsap == 0xaa || ssap == 0xaa)
|
|
{
|
|
byte snapType = llcStream.ReadUInt8();
|
|
if (snapType == 0x03)
|
|
{
|
|
byte[] oui = llcStream.ReadBytes(3);
|
|
ushort snapProtocolId = llcStream.ReadUInt16BE();
|
|
if (snapProtocolId == 0x809b)
|
|
{
|
|
//This is an AppleTalk Frame. We probably don't need it.
|
|
return;
|
|
}
|
|
else if (snapProtocolId == 0x8137)
|
|
{
|
|
//This is an IPX Frame. We probably don't need it.
|
|
return;
|
|
}
|
|
else if (snapProtocolId == 0x2000)
|
|
{
|
|
//This is from Cisco's Discovery Protocol (CDP). Probably not interesting.
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
throw new NotImplementedException(String.Format("SNAP Frame, OUI = {0:X2}-{1:X2}-{2:X2}, Protocol ID = 0x{3:X4}", oui[0], oui[1], oui[2], snapProtocolId));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
throw new NotImplementedException(String.Format("LLC/SNAP Type 0x{0:X2}", snapType));
|
|
}
|
|
}
|
|
|
|
if (dsap == 0xff || ssap == 0xff)
|
|
{
|
|
ushort llcLength = llcStream.ReadUInt16BE();
|
|
if (llcLength == etherType)
|
|
{
|
|
//TEST or XID PDU, likely unneeded.
|
|
return;
|
|
}
|
|
else if (llcLength == 40)
|
|
{
|
|
//TEST or XID PDU, likely unneeded.
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
throw new NotImplementedException("LLC/SNAP (2)");
|
|
}
|
|
}
|
|
|
|
if (dsap == 0x09 && ssap == 0x01)
|
|
{
|
|
//Likely an LLC management packet (TEST or XID)
|
|
return;
|
|
}
|
|
throw new NotImplementedException("LLC/SNAP");
|
|
}
|
|
|
|
public void OnOtvSsuError(int pid, OtvSsuError offsetOutOfRange)
|
|
{
|
|
DvbContext.RegisterPacketProcessor(pid, new PacketDiscarder());
|
|
}
|
|
|
|
public void OnOtvSsuFileAnnouncement(int pid, ushort tableIdExtension, uint fileId, uint unknown1, uint length)
|
|
{
|
|
LogEvent(SkyscraperContextEvent.OtvSsuFileDetected, String.Format("TID = {0}, FID = {1:X8}, Length = {2}", tableIdExtension, fileId, length));
|
|
}
|
|
|
|
public void OnOtvSsuBlock(int pid, ushort tableIdExtension, uint fileId, uint unknown1, uint length)
|
|
{
|
|
}
|
|
|
|
public void OnOtvSsuComplete(int sourcePid, Stream getStream, ushort tableIdExtension, uint fileId, uint unknown1,
|
|
uint length)
|
|
{
|
|
LogEvent(SkyscraperContextEvent.OtvSsuComplete, String.Format("TID = {0}, FID = {1:X8}, Length = {2}", tableIdExtension, fileId, length));
|
|
ObjectStorage.OnOtvSsuComplete(CurrentNetworkId, CurrentTransportStreamId, sourcePid, getStream, tableIdExtension, fileId, unknown1, length);
|
|
}
|
|
|
|
public bool OnOtvCheckFileAlreadyKnown(int sourcePid, ushort tableIdExtension, uint fileId, uint unknown1, uint length)
|
|
{
|
|
return ObjectStorage.OtvSsuTestFile(CurrentNetworkId, CurrentTransportStreamId, sourcePid, tableIdExtension, fileId, unknown1, length);
|
|
}
|
|
|
|
public void OnNdsSsuError(int pid, NdsSsuError error)
|
|
{
|
|
}
|
|
|
|
public void OnNdsSsuFileAnnouncement(int pid, ushort tableIdExtension)
|
|
{
|
|
LogEvent(SkyscraperContextEvent.NdsSsuFileAnnounced, String.Format("PID = 0x{1:X4}, Table ID Extension = {0}", tableIdExtension, pid));
|
|
}
|
|
|
|
public void OnNdsSsuFileComplete(int pid, ushort tableIdExtension, NdsSsuDataMap dataMap)
|
|
{
|
|
ObjectStorage.OnNdsSsuComplete(CurrentNetworkId, CurrentTransportStreamId, pid, tableIdExtension, dataMap);
|
|
}
|
|
|
|
public void OnNdsSsuProgress(int pid, ushort tableIdExtension, int blocksDone, int blocksTotal, bool theresMore)
|
|
{
|
|
LogEvent(SkyscraperContextEvent.NdsSsuProgress, String.Format("PID 0x{0:X4}, File ID {1}, Blocks: {2}/{3}{4}", pid, tableIdExtension, blocksDone, blocksTotal, theresMore ? "+" : ""));
|
|
}
|
|
|
|
public bool TestNdsSsuFileExists(int pid, ushort tableIdExtension)
|
|
{
|
|
return ObjectStorage.NdsSsuTestFile(CurrentNetworkId, CurrentTransportStreamId, pid, tableIdExtension);
|
|
}
|
|
|
|
public void OnReturnTransmissionMOdes(PhysicalAddress macAddress, _0xb2_ReturnTransmissionModesDescriptor descriptor)
|
|
{
|
|
//TODO: Implement this.
|
|
}
|
|
|
|
public void OnConnectionControl(PhysicalAddress macAddress, _0xaf_ConnectionControlDescriptor descriptor)
|
|
{
|
|
//TODO: Implement this.
|
|
}
|
|
|
|
|
|
private HashSet<LldpFrame> lldpFrames;
|
|
public void OnLldpFrame(LldpFrame frame)
|
|
{
|
|
if (lldpFrames == null)
|
|
lldpFrames = new HashSet<LldpFrame>();
|
|
|
|
if (lldpFrames.Add(frame))
|
|
{
|
|
LogEvent(SkyscraperContextEvent.EthernetLinkLayerDiscovery, frame.ToString());
|
|
}
|
|
}
|
|
}
|
|
}
|