Added support for ATSC A/324 SLT and SYSTIME tables.
All checks were successful
🚀 Pack skyscraper8 / make-zip (push) Successful in 1m52s

This commit is contained in:
feyris-tan 2026-06-06 17:40:07 +02:00
parent 3c6b5cf98e
commit f5736fca13
21 changed files with 3527 additions and 222 deletions

View File

@ -0,0 +1,61 @@
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:rrt="tag:atsc.org,2016:XMLSchemas/ATSC3/RRT/1.0/"
targetNamespace="tag:atsc.org,2016:XMLSchemas/ATSC3/RRT/1.0/" elementFormDefault="qualified">
<xs:import namespace="http://www.w3.org/XML/1998/namespace" schemaLocation="W3C/xml.xsd"/>
<xs:element name="RatingRegionTables" type="rrt:RRsType"/>
<xs:complexType name="RRsType">
<xs:sequence>
<xs:element name="RatingRegionTable" type="rrt:RatingRegionTableType" maxOccurs="2"/>
<xs:any namespace="##other" processContents="strict" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<xs:anyAttribute processContents="strict"/>
</xs:complexType>
<xs:complexType name="RatingRegionTableType">
<xs:sequence>
<xs:element name="RegionIdText" type="rrt:TextType" maxOccurs="unbounded"/>
<xs:element name="Dimension" type="rrt:DimensionType" maxOccurs="unbounded"/>
<xs:any namespace="##other" processContents="strict" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute name="regionIdentifier" type="rrt:RIType" use="required"/>
<xs:anyAttribute processContents="strict"/>
</xs:complexType>
<xs:simpleType name="RIType">
<xs:restriction base="xs:unsignedByte">
<xs:minInclusive value="1"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="DimensionType">
<xs:sequence>
<xs:element name="DimensionTitle" type="rrt:TextType" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="Rating" type="rrt:RatingType" maxOccurs="unbounded"/>
<xs:any namespace="##other" processContents="strict" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute name="dimensionLevels" type="rrt:DimLevelType" use="required"/>
<xs:attribute name="dimensionGraduated" type="xs:boolean" default="false"/>
<xs:anyAttribute processContents="strict"/>
</xs:complexType>
<xs:simpleType name="DimLevelType">
<xs:restriction base="xs:unsignedByte">
<xs:minInclusive value="1"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="RatingType">
<xs:sequence>
<xs:element name="RatingValueAbbrev" type="rrt:TextType" maxOccurs="unbounded"/>
<xs:element name="RatingValueString" type="rrt:TextType" maxOccurs="unbounded"/>
<xs:any namespace="##other" processContents="strict" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute name="ratingValue" type="xs:unsignedByte" use="required"/>
<xs:anyAttribute processContents="strict"/>
</xs:complexType>
<xs:complexType name="TextType">
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute ref="xml:lang" default="EN"/>
<xs:anyAttribute processContents="strict"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:schema>

View File

