skyscraper8/skyscraper8/Skyscraper/Scraper/SkyscraperContext.cs
feyris-tan 6e35f26528
All checks were successful
🚀 Pack skyscraper8 / make-zip (push) Successful in 1m32s
Reeimplementing GS/GSE...
2025-10-15 16:05:50 +02:00

3014 lines
123 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.Security.Policy;
using System.Text;
using skyscraper8.Experimentals.NdsSsu;
using skyscraper8.Experimentals.OtvSsu;
using skyscraper8.GSE;
using skyscraper8.Skyscraper.Net;
using skyscraper8.Skyscraper.Scraper;
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
{
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());
UiJunction?.SetGseMode();
LogEvent(SkyscraperContextEvent.SpecialTsMode, "STiD135 encapsulated GS detected.");
SpecialTsType = 3;
}
}
firstPacketDone = true;
}
DvbContext.PushPacket(buffer);
if (totalPacketsPushed++ == 1000)
{
CheckSpecialTs();
}
}
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());
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)
{
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)
{
}
private SkyscraperContext[][] childSkyscrapers;
public void OnT2MiPacket(int pid, byte basebandFramePlpId, byte[] basebandPacket)
{
if (childSkyscrapers == null)
childSkyscrapers = new SkyscraperContext[0x1fff][];
if (childSkyscrapers[pid] == null)
{
childSkyscrapers[pid] = new SkyscraperContext[256];
}
if (childSkyscrapers[pid][basebandFramePlpId] == null)
{
childSkyscrapers[pid][basebandFramePlpId] = new SkyscraperContext(new TsContext(), DataStorage,ObjectStorage);
childSkyscrapers[pid][basebandFramePlpId].IsChild = true;
childSkyscrapers[pid][basebandFramePlpId].ChildName = String.Format("PLP {0}", basebandFramePlpId);
childSkyscrapers[pid][basebandFramePlpId].InitalizeFilterChain();
LogEvent(SkyscraperContextEvent.T2MiPlpDetected, String.Format("PID {0:X4}, PLP {1}", pid, basebandFramePlpId));
}
childSkyscrapers[pid][basebandFramePlpId].IngestSinglePacket(basebandPacket);
}
public string ChildName { get; private set; }
public bool IsChild { get; set; }
public void OnT2MiTimestamp(int pid, _0x20_DvbT2Timestamp t2Timestamp)
{
if (!CurrentNetworkId.HasValue)
return;
if (!CurrentTransportStreamId.HasValue)
return;
if (childSkyscrapers == null)
return;
if (childSkyscrapers[pid] == null)
return;
bool HasEvents(SkyscraperContext[] skyscrapers)
{
for (int i = 0; i < skyscrapers.Length; i++)
{
if (skyscrapers[i] == null)
continue;
if (skyscrapers[i].EventsLogged >= 2)
return true;
}
return false;
}
if (!HasEvents(childSkyscrapers[pid]))
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 (childSkyscrapers == null)
return;
if (childSkyscrapers[relatedPid] == null)
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)
{
if (childSkyscrapers == null)
childSkyscrapers = new SkyscraperContext[0x1fff][];
if (childSkyscrapers[pid] == null)
childSkyscrapers[pid] = new SkyscraperContext[1];
if (childSkyscrapers[pid][0] == null)
{
childSkyscrapers[pid][0] = new SkyscraperContext(new TsContext(), DataStorage, ObjectStorage);
childSkyscrapers[pid][0].IsChild = true;
childSkyscrapers[pid][0].ChildName = String.Format("Abertis Tunnel on PID {0:X4}", pid);
}
childSkyscrapers[pid][0].IngestSinglePacket(outBuffer);
}
public void OnAbertisSyncLoss(int pid, int oldPosition = -1, long newPosition = -1)
{
if (childSkyscrapers == null)
DvbContext.RegisterPacketProcessor(pid, new PacketDiscarder());
else if (childSkyscrapers[pid] == null)
DvbContext.RegisterPacketProcessor(pid, new PacketDiscarder());
else if (childSkyscrapers[pid][0] == null)
DvbContext.RegisterPacketProcessor(pid, new PacketDiscarder());
else if (!childSkyscrapers[pid][0].firstPacketDone)
DvbContext.RegisterPacketProcessor(pid, new PacketDiscarder());
else
throw new NotImplementedException();
}
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);
}
}
}
}
void InteractionChannelHandler.OnTimeslotComposition(ushort interactiveNetworkId, Tct tct)
{
throw new NotImplementedException();
}
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;
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)");
}
}
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.
}
}
}