From 7d218ade9c9a0411d39f5962fef9e6899503aa10 Mon Sep 17 00:00:00 2001 From: feyris-tan <4116042+feyris-tan@users.noreply.github.com> Date: Thu, 17 Jul 2025 17:08:19 +0200 Subject: [PATCH] Added a failed attempt at OpenTV Parsing. --- .../SingleTreeDictionaryEntry.cs | 275 +++++++++++++++++ .../DVBServices/Mpeg2ExtendedHeader.cs | 149 +++++++++ .../OpenTV/OpenTVExtendedDescriptionRecord.cs | 111 +++++++ .../DVBServices/OpenTV/OpenTVRecordBase.cs | 195 ++++++++++++ .../OpenTV/OpenTVSeriesLinkRecord.cs | 111 +++++++ .../OpenTV/OpenTVShortDescriptionRecord.cs | 116 +++++++ .../DVBServices/OpenTV/OpenTVSummaryData.cs | 244 +++++++++++++++ .../DVBServices/OpenTV/OpenTVSummaryHeader.cs | 161 ++++++++++ .../OpenTV/OpenTVSummarySection.cs | 151 +++++++++ .../DVBServices/OpenTV/OpenTVTitleData.cs | 289 ++++++++++++++++++ .../OpenTV/OpenTVTitleDataRecord.cs | 164 ++++++++++ .../DVBServices/OpenTV/OpenTVTitleHeader.cs | 165 ++++++++++ .../DVBServices/OpenTV/OpenTVTitleSection.cs | 126 ++++++++ .../EPGCollectorSide/DVBServices/Utils.cs | 37 +++ .../DomainObjects/TraceEntry.cs | 266 ++++++++++++++++ .../OpenTV/OpenTvDataStorage.cs | 68 +++++ .../OpenTV/OpenTvExpectedDataType.cs | 14 + .../SkyscraperSide/OpenTV/OpenTvHandler.cs | 16 + .../SkyscraperSide/OpenTV/OpenTvScraper.cs | 59 ++++ .../OpenTV/OpenTvSummaryContestant.cs | 68 +++++ .../OpenTV/OpenTvSummaryParser.cs | 42 +++ .../OpenTV/OpenTvTitleContestant.cs | 62 ++++ .../OpenTV/OpenTvTitleParser.cs | 42 +++ skyscraper8/Program.cs | 1 + skyscraper8/Properties/launchSettings.json | 2 +- .../Skyscraper/Scraper/SkyscraperContext.cs | 19 +- 26 files changed, 2934 insertions(+), 19 deletions(-) create mode 100644 PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/EPGCollectorSide/DVBServices/Decompressors/SingleTreeDictionaryEntry.cs create mode 100644 PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/EPGCollectorSide/DVBServices/Mpeg2ExtendedHeader.cs create mode 100644 PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/EPGCollectorSide/DVBServices/OpenTV/OpenTVExtendedDescriptionRecord.cs create mode 100644 PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/EPGCollectorSide/DVBServices/OpenTV/OpenTVRecordBase.cs create mode 100644 PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/EPGCollectorSide/DVBServices/OpenTV/OpenTVSeriesLinkRecord.cs create mode 100644 PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/EPGCollectorSide/DVBServices/OpenTV/OpenTVShortDescriptionRecord.cs create mode 100644 PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/EPGCollectorSide/DVBServices/OpenTV/OpenTVSummaryData.cs create mode 100644 PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/EPGCollectorSide/DVBServices/OpenTV/OpenTVSummaryHeader.cs create mode 100644 PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/EPGCollectorSide/DVBServices/OpenTV/OpenTVSummarySection.cs create mode 100644 PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/EPGCollectorSide/DVBServices/OpenTV/OpenTVTitleData.cs create mode 100644 PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/EPGCollectorSide/DVBServices/OpenTV/OpenTVTitleDataRecord.cs create mode 100644 PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/EPGCollectorSide/DVBServices/OpenTV/OpenTVTitleHeader.cs create mode 100644 PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/EPGCollectorSide/DVBServices/OpenTV/OpenTVTitleSection.cs create mode 100644 PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/EPGCollectorSide/DomainObjects/TraceEntry.cs create mode 100644 PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/SkyscraperSide/OpenTV/OpenTvDataStorage.cs create mode 100644 PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/SkyscraperSide/OpenTV/OpenTvExpectedDataType.cs create mode 100644 PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/SkyscraperSide/OpenTV/OpenTvHandler.cs create mode 100644 PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/SkyscraperSide/OpenTV/OpenTvScraper.cs create mode 100644 PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/SkyscraperSide/OpenTV/OpenTvSummaryContestant.cs create mode 100644 PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/SkyscraperSide/OpenTV/OpenTvSummaryParser.cs create mode 100644 PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/SkyscraperSide/OpenTV/OpenTvTitleContestant.cs create mode 100644 PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/SkyscraperSide/OpenTV/OpenTvTitleParser.cs diff --git a/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/EPGCollectorSide/DVBServices/Decompressors/SingleTreeDictionaryEntry.cs b/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/EPGCollectorSide/DVBServices/Decompressors/SingleTreeDictionaryEntry.cs new file mode 100644 index 0000000..ec03c65 --- /dev/null +++ b/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/EPGCollectorSide/DVBServices/Decompressors/SingleTreeDictionaryEntry.cs @@ -0,0 +1,275 @@ +////////////////////////////////////////////////////////////////////////////////// +// // +// Copyright © 2005-2020 nzsjb // +// // +// This Program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation; either version 2, or (at your option) // +// any later version. // +// // +// This Program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with GNU Make; see the file COPYING. If not, write to // +// the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. // +// http://www.gnu.org/copyleft/gpl.html // +// // +////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Globalization; +using System.IO; +using System.Text; + +using DomainObjects; + +namespace DVBServices +{ + /// + /// The class that describes a dictionary entry for a single tree Huffman scenario. + /// + public class SingleTreeDictionaryEntry + { + /// + /// Get or set the flag for not starting at the first bit in the compressed string. + /// + public static bool OffsetStart + { + get { return (offsetStart); } + set { offsetStart = value; } + } + + /// + /// Get the decode string. + /// + public string Decode { get { return (decode); } } + + private string decode; + private string pattern; + + private static HuffmanEntry[] roots = new HuffmanEntry[2]; + private static bool offsetStart = true; + + /// + /// Initialize a new instance of the SingleTreeDictionaryEntry class. + /// + /// The Huffman bit pattern. + /// The decode for the bit pattern. + public SingleTreeDictionaryEntry(string pattern, string decode) + { + this.pattern = pattern; + this.decode = decode; + } + + /// + /// Load the dictionary entries ino the first root. + /// + /// True if the file has been loaded; false otherwise. + public static bool Load(string fileName) + { + return (Load(fileName, 1)); + } + + /// + /// Load the dictionary entries. + /// + /// True if the file has been loaded; false otherwise. + public static bool Load(string fileName, int rootNumber) + { + FileStream fileStream = null; + + Logger.Instance.Write("Loading Huffman Dictionary from " + fileName); + + try { fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read); } + catch (IOException e) + { + Logger.Instance.Write("Huffman Dictionary file " + fileName + " not available"); + Logger.Instance.Write(e.Message); + return (false); + } + + StreamReader streamReader = new StreamReader(fileStream); + roots[rootNumber - 1] = null; + + while (!streamReader.EndOfStream) + { + string line = streamReader.ReadLine(); + + if (line != string.Empty && !line.StartsWith("####")) + { + string[] parts = line.Split(new char[] { '=' }); + if (parts.Length == 2) + addEntry(rootNumber, parts[1], getDecode(parts[0])); + else + { + if (parts.Length == 3 && parts[0] == string.Empty && parts[1] == string.Empty) + addEntry(rootNumber, parts[2], "="); + else + Logger.Instance.Write("Dictionary line '" + line + "' format wrong - line ignored "); + } + } + } + + streamReader.Close(); + fileStream.Close(); + + Logger.Instance.Write("Dictionary loaded"); + + return (true); + } + + private static void addEntry(int rootNumber, string pattern, string decode) + { + if (roots[rootNumber - 1] == null) + roots[rootNumber - 1] = new HuffmanEntry(); + + HuffmanEntry currentEntry = roots[rootNumber - 1]; + + for (int index = 0; index < pattern.Length; index++) + { + char patternChar = pattern[index]; + + switch (patternChar) + { + case '0': + if (currentEntry.P0 == null) + { + currentEntry.P0 = new HuffmanEntry(); + currentEntry = currentEntry.P0; + if (index == pattern.Length - 1) + currentEntry.Value = decode; + } + else + { + currentEntry = currentEntry.P0; + if (currentEntry.Value != null && index == pattern.Length - 1) + Logger.Instance.Write("Dictionary entry already set"); + } + break; + case '1': + if (currentEntry.P1 == null) + { + currentEntry.P1 = new HuffmanEntry(); + currentEntry = currentEntry.P1; + if (index == pattern.Length - 1) + currentEntry.Value = decode; + } + else + { + currentEntry = currentEntry.P1; + if (currentEntry.Value != null && index == pattern.Length - 1) + Logger.Instance.Write("Dictionary entry already set"); + } + break; + default: + break; + } + } + } + + private static string getDecode(string originalParameter) + { + if (originalParameter.Length != 4) + return (originalParameter); + + if (originalParameter[0] != '<' || originalParameter[3] != '>') + return (originalParameter); + + return (((char)(Int32.Parse(originalParameter.Substring(1, 2), NumberStyles.HexNumber))).ToString()); + } + + /// + /// Decompress a compressed Huffman string using the first root table. + /// + /// The compressed byte data. + /// The decompressed string. + public static string DecodeData(byte[] byteData) + { + return (DecodeData(1, byteData)); + } + + /// + /// Decompress a compressed Huffman string. + /// + /// The root table to use. + /// The compressed byte data. + /// The decompressed string. + public static string DecodeData(int rootNumber, byte[] byteData) + { + StringBuilder outputString = new StringBuilder(); + + HuffmanEntry currentEntry = roots[rootNumber - 1]; + + byte mask; + if (offsetStart) + mask = 0x20; + else + mask = 0x80; + + StringBuilder bitString = new StringBuilder(); + + for (int index = 0; index < byteData.Length; index++) + { + byte dataByte = byteData[index]; + + while (mask > 0) + { + if (currentEntry.Value != null) + { + if (currentEntry.Value != "??") + outputString.Append(currentEntry.Value); + currentEntry = roots[rootNumber - 1]; + bitString = new StringBuilder(); + } + + if ((dataByte & mask) == 0) + { + bitString.Append("0"); + + if (currentEntry.P0 != null) + currentEntry = currentEntry.P0; + else + { + Logger.Instance.Write(" ** DECOMPRESSION FAILED **"); + Logger.Instance.Write("Original data: " + Utils.ConvertToHex(byteData)); + Logger.Instance.Write("Decoded data: " + outputString.ToString()); + Logger.Instance.Write("Bit string: " + bitString.ToString()); + return (outputString.ToString() + " ** DECOMPRESSION FAILED **"); + } + } + else + { + bitString.Append("1"); + + if (currentEntry.P1 != null) + currentEntry = currentEntry.P1; + else + { + Logger.Instance.Write(" ** DECOMPRESSION FAILED **"); + Logger.Instance.Write("Original data: " + Utils.ConvertToHex(byteData)); + Logger.Instance.Write("Decoded data: " + outputString.ToString()); + Logger.Instance.Write("Bit string: " + bitString.ToString()); + return (outputString.ToString() + " ** DECOMPRESSION FAILED **"); + } + } + + mask = (byte)(mask >> 1); + } + + mask = 0x80; + } + + /*if (currentEntry.Value != null && currentEntry.Value != "??") + outputString.Append(currentEntry.Value);*/ + + if (currentEntry.Value != null) + outputString.Append(currentEntry.Value); + + return(outputString.ToString()); + } + } +} + diff --git a/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/EPGCollectorSide/DVBServices/Mpeg2ExtendedHeader.cs b/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/EPGCollectorSide/DVBServices/Mpeg2ExtendedHeader.cs new file mode 100644 index 0000000..3813463 --- /dev/null +++ b/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/EPGCollectorSide/DVBServices/Mpeg2ExtendedHeader.cs @@ -0,0 +1,149 @@ +////////////////////////////////////////////////////////////////////////////////// +// // +// Copyright © 2005-2020 nzsjb // +// // +// This Program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation; either version 2, or (at your option) // +// any later version. // +// // +// This Program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with GNU Make; see the file COPYING. If not, write to // +// the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. // +// http://www.gnu.org/copyleft/gpl.html // +// // +////////////////////////////////////////////////////////////////////////////////// + +using System; + +namespace DVBServices +{ + /// + /// The class that describes an MPEG2 extended header. + /// + public class Mpeg2ExtendedHeader : Mpeg2BasicHeader + { + /// + /// Get the table identification extension. + /// + public int TableIDExtension { get { return (tableIDExtension); } } + /// + /// Return true if the MPEG2 section is current; false otherwise. + /// + public bool CurrentNextIndicator { get { return (currentNextIndicator); } } + /// + /// Get the version number. + /// + public int VersionNumber { get { return (versionNumber); } } + /// + /// Get the section number. + /// + public int SectionNumber { get { return (sectionNumber); } } + /// + /// Get the last section number. + /// + public int LastSectionNumber { get { return (lastSectionNumber); } } + + /// + /// Return true if the MPEG2 section is current; false otherwise. + /// + public bool Current { get { return (currentNextIndicator); } } + + /// + /// Get the index of the next byte in the MPEG2 section following the header. + /// + /// + /// The header has not been processed. + /// + public override int Index + { + get + { + if (lastIndex == -1) + throw (new InvalidOperationException("Mpeg2ExtendedHeader: Index requested before block processed")); + return (lastIndex); + } + } + + private int tableIDExtension; + private bool currentNextIndicator; + private int versionNumber; + private int sectionNumber; + private int lastSectionNumber; + + private int lastIndex = -1; + + // MPEG extended header layout is as follows + // + // table ID extension word uimsbf + // + // reserved 2 bits bslbf + // version no 5 bits uimsbf + // current/next ind 1 bit bslbf + // + // section no byte uimsbf + // last section no byte uimsbf + + /// + /// Initialize a new instance of the Mpeg2ExtendedHeader class. + /// + public Mpeg2ExtendedHeader() { } + + /// + /// Parse the header. + /// + /// The MPEG2 section containing the header. + public override void Process(byte[] byteData) + { + base.Process(byteData); + lastIndex = base.Index; + + try + { + tableIDExtension = (byteData[lastIndex] * 256) + (int)byteData[lastIndex + 1]; + lastIndex += 2; + + versionNumber = ((int)((byteData[lastIndex] >> 1) & 0x1f)); + currentNextIndicator = (byteData[lastIndex] & 0x01) != 0; + lastIndex++; + + sectionNumber = (int)byteData[lastIndex]; + lastIndex++; + + lastSectionNumber = (int)byteData[lastIndex]; + lastIndex++; + + Validate(); + } + catch (IndexOutOfRangeException) + { + throw (new ArgumentOutOfRangeException("MPEG2 extended header short")); + } + } + + /// + /// Validate the header fields. + /// + /// + /// A header field is not valid. + /// + public override void Validate() + { + base.Validate(); + } + + /// + /// Log the header fields. + /// + public override void LogMessage() + { + base.LogMessage(); + } + } +} + diff --git a/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/EPGCollectorSide/DVBServices/OpenTV/OpenTVExtendedDescriptionRecord.cs b/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/EPGCollectorSide/DVBServices/OpenTV/OpenTVExtendedDescriptionRecord.cs new file mode 100644 index 0000000..8e5ed36 --- /dev/null +++ b/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/EPGCollectorSide/DVBServices/OpenTV/OpenTVExtendedDescriptionRecord.cs @@ -0,0 +1,111 @@ +////////////////////////////////////////////////////////////////////////////////// +// // +// Copyright © 2005-2020 nzsjb // +// // +// This Program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation; either version 2, or (at your option) // +// any later version. // +// // +// This Program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with GNU Make; see the file COPYING. If not, write to // +// the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. // +// http://www.gnu.org/copyleft/gpl.html // +// // +////////////////////////////////////////////////////////////////////////////////// + +using System; + +using DomainObjects; + +namespace DVBServices +{ + /// + /// The class that describes an OpenTV extended description record. + /// + internal class OpenTVExtendedDescriptionRecord : OpenTVRecordBase + { + /// + /// Get the tag value for this record. + /// + public const int TagValue = 0xbb; + + /// + /// Get the extended description. + /// + public string Description { get { return (SingleTreeDictionaryEntry.DecodeData(description)); } } + + /// + /// Get the index of the next byte in the MPEG2 section following the record. + /// + /// + /// The record has not been processed. + /// + public override int Index + { + get + { + if (lastIndex == -1) + throw (new InvalidOperationException("OpenTVExtendedDescriptionRecord: Index requested before block processed")); + return (lastIndex); + } + } + + private byte[] description; + + private int lastIndex = -1; + + /// + /// Initialize a new instance of the OpenTVExtendedDescriptionRecord class. + /// + internal OpenTVExtendedDescriptionRecord() { } + + /// + /// Parse the record. + /// + /// The MPEG2 section containing the record. + /// Index of the first byte of the record data in the MPEG2 section. + internal override void Process(byte[] byteData, int index) + { + lastIndex = index; + + try + { + description = Utils.GetBytes(byteData, lastIndex, Length); + lastIndex += Length; + + Validate(); + } + catch (IndexOutOfRangeException) + { + throw (new ArgumentOutOfRangeException("lastIndex = " + lastIndex)); + } + } + + /// + /// Validate the record data fields. + /// + /// + /// A record data field is not valid. + /// + internal override void Validate() { } + + /// + /// Log the record data fields. + /// + internal override void LogMessage() + { + if (Logger.ProtocolLogger == null) + return; + + Logger.ProtocolLogger.Write(Logger.ProtocolIndent + ""); + Logger.ProtocolLogger.Write(Logger.ProtocolIndent + "OPENTV EXTENDED DESCRIPTION RECORD: Description: " + Utils.ConvertToHex(description)); + } + } +} + diff --git a/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/EPGCollectorSide/DVBServices/OpenTV/OpenTVRecordBase.cs b/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/EPGCollectorSide/DVBServices/OpenTV/OpenTVRecordBase.cs new file mode 100644 index 0000000..a034ef7 --- /dev/null +++ b/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/EPGCollectorSide/DVBServices/OpenTV/OpenTVRecordBase.cs @@ -0,0 +1,195 @@ +////////////////////////////////////////////////////////////////////////////////// +// // +// Copyright © 2005-2020 nzsjb // +// // +// This Program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation; either version 2, or (at your option) // +// any later version. // +// // +// This Program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with GNU Make; see the file COPYING. If not, write to // +// the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. // +// http://www.gnu.org/copyleft/gpl.html // +// // +////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.ObjectModel; + +using DomainObjects; + +namespace DVBServices +{ + /// + /// The base OpenTV record class. + /// + internal class OpenTVRecordBase + { + /// + /// Get the tag of the record. + /// + internal int Tag { get { return (tag); } } + /// + /// Get the length of the record data. + /// + internal int Length { get { return (length); } } + /// + /// Get the record data. + /// + internal byte[] Data { get { return (data); } } + + /// + /// Get the total length of the record. + /// + internal int TotalLength { get { return (Length + 2); } } + /// + /// Return true if the record is undefined; false otherwise. + /// + internal bool IsUndefined { get { return (isUndefined); } } + + /// + /// Return the collection of unknown records. + /// + internal static Collection UnknownRecords { get { return (unknownRecords); } } + + /// + /// Get the index of the next byte in the MPEG2 section following this record. + /// + /// + /// The descriptor has not been processed. + /// + public virtual int Index + { + get + { + if (lastIndex == -1) + throw (new InvalidOperationException("OpenTVRecordBase: Index requested before block processed")); + return (lastIndex); + } + } + + private int lastIndex = -1; + + private int tag; + private int length; + private byte[] data; + + private bool isUndefined; + + private static Collection unknownRecords; + + /// + /// Create an instance of the record class. + /// + /// The MPEG2 section containing the record. + /// The index of the tag byte of the record. + /// A descriptor instance. + internal static OpenTVRecordBase Instance(byte[] byteData, int index) + { + OpenTVRecordBase record; + + switch ((int)byteData[index]) + { + case OpenTVTitleDataRecord.TagValue: + record = new OpenTVTitleDataRecord(); + break; + case OpenTVShortDescriptionRecord.TagValue: + record = new OpenTVShortDescriptionRecord(); + break; + case OpenTVExtendedDescriptionRecord.TagValue: + record = new OpenTVExtendedDescriptionRecord(); + break; + case OpenTVSeriesLinkRecord.TagValue: + record = new OpenTVSeriesLinkRecord(); + break; + default: + record = new OpenTVRecordBase(); + + if (DebugEntry.IsDefined(DebugName.LogUnknownRecords)) + { + if (unknownRecords == null) + unknownRecords = new Collection(); + unknownRecords.Add(record); + } + + break; + } + + record.tag = (int)byteData[index]; + index++; + + record.length = (int)byteData[index]; + index++; + + record.Process(byteData, index); + + return (record); + } + + /// + /// Initialize a new instance of the OpenTVRecordBase class. + /// + internal OpenTVRecordBase() { } + + /// + /// Parse the descriptor. + /// + /// The MPEG2 section containing the record. + /// Index of the byte in the MPEG2 section following the descriptor length. + internal virtual void Process(byte[] byteData, int index) + { + lastIndex = index; + + if (Length != 0) + { + data = Utils.GetBytes(byteData, 0, Length); + lastIndex += Length; + } + + isUndefined = true; + } + + /// + /// Validate the record fields. + /// + /// + /// A record field is not valid. + /// + internal virtual void Validate() { } + + /// + /// Log the descriptor fields. + /// + internal virtual void LogMessage() + { + if (DebugEntry.IsDefined(DebugName.LogUnknownRecords)) + logMessage(Logger.Instance); + else + { + if (Logger.ProtocolLogger == null) + return; + + if (!TraceEntry.IsDefined(TraceName.GenericOpenTvRecord)) + return; + + logMessage(Logger.ProtocolLogger); + } + } + + private void logMessage(Logger logger) + { + logger.Write(Logger.ProtocolIndent + "OPENTV GENERIC RECORD: Tag: " + Utils.ConvertToHex(tag) + + " Length: " + length); + + if (length != 0) + logger.Dump("OpenTV Generic Record Data", data, data.Length); + } + } +} + diff --git a/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/EPGCollectorSide/DVBServices/OpenTV/OpenTVSeriesLinkRecord.cs b/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/EPGCollectorSide/DVBServices/OpenTV/OpenTVSeriesLinkRecord.cs new file mode 100644 index 0000000..c22f4b0 --- /dev/null +++ b/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/EPGCollectorSide/DVBServices/OpenTV/OpenTVSeriesLinkRecord.cs @@ -0,0 +1,111 @@ +////////////////////////////////////////////////////////////////////////////////// +// // +// Copyright © 2005-2020 nzsjb // +// // +// This Program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation; either version 2, or (at your option) // +// any later version. // +// // +// This Program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with GNU Make; see the file COPYING. If not, write to // +// the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. // +// http://www.gnu.org/copyleft/gpl.html // +// // +////////////////////////////////////////////////////////////////////////////////// + +using System; + +using DomainObjects; + +namespace DVBServices +{ + /// + /// The class that describes an OpenTV series link record. + /// + internal class OpenTVSeriesLinkRecord : OpenTVRecordBase + { + /// + /// Get the tag value for this record. + /// + public const int TagValue = 0xc1; + + /// + /// Get the series link. + /// + public int SeriesLink { get { return (seriesLink); } } + + /// + /// Get the index of the next byte in the MPEG2 section following the record. + /// + /// + /// The record has not been processed. + /// + public override int Index + { + get + { + if (lastIndex == -1) + throw (new InvalidOperationException("OpenTVSeriesLinkRecord: Index requested before block processed")); + return (lastIndex); + } + } + + private int seriesLink; + + private int lastIndex = -1; + + /// + /// Initialize a new instance of the OpenTVSeriesLinkRecord class. + /// + internal OpenTVSeriesLinkRecord() { } + + /// + /// Parse the record. + /// + /// The MPEG2 section containing the record. + /// Index of the first byte of the record data in the MPEG2 section. + internal override void Process(byte[] byteData, int index) + { + lastIndex = index; + + try + { + seriesLink = Utils.Convert2BytesToInt(byteData, lastIndex); + lastIndex += 2; + + Validate(); + } + catch (IndexOutOfRangeException) + { + throw (new ArgumentOutOfRangeException("lastIndex = " + lastIndex)); + } + } + + /// + /// Validate the record data fields. + /// + /// + /// A record data field is not valid. + /// + internal override void Validate() { } + + /// + /// Log the record data fields. + /// + internal override void LogMessage() + { + if (Logger.ProtocolLogger == null) + return; + + Logger.ProtocolLogger.Write(Logger.ProtocolIndent + ""); + Logger.ProtocolLogger.Write(Logger.ProtocolIndent + "OPENTV SERIES LINK RECORD: Series link: " + seriesLink); + } + } +} + diff --git a/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/EPGCollectorSide/DVBServices/OpenTV/OpenTVShortDescriptionRecord.cs b/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/EPGCollectorSide/DVBServices/OpenTV/OpenTVShortDescriptionRecord.cs new file mode 100644 index 0000000..add3805 --- /dev/null +++ b/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/EPGCollectorSide/DVBServices/OpenTV/OpenTVShortDescriptionRecord.cs @@ -0,0 +1,116 @@ +////////////////////////////////////////////////////////////////////////////////// +// // +// Copyright © 2005-2020 nzsjb // +// // +// This Program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation; either version 2, or (at your option) // +// any later version. // +// // +// This Program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with GNU Make; see the file COPYING. If not, write to // +// the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. // +// http://www.gnu.org/copyleft/gpl.html // +// // +////////////////////////////////////////////////////////////////////////////////// + +using System; + +using DomainObjects; + +namespace DVBServices +{ + /// + /// The class that describes an OpenTV short description record. + /// + internal class OpenTVShortDescriptionRecord : OpenTVRecordBase + { + /// + /// Get the tag value for this record. + /// + public const int TagValue = 0xb9; + + /// + /// Get the short description. + /// + public string Description { get { return (SingleTreeDictionaryEntry.DecodeData(description)); } } + + /// + /// Get the short description bytes. + /// + public byte[] DescriptionBytes { get { return (description); } } + + /// + /// Get the index of the next byte in the MPEG2 section following the record. + /// + /// + /// The record has not been processed. + /// + public override int Index + { + get + { + if (lastIndex == -1) + throw (new InvalidOperationException("OpenTVShortDescriptionRecord: Index requested before block processed")); + return (lastIndex); + } + } + + private byte[] description; + + private int lastIndex = -1; + + /// + /// Initialize a new instance of the OpenTVShortDescriptionRecord class. + /// + internal OpenTVShortDescriptionRecord() { } + + /// + /// Parse the record. + /// + /// The MPEG2 section containing the record. + /// Index of the first byte of the record data in the MPEG2 section. + internal override void Process(byte[] byteData, int index) + { + lastIndex = index; + + try + { + description = Utils.GetBytes(byteData, lastIndex, Length); + lastIndex += Length; + + Validate(); + } + catch (IndexOutOfRangeException) + { + throw (new ArgumentOutOfRangeException("lastIndex = " + lastIndex)); + } + } + + /// + /// Validate the record data fields. + /// + /// + /// A record data field is not valid. + /// + internal override void Validate() { } + + /// + /// Log the record data fields. + /// + internal override void LogMessage() + { + if (Logger.ProtocolLogger == null) + return; + + Logger.ProtocolLogger.Write(Logger.ProtocolIndent + ""); + Logger.ProtocolLogger.Write(Logger.ProtocolIndent + "OPENTV SHORT DESCRIPTION RECORD: Description: " + Utils.ConvertToHex(description)); + } + } +} + diff --git a/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/EPGCollectorSide/DVBServices/OpenTV/OpenTVSummaryData.cs b/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/EPGCollectorSide/DVBServices/OpenTV/OpenTVSummaryData.cs new file mode 100644 index 0000000..206da26 --- /dev/null +++ b/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/EPGCollectorSide/DVBServices/OpenTV/OpenTVSummaryData.cs @@ -0,0 +1,244 @@ +////////////////////////////////////////////////////////////////////////////////// +// // +// Copyright © 2005-2020 nzsjb // +// // +// This Program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation; either version 2, or (at your option) // +// any later version. // +// // +// This Program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with GNU Make; see the file COPYING. If not, write to // +// the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. // +// http://www.gnu.org/copyleft/gpl.html // +// // +////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.ObjectModel; + +using DomainObjects; + +namespace DVBServices +{ + /// + /// The class that describes Open TV summary data. + /// + public class OpenTVSummaryData + { + /// + /// Get the event identification. + /// + public int EventID { get { return (eventID); } } + + /// + /// Get the short description of the event. + /// + public string ShortDescription + { + get + { + OpenTVShortDescriptionRecord record = (OpenTVShortDescriptionRecord)getRecord(OpenTVShortDescriptionRecord.TagValue); + if (record != null) + return (record.Description); + else + return null; + } + } + + /// + /// Get the raw bytes of short description of the event. + /// + public byte[] ShortDescriptionBytes + { + get + { + OpenTVShortDescriptionRecord record = (OpenTVShortDescriptionRecord)getRecord(OpenTVShortDescriptionRecord.TagValue); + if (record != null) + return (record.DescriptionBytes); + else + return (new byte[] { 0x00 }); + } + } + + /// + /// Get the extended description of the event. + /// + public string ExtendedDescription + { + get + { + OpenTVExtendedDescriptionRecord record = (OpenTVExtendedDescriptionRecord)getRecord(OpenTVExtendedDescriptionRecord.TagValue); + if (record != null) + return (record.Description); + else + return (null); + } + } + + /// + /// Get the series link of the event. + /// + public int SeriesLink + { + get + { + OpenTVSeriesLinkRecord record = (OpenTVSeriesLinkRecord)getRecord(OpenTVSeriesLinkRecord.TagValue); + if (record != null) + return (record.SeriesLink); + else + return (-1); + } + } + + /// + /// Get the collection of records for this summary section. + /// + internal Collection Records + { + get + { + if (records == null) + records = new Collection(); + return (records); + } + } + + /// + /// Get the collection of undefined records for this summary section. + /// + internal Collection UndefinedRecords + { + get + { + if (records == null) + return (null); + + Collection undefinedRecords = new Collection(); + + foreach (OpenTVRecordBase record in records) + { + if (record.IsUndefined) + undefinedRecords.Add(record); + } + + return (undefinedRecords); + } + } + + /// + /// Get the index of the next byte in the MPEG2 section following the summary data. + /// + /// + /// The summary data has not been processed. + /// + public int Index + { + get + { + if (lastIndex == -1) + throw (new InvalidOperationException("OpenTVSummaryData: Index requested before block processed")); + return (lastIndex); + } + } + + private int eventID; + private int length; + + private Collection records; + + private int lastIndex = -1; + + /// + /// Initialize a new instance of the OpenTVSummaryData class. + /// + public OpenTVSummaryData() { } + + /// + /// Parse the summary data. + /// + /// The MPEG2 section containing the summary data. + /// Index of the first byte of the summary data in the MPEG2 section. + /// The base date for the program events. + internal void Process(byte[] byteData, int index, DateTime baseDate) + { + lastIndex = index; + + try + { + eventID = Utils.Convert2BytesToInt(byteData, lastIndex); + lastIndex += 2; + + length = ((byteData[lastIndex] & 0x0f) * 256) + byteData[lastIndex + 1]; + lastIndex += 2; + + int recordLength = length; + + while (recordLength != 0) + { + OpenTVRecordBase record = OpenTVRecordBase.Instance(byteData, lastIndex); + Records.Add(record); + + lastIndex += record.TotalLength; + recordLength -= record.TotalLength; + } + + Validate(); + } + catch (IndexOutOfRangeException) + { + throw (new ArgumentOutOfRangeException("The Open TV Summary Data message is short")); + } + } + + /// + /// Validate the summary data fields. + /// + /// + /// A summary data field is not valid. + /// + public void Validate() { } + + /// + /// Log the summary data fields. + /// + public void LogMessage() + { + if (Logger.ProtocolLogger == null) + return; + + Logger.ProtocolLogger.Write(Logger.ProtocolIndent + "OPENTV SUMMARY DATA: Event ID: " + eventID + + " Length: " + length); + + if (records != null) + { + Logger.IncrementProtocolIndent(); + + foreach (OpenTVRecordBase record in records) + record.LogMessage(); + + Logger.DecrementProtocolIndent(); + } + } + + private OpenTVRecordBase getRecord(int tag) + { + if (records == null) + return (null); + + foreach (OpenTVRecordBase record in records) + { + if (record.Tag == tag) + return (record); + } + + return (null); + } + } +} + diff --git a/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/EPGCollectorSide/DVBServices/OpenTV/OpenTVSummaryHeader.cs b/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/EPGCollectorSide/DVBServices/OpenTV/OpenTVSummaryHeader.cs new file mode 100644 index 0000000..53ed182 --- /dev/null +++ b/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/EPGCollectorSide/DVBServices/OpenTV/OpenTVSummaryHeader.cs @@ -0,0 +1,161 @@ +////////////////////////////////////////////////////////////////////////////////// +// // +// Copyright © 2005-2020 nzsjb // +// // +// This Program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation; either version 2, or (at your option) // +// any later version. // +// // +// This Program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with GNU Make; see the file COPYING. If not, write to // +// the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. // +// http://www.gnu.org/copyleft/gpl.html // +// // +////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.ObjectModel; + +using DomainObjects; + +namespace DVBServices +{ + /// + /// The class that describes an Open TV summary header. + /// + public class OpenTVSummaryHeader + { + /// + /// Get the channel identification. + /// + public int ChannelID { get { return (channelID); } } + /// + /// Get the title date base. + /// + public DateTime BaseDate { get { return (baseDate); } } + /// + /// Get the data collection related to this summary. + /// + public Collection SummaryData { get { return (summaryData); } } + + /// + /// Get the index of the next byte in the MPEG2 section following the summary header. + /// + /// + /// The summary header has not been processed. + /// + public int Index + { + get + { + if (lastIndex == -1) + throw (new InvalidOperationException("OpenTVSummaryHeader: Index requested before block processed")); + return (lastIndex); + } + } + + private int channelID; + private DateTime baseDate; + private Collection summaryData; + + private int lastIndex = -1; + + /// + /// Initialize a new instance of the OpenTVSummaryHeader class. + /// + public OpenTVSummaryHeader() { } + + /// + /// Parse the summary header. + /// + /// The MPEG2 section containing the summary header. + /// Index of the first byte of the summary header in the MPEG2 section. + /// The MPEG2 header of the section. + internal void Process(byte[] byteData, int index, Mpeg2ExtendedHeader mpeg2Header) + { + lastIndex = index; + + channelID = mpeg2Header.TableIDExtension; + + try + { + baseDate = getDate(Utils.Convert2BytesToInt(byteData, lastIndex)); + lastIndex += 2; + + while (lastIndex < byteData.Length - 4) + { + OpenTVSummaryData data = new OpenTVSummaryData(); + data.Process(byteData, lastIndex, baseDate); + + if (summaryData == null) + summaryData = new Collection(); + + summaryData.Add(data); + + lastIndex = data.Index; + } + + Validate(); + } + catch (IndexOutOfRangeException) + { + throw (new ArgumentOutOfRangeException("The Open TV Summary Header message is short")); + } + } + + private DateTime getDate(int mjd) + { + int j = mjd + 2400001 + 68569; + int c = 4 * j / 146097; + j = j - (146097 * c + 3) / 4; + + int y = 4000 * (j + 1) / 1461001; + j = j - 1461 * y / 4 + 31; + int m = 80 * j / 2447; + + int day = j - 2447 * m / 80; + j = m / 11; + int month = m + 2 - (12 * j); + int year = 100 * (c - 49) + y + j; + + return (new DateTime(year, month, day) + new TimeSpan(1, 0, 0, 0)); + } + + /// + /// Validate the summary header fields. + /// + /// + /// A summary header field is not valid. + /// + public void Validate() { } + + /// + /// Log the summary header fields. + /// + public void LogMessage() + { + if (Logger.ProtocolLogger == null) + return; + + Logger.ProtocolLogger.Write(Logger.ProtocolIndent + "OPENTV SUMMARY HEADER: Channel ID: " + channelID + + " Base date : " + baseDate); + + if (summaryData != null) + { + Logger.IncrementProtocolIndent(); + + foreach (OpenTVSummaryData data in summaryData) + data.LogMessage(); + + Logger.DecrementProtocolIndent(); + } + } + } +} + diff --git a/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/EPGCollectorSide/DVBServices/OpenTV/OpenTVSummarySection.cs b/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/EPGCollectorSide/DVBServices/OpenTV/OpenTVSummarySection.cs new file mode 100644 index 0000000..d7fa68d --- /dev/null +++ b/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/EPGCollectorSide/DVBServices/OpenTV/OpenTVSummarySection.cs @@ -0,0 +1,151 @@ +////////////////////////////////////////////////////////////////////////////////// +// // +// Copyright © 2005-2020 nzsjb // +// // +// This Program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation; either version 2, or (at your option) // +// any later version. // +// // +// This Program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with GNU Make; see the file COPYING. If not, write to // +// the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. // +// http://www.gnu.org/copyleft/gpl.html // +// // +////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.ObjectModel; + +using DomainObjects; + +namespace DVBServices +{ + /// + /// The class that describes an Open TV Summary section. + /// + public class OpenTVSummarySection + { + /// + /// Get the collection of Open TV Title sections. + /// + public static Collection OpenTVSummarySections + { + get + { + if (openTVSummarySections == null) + openTVSummarySections = new Collection(); + return (openTVSummarySections); + } + } + + /// + /// Get the section number. + /// + public int SectionNumber { get { return (sectionNumber); } } + /// + /// Get the section number. + /// + public int LastSectionNumber { get { return (lastSectionNumber); } } + /// + /// Get the title header. + /// + public OpenTVSummaryHeader SummaryHeader { get { return (summaryHeader); } } + + private int sectionNumber; + private int lastSectionNumber; + private OpenTVSummaryHeader summaryHeader; + + private static Collection openTVSummarySections; + + private int lastIndex = -1; + + /// + /// Initialize a new instance of the OpenTVSummarySection class. + /// + internal OpenTVSummarySection() { } + + /// + /// Parse the section. + /// + /// The MPEG2 section containing the section. + /// The MPEG2 header that preceedes the section. + internal void Process(byte[] byteData, Mpeg2ExtendedHeader mpeg2Header) + { + lastIndex = mpeg2Header.Index; + sectionNumber = mpeg2Header.SectionNumber; + lastSectionNumber = mpeg2Header.LastSectionNumber; + + summaryHeader = new OpenTVSummaryHeader(); + summaryHeader.Process(byteData, lastIndex, mpeg2Header); + } + + /// + /// Log the section fields. + /// + public void LogMessage() + { + if (Logger.ProtocolLogger == null) + return; + + summaryHeader.LogMessage(); + } + + /// + /// Process an MPEG2 section from the Open TV Summary table. + /// + /// The MPEG2 section. + /// An Open TV Summary Section instance. + public static OpenTVSummarySection ProcessOpenTVSummaryTable(byte[] byteData) + { + Mpeg2ExtendedHeader mpeg2Header = new Mpeg2ExtendedHeader(); + + try + { + mpeg2Header.Process(byteData); + + if (mpeg2Header.Current) + { + OpenTVSummarySection openTVSummarySection = new OpenTVSummarySection(); + openTVSummarySection.Process(byteData, mpeg2Header); + openTVSummarySection.LogMessage(); + return (openTVSummarySection); + } + else + return (null); + } + catch (ArgumentOutOfRangeException e) + { + Logger.Instance.Write(" Error processing Summary Section: " + e.Message); + return (null); + } + } + + /// + /// Add a section to the collection. + /// + /// The section to be added. + public static void AddSection(OpenTVSummarySection newSection) + { + /*foreach (OpenTVSummarySection oldSection in OpenTVSummarySections) + { + if (oldSection.sectionNumber == newSection.sectionNumber) + return; + + if (oldSection.SectionNumber > newSection.SectionNumber) + { + OpenTVSummarySections.Insert(OpenTVSummarySections.IndexOf(oldSection), newSection); + return; + } + }*/ + + OpenTVSummarySections.Add(newSection); + } + } +} + diff --git a/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/EPGCollectorSide/DVBServices/OpenTV/OpenTVTitleData.cs b/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/EPGCollectorSide/DVBServices/OpenTV/OpenTVTitleData.cs new file mode 100644 index 0000000..e8e2967 --- /dev/null +++ b/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/EPGCollectorSide/DVBServices/OpenTV/OpenTVTitleData.cs @@ -0,0 +1,289 @@ +////////////////////////////////////////////////////////////////////////////////// +// // +// Copyright © 2005-2020 nzsjb // +// // +// This Program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation; either version 2, or (at your option) // +// any later version. // +// // +// This Program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with GNU Make; see the file COPYING. If not, write to // +// the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. // +// http://www.gnu.org/copyleft/gpl.html // +// // +////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.ObjectModel; + +using DomainObjects; + +namespace DVBServices +{ + /// + /// The class that describes Open TV title data. + /// + public class OpenTVTitleData + { + /// + /// Get the event identification. + /// + public int EventID { get { return (eventID); } } + + /// + /// Get the start time of the event. + /// + public DateTime StartTime + { + get + { + OpenTVTitleDataRecord record = (OpenTVTitleDataRecord)getRecord(OpenTVTitleDataRecord.TagValue); + return (baseDate + record.StartTimeOffset); + } + } + + /// + /// Get the duration of the event. + /// + public TimeSpan Duration + { + get + { + OpenTVTitleDataRecord record = (OpenTVTitleDataRecord)getRecord(OpenTVTitleDataRecord.TagValue); + return (record.Duration); + } + } + + /// + /// Get the theme identification of the event. + /// + public int CategoryID + { + get + { + OpenTVTitleDataRecord record = (OpenTVTitleDataRecord)getRecord(OpenTVTitleDataRecord.TagValue); + return (record.CategoryID); + } + } + + /// + /// Get the name of the event. + /// + public string EventName + { + get + { + OpenTVTitleDataRecord record = (OpenTVTitleDataRecord)getRecord(OpenTVTitleDataRecord.TagValue); + return (record.DecodedEventName); + } + } + + /// + /// Get the raw bytes of the event name. + /// + public byte[] EventNameBytes + { + get + { + OpenTVTitleDataRecord record = (OpenTVTitleDataRecord)getRecord(OpenTVTitleDataRecord.TagValue); + return (record.EventName); + } + } + + /// + /// Get the flags field of the event. + /// + public byte[] Flags + { + get + { + OpenTVTitleDataRecord record = (OpenTVTitleDataRecord)getRecord(OpenTVTitleDataRecord.TagValue); + return (record.Flags); + } + } + + /// + /// Get the collection of records for this title section. + /// + internal Collection Records + { + get + { + if (records == null) + records = new Collection(); + return (records); + } + } + + /// + /// Get the collection of undefined records for this title section. + /// + internal Collection UndefinedRecords + { + get + { + if (records == null) + return (null); + + Collection undefinedRecords = new Collection(); + + foreach (OpenTVRecordBase record in records) + { + if (record.IsUndefined) + undefinedRecords.Add(record); + } + + return (undefinedRecords); + } + } + + /// + /// Return true if the entry is empty; false otherwise. + /// + public bool IsEmpty { get { return (records == null || records.Count == 0); } } + + /// + /// Get the PID of the section containing the data. + /// + public int PID { get { return (pid); } } + /// + /// Get the table ID of the section containing the data. + /// + public int Table { get { return (table); } } + /// + /// Get the timestamp when the data arrived. + /// + public DateTime TimeStamp { get { return (timeStamp); } } + + /// + /// Get the index of the next byte in the MPEG2 section following the title data. + /// + /// + /// The title data has not been processed. + /// + public int Index + { + get + { + if (lastIndex == -1) + throw (new InvalidOperationException("OpenTVTitleData: Index requested before block processed")); + return (lastIndex); + } + } + + private int eventID; + private int length; + + private DateTime baseDate; + + private int pid; + private int table; + private DateTime timeStamp; + + private Collection records; + + private int lastIndex = -1; + + /// + /// Initialize a new instance of the OpenTVTitleData class. + /// + public OpenTVTitleData() { } + + /// + /// Parse the title data. + /// + /// The MPEG2 section containing the title data. + /// Index of the first byte of the title data in the MPEG2 section. + /// The base date for the programs. + /// The channel for the data. + /// The PID of the section. + /// The table ID of the section. + internal void Process(byte[] byteData, int index, DateTime baseDate, int channel, int pid, int table) + { + lastIndex = index; + + this.pid = pid; + this.table = table; + this.baseDate = baseDate; + + try + { + eventID = Utils.Convert2BytesToInt(byteData, lastIndex); + lastIndex += 2; + + length = ((byteData[lastIndex] & 0x0f) * 256) + byteData[lastIndex + 1]; + lastIndex += 2; + + int recordLength = length; + + while (recordLength != 0) + { + OpenTVRecordBase record = OpenTVRecordBase.Instance(byteData, lastIndex); + Records.Add(record); + + lastIndex += record.TotalLength; + recordLength -= record.TotalLength; + } + + timeStamp = DateTime.Now; + + Validate(); + } + catch (IndexOutOfRangeException) + { + throw (new ArgumentOutOfRangeException("lastIndex = " + lastIndex)); + } + } + + /// + /// Validate the title data fields. + /// + /// + /// A title data field is not valid. + /// + public void Validate() { } + + /// + /// Log the title data fields. + /// + public void LogMessage() + { + if (Logger.ProtocolLogger == null) + return; + + Logger.ProtocolLogger.Write(Logger.ProtocolIndent + "OPENTV TITLE DATA: Event ID: " + eventID + + " Length: " + length); + + if (records != null) + { + Logger.IncrementProtocolIndent(); + + foreach (OpenTVRecordBase record in records) + record.LogMessage(); + + Logger.DecrementProtocolIndent(); + } + } + + private OpenTVRecordBase getRecord(int tag) + { + if (records == null) + return(null); + + foreach (OpenTVRecordBase record in records) + { + if (record.Tag == tag) + return(record); + } + + return(null); + } + } +} + diff --git a/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/EPGCollectorSide/DVBServices/OpenTV/OpenTVTitleDataRecord.cs b/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/EPGCollectorSide/DVBServices/OpenTV/OpenTVTitleDataRecord.cs new file mode 100644 index 0000000..6972784 --- /dev/null +++ b/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/EPGCollectorSide/DVBServices/OpenTV/OpenTVTitleDataRecord.cs @@ -0,0 +1,164 @@ +////////////////////////////////////////////////////////////////////////////////// +// // +// Copyright © 2005-2020 nzsjb // +// // +// This Program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation; either version 2, or (at your option) // +// any later version. // +// // +// This Program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with GNU Make; see the file COPYING. If not, write to // +// the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. // +// http://www.gnu.org/copyleft/gpl.html // +// // +////////////////////////////////////////////////////////////////////////////////// + +using System; + +using DomainObjects; + +namespace DVBServices +{ + /// + /// The class that describes an OpenTV title data record. + /// + internal class OpenTVTitleDataRecord : OpenTVRecordBase + { + /// + /// Get the tag value for this record. + /// + public const int TagValue = 0xb5; + + /// + /// Get the start time of the event. + /// + public TimeSpan StartTimeOffset { get { return (startTimeOffset); } } + /// + /// Get the duration of the event. + /// + public TimeSpan Duration { get { return (duration); } } + /// + /// Get the theme identification of the event. + /// + public int CategoryID { get { return (categoryID); } } + /// + /// Get the flags field of the event. + /// + public byte[] Flags { get { return (flags); } } + /// + /// Get the name of the event. + /// + public byte[] EventName { get { return (eventName); } } + + /// + /// Get the decompressed event name. + /// + public string DecodedEventName { get { return (SingleTreeDictionaryEntry.DecodeData(eventName)); } } + + /// + /// Get the index of the next byte in the MPEG2 section following the record. + /// + /// + /// The record has not been processed. + /// + public override int Index + { + get + { + if (lastIndex == -1) + throw (new InvalidOperationException("OpenTVTitleDataRecord: Index requested before block processed")); + return (lastIndex); + } + } + + private TimeSpan startTimeOffset; + private TimeSpan duration; + private int categoryID; + private byte[] flags; + private byte[] eventName; + + private int lastIndex = -1; + + /// + /// Initialize a new instance of the OpenTVTitleDataRecord class. + /// + internal OpenTVTitleDataRecord() { } + + /// + /// Parse the record. + /// + /// The MPEG2 section containing the record. + /// Index of the first byte of the record data in the MPEG2 section. + internal override void Process(byte[] byteData, int index) + { + lastIndex = index; + + try + { + // Source value to 2 second resolution + startTimeOffset = getTime((byteData[lastIndex] * 512) + (byteData[lastIndex + 1] * 2)); + lastIndex += 2; + + duration = getTime(Utils.Convert2BytesToInt(byteData, lastIndex) * 2); + lastIndex += 2; + + categoryID = (int)byteData[lastIndex]; + lastIndex++; + + flags = Utils.GetBytes(byteData, lastIndex, 2); + lastIndex += 2; + + eventName = Utils.GetBytes(byteData, lastIndex, Length - 7); + lastIndex += Length - 7; + + Validate(); + } + catch (IndexOutOfRangeException) + { + throw (new ArgumentOutOfRangeException("lastIndex = " + lastIndex)); + } + } + + private TimeSpan getTime(int totalSeconds) + { + int days = totalSeconds / 86400; + int hours = (totalSeconds - (days * 86400)) / 3600; + int minutes = (totalSeconds - ((days * 86400) + (hours * 3600))) / 60; + int seconds = totalSeconds % 60; + + return (new TimeSpan(days, hours, minutes, seconds)); + } + + /// + /// Validate the record data fields. + /// + /// + /// A record data field is not valid. + /// + internal override void Validate() { } + + /// + /// Log the record data fields. + /// + internal override void LogMessage() + { + if (Logger.ProtocolLogger == null) + return; + + Logger.ProtocolLogger.Write(Logger.ProtocolIndent + ""); + Logger.ProtocolLogger.Write(Logger.ProtocolIndent + "OPENTV TITLE DATA RECORD: " + + "Start time: " + startTimeOffset + + " Duration: " + duration + + " Category: " + categoryID + + " Unknown: " + Utils.ConvertToHex(flags) + + " Name: " + Utils.ConvertToHex(eventName)); + } + } +} + diff --git a/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/EPGCollectorSide/DVBServices/OpenTV/OpenTVTitleHeader.cs b/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/EPGCollectorSide/DVBServices/OpenTV/OpenTVTitleHeader.cs new file mode 100644 index 0000000..141b22e --- /dev/null +++ b/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/EPGCollectorSide/DVBServices/OpenTV/OpenTVTitleHeader.cs @@ -0,0 +1,165 @@ +////////////////////////////////////////////////////////////////////////////////// +// // +// Copyright © 2005-2020 nzsjb // +// // +// This Program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation; either version 2, or (at your option) // +// any later version. // +// // +// This Program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with GNU Make; see the file COPYING. If not, write to // +// the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. // +// http://www.gnu.org/copyleft/gpl.html // +// // +////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.ObjectModel; + +using DomainObjects; + +namespace DVBServices +{ + /// + /// The class that describes an Open TV title header. + /// + public class OpenTVTitleHeader + { + /// + /// Get the channel identification. + /// + public int ChannelID { get { return (channelID); } } + /// + /// Get the title date base. + /// + public DateTime BaseDate { get { return (baseDate); } } + /// + /// Get the data collection related to this title. + /// + public Collection TitleData { get { return (titleData); } } + + /// + /// Get the index of the next byte in the MPEG2 section following the title header. + /// + /// + /// The title header has not been processed. + /// + public int Index + { + get + { + if (lastIndex == -1) + throw (new InvalidOperationException("OpenTVTitleHeader: Index requested before block processed")); + return (lastIndex); + } + } + + private int channelID; + private DateTime baseDate; + private Collection titleData; + + private int lastIndex = -1; + + /// + /// Initialize a new instance of the OpenTVTitleHeader class. + /// + public OpenTVTitleHeader() { } + + /// + /// Parse the title header. + /// + /// The MPEG2 section containing the title header. + /// Index of the first byte of the title header in the MPEG2 section. + /// The MPEG2 header of the section. + /// The PID of the section. + /// The table ID of the section. + internal void Process(byte[] byteData, int index, Mpeg2ExtendedHeader mpeg2Header, int pid, int tid) + { + lastIndex = index; + + channelID = mpeg2Header.TableIDExtension; + + try + { + baseDate = getDate(Utils.Convert2BytesToInt(byteData, lastIndex)); + lastIndex += 2; + + while (lastIndex < byteData.Length - 4) + { + OpenTVTitleData data = new OpenTVTitleData(); + data.Process(byteData, lastIndex, baseDate, channelID, pid, tid); + + if (!data.IsEmpty) + { + if (titleData == null) + titleData = new Collection(); + titleData.Add(data); + } + + lastIndex = data.Index; + } + + Validate(); + } + catch (IndexOutOfRangeException) + { + throw (new ArgumentOutOfRangeException("lastIndex = " + lastIndex)); + } + } + + private DateTime getDate(int mjd) + { + int j = mjd + 2400001 + 68569; + int c = 4 * j / 146097; + j = j - (146097 * c + 3) / 4; + + int y = 4000 * (j + 1) / 1461001; + j = j - 1461 * y / 4 + 31; + int m = 80 * j / 2447; + + int day = j - 2447 * m / 80; + j = m / 11; + int month = m + 2 - (12 * j); + int year = 100 * (c - 49) + y + j; + + return (new DateTime(year, month, day)); + } + + /// + /// Validate the title header fields. + /// + /// + /// A title header field is not valid. + /// + public void Validate() { } + + /// + /// Log the title header fields. + /// + public void LogMessage() + { + if (Logger.ProtocolLogger == null) + return; + + Logger.ProtocolLogger.Write(Logger.ProtocolIndent + "OPENTV TITLE HEADER: Channel ID: " + channelID + + " Base date : " + baseDate); + + if (titleData != null) + { + Logger.IncrementProtocolIndent(); + + foreach (OpenTVTitleData data in titleData) + data.LogMessage(); + + Logger.DecrementProtocolIndent(); + } + } + } +} + diff --git a/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/EPGCollectorSide/DVBServices/OpenTV/OpenTVTitleSection.cs b/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/EPGCollectorSide/DVBServices/OpenTV/OpenTVTitleSection.cs new file mode 100644 index 0000000..90a3405 --- /dev/null +++ b/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/EPGCollectorSide/DVBServices/OpenTV/OpenTVTitleSection.cs @@ -0,0 +1,126 @@ +////////////////////////////////////////////////////////////////////////////////// +// // +// Copyright © 2005-2020 nzsjb // +// // +// This Program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation; either version 2, or (at your option) // +// any later version. // +// // +// This Program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with GNU Make; see the file COPYING. If not, write to // +// the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. // +// http://www.gnu.org/copyleft/gpl.html // +// // +////////////////////////////////////////////////////////////////////////////////// + +using System; + +using DomainObjects; + +namespace DVBServices +{ + /// + /// The class that describes an Open TV Title section. + /// + public class OpenTVTitleSection + { + /// + /// Get the section number. + /// + public int SectionNumber { get { return (sectionNumber); } } + /// + /// Get the section number. + /// + public int LastSectionNumber { get { return (lastSectionNumber); } } + /// + /// Get the title header. + /// + public OpenTVTitleHeader TitleHeader { get { return (titleHeader); } } + + private int sectionNumber; + private int lastSectionNumber; + private OpenTVTitleHeader titleHeader; + + private int lastIndex = -1; + + /// + /// Initialize a new instance of the OpenTVTitleSection class. + /// + internal OpenTVTitleSection() { } + + /// + /// Parse the section. + /// + /// The MPEG2 section containing the section. + /// The MPEG2 header that preceedes the section. + /// The PID containing the section. + /// The table ID containing the section. + internal void Process(byte[] byteData, Mpeg2ExtendedHeader mpeg2Header, int pid, int tid) + { + lastIndex = mpeg2Header.Index; + sectionNumber = mpeg2Header.SectionNumber; + lastSectionNumber = mpeg2Header.LastSectionNumber; + + titleHeader = new OpenTVTitleHeader(); + titleHeader.Process(byteData, lastIndex, mpeg2Header, pid, tid); + } + + /// + /// Log the section fields. + /// + public void LogMessage() + { + if (Logger.ProtocolLogger == null) + return; + + titleHeader.LogMessage(); + } + + /// + /// Process an MPEG2 section from the Open TV Title table. + /// + /// The MPEG2 section. + /// The PID containing the section. + /// The table ID containing the section. + /// An Open TV Title Section instance or null if a section is not created. + public static OpenTVTitleSection ProcessOpenTVTitleTable(byte[] byteData, int pid, int table) + { + Mpeg2ExtendedHeader mpeg2Header = new Mpeg2ExtendedHeader(); + + try + { + mpeg2Header.Process(byteData); + + if (mpeg2Header.Current) + { + OpenTVTitleSection openTVTitleSection = new OpenTVTitleSection(); + openTVTitleSection.Process(byteData, mpeg2Header, pid, table); + openTVTitleSection.LogMessage(); + return (openTVTitleSection); + } + else + return (null); + } + catch (ArgumentOutOfRangeException e) + { + Logger.Instance.Write(" Error processing Title Section: " + e.Message); + + if (DebugEntry.IsDefined(DebugName.TitleSection)) + { + Logger.Instance.Write(e.Message); + Logger.Instance.Write(e.StackTrace); + Logger.Instance.Dump("Title Section", byteData, byteData.Length); + } + + return (null); + } + } + } +} + diff --git a/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/EPGCollectorSide/DVBServices/Utils.cs b/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/EPGCollectorSide/DVBServices/Utils.cs index fb97cf3..0be70c1 100644 --- a/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/EPGCollectorSide/DVBServices/Utils.cs +++ b/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/EPGCollectorSide/DVBServices/Utils.cs @@ -138,5 +138,42 @@ namespace DVBServices temp = ""; return temp; } + + /// + /// Convert an integer value to a hex string. + /// + /// The value to be converted. + /// The converted string. + public static string ConvertToHex(int value) + { + if (value == 0) + return ("0x00"); + + uint tempValue = (uint)value; + + char[] outputChars = new char[8]; + + int outputIndex = 7; + + for (int index = 3; index > -1; index--) + { + uint hexByte = (tempValue << 24) >> 24; + int hexByteLeft = (int)(hexByte >> 4); + int hexByteRight = (int)(hexByte & 0x0f); + + outputChars[outputIndex] = getHex(hexByteRight); + outputChars[outputIndex - 1] = getHex(hexByteLeft); + + outputIndex -= 2; + tempValue = tempValue >> 8; + } + + string replyString = new string(outputChars).TrimStart(new char[] { '0' }); + + if (replyString.Length % 2 == 0) + return ("0x" + replyString); + else + return ("0x0" + replyString); + } } } diff --git a/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/EPGCollectorSide/DomainObjects/TraceEntry.cs b/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/EPGCollectorSide/DomainObjects/TraceEntry.cs new file mode 100644 index 0000000..9d5158a --- /dev/null +++ b/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/EPGCollectorSide/DomainObjects/TraceEntry.cs @@ -0,0 +1,266 @@ +////////////////////////////////////////////////////////////////////////////////// +// // +// Copyright © 2005-2020 nzsjb // +// // +// This Program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation; either version 2, or (at your option) // +// any later version. // +// // +// This Program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with GNU Make; see the file COPYING. If not, write to // +// the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. // +// http://www.gnu.org/copyleft/gpl.html // +// // +////////////////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.ObjectModel; + +namespace DomainObjects +{ + /// + /// The class that describes a trace parameter entry. + /// + public class TraceEntry + { + /// + /// Get the last error message. + /// + public static string LastError { get { return (lastError); } } + + /// + /// Get the entry name. + /// + public TraceName Name { get; private set; } + + /// + /// Get or set the number entry parameter. + /// + public int NumberParameter + { + get + { + if (!numberParameterSet) + throw (new InvalidOperationException("TraceEntry number parameter not set")); + + return (numberParameter); + } + set + { + numberParameter = value; + numberParameterSet = true; + } + } + + /// + /// Get or set the string entry parameter. + /// + public string StringParameter + { + get + { + if (!stringParameterSet) + throw (new InvalidOperationException("TraceEntry string parameter not set")); + + return (stringParameter); + } + set + { + stringParameter = value; + stringParameterSet = true; + } + } + + /// + /// Return true if the entry number parameter has been set; false otherwise. + /// + public bool NumberParameterSet { get { return (numberParameterSet); } } + /// + /// Return true if the entry string parameter has been set; false otherwise. + /// + public bool StringParameterSet { get { return (stringParameterSet); } } + + private int numberParameter; + private bool numberParameterSet; + private string stringParameter; + private bool stringParameterSet; + + private static string lastError; + + private TraceEntry() { } + + /// + /// Initialize a new instance of the TraceEntry class with a name. + /// + /// The name of the entry. + public TraceEntry(TraceName name) + { + Name = name; + } + + /// + /// Initialize a new entry of the TraceEntry class with a name and number parameter. + /// + /// + /// + public TraceEntry(TraceName name, int parameter) : this(name) + { + NumberParameter = parameter; + } + + /// + /// Initialize a new entry of the TraceEntry class with a name and string parameter. + /// + /// + /// + public TraceEntry(TraceName name, string parameter) : this(name) + { + StringParameter = parameter; + } + + /// + /// Get a string representation of the instance. + /// + /// A string representing the instance. + public override string ToString() + { + if (numberParameterSet) + return (Name.ToString() + "-" + NumberParameter); + else + { + if (stringParameterSet) + return (Name.ToString() + "-" + '"' + StringParameter + '"'); + else + return (Name.ToString()); + } + } + + /// + /// Copy the instance. + /// + /// A new instance of the TraceEntry class with the same values as this instance. + public TraceEntry Clone() + { + TraceEntry newEntry = new TraceEntry(Name); + + if (numberParameterSet) + newEntry.NumberParameter = NumberParameter; + if (stringParameterSet) + newEntry.StringParameter = StringParameter; + + return (newEntry); + } + + /// + /// Get an instance of the TraceEntry from a parameter file entry. + /// + /// The parameter file entry. + /// A new instance of the class. + public static TraceEntry GetInstance(string parameter) + { + string[] parameterParts = parameter.Split(new char[] { '-' }); + + if (parameterParts.Length == 2 && string.IsNullOrWhiteSpace(parameterParts[1])) + return(null); + + try + { + TraceEntry traceEntry = new TraceEntry((TraceName)Enum.Parse(typeof(TraceName), parameterParts[0].Trim(), true)); + + if (parameterParts.Length == 2) + { + if (parameterParts[1].Trim()[0] != '"') + { + try + { + traceEntry.NumberParameter = Int32.Parse(parameterParts[1]); + } + catch (FormatException) + { + lastError = "The Trace name '" + parameterParts[0].Trim() + "' has a parameter in the wrong format."; + return (null); + } + catch (OverflowException) + { + lastError = "The Trace name '" + parameterParts[0].Trim() + "' has a parameter out of range."; + return (null); + } + } + else + { + if (parameterParts[1].Trim().Length < 3 || parameterParts[1].Trim()[parameterParts[1].Length - 1] != '"') + return (null); + + traceEntry.StringParameter = parameterParts[1].Trim().Substring(1, parameterParts[1].Length - 2); + } + } + + return (traceEntry); + } + catch (ArgumentException) + { + lastError = "The Trace ID '" + parameter.Trim() + "' is undefined and will be ignored."; + return (null); + } + } + + /// + /// Check if a trace name is present. + /// + /// The name of the trace entry. + /// True if the trace name is present; false otherwise. + public static bool IsDefined(TraceName traceName) + { + return false; + } + + /// + /// Find a trace entry. + /// + /// The name of the trace entry. + /// The trace entry if it is found; otherwise null + public static TraceEntry FindEntry(TraceName traceName) + { + return (FindEntry(traceName, false)); + } + + /// + /// Find a trace entry. + /// + /// The name of the trace entry. + /// True if a parameter must be present; false otherwise. + /// The trace entry if it is found; otherwise null + public static TraceEntry FindEntry(TraceName traceName, bool withParameter) + { + //feyris-tan: We don't use these in skyscraper, so we'll just always return null + return null; + } + + /// + /// Find a trace entry. + /// + /// The list of trace entries to search. + /// The string representation of the debug entry. + /// The trace entry if it is found; otherwise null + public static TraceEntry FindEntry(Collection traceEntries, string identifier) + { + if (traceEntries == null) + return (null); + + foreach (TraceEntry traceEntry in traceEntries) + { + if (traceEntry.ToString().ToUpperInvariant() == identifier.ToUpperInvariant()) + return (traceEntry); + } + + return (null); + } + } +} + diff --git a/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/SkyscraperSide/OpenTV/OpenTvDataStorage.cs b/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/SkyscraperSide/OpenTV/OpenTvDataStorage.cs new file mode 100644 index 0000000..a12b191 --- /dev/null +++ b/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/SkyscraperSide/OpenTV/OpenTvDataStorage.cs @@ -0,0 +1,68 @@ +using skyscraper5.Data.PostgreSql; +using skyscraper5.Skyscraper.Scraper.Storage.InMemory; +using skyscraper8.EPGCollectorPort.SkyscraperSide.MediaHighway2; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace skyscraper8.EPGCollectorPort.SkyscraperSide.OpenTV +{ + internal interface IOpenTvDataStorage + { + bool TestForTitles(int skyscraperContextCurrentNetworkId, int skyscraperContextCurrentTransportStreamId, int titleDataEventId, DateTime titleDataStartTime); + } + + internal class OpenTvDataStorageImpl : IOpenTvDataStorage + { + private IOpenTvDataStorage _storageEngine; + + public OpenTvDataStorageImpl(object[] getPluginConnector) + { + object o = getPluginConnector[0]; + switch (o) + { + case InMemoryPluginToken t1: + _storageEngine = new OpenTvDataStorageMemory(); + break; + case PostgresqlToken t2: + _storageEngine = new OpenTvDataStoragePostgresql(t2); + break; + default: + throw new NotImplementedException(o.GetType().FullName); + } + } + + public bool TestForTitles(int skyscraperContextCurrentNetworkId, int skyscraperContextCurrentTransportStreamId, + int titleDataEventId, DateTime titleDataStartTime) + { + return _storageEngine.TestForTitles(skyscraperContextCurrentNetworkId, skyscraperContextCurrentTransportStreamId, titleDataEventId, titleDataStartTime); + } + } + + internal class OpenTvDataStorageMemory : IOpenTvDataStorage + { + public bool TestForTitles(int skyscraperContextCurrentNetworkId, int skyscraperContextCurrentTransportStreamId, + int titleDataEventId, DateTime titleDataStartTime) + { + throw new NotImplementedException(); + } + } + + internal class OpenTvDataStoragePostgresql : IOpenTvDataStorage + { + private readonly PostgresqlToken _postgresql; + + public OpenTvDataStoragePostgresql(PostgresqlToken postgresql) + { + _postgresql = postgresql; + } + + public bool TestForTitles(int skyscraperContextCurrentNetworkId, int skyscraperContextCurrentTransportStreamId, + int titleDataEventId, DateTime titleDataStartTime) + { + throw new NotImplementedException(); + } + } +} diff --git a/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/SkyscraperSide/OpenTV/OpenTvExpectedDataType.cs b/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/SkyscraperSide/OpenTV/OpenTvExpectedDataType.cs new file mode 100644 index 0000000..32db809 --- /dev/null +++ b/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/SkyscraperSide/OpenTV/OpenTvExpectedDataType.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace skyscraper8.EPGCollectorPort.SkyscraperSide.OpenTV +{ + internal enum OpenTvExpectedDataType + { + Titles, + Summaries + } +} diff --git a/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/SkyscraperSide/OpenTV/OpenTvHandler.cs b/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/SkyscraperSide/OpenTV/OpenTvHandler.cs new file mode 100644 index 0000000..10d8242 --- /dev/null +++ b/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/SkyscraperSide/OpenTV/OpenTvHandler.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using DVBServices; + +namespace skyscraper8.EPGCollectorPort.SkyscraperSide.OpenTV +{ + internal interface OpenTvHandler + { + void OnNonOpenTvTraffic(int sourcePid, OpenTvExpectedDataType titles, int sectionTableId); + void OnTitles(int sourcePid, int sectionTableId, OpenTVTitleSection titleSection); + void OnSummaries(int sourcePid, int sectionTableId, OpenTVSummarySection summarySection); + } +} diff --git a/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/SkyscraperSide/OpenTV/OpenTvScraper.cs b/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/SkyscraperSide/OpenTV/OpenTvScraper.cs new file mode 100644 index 0000000..5a22865 --- /dev/null +++ b/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/SkyscraperSide/OpenTV/OpenTvScraper.cs @@ -0,0 +1,59 @@ +using DVBServices; +using skyscraper5.Skyscraper.Scraper; +using skyscraper8.Skyscraper.Plugins; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace skyscraper8.EPGCollectorPort.SkyscraperSide.OpenTV +{ + internal class OpenTvScraper : OpenTvHandler + { + private readonly SkyscraperContext _skyscraperContext; + private readonly IOpenTvDataStorage storage; + private static PluginLogger _logger; + + public OpenTvScraper(SkyscraperContext skyscraperContext) + { + _skyscraperContext = skyscraperContext; + storage = new OpenTvDataStorageImpl(skyscraperContext.DataStorage.GetPluginConnector()); + if (_logger == null) + { + _logger = PluginLogManager.GetLogger(GetType()); + } + } + + public void OnNonOpenTvTraffic(int sourcePid, OpenTvExpectedDataType titles, int sectionTableId) + { + throw new NotImplementedException(); + } + + public void OnTitles(int sourcePid, int sectionTableId, OpenTVTitleSection titleSection) + { + if (!_skyscraperContext.CurrentNetworkId.HasValue) + return; + if (!_skyscraperContext.CurrentTransportStreamId.HasValue) + return; + + foreach (OpenTVTitleData titleData in titleSection.TitleHeader.TitleData) + { + if (!storage.TestForTitles(_skyscraperContext.CurrentNetworkId.Value, _skyscraperContext.CurrentTransportStreamId.Value, titleData.EventID, titleData.StartTime)) + { + + } + } + } + + public void OnSummaries(int sourcePid, int sectionTableId, OpenTVSummarySection summarySection) + { + if (!_skyscraperContext.CurrentNetworkId.HasValue) + return; + if (!_skyscraperContext.CurrentTransportStreamId.HasValue) + return; + + throw new NotImplementedException(); + } + } +} diff --git a/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/SkyscraperSide/OpenTV/OpenTvSummaryContestant.cs b/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/SkyscraperSide/OpenTV/OpenTvSummaryContestant.cs new file mode 100644 index 0000000..d959f45 --- /dev/null +++ b/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/SkyscraperSide/OpenTV/OpenTvSummaryContestant.cs @@ -0,0 +1,68 @@ +using DVBServices; +using skyscraper5.Mpeg2; +using skyscraper5.Skyscraper.Plugins; +using skyscraper5.Skyscraper.Scraper; +using skyscraper5.Skyscraper.Scraper.StreamAutodetection; +using skyscraper8.EPGCollectorPort.SkyscraperSide.MediaHighway2; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace skyscraper8.EPGCollectorPort.SkyscraperSide.OpenTV +{ + ////Disabled due to the Huffmann Table being dynamically determined. + //[SkyscraperPlugin] + internal class OpenTvSummaryContestant : Contestant, OpenTvHandler + { + public OpenTvSummaryContestant(int pid) + : base("OpenTV Summaries", pid) + { + PacketProcessor = new PsiDecoder(pid, new OpenTvSummaryParser(this)); + } + + public override void Dispose() + { + + } + + public override void DeclareWinner(SkyscraperContext skyscraperContext, int pid, ProgramContext programContext) + { + OpenTvScraper openTvScraper = skyscraperContext.PluginContext.FirstOrDefault(x => x is OpenTvScraper) as OpenTvScraper; + if (openTvScraper == null) + { + openTvScraper = new OpenTvScraper(skyscraperContext); + skyscraperContext.PluginContext.Add(openTvScraper); + } + + skyscraperContext.DvbContext.RegisterPacketProcessor(pid, new PsiDecoder(pid, new OpenTvSummaryParser(openTvScraper))); + } + + public override void Introduce(ProgramContext programContext) + { + + } + + private byte[] nonTrafficMarkers; + public void OnNonOpenTvTraffic(int sourcePid, OpenTvExpectedDataType titles, int sectionTableId) + { + if (nonTrafficMarkers == null) + nonTrafficMarkers = new byte[byte.MaxValue]; + + nonTrafficMarkers[sectionTableId]++; + if (nonTrafficMarkers[sectionTableId] == 1) + Score++; + } + + public void OnTitles(int sourcePid, int sectionTableId, OpenTVTitleSection titleSection) + { + throw new NotImplementedException(); + } + + public void OnSummaries(int sourcePid, int sectionTableId, OpenTVSummarySection summarySection) + { + Score++; + } + } +} diff --git a/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/SkyscraperSide/OpenTV/OpenTvSummaryParser.cs b/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/SkyscraperSide/OpenTV/OpenTvSummaryParser.cs new file mode 100644 index 0000000..9836ab9 --- /dev/null +++ b/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/SkyscraperSide/OpenTV/OpenTvSummaryParser.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using DVBServices; +using skyscraper5.Mpeg2; + +namespace skyscraper8.EPGCollectorPort.SkyscraperSide.OpenTV +{ + internal class OpenTvSummaryParser : IPsiProcessor + { + private readonly OpenTvHandler _openTvScraper; + + public OpenTvSummaryParser(OpenTvHandler openTvScraper) + { + _openTvScraper = openTvScraper; + } + + public void GatherPsi(PsiSection section, int sourcePid) + { + if (section.TableId >= 0xa8 && section.TableId <= 0xab) + { + OpenTVSummarySection summarySection = OpenTVSummarySection.ProcessOpenTVSummaryTable(section.GetData()); + if (summarySection != null) + { + _openTvScraper.OnSummaries(sourcePid, section.TableId, summarySection); + } + } + else + { + if (section.TableId >= 0xa0 && section.TableId <= 0xa7) + { + //Titles + return; + } + + _openTvScraper.OnNonOpenTvTraffic(sourcePid, OpenTvExpectedDataType.Summaries, section.TableId); + } + } + } +} diff --git a/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/SkyscraperSide/OpenTV/OpenTvTitleContestant.cs b/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/SkyscraperSide/OpenTV/OpenTvTitleContestant.cs new file mode 100644 index 0000000..4720555 --- /dev/null +++ b/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/SkyscraperSide/OpenTV/OpenTvTitleContestant.cs @@ -0,0 +1,62 @@ +using DVBServices; +using skyscraper5.Mpeg2; +using skyscraper5.Skyscraper.Plugins; +using skyscraper5.Skyscraper.Scraper; +using skyscraper5.Skyscraper.Scraper.StreamAutodetection; +using skyscraper8.EPGCollectorPort.SkyscraperSide.MediaHighway2; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace skyscraper8.EPGCollectorPort.SkyscraperSide.OpenTV +{ + //Disabled due to the Huffmann Table being dynamically determined. + //[SkyscraperPlugin] + internal class OpenTvTitleContestant : Contestant, OpenTvHandler + { + public OpenTvTitleContestant(int pid) + : base("OpenTV Titles", pid) + { + PacketProcessor = new PsiDecoder(pid, new OpenTvTitleParser(this)); + } + + public override void Dispose() + { + + } + + public override void DeclareWinner(SkyscraperContext skyscraperContext, int pid, ProgramContext programContext) + { + OpenTvScraper openTvScraper = skyscraperContext.PluginContext.FirstOrDefault(x => x is OpenTvScraper) as OpenTvScraper; + if (openTvScraper == null) + { + openTvScraper = new OpenTvScraper(skyscraperContext); + skyscraperContext.PluginContext.Add(openTvScraper); + } + + skyscraperContext.DvbContext.RegisterPacketProcessor(pid, new PsiDecoder(pid, new OpenTvTitleParser(openTvScraper))); + } + + public override void Introduce(ProgramContext programContext) + { + + } + + public void OnNonOpenTvTraffic(int sourcePid, OpenTvExpectedDataType titles, int sectionTableId) + { + throw new NotImplementedException(); + } + + public void OnTitles(int sourcePid, int sectionTableId, OpenTVTitleSection titleSection) + { + Score++; + } + + public void OnSummaries(int sourcePid, int sectionTableId, OpenTVSummarySection summarySection) + { + throw new NotImplementedException(); + } + } +} diff --git a/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/SkyscraperSide/OpenTV/OpenTvTitleParser.cs b/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/SkyscraperSide/OpenTV/OpenTvTitleParser.cs new file mode 100644 index 0000000..0f2b165 --- /dev/null +++ b/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/SkyscraperSide/OpenTV/OpenTvTitleParser.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using DVBServices; +using skyscraper5.Mpeg2; + +namespace skyscraper8.EPGCollectorPort.SkyscraperSide.OpenTV +{ + internal class OpenTvTitleParser : IPsiProcessor + { + private readonly OpenTvHandler _handler; + + public OpenTvTitleParser(OpenTvHandler handler) + { + _handler = handler; + } + + public void GatherPsi(PsiSection section, int sourcePid) + { + if (section.TableId >= 0xa0 && section.TableId <= 0xa7) + { + OpenTVTitleSection titleSection = OpenTVTitleSection.ProcessOpenTVTitleTable(section.GetData(),sourcePid,section.TableId); + if (titleSection != null) + { + _handler.OnTitles(sourcePid, section.TableId, titleSection); + } + } + else + { + if (section.TableId >= 0xa8 && section.TableId <= 0xab) + { + //Summaries. + return; + } + + _handler.OnNonOpenTvTraffic(sourcePid,OpenTvExpectedDataType.Titles, section.TableId); + } + } + } +} diff --git a/skyscraper8/Program.cs b/skyscraper8/Program.cs index d661098..375565a 100644 --- a/skyscraper8/Program.cs +++ b/skyscraper8/Program.cs @@ -40,6 +40,7 @@ namespace skyscraper5 private static void IntegrationTest() { + } static void Main(string[] args) diff --git a/skyscraper8/Properties/launchSettings.json b/skyscraper8/Properties/launchSettings.json index 412f243..11f87ae 100644 --- a/skyscraper8/Properties/launchSettings.json +++ b/skyscraper8/Properties/launchSettings.json @@ -2,7 +2,7 @@ "profiles": { "skyscraper8": { "commandName": "Project", - "commandLineArgs": "cscan-live tcp://tetsuro:6969", + "commandLineArgs": "file-live \"C:\\Temp\\sky-uk-000000.ts\"", "remoteDebugEnabled": false }, "Container (Dockerfile)": { diff --git a/skyscraper8/Skyscraper/Scraper/SkyscraperContext.cs b/skyscraper8/Skyscraper/Scraper/SkyscraperContext.cs index a2818cd..9631f33 100644 --- a/skyscraper8/Skyscraper/Scraper/SkyscraperContext.cs +++ b/skyscraper8/Skyscraper/Scraper/SkyscraperContext.cs @@ -633,12 +633,7 @@ namespace skyscraper5.Skyscraper.Scraper interactionChannelDecoder.Handler = this; DvbContext.RegisterPacketProcessor(mappingStream.ElementaryPid, new PsiDecoder(mappingStream.ElementaryPid, interactionChannelDecoder)); break; - case StreamType.OpenTvPrivate: - DvbContext.RegisterPacketProcessor(mappingStream.ElementaryPid, new OpenTvPrivate()); - break; - case StreamType.BSkyBPrivate: - DvbContext.RegisterPacketProcessor(mappingStream.ElementaryPid, new BSkyBPrivate()); - break; + case StreamType.Id3: DvbContext.RegisterPacketProcessor(mappingStream.ElementaryPid, new PesDecoder(new Id3PesProcessor(this))); break; @@ -934,18 +929,6 @@ namespace skyscraper5.Skyscraper.Scraper } } - if (stream.PrivateDataSpecifier == 0x00000002) - { - if (stream.UnknownUserDefines != null) - { - UserDefinedDescriptor userDefinedDescriptor = stream.UnknownUserDefines.Find(x => x.DescriptorTag == 0xb0); - if (userDefinedDescriptor != null) - { - //BSkyB EPG Related descriptor - return StreamType.BSkyBPrivate; - } - } - } if (stream.MetadataApplicationFormat == 0xffff && stream.MetadataApplicationFormatIdentifier == 0x49443320 && stream.MetadataFormat == 0xff && stream.MetadataFormatIdentifier == 0x49443320) {