@ -0,0 +1,225 @@
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:sa="tag:atsc.org,2016:XMLSchemas/ATSC3/SA/1.0/"
xmlns:rrt="tag:atsc.org,2016:XMLSchemas/ATSC3/RRT/1.0/"
targetNamespace="tag:atsc.org,2016:XMLSchemas/ATSC3/SA/1.0/"
elementFormDefault="qualified">
<xs:import namespace="http://www.w3.org/XML/1998/namespace"
schemaLocation="W3C/xml.xsd"/>
<xs:import namespace="tag:atsc.org,2016:XMLSchemas/ATSC3/RRT/1.0/"
schemaLocation="RRT-1.0-20170209.xsd"/>
<xs:annotation>
<xs:documentation>
The definition of this schema can also be found in an XML schema file, SA-1.0-20170921.xsd at the ATSC website.The XML schema file RRT-1.0-20170209.xsd can also be found at the ATSC website.
</xs:documentation>
</xs:annotation>
<xs:element name="Capabilities" type="sa:CapabilitiesType" />
<xs:simpleType name="CapabilitiesType"><xs:restriction base="xs:string"/></xs:simpleType>
<xs:annotation>
<xs:documentation>
Name and Description elements in Service and Content Fragments of OMA BCAST service guide will be replaced with following extended elements
</xs:documentation>
</xs:annotation>
<xs:element name="Name" type="sa:NameDescriptionType" />
<xs:element name="Description" type="sa:NameDescriptionType" />
<xs:complexType name="NameDescriptionType">
<xs:sequence>
<xs:element name="SpeechInfoURI" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:anyURI">
<xs:attribute name="content-type" type="xs:string" use="optional"/>
<xs:attribute name="content-enc" type="xs:string" use="optional"/>
<xs:anyAttribute processContents="skip"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
<xs:element name="SpeechInfo" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="content-type" type="xs:string" use="optional"/>
<xs:attribute name="content-enc" type="xs:string" use="optional"/>
<xs:anyAttribute processContents="skip"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
<xs:any namespace="##other" processContents="skip" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute name="text" type="xs:string" use="required" />
<xs:attribute ref="xml:lang" use="optional"/>
<xs:anyAttribute processContents="skip"/>
</xs:complexType>
<xs:element name="ContentAdvisoryRatings" type="sa:CARatingType"/>
<xs:complexType name="CARatingType">
<xs:sequence>
<xs:element name="RegionIdentifier" type="xs:unsignedByte"
minOccurs="0" maxOccurs="1"/>
<xs:element name="RatingDescription" maxOccurs="1">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute ref="xml:lang" use="optional"
default="en"/>
<xs:anyAttribute processContents="skip"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
<xs:element name="RatedDimensions" type="xs:unsignedByte"
minOccurs="0" maxOccurs="1"/>
<xs:element name="RatingDimVal" type="sa:RatingDimValType"
minOccurs="1" maxOccurs="unbounded"/>
<xs:any namespace="##other" processContents="skip" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<xs:anyAttribute processContents="skip"/>
</xs:complexType>
<xs:complexType name="RatingDimValType">
<xs:sequence>
<xs:element name="RatingDimension" type="xs:unsignedByte"
minOccurs="0" maxOccurs="1"/>
<xs:element name="RatingValueString" type="xs:string"
minOccurs="1"/>
<xs:any namespace="##other" processContents="skip" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<xs:anyAttribute processContents="skip"/>
</xs:complexType>
<xs:element name="OtherRatings" type="sa:OtherRatingType"/>
<xs:complexType name="OtherRatingType">
<xs:sequence>
<xs:choice>
<xs:element name="RatingInformationString" type="xs:string" minOccurs="1"/>
<xs:element name="RatingInformationData" type="sa:RatingContainerType" minOccurs="1"/>
</xs:choice>
<xs:any namespace="##other" processContents="skip" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute name="ratingScheme" type="xs:anyURI" use="required" />
<xs:anyAttribute processContents="skip"/>
</xs:complexType>
<xs:complexType name="RatingContainerType">
<xs:sequence>
<xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<xs:anyAttribute processContents="skip"/>
</xs:complexType>
<xs:element name="ATSC3ServiceExtension" type="sa:ATSC3ServiceExtensionType" >
<xs:annotation>
<xs:documentation>
This element is a wrapper for ATSC service extensions to OMA BCAST SG Service fragments. It shall only be used inside a PrivateExt element within a Service fragment.
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:complexType name="ATSC3ServiceExtensionType">
<xs:sequence>
<xs:element name="Icon" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:anyURI">
<xs:attribute name="MIMEType" type="xs:string" use="optional" />
<xs:attribute name="width" type="xs:unsignedInt" use="optional" />
<xs:attribute name="height" type="xs:unsignedInt" use="optional" />
<xs:attribute name="dataSize" type="xs:unsignedInt" use="optional" />
<xs:anyAttribute processContents="skip"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
<xs:element name="MajorChannelNum" type="sa:LangString" minOccurs="0" maxOccurs="1"/>
<xs:element name="MinorChannelNum" type="sa:LangString" minOccurs="0" maxOccurs="1"/>
<xs:element name="RatingRegionTables" type="rrt:RRsType" minOccurs="0" maxOccurs="1"/>
<xs:any namespace="##other" processContents="skip" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:element name="Components" type="sa:ComponentsType" >
<xs:annotation>
<xs:documentation>
This element is a wrapper for ATSC media extensions to OMA BCAST SG Content fragments. It shall only be used inside a PrivateExt element within a Content fragment.
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:complexType name="ComponentsType">
<xs:sequence>
<xs:element name="VideoComponent" type="sa:IndividualComponentType" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="AudioComponent" type="sa:IndividualComponentType" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="CCComponent" type="sa:IndividualComponentType" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="AppComponent" type="sa:IndividualComponentType" minOccurs="0" maxOccurs="unbounded"/>
<xs:any namespace="##other" processContents="skip" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<xs:anyAttribute processContents="skip"/>
</xs:complexType>
<xs:complexType name="IndividualComponentType">
<xs:complexContent>
<xs:extension base="sa:LangString">
<xs:attribute name="language" type="xs:string" use="optional" default="en"/>
<xs:anyAttribute processContents="skip"/>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="LangString">
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute ref="xml:lang" use="optional" default="en"/>
<xs:anyAttribute processContents="skip"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:element name="ContentIcon" >
<xs:annotation><xs:documentation>This element is only used inside a PrivateExt element within a Content fragment as defined in Table 5.7 and its cardinality is defined in Table 5.9. </xs:documentation></xs:annotation>
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:anyURI">
<xs:attribute name="MIMEType" type="xs:string" use="optional" />
<xs:attribute name="width" type="xs:unsignedInt" use="optional" />
<xs:attribute name="height" type="xs:unsignedInt" use="optional" />
<xs:attribute name="dataSize" type="xs:unsignedInt" use="optional" />
<xs:anyAttribute processContents="skip"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
<xs:element name="Preview" type="sa:PreviewType" />
<xs:complexType name="PreviewType">
<xs:sequence>
<xs:element name="PreviewIcon" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:anyURI">
<xs:attribute name="MIMEType" type="xs:string" use="optional" />
<xs:attribute name="width" type="xs:unsignedInt" use="optional" />
<xs:attribute name="height" type="xs:unsignedInt" use="optional" />
<xs:attribute name="dataSize" type="xs:unsignedInt" use="optional" />
<xs:anyAttribute processContents="skip"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
<xs:element name="Description" type="sa:NameDescriptionType" minOccurs="1" maxOccurs="unbounded"/>
<xs:element name="PreviewContent" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:anyURI">
<xs:attribute name="MIMEType" type="xs:string" use="optional" />
<xs:attribute name="width" type="xs:unsignedInt" use="optional" />
<xs:attribute name="height" type="xs:unsignedInt" use="optional" />
<xs:attribute name="dataSize" type="xs:unsignedInt" use="optional" />
<xs:anyAttribute processContents="skip"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
<xs:any namespace="##other" processContents="skip" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute name="usage" type="xs:string" use="optional" default="promo"/>
<xs:anyAttribute processContents="skip"/>
</xs:complexType>
</xs:schema>

View File

@ -0,0 +1,91 @@
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:slt="tag:atsc.org,2016:XMLSchemas/ATSC3/Delivery/SLT/1.0/" xmlns:sa="tag:atsc.org,2016:XMLSchemas/ATSC3/SA/1.0/" targetNamespace="tag:atsc.org,2016:XMLSchemas/ATSC3/Delivery/SLT/1.0/" elementFormDefault="qualified">
<xs:import namespace="tag:atsc.org,2016:XMLSchemas/ATSC3/SA/1.0/" schemaLocation="SA-1.0-20170921.xsd"/>
<xs:element name="SLT" type="slt:sltType"/>
<xs:complexType name="sltType">
<xs:sequence>
<xs:element name="SLTCapabilities" type="sa:CapabilitiesType" minOccurs="0"/>
<xs:element name="SLTInetUrl" type="slt:URLType" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="Service" type="slt:serviceType" maxOccurs="unbounded"/>
<xs:any namespace="##other" processContents="strict" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute name="bsid" type="slt:listOfUnsignedShortType" use="required"/>
<xs:anyAttribute processContents="strict"/>
</xs:complexType>
<xs:complexType name="serviceType">
<xs:sequence>
<xs:element name="SimulcastTSID" type="slt:SimulcastTSIDType" minOccurs="0"/>
<xs:element name="SvcCapabilities" type="sa:CapabilitiesType" minOccurs="0"/>
<xs:element name="BroadcastSvcSignaling" type="slt:broadcastSigType" minOccurs="0"/>
<xs:element name="SvcInetUrl" type="slt:URLType" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="OtherBsid" type="slt:OtherBsidType" minOccurs="0" maxOccurs="unbounded"/>
<xs:any namespace="##other" processContents="strict" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute name="serviceId" type="xs:unsignedShort" use="required"/>
<xs:attribute name="globalServiceID" type="xs:anyURI"/>
<xs:attribute name="sltSvcSeqNum" type="xs:unsignedByte" use="required"/>
<xs:attribute name="protected" type="xs:boolean" default="false"/>
<xs:attribute name="majorChannelNo" type="slt:ChannelNumType"/>
<xs:attribute name="minorChannelNo" type="slt:ChannelNumType"/>
<xs:attribute name="serviceCategory" type="xs:unsignedByte" use="required"/>
<xs:attribute name="shortServiceName" type="xs:string"/>
<xs:attribute name="hidden" type="xs:boolean" default="false"/>
<xs:attribute name="broadbandAccessRequired" type="xs:boolean" default="false"/>
<xs:attribute name="essential" type="xs:boolean"/>
<xs:attribute name="drmSystemID" type="xs:anyURI"/>
<xs:anyAttribute processContents="strict"/>
</xs:complexType>
<xs:complexType name="SimulcastTSIDType">
<xs:simpleContent>
<xs:extension base="xs:unsignedShort">
<xs:attribute name="simulcastMajorChannelNo" type="slt:ChannelNumType"/>
<xs:attribute name="simulcastMinorChannelNo" type="slt:ChannelNumType"/>
<xs:anyAttribute processContents="strict"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:complexType name="broadcastSigType">
<xs:attribute name="slsProtocol" type="xs:unsignedByte" use="required"/>
<xs:attribute name="slsMajorProtocolVersion" type="xs:unsignedByte" default="1"/>
<xs:attribute name="slsMinorProtocolVersion" type="xs:unsignedByte" default="0"/>
<xs:attribute name="slsDestinationIpAddress" type="slt:IPv4addressType" use="required"/>
<xs:attribute name="slsDestinationUdpPort" type="slt:PortType" use="required"/>
<xs:attribute name="slsSourceIpAddress" type="slt:IPv4addressType"/>
<xs:anyAttribute processContents="strict"/>
</xs:complexType>
<xs:complexType name="URLType">
<xs:simpleContent>
<xs:extension base="xs:anyURI">
<xs:attribute name="urlType" type="xs:unsignedByte" use="required"/>
<xs:anyAttribute processContents="strict"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:complexType name="OtherBsidType">
<xs:simpleContent>
<xs:extension base="slt:listOfUnsignedShortType">
<xs:attribute name="type" type="xs:unsignedByte" use="required"/>
<xs:anyAttribute processContents="strict"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:simpleType name="ChannelNumType">
<xs:restriction base="xs:unsignedShort">
<xs:minInclusive value="1"/>
<xs:maxInclusive value="999"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="IPv4addressType">
<xs:restriction base="xs:token">
<xs:pattern value="(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="PortType">
<xs:restriction base="xs:unsignedShort">
<xs:minInclusive value="1"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="listOfUnsignedShortType">
<xs:list itemType="xs:unsignedShort"/>
</xs:simpleType>
</xs:schema>

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:time="tag:atsc.org,2016:XMLSchemas/ATSC3/Delivery/SYSTIME/1.0/" targetNamespace="tag:atsc.org,2016:XMLSchemas/ATSC3/Delivery/SYSTIME/1.0/" elementFormDefault="qualified">
<xs:element name="SystemTime" type="time:SysTimeType"/>
<xs:complexType name="SysTimeType">
<xs:attribute name="currentUtcOffset" type="xs:short" use="required"/>
<xs:attribute name="ptpPrepend" type="xs:unsignedShort" default="0"/>
<xs:attribute name="leap59" type="xs:boolean" default="false"/>
<xs:attribute name="leap61" type="xs:boolean" default="false"/>
<xs:attribute name="utcLocalOffset" type="xs:duration" use="required"/>
<xs:attribute name="dsStatus" type="xs:boolean" default="false"/>
<xs:attribute name="dsDayOfMonth" type="time:dayType"/>
<xs:attribute name="dsHour" type="time:hourType"/>
<xs:anyAttribute processContents="strict"/>
</xs:complexType>
<xs:simpleType name="dayType">
<xs:restriction base="xs:unsignedByte">
<xs:minInclusive value="1"/>
<xs:maxInclusive value="31"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="hourType">
<xs:restriction base="xs:unsignedByte">
<xs:maxInclusive value="24"/>
</xs:restriction>
</xs:simpleType>
</xs:schema>

View File

@ -0,0 +1,3 @@
"C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8.1 Tools\x64\xsd.exe" /classes SLT-1.0-20180228.xsd SA-1.0-20170921.xsd RRT-1.0-20170209.xsd /out:. /namespace:skyscraper8.Atsc.A331.Schema
"C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8.1 Tools\x64\xsd.exe" /classes SYSTIME-1.0-20170921.xsd /out:. /namespace:skyscraper8.Atsc.A331.Schema
pause

View File

@ -17,6 +17,7 @@ using System.Threading.Tasks;
namespace skyscraper8.Atsc.A324
{
[SkyscraperPlugin]
[PluginPriority(2)]
internal class BasebandPacketDataStreamReceiver : ISkyscraperMpePlugin
{
private static readonly ILog logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name);

View File

@ -14,6 +14,7 @@ using System.Threading.Tasks;
namespace skyscraper8.Atsc.A324
{
[SkyscraperPlugin]
[PluginPriority(1)]
internal class StltpReceiver : ISkyscraperMpePlugin
{
public bool CanHandlePacket(InternetHeader internetHeader, byte[] ipv4Packet)

View File

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using skyscraper8.Atsc.A331.Schema;
namespace skyscraper8.Atsc.A331
{
internal interface IAtsc3EventHandler
{
void OnAtsc3Detected();
void OnAtsc3ServiceList(sltType slt);
}
}

View File

@ -0,0 +1,119 @@
using log4net;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using skyscraper5.Ietf.Rfc768;
using skyscraper5.Ietf.Rfc971;
using skyscraper5.Skyscraper;
using skyscraper5.Skyscraper.Plugins;
using skyscraper5.Skyscraper.Scraper;
using skyscraper8.Atsc.A331.Schema;
namespace skyscraper8.Atsc.A331
{
[SkyscraperPlugin]
[PluginPriority(3)]
internal class Atsc3Unpacker : ISkyscraperMpePlugin
{
private static readonly ILog logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name);
private static readonly IPAddress LLS_IP = IPAddress.Parse("224.0.23.60");
private static readonly ushort LLS_PORT = 4937;
private IAtsc3EventHandler eventHandler;
private bool systimeDetected;
public void ConnectToStorage(object[] connector)
{
throw new NotImplementedException();
}
public void SetContext(DateTime? currentTime, object skyscraperContext)
{
eventHandler = (IAtsc3EventHandler)skyscraperContext;
}
private bool atsc3detected;
public bool CanHandlePacket(InternetHeader internetHeader, byte[] ipv4Packet)
{
if (internetHeader.Protocol != 0x11)
return false;
if (atsc3detected && internetHeader.IsDestinationMulticast)
return true;
if (internetHeader.DestinationAddress.Equals(LLS_IP))
{
if (ipv4Packet.GetUInt16BE(2) == LLS_PORT)
{
return true;
}
}
return false;
}
public void HandlePacket(InternetHeader internetHeader, byte[] ipv4Packet)
{
_stopProcessIndicator = false;
UserDatagram udpPacket = new UserDatagram(ipv4Packet);
if (internetHeader.DestinationAddress.Equals(LLS_IP) && udpPacket.DestinationPort == LLS_PORT)
{
//Handle LLS
LlsTable lls = new LlsTable(udpPacket.Payload);
HandleLls(lls);
return;
}
if (!atsc3detected)
{
return;
}
}
private void HandleLls(LlsTable lls)
{
if (!lls.Valid)
return;
_stopProcessIndicator = true;
switch (lls.LlsTableId)
{
case 0x01:
//SLT
if (!atsc3detected)
{
atsc3detected = true;
eventHandler.OnAtsc3Detected();
}
sltType slt = Atsc3Utilities.UnpackSlt(lls.Payload);
eventHandler.OnAtsc3ServiceList(slt);
break;
case 0x03:
SysTimeType systime = Atsc3Utilities.UnpackSystime(lls.Payload);
if (systime.ptpPrepend != 0 && !systimeDetected)
{
logger.WarnFormat("Found valid ATSC A/331 SYSTIME, however it is not implemented yet. ");
systimeDetected = true;
}
break;
case 0xff:
//User defined, can be removed.
break;
default:
logger.InfoFormat(
"Encountered unknown ATSC A/331 LLS Table: 0x{0:X2} This means that this stream uses a version of ATSC 3 than skyscraper8 knows about. It would be great if you could share a recording of this stream so this can be implemented.", lls.LlsTableId);
break;
}
}
private bool _stopProcessIndicator;
public bool StopProcessingAfterThis()
{
return _stopProcessIndicator;
}
}
}

View File

@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;
using skyscraper8.Atsc.A331.Schema;
namespace skyscraper8.Atsc.A331
{
internal static class Atsc3Utilities
{
private static XmlSerializer sltSerializer;
private static XmlSerializer systimeSerializer;
public static sltType UnpackSlt(byte[] buffer)
{
if (sltSerializer == null)
{
sltSerializer = new XmlSerializer(typeof(sltType));
}
MemoryStream ms = new MemoryStream(buffer, false);
object deserialize = sltSerializer.Deserialize(ms);
sltType result = (sltType)deserialize;
return result;
}
public static SysTimeType UnpackSystime(byte[] buffer)
{
if (systimeSerializer == null)
{
systimeSerializer = new XmlSerializer(typeof(SysTimeType));
}
MemoryStream ms = new MemoryStream(buffer, false);
object deserialize = systimeSerializer.Deserialize(ms);
SysTimeType result = (SysTimeType)deserialize;
return result;
}
}
}

View File

@ -0,0 +1,59 @@
using Ionic.Zlib;
using log4net;
using skyscraper5.Skyscraper;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace skyscraper8.Atsc.A331
{
internal class LlsTable : Validatable
{
private static readonly ILog logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name);
public LlsTable(Span<byte> data)
{
LlsTableId = data[0];
LlsGroupId = data[1];
GroupCount = data[2] + 1;
TableVersion = data[3];
data = data.Slice(4);
if (data[0] != 0x1f)
{
Valid = false;
return;
}
if (data[1] != 0x8b)
{
Valid = false;
return;
}
try
{
Payload = GZipStream.UncompressBuffer(data.ToArray());
Valid = true;
}
catch (Exception e)
{
Valid = false;
logger.ErrorFormat("Failed to unpack the payload of an ATSC A/331 LLS table.");
}
}
public byte[] Payload { get; private set; }
public byte TableVersion { get; private set; }
public int GroupCount { get; private set; }
public byte LlsGroupId { get; private set; }
public byte LlsTableId { get; private set; }
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,181 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
//
// This source code was auto-generated by xsd, Version=4.8.9037.0.
//
namespace skyscraper8.Atsc.A331.Schema {
using System.Xml.Serialization;
/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.9037.0")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace="tag:atsc.org,2016:XMLSchemas/ATSC3/Delivery/SYSTIME/1.0/")]
[System.Xml.Serialization.XmlRootAttribute("SystemTime", Namespace="tag:atsc.org,2016:XMLSchemas/ATSC3/Delivery/SYSTIME/1.0/", IsNullable=false)]
public partial class SysTimeType {
private short currentUtcOffsetField;
private ushort ptpPrependField;
private bool leap59Field;
private bool leap61Field;
private string utcLocalOffsetField;
private bool dsStatusField;
private byte dsDayOfMonthField;
private bool dsDayOfMonthFieldSpecified;
private byte dsHourField;
private bool dsHourFieldSpecified;
private System.Xml.XmlAttribute[] anyAttrField;
public SysTimeType() {
this.ptpPrependField = ((ushort)(0));
this.leap59Field = false;
this.leap61Field = false;
this.dsStatusField = false;
}
/// <remarks/>
[System.Xml.Serialization.XmlAttributeAttribute()]
public short currentUtcOffset {
get {
return this.currentUtcOffsetField;
}
set {
this.currentUtcOffsetField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlAttributeAttribute()]
[System.ComponentModel.DefaultValueAttribute(typeof(ushort), "0")]
public ushort ptpPrepend {
get {
return this.ptpPrependField;
}
set {
this.ptpPrependField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlAttributeAttribute()]
[System.ComponentModel.DefaultValueAttribute(false)]
public bool leap59 {
get {
return this.leap59Field;
}
set {
this.leap59Field = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlAttributeAttribute()]
[System.ComponentModel.DefaultValueAttribute(false)]
public bool leap61 {
get {
return this.leap61Field;
}
set {
this.leap61Field = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlAttributeAttribute(DataType="duration")]
public string utcLocalOffset {
get {
return this.utcLocalOffsetField;
}
set {
this.utcLocalOffsetField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlAttributeAttribute()]
[System.ComponentModel.DefaultValueAttribute(false)]
public bool dsStatus {
get {
return this.dsStatusField;
}
set {
this.dsStatusField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlAttributeAttribute()]
public byte dsDayOfMonth {
get {
return this.dsDayOfMonthField;
}
set {
this.dsDayOfMonthField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlIgnoreAttribute()]
public bool dsDayOfMonthSpecified {
get {
return this.dsDayOfMonthFieldSpecified;
}
set {
this.dsDayOfMonthFieldSpecified = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlAttributeAttribute()]
public byte dsHour {
get {
return this.dsHourField;
}
set {
this.dsHourField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlIgnoreAttribute()]
public bool dsHourSpecified {
get {
return this.dsHourFieldSpecified;
}
set {
this.dsHourFieldSpecified = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlAnyAttributeAttribute()]
public System.Xml.XmlAttribute[] AnyAttr {
get {
return this.anyAttrField;
}
set {
this.anyAttrField = value;
}
}
}
}

View File

@ -1,4 +1,5 @@
using skyscraper5.Skyscraper.Plugins;
using System.Diagnostics;
namespace skyscraper5.Skyscraper
{
@ -27,5 +28,17 @@ namespace skyscraper5.Skyscraper
data[data.Length - i - 1] = temp;
}
}
public static ushort GetUInt16BE(this byte[] input, int offset)
{
byte[] buffer = new byte[2];
buffer[0] = input[offset + 0];
buffer[1] = input[offset + 1];
if (BitConverter.IsLittleEndian)
Array.Reverse(buffer, 0, 2);
return BitConverter.ToUInt16(buffer, 0);
}
}
}

View File

@ -701,6 +701,7 @@ namespace skyscraper5.Skyscraper.Plugins
if (mpePlugin != null)
result.Add(mpePlugin);
}
result.Sort(new PluginPrioritySorter());
return result.AsReadOnly();
}

View File

@ -90,6 +90,8 @@ using System.Text;
using System.Threading;
using skyscraper8.Atsc.A322;
using skyscraper8.Atsc.A330;
using skyscraper8.Atsc.A331;
using skyscraper8.Atsc.A331.Schema;
using Tsubasa.IO;
using Platform = skyscraper5.Dvb.SystemSoftwareUpdate.Model.Platform;
using RntParser = skyscraper5.Dvb.TvAnytime.RntParser;
@ -102,7 +104,7 @@ namespace skyscraper5.Skyscraper.Scraper
UpdateNotificationEventHandler, DataCarouselEventHandler, RdsEventHandler, IScte35EventHandler,
IAutodetectionEventHandler, IRstEventHandler, IRntEventHandler, IMultiprotocolEncapsulationEventHandler, ObjectCarouselEventHandler, T2MIEventHandler,
IDisposable, IFrameGrabberEventHandler, IntEventHandler, IRctEventHandler, ISkyscraperContext, IDocsisEventHandler, AbertisDecoderEventHandler, Id3Handler,
InteractionChannelHandler, SgtEventHandler, IDvbNipEventHandler, UleEventHandler, OtvSsuHandler, NdsSsuHandler, ISubTsHandler, ILldpFrameHandler, SisHandler, IWneHandler, IAtscPlpEventHandler
InteractionChannelHandler, SgtEventHandler, IDvbNipEventHandler, UleEventHandler, OtvSsuHandler, NdsSsuHandler, ISubTsHandler, ILldpFrameHandler, SisHandler, IWneHandler, IAtscPlpEventHandler, IAtsc3EventHandler
{
public const bool ALLOW_STREAM_TYPE_AUTODETECTION = true;
public const bool ALLOW_FFMPEG_FRAMEGRABBER = true;
@ -3555,5 +3557,21 @@ namespace skyscraper5.Skyscraper.Scraper
{
UiJunction?.WneStoryProgress(wneStoryProgress.SessionId, wneStoryProgress.Filename, wneStoryProgress.FileCaught, wneStoryProgress.FileSize);
}
public void OnAtsc3Detected()
{
LogEvent(SkyscraperContextEvent.Atsc3Detected);
}
public void OnAtsc3ServiceList(sltType slt)
{
foreach (serviceType service in slt.Service)
{
if (DataStorage.UpsertAtsc3Service(service))
{
LogEvent(SkyscraperContextEvent.Atsc3ServiceFound, service.shortServiceName);
}
}
}
}
}

View File

@ -104,6 +104,8 @@
DvbSisDaughterSitePsi,
DvbSisFramingInformation,
DvbSisTimestamp,
DvbSisDaughterSiteAdapterConfiguration
DvbSisDaughterSiteAdapterConfiguration,
Atsc3Detected,
Atsc3ServiceFound
}
}

View File

@ -26,6 +26,7 @@ using skyscraper8.Ses;
using System.Net;
using System.Net.NetworkInformation;
using skyscraper5.src.InteractionChannel.Model2;
using skyscraper8.Atsc.A331.Schema;
using skyscraper8.InteractionChannel.Model;
using skyscraper8.InteractionChannel.Model2;
@ -196,5 +197,12 @@ namespace skyscraper8.Skyscraper.Scraper.Storage
Tim GetLastTim(ushort interactiveNetworkId, PhysicalAddress physicalAddress);
void UpsertTim(ushort interactiveNetworkId, PhysicalAddress physicalAddress, Tim currentTim);
void StoreUpdateNotification2(UpdateNotificationGroup common, UpdateNotificationTarget target);
}
/// <summary>
/// Inserts or Updates an ATSC 3 service in the underlaying data storage.
/// </summary>
/// <param name="service">An ATSC3 Service</param>
/// <returns>true if the service was added or was changed, false if no changes in the DB were made.</returns>
bool UpsertAtsc3Service(serviceType service);
}
}

View File

@ -46,6 +46,7 @@ using System.Net;
using System.Net.NetworkInformation;
using System.Text;
using System.Text.RegularExpressions;
using skyscraper8.Atsc.A331.Schema;
using Platform = skyscraper5.Dvb.SystemSoftwareUpdate.Model.Platform;
namespace skyscraper5.Skyscraper.Scraper.Storage.Filesystem
@ -1782,6 +1783,11 @@ namespace skyscraper5.Skyscraper.Scraper.Storage.Filesystem
throw new NotImplementedException();
}
public bool UpsertAtsc3Service(serviceType service)
{
throw new NotImplementedException();
}
public RfSpectrumData GetRfSpectrum(Guid jobGuid)
{
string filename = Path.Combine(rootDirectory.FullName, "rf", jobGuid.ToString() + ".rf");

View File

@ -36,6 +36,7 @@ using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using skyscraper5.src.InteractionChannel.Model2;
using skyscraper8.Atsc.A331.Schema;
using skyscraper8.Ietf.FLUTE;
using skyscraper8.InteractionChannel.Model;
using skyscraper8.InteractionChannel.Model2;
@ -1888,5 +1889,21 @@ namespace skyscraper5.Skyscraper.Scraper.Storage.InMemory
knownUnts[common.Oui] = new Tuple<UpdateNotificationGroup, UpdateNotificationTarget>(common, target);
}
private Dictionary<string, serviceType> _atsc3Services;
public bool UpsertAtsc3Service(serviceType service)
{
if (_atsc3Services == null)
_atsc3Services = new Dictionary<string, serviceType>();
if (_atsc3Services.ContainsKey(service.globalServiceID))
{
return false;
}
else
{
_atsc3Services.Add(service.globalServiceID, service);
return true;
}
}
}
}

View File

@ -1,220 +1,220 @@
using log4net;
using skyscraper5.Skyscraper;
using skyscraper5.Skyscraper.Plugins;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using skyscraper5.Skyscraper.Net;
using skyscraper5.Skyscraper.Scraper.Storage;
using skyscraper8.Skyscraper.Net;
using skyscraper8.Skyscraper.Net.Pcap;
namespace skyscraper8.Skyscraper.Scraper.Storage
{
public class StorageConnectionManager
{
private StorageConnectionManager()
{
plugins = PluginManager.GetInstance();
ini = plugins.Ini;
}
private readonly PluginManager plugins;
private static StorageConnectionManager _instance;
private static readonly ILog logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name);
private static Type storageNameAttributeType = typeof(StorageNameAttribute);
private readonly Ini ini;
public static StorageConnectionManager GetInstance()
{
if (_instance == null)
_instance = new StorageConnectionManager();
return _instance;
}
public IEnumerable<Tuple<int,bool,string>> GetAllKnownFactoryNames()
{
ReadOnlyDictionary<int, DataStorageFactory> dataStorages = plugins.GetDataStorages();
foreach (KeyValuePair<int, DataStorageFactory> dataStorageFactory in dataStorages)
{
yield return new Tuple<int, bool, string>(dataStorageFactory.Key, true, GetName(dataStorageFactory.Value));
}
ReadOnlyDictionary<int, ObjectStorageFactory> objectStorages = plugins.GetObjectStorages();
foreach (KeyValuePair<int, ObjectStorageFactory> objectStorageFactory in objectStorages)
{
yield return new Tuple<int, bool, string>(objectStorageFactory.Key, false, GetName(objectStorageFactory.Value));
}
}
public string GetName(object o)
{
Type type = o.GetType();
Attribute attribute = type.GetCustomAttribute(storageNameAttributeType);
StorageNameAttribute storageNameAttribute = attribute as StorageNameAttribute;
if (storageNameAttribute == null)
{
logger.WarnFormat("{0} does not have a {1}.", type.Name, storageNameAttributeType.Name);
return type.Name;
}
return storageNameAttribute.Name;
}
public DataStorageFactory GetDefaultDataStorageFactory()
{
int requiredDataStorageId = ini.ReadValue("startup", "dataStorage", -1);
if (requiredDataStorageId == -1)
{
throw new ScraperStorageException("Could not determine the data storage factory from ini file. Configure it using the UI!");
}
string iniCategoryName = String.Format("dataStorage{0}", requiredDataStorageId);
if (!ini.ContainsKey(iniCategoryName))
{
throw new ScraperStorageException("Could not find configuration for the storage factory. Configure it using the UI!");
}
ReadOnlyDictionary<int, DataStorageFactory> dataStorages = plugins.GetDataStorages();
KeyValuePair<int, DataStorageFactory> valuePair = dataStorages.First(x => x.Key == requiredDataStorageId);
if (valuePair.Value == null)
{
throw new ScraperStorageException(String.Format("Storage Factory {0} was not loaded. Reconfigure with the UI!", requiredDataStorageId));
}
string factoryCname = String.Format("dataStorage{0}", requiredDataStorageId);
PluginManager.GetInstance().AutoconfigureObject(factoryCname, valuePair.Value);
return valuePair.Value;
}
public ObjectStorageFactory GetDefaultObjectStorageFactory()
{
int requiredObjectStorageId = ini.ReadValue("startup", "objectStorage", -1);
if (requiredObjectStorageId == -1)
{
throw new ScraperStorageException("Could not determine the object storage factory from ini file. Configure it using the UI!");
}
string iniCategoryName = String.Format("objectStorage{0}", requiredObjectStorageId);
if (!ini.ContainsKey(iniCategoryName))
{
throw new ScraperStorageException("Could not find configuration for the object storage factory. Configure it using the UI!");
}
ReadOnlyDictionary<int, ObjectStorageFactory> dataStorages = plugins.GetObjectStorages();
KeyValuePair<int, ObjectStorageFactory> valuePair = dataStorages.First(x => x.Key == requiredObjectStorageId);
if (valuePair.Value == null)
{
throw new ScraperStorageException(String.Format("Object Storage Factory {0} was not loaded. Reconfigure with the UI!", requiredObjectStorageId));
}
string factoryCname = String.Format("objectStorage{0}", requiredObjectStorageId);
PluginManager.GetInstance().AutoconfigureObject(factoryCname, valuePair.Value);
return valuePair.Value;
}
public static object[] GetPluginConnectors(DataStorage dataStorage, ObjectStorage objectStorage)
{
object[] dataConnector = dataStorage.GetPluginConnector();
object[] objectConnector = objectStorage.GetPluginConnector();
object[] result = new object[dataConnector.Length + objectConnector.Length];
Array.Copy(dataConnector, 0, result, 0, dataConnector.Length);
Array.Copy(objectConnector, 0, result, dataConnector.Length, objectConnector.Length);
return result;
}
public bool IniExists()
{
return ini != null;
}
public ReadOnlyDictionary<int, ObjectStorageFactory> GetObjectStorages()
{
return plugins.GetObjectStorages();
}
public ReadOnlyDictionary<int, DataStorageFactory> GetDataStorages()
{
return plugins.GetDataStorages();
}
private bool IsIpTafficHandlerConfigured()
{
if (ini == null)
return false;
if (!ini.ContainsKey("ip_handler"))
return false;
if (!ini["ip_handler"].ContainsKey("type"))
return false;
return true;
}
internal IpTrafficHandler GetDefaultIpTrafficHandler()
{
Type targetType;
if (IsIpTafficHandlerConfigured())
{
ReadOnlyDictionary<int, Type> handlers = plugins.GetIpTrafficHandlers();
int type = Int32.Parse(ini["ip_handler"]["type"]);
if (!handlers.ContainsKey(type))
{
logger.WarnFormat("The IP traffic handler with ID {0} was not found. I'm gonna assume you want the pcap files.", type);
targetType = typeof(PcapIpTrafficHandler);
}
else
{
targetType = handlers[type];
}
}
else
{
logger.WarnFormat("You didn't call \"pcapon\" or \"pcapoff\" before. I'm gonna assume you want the pcap files.");
targetType = typeof(PcapIpTrafficHandler);
}
ConstructorInfo constructorInfo = targetType.GetConstructor(new Type[] { });
if (constructorInfo == null)
{
throw new ScraperStorageException(String.Format("The type {0} doesn't contain a parameterless constructor.", targetType.Name));
}
object invoke = constructorInfo.Invoke(new object[] { });
return (IpTrafficHandler) invoke;
}
private bool IsSubTsDumpingConfigured()
{
if (ini == null)
return false;
if (!ini.ContainsKey("subts"))
return false;
if (!ini["subts"].ContainsKey("dump"))
return false;
return true;
}
internal bool IsSubTsDumpingEnabled()
{
if (IsSubTsDumpingConfigured())
{
return ini.ReadValue("subts", "dump", true);
}
else
{
logger.WarnFormat("You didn't call the subts option before. I'm gonna assume you want me to dump inner TS packets.");
return true;
}
}
using log4net;
using skyscraper5.Skyscraper;
using skyscraper5.Skyscraper.Plugins;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using skyscraper5.Skyscraper.Net;
using skyscraper5.Skyscraper.Scraper.Storage;
using skyscraper8.Skyscraper.Net;
using skyscraper8.Skyscraper.Net.Pcap;
namespace skyscraper8.Skyscraper.Scraper.Storage
{
public class StorageConnectionManager
{
private StorageConnectionManager()
{
plugins = PluginManager.GetInstance();
ini = plugins.Ini;
}
private readonly PluginManager plugins;
private static StorageConnectionManager _instance;
private static readonly ILog logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name);
private static Type storageNameAttributeType = typeof(StorageNameAttribute);
private readonly Ini ini;
public static StorageConnectionManager GetInstance()
{
if (_instance == null)
_instance = new StorageConnectionManager();
return _instance;
}
public IEnumerable<Tuple<int,bool,string>> GetAllKnownFactoryNames()
{
ReadOnlyDictionary<int, DataStorageFactory> dataStorages = plugins.GetDataStorages();
foreach (KeyValuePair<int, DataStorageFactory> dataStorageFactory in dataStorages)
{
yield return new Tuple<int, bool, string>(dataStorageFactory.Key, true, GetName(dataStorageFactory.Value));
}
ReadOnlyDictionary<int, ObjectStorageFactory> objectStorages = plugins.GetObjectStorages();
foreach (KeyValuePair<int, ObjectStorageFactory> objectStorageFactory in objectStorages)
{
yield return new Tuple<int, bool, string>(objectStorageFactory.Key, false, GetName(objectStorageFactory.Value));
}
}
public string GetName(object o)
{
Type type = o.GetType();
Attribute attribute = type.GetCustomAttribute(storageNameAttributeType);
StorageNameAttribute storageNameAttribute = attribute as StorageNameAttribute;
if (storageNameAttribute == null)
{
logger.WarnFormat("{0} does not have a {1}.", type.Name, storageNameAttributeType.Name);
return type.Name;
}
return storageNameAttribute.Name;
}
public DataStorageFactory GetDefaultDataStorageFactory()
{
int requiredDataStorageId = ini.ReadValue("startup", "dataStorage", -1);
if (requiredDataStorageId == -1)
{
throw new ScraperStorageException("Could not determine the data storage factory from ini file. Configure it using the UI!");
}
string iniCategoryName = String.Format("dataStorage{0}", requiredDataStorageId);
if (!ini.ContainsKey(iniCategoryName))
{
throw new ScraperStorageException("Could not find configuration for the storage factory. Configure it using the UI!");
}
ReadOnlyDictionary<int, DataStorageFactory> dataStorages = plugins.GetDataStorages();
KeyValuePair<int, DataStorageFactory> valuePair = dataStorages.First(x => x.Key == requiredDataStorageId);
if (valuePair.Value == null)
{
throw new ScraperStorageException(String.Format("Storage Factory {0} was not loaded. Reconfigure with the UI!", requiredDataStorageId));
}
string factoryCname = String.Format("dataStorage{0}", requiredDataStorageId);
PluginManager.GetInstance().AutoconfigureObject(factoryCname, valuePair.Value);
return valuePair.Value;
}
public ObjectStorageFactory GetDefaultObjectStorageFactory()
{
int requiredObjectStorageId = ini.ReadValue("startup", "objectStorage", -1);
if (requiredObjectStorageId == -1)
{
throw new ScraperStorageException("Could not determine the object storage factory from ini file. Configure it using the UI!");
}
string iniCategoryName = String.Format("objectStorage{0}", requiredObjectStorageId);
if (!ini.ContainsKey(iniCategoryName))
{
throw new ScraperStorageException("Could not find configuration for the object storage factory. Configure it using the UI!");
}
ReadOnlyDictionary<int, ObjectStorageFactory> dataStorages = plugins.GetObjectStorages();
KeyValuePair<int, ObjectStorageFactory> valuePair = dataStorages.First(x => x.Key == requiredObjectStorageId);
if (valuePair.Value == null)
{
throw new ScraperStorageException(String.Format("Object Storage Factory {0} was not loaded. Reconfigure with the UI!", requiredObjectStorageId));
}
string factoryCname = String.Format("objectStorage{0}", requiredObjectStorageId);
PluginManager.GetInstance().AutoconfigureObject(factoryCname, valuePair.Value);
return valuePair.Value;
}
public static object[] GetPluginConnectors(DataStorage dataStorage, ObjectStorage objectStorage)
{
object[] dataConnector = dataStorage.GetPluginConnector();
object[] objectConnector = objectStorage.GetPluginConnector();
object[] result = new object[dataConnector.Length + objectConnector.Length];
Array.Copy(dataConnector, 0, result, 0, dataConnector.Length);
Array.Copy(objectConnector, 0, result, dataConnector.Length, objectConnector.Length);
return result;
}
public bool IniExists()
{
return ini != null;
}
public ReadOnlyDictionary<int, ObjectStorageFactory> GetObjectStorages()
{
return plugins.GetObjectStorages();
}
public ReadOnlyDictionary<int, DataStorageFactory> GetDataStorages()
{
return plugins.GetDataStorages();
}
private bool IsIpTafficHandlerConfigured()
{
if (ini == null)
return false;
if (!ini.ContainsKey("ip_handler"))
return false;
if (!ini["ip_handler"].ContainsKey("type"))
return false;
return true;
}
internal IpTrafficHandler GetDefaultIpTrafficHandler()
{
Type targetType;
if (IsIpTafficHandlerConfigured())
{
ReadOnlyDictionary<int, Type> handlers = plugins.GetIpTrafficHandlers();
int type = Int32.Parse(ini["ip_handler"]["type"]);
if (!handlers.ContainsKey(type))
{
logger.WarnFormat("The IP traffic handler with ID {0} was not found. I'm gonna assume you want the pcap files.", type);
targetType = typeof(PcapIpTrafficHandler);
}
else
{
targetType = handlers[type];
}
}
else
{
logger.WarnFormat("You didn't call \"pcapon\" or \"pcapoff\" before. I'm gonna assume you want the pcap files.");
targetType = typeof(PcapIpTrafficHandler);
}
ConstructorInfo constructorInfo = targetType.GetConstructor(new Type[] { });
if (constructorInfo == null)
{
throw new ScraperStorageException(String.Format("The type {0} doesn't contain a parameterless constructor.", targetType.Name));
}
object invoke = constructorInfo.Invoke(new object[] { });
return (IpTrafficHandler) invoke;
}
private bool IsSubTsDumpingConfigured()
{
if (ini == null)
return false;
if (!ini.ContainsKey("subts"))
return false;
if (!ini["subts"].ContainsKey("dump"))
return false;
return true;
}
internal bool IsSubTsDumpingEnabled()
{
if (IsSubTsDumpingConfigured())
{
return ini.ReadValue("subts", "dump", true);
}
else
{
logger.WarnFormat("You didn't call the subts option before. I'm gonna assume you want me to dump inner TS packets.");
return true;
}
}
internal string GetSubTsOutputPath()
{
return ini.ReadValue("subts", "outdir", "");
}
}
}
{
return ini.ReadValue("subts", "outdir", "");
}
}
}