Added rudimentary (and very buggy!) support for reading MediaHighway2 EPG.

This commit is contained in:
feyris-tan 2025-07-14 23:39:37 +02:00
parent 226901607c
commit 6302a25f26
50 changed files with 3096 additions and 98 deletions

1
.gitignore vendored
View File

@ -113,3 +113,4 @@ imgui.ini
/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/bin/Debug/net8.0
/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/obj/Debug/net8.0
/PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/obj
/.vs/skyscraper8/CopilotIndices/17.14.827.52834

View File

@ -178,9 +178,24 @@ namespace skyscraper5.Data.PostgreSql
private void UpdateDvbNipPrivateDataSpecifier(NpgsqlConnection connection, Tuple<ushort, ushort, ushort, ushort, uint> coordinates, DateTime versionUpdate, List<string> privateDataSessions)
{
throw new NotImplementedException();
DateTime localUpdate = new DateTime(versionUpdate.Year, versionUpdate.Month, versionUpdate.Day,
versionUpdate.Hour, versionUpdate.Minute, versionUpdate.Second, DateTimeKind.Local);
NpgsqlCommand command = connection.CreateCommand();
command.CommandText = "UPDATE dvbnip_private_data_specifiers SET version_update = @vu, sessions = @s WHERE nid = @nid AND cid = @cid AND lid=@lid AND sid=@sid AND private_data_specifier = @pds";
command.Parameters.AddWithValue("@nid", NpgsqlDbType.Integer, (int)coordinates.Item1);
command.Parameters.AddWithValue("@cid", NpgsqlDbType.Integer, (int)coordinates.Item2);
command.Parameters.AddWithValue("@lid", NpgsqlDbType.Integer, (int)coordinates.Item3);
command.Parameters.AddWithValue("@sid", NpgsqlDbType.Integer, (int)coordinates.Item4);
command.Parameters.AddWithValue("@pds", NpgsqlDbType.Bigint, (long)coordinates.Item5);
command.Parameters.AddWithValue("@vu", NpgsqlDbType.Timestamp, localUpdate);
command.Parameters.AddWithValue("@s", NpgsqlDbType.Json, JsonConvert.SerializeObject(privateDataSessions));
int executeNonQuery = command.ExecuteNonQuery();
if (executeNonQuery != 1)
{
throw new DataException(String.Format("Expected to insert {0} rows, but it were {1}", 1, executeNonQuery));
}
}
private bool[] _dvbNipKnownNetworks;
public bool DvbNipTestForNetwork(BroadcastNetworkType network)
{

View File

@ -182,7 +182,7 @@ namespace skyscraper5.Data.PostgreSql
private void AddDvbiServiceToServiceList(NpgsqlConnection x, string id, string serviceListId)
{
NpgsqlCommand checkCommand = x.CreateCommand();
checkCommand.CommandText = "SELECT dateadded FROM dvbi_services_to_service_lists WHERE service = @service AND service_list = @service_list";
checkCommand.CommandText = "SELECT dateadded FROM dvbi_services_to_service_lists WHERE service = @service AND service_list = @servicelist";
checkCommand.Parameters.AddWithValue("@service", NpgsqlDbType.Text, id);
checkCommand.Parameters.AddWithValue("@servicelist", NpgsqlDbType.Text, serviceListId);
NpgsqlDataReader dataReader = checkCommand.ExecuteReader();

View File

@ -18,7 +18,7 @@ namespace skyscraper5.Data.PostgreSql
connectionStringBuilder = stringBuilder;
}
private NpgsqlConnectionStringBuilder connectionStringBuilder;
internal NpgsqlConnectionStringBuilder connectionStringBuilder;
public bool TestForRelatedContent(EitEvent lEvent, RctLinkInfo rctLinkInfo)
@ -157,10 +157,15 @@ namespace skyscraper5.Data.PostgreSql
connection.Close();
}
}
private PostgresqlToken pluginToken;
public object[] GetPluginConnector()
{
return new object[] { connectionStringBuilder };
if (pluginToken == null)
{
pluginToken = new PostgresqlToken(this);
}
return new object[] { pluginToken };
}

View File

@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Npgsql;
namespace skyscraper5.Data.PostgreSql
{
public class PostgresqlToken
{
private readonly PostgresqlDataStore _postgresqlDataStore;
public PostgresqlToken(PostgresqlDataStore postgresqlDataStore)
{
_postgresqlDataStore = postgresqlDataStore;
}
public NpgsqlConnection GetConnection()
{
return new NpgsqlConnection(_postgresqlDataStore.connectionStringBuilder.ToString());
}
public void EnqueueTask(WriterTask task)
{
_postgresqlDataStore.EnqueueTask(task);
}
}
}

View File

@ -9,15 +9,17 @@ using Npgsql;
namespace skyscraper5.Data.PostgreSql
{
public partial class PostgresqlDataStore
public delegate void WriterTask(NpgsqlConnection connection);
public partial class PostgresqlDataStore
{
private Thread _writerThread;
private delegate void WriterTask(NpgsqlConnection connection);
private Queue<WriterTask> _writerTasks;
private void EnqueueTask(WriterTask task)
internal void EnqueueTask(WriterTask task)
{
if (_writerTasks == null)
{

View File

@ -0,0 +1,219 @@
//////////////////////////////////////////////////////////////////////////////////
// //
// 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
{
/// <summary>
/// The reply codes from the protocol collectors.
/// </summary>
public enum CollectorReply
{
/// <summary>
/// The collection was successful.
/// </summary>
OK,
/// <summary>
/// The collection failed.
/// </summary>
GeneralFailure,
/// <summary>
/// There was a format error in the received data.
/// </summary>
FatalFormatError,
/// <summary>
/// The was an error loading the reference data.
/// </summary>
ReferenceDataError,
/// <summary>
/// There was an error in the broadcast data.
/// </summary>
BroadcastDataError,
/// <summary>
/// The collection was cancelled.
/// </summary>
Cancelled
}
/// <summary>
/// The current scope
/// </summary>
[Flags]
public enum Scope
{
/// <summary>
/// No scope restrictions
/// </summary>
All = 0x7fffffff,
/// <summary>
/// Program Map table
/// </summary>
PMT = 0x0002,
/// <summary>
/// Program Association table
/// </summary>
PAT = 0x0004,
/// <summary>
/// Network Information table
/// </summary>
NIT = 0x0008,
/// <summary>
/// Bouquet Association table
/// </summary>
BAT = 0x0010,
/// <summary>
/// Service Description table
/// </summary>
SDT = 0x0020,
/// <summary>
/// Event Information table
/// </summary>
EIT = 0x0040,
/// <summary>
/// Time and Date table
/// </summary>
TDT = 0x0080,
/// <summary>
/// Time offset table
/// </summary>
TOT = 0x0100,
/// <summary>
/// Running Status table
/// </summary>
RST = 0x0200,
/// <summary>
/// Stuffing table
/// </summary>
ST = 0x0400,
/// <summary>
/// Discontinuity Information table
/// </summary>
DIT = 0x0800,
/// <summary>
/// Selection Information table
/// </summary>
SIT = 0x1000,
/// <summary>
/// Application Information table
/// </summary>
AIT = 0x2000,
/// <summary>
/// DSMCC sections
/// </summary>
DSMCC = 0x4000,
}
/// <summary>
/// The action to take for non-Ascii characters in text strings.
/// </summary>
public enum ReplaceMode
{
/// <summary>
/// The non-ASCII character is removed.
/// </summary>
Ignore,
/// <summary>
/// The non-ASCII character is set to space.
/// </summary>
SetToSpace,
/// <summary>
/// The non-ASCII character is not changed in the output text.
/// </summary>
TransferUnchanged,
/// <summary>
/// The non-ASCII character is converted to its ASCII equivalent (EIT 0x8a only at present)
/// </summary>
Convert,
/// <summary>
/// The non-ASCII character is converted using the byte conversion table)
/// </summary>
ConvertUsingTable
}
/// <summary>
/// The data broadcast ID values.
/// </summary>
public enum DataBroadcastId
{
/// <summary>
/// DVB data pipe.
/// </summary>
DvbDataPipe = 0x0001, // EN 301 192 section 4.2.1
/// <summary>
/// Asynchronous data stream
/// </summary>
AsyncDataStream = 0x0002, // EN 301 192 section 5.2.1
/// <summary>
/// Synchronous data stream
/// </summary>
SynchronousDataStream = 0x0003, // EN 301 192 section 6.2.1
/// <summary>
/// Synchronous data streams
/// </summary>
SynchronousDataStreams = 0x0004, // EN 301 192 section 6.2.1
/// <summary>
/// Multiprotocol encapsulation
/// </summary>
MultiProtocolEncapsulation = 0x0005, // EN 301 192 section 4.2.1
/// <summary>
/// Data carousel
/// </summary>
DataCarousel = 0x0006,
/// <summary>
/// Object carousel
/// </summary>
ObjectCarousel = 0x0007,
/// <summary>
/// DVB ATM stream
/// </summary>
DvbAtmStream = 0x0008,
/// <summary>
/// Higher protocol asynchronous stream
/// </summary>
HigherProtcolAsyncStream = 0x0009,
/// <summary>
/// System software update service
/// </summary>
SsuService = 0x000a,
/// <summary>
/// IP/MAC notification service
/// </summary>
IpMacNotificationService = 0x000b,
/// <summary>
/// MHP object carousel
/// </summary>
MhpObjectCarousel = 0x00f0, // TS 101 812
/// <summary>
/// MHP multiprotocol encapsulation
/// </summary>
MhpMultiProtoclEncapsulation = 0x000f1, // TS 101 812
/// <summary>
/// MHP application presence
/// </summary>
MhpApplicationPresence = 0x00f2,
/// <summary>
/// MHEG5 stream
/// </summary>
Mheg5 = 0x0106
}
}

View File

@ -0,0 +1,181 @@
//////////////////////////////////////////////////////////////////////////////////
// //
// 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
{
/// <summary>
/// The class that describes a MediaHighway2 category section.
/// </summary>
public class MediaHighway2CategorySection
{
/// <summary>
/// Get the collection of categories.
/// </summary>
public Collection<MediaHighwayCategoryEntry> Categories
{
get
{
if (categories == null)
categories = new Collection<MediaHighwayCategoryEntry>();
return (categories);
}
}
private int categoryCount;
private Collection<MediaHighwayCategoryEntry> categories;
private int lastIndex = -1;
/// <summary>
/// Initialize a new instance of the MediaHighway2CategorySection class.
/// </summary>
internal MediaHighway2CategorySection() { }
/// <summary>
/// Parse the section.
/// </summary>
/// <param name="byteData">The MPEG2 section containing the section.</param>
/// <param name="index">The index of the first byte of the data portion.</param>
/// <returns>True if the section is an MHW2 category section; false otherwise.</returns>
internal bool Process(byte[] byteData, int index)
{
lastIndex = index;
if (byteData[lastIndex] != 0x01)
return (false);
lastIndex++;
categoryCount = (int)byteData[lastIndex];
lastIndex++;
try
{
int p1 = 0;
int p2 = 0;
string themeName = null;
for (int themeIndex = 0; themeIndex < categoryCount; themeIndex++)
{
p1 = ((byteData[lastIndex + (themeIndex * 2)] << 8) | byteData[lastIndex + 1 + (themeIndex * 2)]) + 3;
for (int descriptionIndex = 0; descriptionIndex <= (byteData[p1] & 0x3f); descriptionIndex++)
{
p2 = ((byteData[p1 + 1 + (descriptionIndex * 2)] << 8) | byteData[p1 + 2 + (descriptionIndex * 2)] + 3);
MediaHighwayCategoryEntry categoryEntry = new MediaHighwayCategoryEntry();
categoryEntry.Number = ((themeIndex & 0x3f) << 6) | (descriptionIndex & 0x3f);
if (descriptionIndex == 0)
{
themeName = Utils.GetString(byteData, p2 + 1, byteData[p2] & 0x1f, ReplaceMode.TransferUnchanged).Trim();
categoryEntry.Description = themeName;
}
else
categoryEntry.Description = themeName + " " + Utils.GetString(byteData, p2 + 1, byteData[p2] & 0x1f, ReplaceMode.TransferUnchanged).Trim();
Categories.Add(categoryEntry);
}
}
}
catch (IndexOutOfRangeException)
{
throw (new ArgumentOutOfRangeException("The MediaHighway2 Category Section message is short"));
}
Validate();
return (true);
}
/// <summary>
/// Validate the section fields.
/// </summary>
/// <exception cref="ArgumentOutOfRangeException">
/// A section field is not valid.
/// </exception>
public void Validate() { }
/// <summary>
/// Log the section fields.
/// </summary>
public void LogMessage()
{
if (Logger.ProtocolLogger == null)
return;
Logger.ProtocolLogger.Write(Logger.ProtocolIndent + "MHW2 CATEGORY SECTION");
if (categories != null)
{
foreach (MediaHighwayCategoryEntry categoryEntry in categories)
{
Logger.IncrementProtocolIndent();
categoryEntry.LogMessage();
Logger.DecrementProtocolIndent();
}
}
}
/// <summary>
/// Process an MPEG2 section from the Open TV Title table.
/// </summary>
/// <param name="byteData">The MPEG2 section.</param>
public static MediaHighway2CategorySection ProcessMediaHighwayCategoryTable(byte[] byteData)
{
Mpeg2BasicHeader mpeg2Header = new Mpeg2BasicHeader();
try
{
mpeg2Header.Process(byteData);
MediaHighway2CategorySection categorySection = new MediaHighway2CategorySection();
bool process = categorySection.Process(byteData, mpeg2Header.Index);
if (process)
{
categorySection.LogMessage();
return (categorySection);
}
else
return (null);
}
catch (ArgumentOutOfRangeException e)
{
Logger.Instance.Write("<e> Category section parsing failed: " + e.Message);
return (null);
}
catch (NotImplementedException e)
{
return null;
}
}
}
}

View File

@ -0,0 +1,170 @@
//////////////////////////////////////////////////////////////////////////////////
// //
// 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
{
/// <summary>
/// The class that describes a MediaHighway2 channel section.
/// </summary>
public class MediaHighway2ChannelSection
{
/// <summary>
/// Get the collection of channels.
/// </summary>
public Collection<MediaHighwayChannelInfoEntry> Channels
{
get
{
if (channels == null)
channels = new Collection<MediaHighwayChannelInfoEntry>();
return (channels);
}
}
private byte[] fillBytes;
private int channelCount;
private Collection<MediaHighwayChannelInfoEntry> channels;
private int lastIndex = -1;
/// <summary>
/// Initialize a new instance of the MediaHighway2ChannelSection class.
/// </summary>
internal MediaHighway2ChannelSection() { }
/// <summary>
/// Parse the section.
/// </summary>
/// <param name="byteData">The MPEG2 section containing the section.</param>
/// <param name="index">The index of the first byte of the data portion.</param>
/// <returns>True if the section is an MHW2 channel section; false otherwise.</returns>
internal bool Process(byte[] byteData, int index)
{
lastIndex = index;
/*if (byteData[lastIndex] != 0x00 && byteData[lastIndex] != 0x02 && byteData[lastIndex] != 0x03)*/
if (byteData[lastIndex] != 0x00)
return (false);
try
{
fillBytes = Utils.GetBytes(byteData, lastIndex, 117);
lastIndex += fillBytes.Length;
channelCount = (int)byteData[lastIndex];
lastIndex++;
int nameIndex = lastIndex + (8 * channelCount);
while (Channels.Count < channelCount)
{
MediaHighwayChannelInfoEntry channelEntry = new MediaHighwayChannelInfoEntry();
channelEntry.Process(byteData, lastIndex, nameIndex);
Channels.Add(channelEntry);
lastIndex = channelEntry.Index;
nameIndex = channelEntry.NameIndex;
}
}
catch (IndexOutOfRangeException)
{
throw (new ArgumentOutOfRangeException("The MediaHighway2 Channel Section message is short"));
}
catch (NotImplementedException e)
{
throw e;
}
Validate();
return (true);
}
/// <summary>
/// Validate the section fields.
/// </summary>
/// <exception cref="ArgumentOutOfRangeException">
/// A section field is not valid.
/// </exception>
public void Validate() { }
/// <summary>
/// Log the section fields.
/// </summary>
public void LogMessage()
{
if (Logger.ProtocolLogger == null)
return;
Logger.ProtocolLogger.Write(Logger.ProtocolIndent + "MHW2 CHANNEL SECTION");
if (channels != null)
{
foreach (MediaHighwayChannelInfoEntry channelInfoEntry in channels)
{
Logger.IncrementProtocolIndent();
channelInfoEntry.LogMessage();
Logger.DecrementProtocolIndent();
}
}
}
/// <summary>
/// Process an MPEG2 section from the Open TV Title table.
/// </summary>
/// <param name="byteData">The MPEG2 section.</param>
public static MediaHighway2ChannelSection ProcessMediaHighwayChannelTable(byte[] byteData)
{
Mpeg2BasicHeader mpeg2Header = new Mpeg2BasicHeader();
try
{
mpeg2Header.Process(byteData);
MediaHighway2ChannelSection channelSection = new MediaHighway2ChannelSection();
bool process = channelSection.Process(byteData, mpeg2Header.Index);
if (process)
{
channelSection.LogMessage();
return (channelSection);
}
else
return (null);
}
catch (ArgumentOutOfRangeException e)
{
Logger.Instance.Write("<e> Channel section parsing failed: " + e.Message);
return (null);
}
catch (NotImplementedException e)
{
return null;
}
}
}
}

View File

@ -0,0 +1,190 @@
//////////////////////////////////////////////////////////////////////////////////
// //
// 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
{
/// <summary>
/// The class that describes MediaHighway2 summary data.
/// </summary>
public class MediaHighway2SummaryData
{
/// <summary>
/// Get the event identification.
/// </summary>
public int EventID { get { return (eventID); } }
/// <summary>
/// Get the short description of the event.
/// </summary>
public string ShortDescription
{
get
{
return shortDescription;
}
}
/// <summary>
/// Get the unidentified data.
/// </summary>
public byte[] Unknown { get { return (unknown); } }
/// <summary>
/// Get the index of the next byte in the MPEG2 section following the summary data.
/// </summary>
/// <exception cref="InvalidOperationException">
/// The summary data has not been processed.
/// </exception>
public int Index
{
get
{
if (lastIndex == -1)
throw (new InvalidOperationException("MediaHighway2SummaryData: Index requested before block processed"));
return (lastIndex);
}
}
private int eventID;
private string shortDescription;
private byte[] unknown;
private int lastIndex = -1;
int summaryLength;
int lineCount;
int lineLength;
/// <summary>
/// Initialize a new instance of the MediaHighway2SummaryData class.
/// </summary>
public MediaHighway2SummaryData() { }
/// <summary>
/// Parse the summary data.
/// </summary>
/// <param name="byteData">The MPEG2 section containing the summary data.</param>
/// <param name="index">Index of the first byte of the summary data in the MPEG2 section.</param>
/// <returns>True if the block contains data; false otherwise.</returns>
internal bool Process(byte[] byteData, int index)
{
lastIndex = index;
try
{
eventID = Utils.Convert2BytesToInt(byteData, lastIndex);
lastIndex += 2;
unknown = Utils.GetBytes(byteData, lastIndex, 9);
lastIndex += unknown.Length;
if (unknown[1] != 0x00)
{
if (DebugEntry.IsDefined(DebugName.Mhw2Unknown))
{
Logger.Instance.Write("Index: " + index);
Logger.Instance.Dump("Unknown Data Block", byteData, byteData.Length);
}
return (false);
}
if (unknown[2] != 0x00 && unknown[2] != 0x01)
{
if (DebugEntry.IsDefined(DebugName.Mhw2Unknown))
{
Logger.Instance.Write("Index: " + index);
Logger.Instance.Dump("Unknown Data Block", byteData, byteData.Length);
}
return (false);
}
summaryLength = (int)byteData[lastIndex];
lastIndex++;
if (summaryLength == 0)
{
if (DebugEntry.IsDefined(DebugName.Mhw2Unknown))
{
Logger.Instance.Write("Index: " + index);
Logger.Instance.Dump("Summary Length Zero", byteData, byteData.Length);
}
return (false);
}
shortDescription = Utils.GetString(byteData, lastIndex, summaryLength, ReplaceMode.TransferUnchanged);
lastIndex += summaryLength;
lineCount = byteData[lastIndex] & 0x0f;
lastIndex++;
while (lineCount > 0)
{
lineLength = (int)byteData[lastIndex];
lastIndex++;
if (lineLength > 0)
{
shortDescription += " " + Utils.GetString(byteData, lastIndex, lineLength, ReplaceMode.TransferUnchanged);
lastIndex += lineLength;
}
lineCount--;
}
Validate();
return (true);
}
catch (IndexOutOfRangeException)
{
byte[] data = (Utils.GetBytes(byteData, 0, byteData.Length));
Logger.Instance.Dump("Exception Data", data, data.Length);
throw (new ArgumentOutOfRangeException("The MediaHighway2 Summary Data message is short"));
}
}
/// <summary>
/// Validate the summary data fields.
/// </summary>
/// <exception cref="ArgumentOutOfRangeException">
/// A summary data field is not valid.
/// </exception>
public void Validate() { }
/// <summary>
/// Log the summary data fields.
/// </summary>
public void LogMessage()
{
if (Logger.ProtocolLogger == null)
return;
Logger.ProtocolLogger.Write(Logger.ProtocolIndent + "MHW2 SUMMARY DATA: Event ID: " + eventID +
" Unknown: " + Utils.ConvertToHex(unknown) +
" Short desc: " + shortDescription);
}
}
}

View File

@ -0,0 +1,103 @@
//////////////////////////////////////////////////////////////////////////////////
// //
// 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
{
/// <summary>
/// The class that describes a MediaHighway2 Summary section.
/// </summary>
public class MediaHighway2SummarySection
{
/// <summary>
/// Get the summary data for this section.
/// </summary>
public MediaHighway2SummaryData SummaryData { get { return (summaryData); } }
private MediaHighway2SummaryData summaryData;
private int lastIndex = -1;
/// <summary>
/// Initialize a new instance of the MediaHighway2SummarySection class.
/// </summary>
internal MediaHighway2SummarySection() { }
/// <summary>
/// Parse the section.
/// </summary>
/// <param name="byteData">The MPEG2 section containing the summary data.</param>
/// <param name="index">Index of the first byte of the summary data in the MPEG2 section.</param>
/// <returns>True if the block contains data; false otherwise.</returns>
internal bool Process(byte[] byteData, int index)
{
lastIndex = index;
summaryData = new MediaHighway2SummaryData();
return(summaryData.Process(byteData, lastIndex));
}
/// <summary>
/// Log the section fields.
/// </summary>
public void LogMessage()
{
if (Logger.ProtocolLogger == null)
return;
summaryData.LogMessage();
}
/// <summary>
/// Process an MPEG2 section from the MediaHighway1 summary table.
/// </summary>
/// <param name="byteData">The MPEG2 section.</param>
/// <returns>A MediaHighway2SummarySection instance.</returns>
public static MediaHighway2SummarySection ProcessMediaHighwaySummaryTable(byte[] byteData)
{
Mpeg2BasicHeader mpeg2Header = new Mpeg2BasicHeader();
try
{
mpeg2Header.Process(byteData);
MediaHighway2SummarySection summarySection = new MediaHighway2SummarySection();
bool process = summarySection.Process(byteData, mpeg2Header.Index);
if (process)
{
summarySection.LogMessage();
return (summarySection);
}
else
return (null);
}
catch (ArgumentOutOfRangeException e)
{
Logger.Instance.Write("<e> Summary section parsing failed: " + e.Message);
return (null);
}
}
}
}

View File

@ -0,0 +1,222 @@
//////////////////////////////////////////////////////////////////////////////////
// //
// 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
{
/// <summary>
/// The class that describes MediaHighway2 title data.
/// </summary>
public class MediaHighway2TitleData
{
/// <summary>
/// Get the channel identification.
/// </summary>
public int ChannelID { get { return (channelID); } }
/// <summary>
/// Get the event identification.
/// </summary>
public int EventID { get { return (eventID); } }
/// <summary>
/// Get the start time of the event.
/// </summary>
public DateTime StartTime { get { return (baseDate + new TimeSpan(hours, minutes, 0)); }}
/// <summary>
/// Get the duration of the event.
/// </summary>
public TimeSpan Duration { get { return (duration); } }
/// <summary>
/// Get the theme identification of the event.
/// </summary>
public int CategoryID { get { return ((mainCategory << 6) + subCategory); } }
/// <summary>
/// Get the theme group of the event.
/// </summary>
public int MainCategory { get { return (mainCategory); } }
/// <summary>
/// Get the theme subgroup of the event.
/// </summary>
public int SubCategory { get { return (subCategory); } }
/// <summary>
/// Get the name of the event.
/// </summary>
public string EventName { get { return (eventName); } }
/// <summary>
/// Get the unknown data.
/// </summary>
public byte[] Unknown
{
get
{
byte[] outputBytes = new byte[unknown0.Length + unknown1.Length + unknown2.Length];
unknown0.CopyTo(outputBytes, 0);
unknown1.CopyTo(outputBytes, unknown0.Length);
unknown2.CopyTo(outputBytes, unknown0.Length + unknown1.Length);
return (outputBytes);
}
}
/// <summary>
/// Get the index of the next byte in the MPEG2 section following the title data.
/// </summary>
/// <exception cref="InvalidOperationException">
/// The title data has not been processed.
/// </exception>
public int Index
{
get
{
if (lastIndex == -1)
throw (new InvalidOperationException("MediaHighway2TitleData: Index requested before block processed"));
return (lastIndex);
}
}
private byte[] unknown0;
private int channelID;
private byte[] unknown1;
private int mainCategory;
private DateTime baseDate;
private int hours;
private int minutes;
private byte[] unknown2;
private TimeSpan duration;
private string eventName;
private int subCategory;
private int eventID;
private int lastIndex = -1;
/// <summary>
/// Initialize a new instance of the MediaHighway2TitleData class.
/// </summary>
public MediaHighway2TitleData() { }
/// <summary>
/// Parse the title data.
/// </summary>
/// <param name="byteData">The MPEG2 section containing the title data.</param>
/// <param name="index">Index of the first byte of the title data in the MPEG2 section.</param>
internal void Process(byte[] byteData, int index)
{
lastIndex = index;
try
{
unknown0 = Utils.GetBytes(byteData, 3, 15);
channelID = (int)byteData[lastIndex];
lastIndex++;
unknown1 = Utils.GetBytes(byteData, lastIndex, 10);
lastIndex += unknown1.Length;
mainCategory = byteData[7] & 0x0f;
baseDate = getDate(Utils.Convert2BytesToInt(byteData, lastIndex));
lastIndex += 2;
hours = ((byteData[lastIndex] >> 4) * 10) + (byteData[lastIndex] & 0x0f);
lastIndex++;
minutes = ((byteData[lastIndex] >> 4) * 10) + (byteData[lastIndex] & 0x0f);
lastIndex++;
unknown2 = Utils.GetBytes(byteData, lastIndex, 1);
lastIndex += unknown2.Length;
int durationMinutes = (byteData[lastIndex] << 4) + (byteData[lastIndex + 1] >> 4);
duration = new TimeSpan(durationMinutes / 60, durationMinutes % 60, 0);
lastIndex += 2;
int eventNameLength = byteData[lastIndex] & 0x3f;
lastIndex++;
eventName = Utils.GetString(byteData, lastIndex, eventNameLength, ReplaceMode.TransferUnchanged);
lastIndex += eventNameLength;
subCategory = byteData[lastIndex] & 0x3f;
lastIndex++;
eventID = Utils.Convert2BytesToInt(byteData, lastIndex);
lastIndex += 2;
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));
}
/// <summary>
/// Validate the title data fields.
/// </summary>
/// <exception cref="ArgumentOutOfRangeException">
/// A title data field is not valid.
/// </exception>
public void Validate() { }
/// <summary>
/// Log the title data fields.
/// </summary>
public void LogMessage()
{
if (Logger.ProtocolLogger == null)
return;
Logger.ProtocolLogger.Write(Logger.ProtocolIndent + "MHW2 TITLE DATA: Channel ID: " + channelID +
" Unknown0: " + Utils.ConvertToHex(unknown0) +
" Unknown1: " + Utils.ConvertToHex(unknown1) +
" Main cat: " + mainCategory +
" Base date: " + baseDate +
" Hours: " + hours +
" Minutes: " + minutes +
" Unknown2: " + Utils.ConvertToHex(unknown2) +
" Duration: " + duration +
" Event name: " + eventName +
" Sub cat: " + mainCategory +
" Event ID: " + eventID);
}
}
}

View File

@ -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 System.Collections.ObjectModel;
using DomainObjects;
namespace DVBServices
{
/// <summary>
/// The class that describes a MediaHighway2 Title section.
/// </summary>
public class MediaHighway2TitleSection
{
/// <summary>
/// Get the title data for this section.
/// </summary>
public Collection<MediaHighway2TitleData> Titles { get { return (titles); } }
private byte[] unknown;
private Collection<MediaHighway2TitleData> titles;
private int lastIndex = -1;
/// <summary>
/// Initialize a new instance of the MediaHighway2TitleSection class.
/// </summary>
internal MediaHighway2TitleSection()
{
titles = new Collection<MediaHighway2TitleData>();
}
/// <summary>
/// Parse the section.
/// </summary>
/// <param name="byteData">The MPEG2 section containing the section.</param>
/// <param name="index">Index of the first byte of the title section in the MPEG2 section.</param>
/// <returns>True if the section is an MHW2 title section; false otherwise.</returns>
internal bool Process(byte[] byteData, int index)
{
lastIndex = index;
/*int checkIndex = 18;
bool process = false;
while (checkIndex < byteData.Length)
{
process = false;
checkIndex += 7;
if (checkIndex < byteData.Length)
{
checkIndex += 3;
if (checkIndex < byteData.Length)
{
if (byteData[checkIndex] > 0xc0)
{
checkIndex += (byteData[checkIndex] - 0xc0);
checkIndex += 4;
if (checkIndex < byteData.Length)
{
if (byteData[checkIndex] == 0xff)
{
checkIndex += 1;
process = true;
}
}
}
}
}
}
if (!process)
return (false);*/
/*Logger.Instance.Dump("Title Section", byteData, byteData.Length);*/
unknown = Utils.GetBytes(byteData, lastIndex, 15);
lastIndex += unknown.Length;
while (lastIndex < byteData.Length)
{
MediaHighway2TitleData title = new MediaHighway2TitleData();
title.Process(byteData, lastIndex);
titles.Add(title);
lastIndex = title.Index;
}
return (true);
}
/// <summary>
/// Log the section fields.
/// </summary>
public void LogMessage()
{
if (Logger.ProtocolLogger == null)
return;
if (titles != null)
{
Logger.IncrementProtocolIndent();
foreach (MediaHighway2TitleData title in titles)
title.LogMessage();
Logger.DecrementProtocolIndent();
}
}
/// <summary>
/// Process an MPEG2 section from the MediaHighway title table.
/// </summary>
/// <param name="byteData">The MPEG2 section.</param>
/// <returns>A MediaHighway2TitleSection instance.</returns>
public static MediaHighway2TitleSection ProcessMediaHighwayTitleTable(byte[] byteData)
{
Mpeg2BasicHeader mpeg2Header = new Mpeg2BasicHeader();
try
{
mpeg2Header.Process(byteData);
MediaHighway2TitleSection titleSection = new MediaHighway2TitleSection();
bool process = titleSection.Process(byteData, mpeg2Header.Index);
if (process)
{
titleSection.LogMessage();
return (titleSection);
}
else
return (null);
}
catch (ArgumentOutOfRangeException e)
{
Logger.Instance.Write("<e> Title section parsing failed: " + e.Message);
return (null);
}
}
}
}

View File

@ -0,0 +1,70 @@
//////////////////////////////////////////////////////////////////////////////////
// //
// 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 DomainObjects;
namespace DVBServices
{
/// <summary>
/// The class that describes a MediaHighway category entry.
/// </summary>
public class MediaHighwayCategoryEntry
{
/// <summary>
/// Get or set the category number.
/// </summary>
public int Number
{
get { return (number); }
set { number = value; }
}
/// <summary>
/// Get or set the category description.
/// </summary>
public string Description
{
get { return (description); }
set { description = value; }
}
private int number;
private string description;
/// <summary>
/// Initialize a new instance of the MediaHighwayCategoryEntry class.
/// </summary>
public MediaHighwayCategoryEntry() { }
/// <summary>
/// Log the section fields.
/// </summary>
public void LogMessage()
{
if (Logger.ProtocolLogger == null)
return;
Logger.ProtocolLogger.Write(Logger.ProtocolIndent + "MHW CATEGORY ENTRY: Number: " + number +
" Description: " + description);
}
}
}

View File

@ -0,0 +1,214 @@
//////////////////////////////////////////////////////////////////////////////////
// //
// 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
{
/// <summary>
/// The class that describes a MediaHighway channel info entry.
/// </summary>
public class MediaHighwayChannelInfoEntry
{
/// <summary>
/// Get the original network ID for the channel.
/// </summary>
public int OriginalNetworkID { get { return (originalNetworkID); } }
/// <summary>
/// Get the transport stream ID for the channel.
/// </summary>
public int TransportStreamID { get { return (transportStreamID); } }
/// <summary>
/// Get the service ID for the channel.
/// </summary>
public int ServiceID { get { return (serviceID); } }
/// <summary>
/// Get the name of the channel.
/// </summary>
public string Name { get { return (name); } }
/// <summary>
/// Get the unknown data.
/// </summary>
public byte[] Unknown { get { return (unknown); } }
/// <summary>
/// Get the index of the next byte in the section following this entry.
/// </summary>
/// <exception cref="InvalidOperationException">
/// The entry has not been processed.
/// </exception>
public int Index
{
get
{
if (lastIndex == -1)
throw (new InvalidOperationException("MediaHighwayChannelEntry: Index requested before block processed"));
return (lastIndex);
}
}
/// <summary>
/// Get the index of the next name byte in the section following this entry.
/// </summary>
/// <exception cref="InvalidOperationException">
/// The entry has not been processed.
/// </exception>
public int NameIndex
{
get
{
if (nameIndex == -1)
throw (new InvalidOperationException("MediaHighwayChannelEntry: Name index requested before block processed"));
return (nameIndex);
}
}
/// <summary>
/// Get the length of the entry.
/// </summary>
public int Length { get { return (length); } }
private int originalNetworkID;
private int transportStreamID;
private int serviceID;
private string name;
private byte[] unknown;
private int lastIndex = -1;
private int nameIndex;
private int length;
/// <summary>
/// Initialize a new instance of the MediaHighwayChannelInfoEntry.
/// </summary>
public MediaHighwayChannelInfoEntry() { }
/// <summary>
/// Parse the MHW1 descriptor.
/// </summary>
/// <param name="byteData">The MPEG2 section containing the entry.</param>
/// <param name="index">Index of the first byte in the MPEG2 section of the entry.</param>
internal void Process(byte[] byteData, int index)
{
lastIndex = index;
try
{
originalNetworkID = Utils.Convert2BytesToInt(byteData, lastIndex);
lastIndex += 2;
transportStreamID = Utils.Convert2BytesToInt(byteData, lastIndex);
lastIndex += 2;
serviceID = Utils.Convert2BytesToInt(byteData, lastIndex);
lastIndex += 2;
name = Utils.GetString(byteData, lastIndex, 16, ReplaceMode.TransferUnchanged).Trim();
lastIndex += 16;
length = lastIndex - index;
Validate();
}
catch (IndexOutOfRangeException)
{
throw (new ArgumentOutOfRangeException("The MediaHighway Channel Info Entry message is short"));
}
}
/// <summary>
/// Parse the MHW2 descriptor.
/// </summary>
/// <param name="byteData">The MPEG2 section containing the entry.</param>
/// <param name="index">Index of the first byte in the MPEG2 section of the entry.</param>
/// <param name="nameLengthIndex">Index of the first byte channel name in the MPEG2 section of the entry.</param>
internal void Process(byte[] byteData, int index, int nameLengthIndex)
{
lastIndex = index;
nameIndex = nameLengthIndex;
try
{
originalNetworkID = Utils.Convert2BytesToInt(byteData, lastIndex);
lastIndex += 2;
transportStreamID = Utils.Convert2BytesToInt(byteData, lastIndex);
lastIndex += 2;
serviceID = Utils.Convert2BytesToInt(byteData, lastIndex);
lastIndex += 2;
unknown = Utils.GetBytes(byteData, lastIndex, 2);
lastIndex += unknown.Length;
int nameLength = (int)byteData[nameIndex] & 0x3f;
nameIndex++;
name = Utils.GetString(byteData, nameIndex, nameLength, ReplaceMode.TransferUnchanged).Trim();
nameIndex += nameLength;
length = lastIndex - index;
Validate();
}
catch (IndexOutOfRangeException)
{
throw (new ArgumentOutOfRangeException("The MediaHighway Channel Info Entry message is short"));
}
catch (NotImplementedException e)
{
throw e;
}
}
/// <summary>
/// Validate the entry fields.
/// </summary>
/// <exception cref="ArgumentOutOfRangeException">
/// A descriptor field is not valid.
/// </exception>
internal void Validate() { }
/// <summary>
/// Log the entry fields.
/// </summary>
internal void LogMessage()
{
if (Logger.ProtocolLogger == null)
return;
string unknownString;
if (unknown == null)
unknownString = "n/a";
else
unknownString = Utils.ConvertToHex(unknown);
Logger.ProtocolLogger.Write(Logger.ProtocolIndent + "MHW CHANNEL INFO ENTRY: ONID: " + originalNetworkID +
" TSID: " + transportStreamID +
" SID: " + serviceID +
" Name: " + name +
" Unknown: " + unknownString);
}
}
}

View File

@ -0,0 +1,134 @@
//////////////////////////////////////////////////////////////////////////////////
// //
// 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
{
/// <summary>
/// The class that describes a basic MPEG2 section header.
/// </summary>
public class Mpeg2BasicHeader
{
/// <summary>
/// Get the table identification.
/// </summary>
public int TableID { get { return (tableID); } }
/// <summary>
/// Get the section length.
/// </summary>
public int SectionLength { get { return (sectionLength); } }
/// <summary>
/// Return true if the section is private data; false otherwise.
/// </summary>
public bool PrivateIndicator { get { return (privateIndicator); } }
/// <summary>
/// Return true if the sysntax indicator is set; false otherwise.
/// </summary>
public bool SyntaxIndicator { get { return (syntaxIndicator); } }
/// <summary>
/// Get the index of the next byte in the MPEG2 section following the header.
/// </summary>
/// <exception cref="InvalidOperationException">
/// The header has not been processed.
/// </exception>
public virtual int Index
{
get
{
if (lastIndex == -1)
throw (new InvalidOperationException("Mpeg2BasicHeader: Index requested before block processed"));
return (lastIndex);
}
}
private int tableID;
private int sectionLength;
private bool privateIndicator;
private bool syntaxIndicator;
private int lastIndex = -1;
private int dataLength;
// MPEG basic header layout is as follows
//
// tableID byte uimsbf
//
// section syntax ind 1 bit bslbf
// private indicator 1 bit bslbf
// reserved 2 bits bslbf
// section length 12 bits uimsbf
/// <summary>
/// Initialize a new instance of the Mpeg2BasicHeader class.
/// </summary>
public Mpeg2BasicHeader() { }
/// <summary>
/// Parse the header.
/// </summary>
/// <param name="byteData">The MPEG2 section containing the header.</param>
public virtual void Process(byte[] byteData)
{
lastIndex = 0;
dataLength = byteData.Length;
try
{
tableID = (int)byteData[lastIndex];
lastIndex++;
syntaxIndicator = (byteData[lastIndex] & 0x80) != 0;
privateIndicator = (byteData[lastIndex] & 0x40) != 0;
sectionLength = ((byteData[lastIndex] & 0x0f) * 256) + (int)byteData[lastIndex + 1];
lastIndex += 2;
Validate();
}
catch (IndexOutOfRangeException)
{
throw (new ArgumentOutOfRangeException("MPEG2 basic header is short"));
}
}
/// <summary>
/// Validate the header fields.
/// </summary>
/// <exception cref="ArgumentOutOfRangeException">
/// A header field is not valid.
/// </exception>
public virtual void Validate()
{
if (dataLength > 4096)
throw (new ArgumentOutOfRangeException("MPEG2 Section data length wrong"));
if (sectionLength < 1 || sectionLength > dataLength - 3)
throw (new ArgumentOutOfRangeException("MPEG2 Section length wrong"));
}
/// <summary>
/// Log the header fields.
/// </summary>
public virtual void LogMessage() { }
}
}

View File

@ -1,8 +1,11 @@
using System;
using DomainObjects;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using skyscraper5.Skyscraper.Text;
namespace DVBServices
{
@ -63,5 +66,77 @@ namespace DVBServices
return ((char)('a' + (value - 10)));
}
/// <summary>
/// Get subset of bytes from an array.
/// </summary>
/// <param name="byteData">The array of bytes.</param>
/// <param name="offset">The index of the first byte of the subset.</param>
/// <param name="length">The length of the subset.</param>
/// <returns>The subset of bytes.</returns>
public static byte[] GetBytes(byte[] byteData, int offset, int length)
{
if (length < 1)
throw (new ArgumentOutOfRangeException("GetBytes length wrong"));
try
{
byte[] outputBytes = new byte[length];
for (int index = 0; index < length; index++)
outputBytes[index] = byteData[offset + index];
return (outputBytes);
}
catch (OutOfMemoryException)
{
throw (new ArgumentOutOfRangeException("GetBytes length wrong"));
}
}
/// <summary>
/// Convert 2 bytes to an integer with the most significant byte first.
/// </summary>
/// <param name="byteData">The byte array containing the byes to convert.</param>
/// <param name="index">The index of the first byte in the array.</param>
/// <returns>The converted value.</returns>
public static int Convert2BytesToInt(byte[] byteData, int index)
{
return (Convert2BytesToInt(byteData, index, 0xff));
}
/// <summary>
/// Convert 2 bytes to an integer with the most significant byte first and a mask.
/// </summary>
/// <param name="byteData">The byte array containing the byes to convert.</param>
/// <param name="index">The index of the first byte in the array.</param>
/// <param name="mask">The mask for the most significant byte.</param>
/// <returns>The converted value.</returns>
public static int Convert2BytesToInt(byte[] byteData, int index, byte mask)
{
return (((byteData[index] & mask) * 256) + (int)byteData[index + 1]);
}
private static En300468AnnexATextDecoder _encodingProvider;
/// <summary>
/// Convert a subset of an array of text bytes to a string.
/// </summary>
/// <param name="byteData">The array of bytes.</param>
/// <param name="offset">The index of the first byte to be converted.</param>
/// <param name="length">The number of bytes to be converted.</param>
/// <param name="replaceMode">Action to be taken for non-Ascii bytes.</param>
/// <returns>The converted string.</returns>
public static string GetString(byte[] byteData, int offset, int length, ReplaceMode replaceMode)
{
if (_encodingProvider == null)
{
_encodingProvider = En300468AnnexATextDecoder.GetInstance();
}
string temp = _encodingProvider.Decode(byteData, offset, length);
if (temp == null)
temp = "";
return temp;
}
}
}

View File

@ -20,6 +20,14 @@ namespace DomainObjects
}
}
public static string ProtocolIndent
{
get
{
return new string(' ', indentation);
}
}
private class EPGCollector
{
@ -47,5 +55,90 @@ namespace DomainObjects
Write("============================ " + identity + " ==============================");
Write("");
}
private static int indentation;
public static void IncrementProtocolIndent()
{
indentation++;
}
public static void DecrementProtocolIndent()
{
indentation--;
if (indentation <= -1)
{
indentation = 0;
}
}
private class EPGCollectorProtocolLogger
{
}
private static Logger _protocolLogger;
public static Logger ProtocolLogger
{
get
{
if (_protocolLogger == null)
{
_protocolLogger = new Logger();
_protocolLogger._realLogger = PluginLogManager.GetLogger(typeof(EPGCollectorProtocolLogger));
}
return _protocolLogger;
}
}
public void Dump(string identity, byte[] buffer, int length)
{
Dump(identity, buffer, 0, length);
}
public void Dump(string identity, byte[] buffer, int offset, int length)
{
Write("============================ " + identity + " starting ==============================");
StringBuilder loggerLineHex = new StringBuilder("0000 ");
StringBuilder loggerLineChar = new StringBuilder();
int lineIndex = 0;
for (int index = 0; index < length; index++)
{
if (lineIndex == 16)
{
Write(loggerLineHex.ToString() + " " + loggerLineChar.ToString());
loggerLineHex.Remove(0, loggerLineHex.Length);
loggerLineChar.Remove(0, loggerLineChar.Length);
loggerLineHex.Append(index.ToString("0000 "));
lineIndex = 0;
}
loggerLineHex.Append(getHex(buffer[index + offset] >> 4));
loggerLineHex.Append(getHex(buffer[index + offset] & 0x0f));
loggerLineHex.Append(' ');
if (buffer[index + offset] > ' ' - 1 && buffer[index + offset] < 0x7f)
loggerLineChar.Append((char)buffer[index + offset]);
else
loggerLineChar.Append('.');
lineIndex++;
}
if (loggerLineHex.Length != 0)
Write(loggerLineHex.ToString() + " " + loggerLineChar.ToString());
Write("============================ " + identity + " ending ==============================");
}
private char getHex(int dataChar)
{
if (dataChar < 10)
return ((char)('0' + dataChar));
return ((char)('a' + dataChar - 10));
}
}
}

View File

@ -9,14 +9,16 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using skyscraper5.Skyscraper.Scraper.Utils;
namespace skyscraper8.EPGCollectorPort.SkyscraperSide
namespace skyscraper8.EPGCollectorPort.SkyscraperSide.Freesat
{
[SkyscraperPlugin]
internal class FreesatBatSdtContestant : Contestant
{
public FreesatBatSdtContestant(int pid) : base("Freesat BAT/SDT", pid)
{
PacketProcessor = new PacketDiscarder();
}
public override void Dispose()
@ -29,7 +31,7 @@ namespace skyscraper8.EPGCollectorPort.SkyscraperSide
if (freesatScraper == null)
{
freesatScraper = new FreesatTunnelScraper();
freesatScraper.ConnectToStorage(skyscraperContext.DataStorage.GetPluginConnector());
freesatScraper.ConnectToStorage(skyscraperContext.DataStorage.GetPluginConnector(), skyscraperContext.OnPluginEvent);
skyscraperContext.PluginContext.Add(freesatScraper);
}

View File

@ -4,13 +4,14 @@ using skyscraper5.Mpeg2.Descriptors;
using skyscraper5.Skyscraper.Plugins;
using skyscraper5.Skyscraper.Scraper;
using skyscraper5.Skyscraper.Scraper.StreamAutodetection;
using skyscraper5.Skyscraper.Scraper.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace skyscraper8.EPGCollectorPort.SkyscraperSide
namespace skyscraper8.EPGCollectorPort.SkyscraperSide.Freesat
{
[SkyscraperPlugin]
internal class FreesatEpgContestant : Contestant
@ -18,6 +19,7 @@ namespace skyscraper8.EPGCollectorPort.SkyscraperSide
public FreesatEpgContestant(int pid)
: base("Freesat EPG", pid)
{
PacketProcessor = new PacketDiscarder();
}
public override void Dispose()
@ -30,7 +32,7 @@ namespace skyscraper8.EPGCollectorPort.SkyscraperSide
if (freesatScraper == null)
{
freesatScraper = new FreesatTunnelScraper();
freesatScraper.ConnectToStorage(skyscraperContext.DataStorage.GetPluginConnector());
freesatScraper.ConnectToStorage(skyscraperContext.DataStorage.GetPluginConnector(), skyscraperContext.OnPluginEvent);
skyscraperContext.PluginContext.Add(freesatScraper);
}

View File

@ -4,13 +4,14 @@ using skyscraper5.Mpeg2.Descriptors;
using skyscraper5.Skyscraper.Plugins;
using skyscraper5.Skyscraper.Scraper;
using skyscraper5.Skyscraper.Scraper.StreamAutodetection;
using skyscraper5.Skyscraper.Scraper.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace skyscraper8.EPGCollectorPort.SkyscraperSide
namespace skyscraper8.EPGCollectorPort.SkyscraperSide.Freesat
{
[SkyscraperPlugin]
internal class FreesatNitContestant : Contestant
@ -18,6 +19,7 @@ namespace skyscraper8.EPGCollectorPort.SkyscraperSide
public FreesatNitContestant(int pid)
: base("Freesat Network Information", pid)
{
PacketProcessor = new PacketDiscarder();
}
public override void Dispose()
@ -30,7 +32,7 @@ namespace skyscraper8.EPGCollectorPort.SkyscraperSide
if (freesatScraper == null)
{
freesatScraper = new FreesatTunnelScraper();
freesatScraper.ConnectToStorage(skyscraperContext.DataStorage.GetPluginConnector());
freesatScraper.ConnectToStorage(skyscraperContext.DataStorage.GetPluginConnector(), skyscraperContext.OnPluginEvent);
skyscraperContext.PluginContext.Add(freesatScraper);
}

View File

@ -1,16 +1,17 @@
using skyscraper5.Mpeg2.Descriptors;
using skyscraper5.Dvb.Psi;
using skyscraper5.Mpeg2;
using skyscraper5.Mpeg2.Descriptors;
using skyscraper5.Skyscraper.Plugins;
using skyscraper5.Skyscraper.Scraper;
using skyscraper5.Skyscraper.Scraper.StreamAutodetection;
using skyscraper5.Skyscraper.Scraper.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using skyscraper5.Dvb.Psi;
using skyscraper5.Mpeg2;
using skyscraper5.Skyscraper.Plugins;
namespace skyscraper8.EPGCollectorPort.SkyscraperSide
namespace skyscraper8.EPGCollectorPort.SkyscraperSide.Freesat
{
[SkyscraperPlugin]
internal class FreesatTdtContestant : Contestant
@ -18,6 +19,7 @@ namespace skyscraper8.EPGCollectorPort.SkyscraperSide
public FreesatTdtContestant(int pid)
: base("Freesat Timestamp", pid)
{
PacketProcessor = new PacketDiscarder();
}
public override void Dispose()
@ -30,7 +32,7 @@ namespace skyscraper8.EPGCollectorPort.SkyscraperSide
if (freesatScraper == null)
{
freesatScraper = new FreesatTunnelScraper();
freesatScraper.ConnectToStorage(skyscraperContext.DataStorage.GetPluginConnector());
freesatScraper.ConnectToStorage(skyscraperContext.DataStorage.GetPluginConnector(), skyscraperContext.OnPluginEvent);
skyscraperContext.PluginContext.Add(freesatScraper);
}

View File

@ -8,7 +8,7 @@ using skyscraper5.Skyscraper.Plugins;
using skyscraper8.EPGCollectorPort.Properties;
using skyscraper8.Skyscraper.Text;
namespace skyscraper8.EPGCollectorPort.SkyscraperSide
namespace skyscraper8.EPGCollectorPort.SkyscraperSide.Freesat
{
[SkyscraperPlugin]
[EncodingTypeId(0x01)]

View File

@ -3,11 +3,12 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using skyscraper5.Data.PostgreSql;
using skyscraper5.Dvb.Psi.Model;
using skyscraper5.Ietf.Rfc971;
using skyscraper5.Skyscraper.Scraper.Storage.InMemory;
namespace skyscraper8.EPGCollectorPort.SkyscraperSide
namespace skyscraper8.EPGCollectorPort.SkyscraperSide.Freesat
{
internal interface IFreesatTunnelDataStorage
{
@ -28,6 +29,9 @@ namespace skyscraper8.EPGCollectorPort.SkyscraperSide
case InMemoryPluginToken t1:
_storageEngine = new FreesatTunnelDataStorageInMemory();
break;
case PostgresqlToken t2:
_storageEngine = new FreesatTunnelDataStoragePostgresql(t2);
break;
default:
throw new NotImplementedException(o.GetType().FullName);
}

View File

@ -0,0 +1,201 @@
using Npgsql;
using NpgsqlTypes;
using skyscraper5.Data.PostgreSql;
using skyscraper5.Dvb.Psi.Model;
using skyscraper5.Skyscraper.Scraper.Storage.Utilities;
using System.Data.Common;
using System.Text;
using Newtonsoft.Json;
namespace skyscraper8.EPGCollectorPort.SkyscraperSide.Freesat;
class FreesatTunnelDataStoragePostgresql : IFreesatTunnelDataStorage
{
private readonly PostgresqlToken token;
public FreesatTunnelDataStoragePostgresql(PostgresqlToken t2)
{
token = t2;
}
private HashSet<DatabaseKeyEitEvent> knownEitEvents;
public bool StoreEitEvent(EitEvent eitEvent)
{
if (knownEitEvents == null)
knownEitEvents = new HashSet<DatabaseKeyEitEvent>();
DatabaseKeyEitEvent key = new DatabaseKeyEitEvent(eitEvent.ServiceId, eitEvent.TransportStreamId, eitEvent.OriginalNetworkId, eitEvent.EventId, eitEvent.StartTime);
if (knownEitEvents.Contains(key))
return false;
if (TestForEitEvent(key))
{
knownEitEvents.Add(key);
return false;
}
token.EnqueueTask(x => WriteEitEvent(x, eitEvent));
knownEitEvents.Add(key);
return true;
}
private void WriteEitEvent(NpgsqlConnection connection, EitEvent eitEvent)
{
DatabaseKeyEitEvent key = new DatabaseKeyEitEvent(eitEvent.ServiceId, eitEvent.TransportStreamId, eitEvent.OriginalNetworkId, eitEvent.EventId, eitEvent.StartTime);
if (TestForEitEventEx(key, connection))
{
return;
}
NpgsqlCommand command = connection.CreateCommand();
command.CommandText =
"INSERT INTO freesat_eit " +
"(sid, tsid, onid, event_id, start_time, event) " +
"VALUES " +
"(@sid, @tsid, @onid, @event_id, @start_time, @event)";
command.Parameters.AddWithValue("@sid", NpgsqlDbType.Integer, (int)eitEvent.ServiceId);
command.Parameters.AddWithValue("@tsid", NpgsqlDbType.Integer, (int)eitEvent.TransportStreamId);
command.Parameters.AddWithValue("@onid", NpgsqlDbType.Integer, (int)eitEvent.OriginalNetworkId);
command.Parameters.AddWithValue("@event_id", NpgsqlDbType.Integer, (int)eitEvent.EventId);
command.Parameters.AddWithValue("@start_time", NpgsqlDbType.Timestamp, eitEvent.StartTime);
command.Parameters.AddWithValue("@event", NpgsqlDbType.Json, JsonConvert.SerializeObject(eitEvent));
int executeNonQuery = command.ExecuteNonQuery();
if (executeNonQuery != 1)
throw new NotImplementedException(string.Format("Wanted to insert 1 line, but it was {0}", executeNonQuery));
}
private bool TestForEitEvent(DatabaseKeyEitEvent eitEvent)
{
using (NpgsqlConnection conn = token.GetConnection())
{
conn.Open();
return TestForEitEventEx(eitEvent, conn);
}
}
private static bool TestForEitEventEx(DatabaseKeyEitEvent eitEvent, NpgsqlConnection conn)
{
NpgsqlCommand command = conn.CreateCommand();
command.CommandText =
"SELECT dateadded FROM freesat_eit WHERE sid = @sid AND tsid = @tsid AND onid = @onid AND event_id = @event_id AND start_time = @start_time";
command.Parameters.AddWithValue("@sid", NpgsqlDbType.Integer, (int)eitEvent.ServiceId);
command.Parameters.AddWithValue("@tsid", NpgsqlDbType.Integer, (int)eitEvent.TransportStreamId);
command.Parameters.AddWithValue("@onid", NpgsqlDbType.Integer, (int)eitEvent.OriginalNetworkId);
command.Parameters.AddWithValue("@event_id", NpgsqlDbType.Integer, (int)eitEvent.EventId);
command.Parameters.AddWithValue("@start_time", NpgsqlDbType.Timestamp, eitEvent.StartTime);
NpgsqlDataReader dataReader = command.ExecuteReader();
bool result = dataReader.Read();
dataReader.Close();
return result;
}
private HashSet<DatabaseKeyNitNetwork> _knownUpdatedNitNetworks;
public void StoreNitNetwork(NitNetwork nitNetwork)
{
if (_knownUpdatedNitNetworks == null)
_knownUpdatedNitNetworks = new HashSet<DatabaseKeyNitNetwork>();
DatabaseKeyNitNetwork key = new DatabaseKeyNitNetwork(nitNetwork.NetworkId);
token.EnqueueTask(x => WriteNit(x, nitNetwork));
knownNitNetworks.Add(key);
_knownUpdatedNitNetworks.Add(key);
}
private void WriteNit(NpgsqlConnection connection, NitNetwork nitNetwork)
{
NpgsqlCommand command = connection.CreateCommand();
command.CommandText =
"INSERT INTO freesat_nit (id, network) " +
"VALUES (@id, @network)";
command.Parameters.AddWithValue("@id", NpgsqlDbType.Integer, (int)nitNetwork.NetworkId);
command.Parameters.AddWithValue("@network", NpgsqlDbType.Json, JsonConvert.SerializeObject(nitNetwork));
command.ExecuteNonQuery();
}
private HashSet<DatabaseKeyNitNetwork> knownNitNetworks;
public bool TestNitNetwork(NitNetwork nitNetwork)
{
if (knownNitNetworks == null)
knownNitNetworks = new HashSet<DatabaseKeyNitNetwork>();
DatabaseKeyNitNetwork key = new DatabaseKeyNitNetwork(nitNetwork.NetworkId);
if (knownNitNetworks.Contains(key))
return true;
bool result = false;
using (NpgsqlConnection conn = token.GetConnection())
{
conn.Open();
NpgsqlCommand command = conn.CreateCommand();
command.CommandText = "SELECT dateadded FROM freesat_nit WHERE id = @id";
command.Parameters.AddWithValue("@id", NpgsqlDbType.Integer, (int)nitNetwork.NetworkId);
NpgsqlDataReader dataReader = command.ExecuteReader();
result = dataReader.Read();
dataReader.Close();
dataReader.Dispose();
command.Dispose();
conn.Close();
}
if (result)
knownNitNetworks.Add(key);
return result;
}
private HashSet<DatabaseKeyNitTs> _knownNitTs;
public bool TestForNitTransportStream(ushort networkId, NitTransportStream transportStream)
{
if (_knownNitTs == null)
_knownNitTs = new HashSet<DatabaseKeyNitTs>();
DatabaseKeyNitTs key = new DatabaseKeyNitTs(networkId, transportStream.TransportStreamId);
if (_knownNitTs.Contains(key))
return true;
bool result = false;
using (NpgsqlConnection conn = token.GetConnection())
{
conn.Open();
NpgsqlCommand command = conn.CreateCommand();
command.CommandText =
"SELECT dateadded FROM freesat_nit_transport_stream WHERE nid = @nid AND tsid = @tsid";
command.Parameters.AddWithValue("@nid", NpgsqlDbType.Integer, (int)networkId);
command.Parameters.AddWithValue("@tsid", NpgsqlDbType.Integer, (int)transportStream.TransportStreamId);
NpgsqlDataReader dataReader = command.ExecuteReader();
result = dataReader.Read();
dataReader.Close();
command.Dispose();
}
if (result)
_knownNitTs.Add(key);
return result;
}
private HashSet<DatabaseKeyNitTs> _knownUpdatedNitTransportStream;
public void StoreNitTransportStream(ushort networkId, NitTransportStream transportStream)
{
token.EnqueueTask(x => WriteNitTransportStream(x, networkId, transportStream));
DatabaseKeyNitTs ts = new DatabaseKeyNitTs(networkId, transportStream.TransportStreamId);
_knownNitTs.Add(ts);
if (_knownUpdatedNitTransportStream == null)
return;
_knownUpdatedNitTransportStream.Add(ts);
}
private void WriteNitTransportStream(NpgsqlConnection conn, ushort networkId, NitTransportStream transportStream)
{
/*if (TestForNitTransportStream(networkId, transportStream))
{
return;
}*/
NpgsqlCommand command = conn.CreateCommand();
command.CommandText =
"insert into freesat_nit_transport_stream (nid, tsid, transport_stream) " +
"values " +
"(@nid,@tsid,@transport_stream);";
command.Parameters.AddWithValue("@nid", NpgsqlDbType.Integer, (int)networkId);
command.Parameters.AddWithValue("@tsid", NpgsqlDbType.Integer, (int)transportStream.TransportStreamId);
command.Parameters.AddWithValue("@transport_stream", NpgsqlDbType.Json, JsonConvert.SerializeObject(transportStream));
command.ExecuteNonQuery();
}
}

View File

@ -1,7 +1,6 @@
using skyscraper5.Dvb.Descriptors;
using skyscraper5.Dvb.Psi;
using skyscraper5.Dvb.Psi.Model;
using skyscraper8.EPGCollectorPort.SkyscraperSide;
using skyscraper8.Skyscraper.Scraper.Storage;
using System;
using System.Collections.Generic;
@ -10,7 +9,7 @@ using System.Text;
using System.Threading.Tasks;
using skyscraper8.Skyscraper.Plugins;
namespace skyscraper5.Skyscraper.Scraper
namespace skyscraper8.EPGCollectorPort.SkyscraperSide.Freesat
{
internal class FreesatTunnelScraper : IBatEventHandler, ISdtEventHandler, ITdtEventHandler, ITotEventHandler, IEitEventHandler, INitEventHandler
{
@ -72,7 +71,8 @@ namespace skyscraper5.Skyscraper.Scraper
{
_logger.Log(PluginLogLevel.Info, "NIT Network {0} Transport Stream {1}", networkId, transportStream.TransportStreamId);
_dataStorage.StoreNitTransportStream(networkId, transportStream);
}
_hostNotification.Invoke();
}
}
public void OnNitNetwork(NitNetwork nitNetwork)
@ -81,7 +81,8 @@ namespace skyscraper5.Skyscraper.Scraper
{
_logger.Log(PluginLogLevel.Info, "NIT Network {0}", nitNetwork.NetworkId);
_dataStorage.StoreNitNetwork(nitNetwork);
}
_hostNotification.Invoke();
}
}
private EitEvent[] runningEvents;
@ -90,26 +91,25 @@ namespace skyscraper5.Skyscraper.Scraper
if (_dataStorage.StoreEitEvent(eitEvent))
{
_logger.Log(PluginLogLevel.Info, "EIT Event: {0}", eitEvent.EventName);
_hostNotification.Invoke();
}
if (eitEvent.RunningStatus == RunningStatus.Running)
{
if (runningEvents == null)
runningEvents = new EitEvent[UInt16.MaxValue];
runningEvents = new EitEvent[ushort.MaxValue];
runningEvents[eitEvent.ServiceId] = eitEvent;
}
}
private PluginLogger _logger;
private IFreesatTunnelDataStorage _dataStorage;
public void ConnectToStorage(object[] getPluginConnector)
private Action _hostNotification;
public void ConnectToStorage(object[] getPluginConnector, Action onPluginEvent)
{
if (_dataStorage == null)
{
_dataStorage = new FreesatTunnelDataStorage(getPluginConnector);
}
_logger = PluginLogManager.GetLogger(this.GetType());
_dataStorage = new FreesatTunnelDataStorage(getPluginConnector);
_logger = PluginLogManager.GetLogger(GetType());
_hostNotification = onPluginEvent;
}
}
}

View File

@ -0,0 +1,95 @@
using DVBServices;
using skyscraper5.Mpeg2;
using skyscraper5.Skyscraper.Plugins;
using skyscraper5.Skyscraper.Scraper;
using skyscraper5.Skyscraper.Scraper.StreamAutodetection;
using skyscraper8.Skyscraper.Plugins;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace skyscraper8.EPGCollectorPort.SkyscraperSide.MediaHighway2
{
//Look for PIDs 0x231, 0x234, and 0x236
[SkyscraperPlugin]
[Experimental]
class Mhw2Contestant : Contestant, Mhw2EventHandler
{
public Mhw2Contestant(int pid) : base("MediaHighway 2", pid)
{
PacketProcessor = new PsiDecoder(pid, new Mhw2Parser(this));
if (_logger == null)
{
_logger = PluginLogManager.GetLogger(GetType());
}
}
public override void Dispose()
{
}
public override void DeclareWinner(SkyscraperContext skyscraperContext, int pid, ProgramContext programContext)
{
Mhw2Scraper mhw2Scraper = skyscraperContext.PluginContext.FirstOrDefault(x => x is Mhw2Scraper) as Mhw2Scraper;
if (mhw2Scraper == null)
{
mhw2Scraper = new Mhw2Scraper(skyscraperContext);
skyscraperContext.PluginContext.Add(mhw2Scraper);
}
skyscraperContext.DvbContext.RegisterPacketProcessor(pid, new PsiDecoder(pid, new Mhw2Parser(mhw2Scraper)));
}
public override void Introduce(ProgramContext programContext)
{
}
private byte[] deadSections;
private static PluginLogger _logger;
public void OnNonMhw2Traffic(int sourcePid, int sectionTableId, byte mhwType)
{
if (deadSections == null)
deadSections = new byte[byte.MaxValue];
deadSections[sectionTableId]++;
if (deadSections[sectionTableId] == 1)
Score--;
}
public void OnMhw2Channels(int sourcePid, MediaHighway2ChannelSection channelSection)
{
if (channelSection == null)
return;
if (channelSection.Channels.Count > 0)
{
_logger.Log(PluginLogLevel.Info, "Found MHW2 Channel Section in PID {0}", sourcePid);
Score += channelSection.Channels.Count;
}
}
public void OnMhw2Categories(int sourcePid, MediaHighway2CategorySection categorySection)
{
_logger.Log(PluginLogLevel.Info, "Found MHW2 Category Section in PID {0}", sourcePid);
Score += categorySection.Categories.Count;
}
public void OnMhw2Titles(int sourcePid, MediaHighway2TitleSection titleSection)
{
_logger.Log(PluginLogLevel.Info, "Found MHW2 Title Section in PID {0}", sourcePid);
Score++;
}
public void OnMhw2Summary(int sourcePid, MediaHighway2SummarySection summarySection)
{
_logger.Log(PluginLogLevel.Info, "Found MHW2 Summary Section in PID {0}", sourcePid);
Score++;
}
}
}

View File

@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace skyscraper8.EPGCollectorPort.SkyscraperSide.MediaHighway2
{
internal class Mhw2EventBinder
{
public int EventId { get; }
public Mhw2EventBinder(int eventId)
{
EventId = eventId;
}
public string EventName { get; set; }
public int CategoryID { get; set; }
public int ChannelID { get; set; }
public TimeSpan Duration { get; set; }
public int MainCategory { get; set; }
public DateTime StartTime { get; set; }
public int SubCategory { get; set; }
public string ShortDescription { get; set; }
public bool ShortDescriptionSeen { get; set; }
public bool TitleDataSeen { get; set; }
}
}

View File

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using DVBServices;
namespace skyscraper8.EPGCollectorPort.SkyscraperSide.MediaHighway2
{
internal interface Mhw2EventHandler
{
void OnNonMhw2Traffic(int sourcePid, int sectionTableId, byte trafficType);
void OnMhw2Channels(int sourcePid, MediaHighway2ChannelSection channelSection);
void OnMhw2Categories(int sourcePid, MediaHighway2CategorySection categorySection);
void OnMhw2Titles(int sourcePid, MediaHighway2TitleSection titleSection);
void OnMhw2Summary(int sourcePid, MediaHighway2SummarySection summarySection);
}
}

View File

@ -0,0 +1,95 @@
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.MediaHighway2
{
internal class Mhw2Parser : IPsiProcessor
{
private Mhw2EventHandler eventHandler;
public Mhw2Parser(Mhw2EventHandler eventHandler)
{
this.eventHandler = eventHandler;
}
public void GatherPsi(PsiSection section, int sourcePid)
{
if (section.TableId == 0xc8)
{
byte[] bytes = section.GetData();
switch (bytes[3])
{
case 0:
try
{
MediaHighway2ChannelSection channelSection = MediaHighway2ChannelSection.ProcessMediaHighwayChannelTable(bytes);
eventHandler.OnMhw2Channels(sourcePid, channelSection);
}
catch (NotImplementedException e)
{
Console.WriteLine(e);
throw;
}
return;
case 1:
try
{
MediaHighway2CategorySection categorySection = MediaHighway2CategorySection.ProcessMediaHighwayCategoryTable(bytes);
eventHandler.OnMhw2Categories(sourcePid, categorySection);
}
catch (NotImplementedException e)
{
Console.WriteLine(e);
throw;
}
return;
case 2:
//Unknown
break;
case 3:
//Unknown
break;
case 4:
//Channel Name String Table, uninteresting.
return;
default:
eventHandler.OnNonMhw2Traffic(sourcePid, section.TableId, bytes[3]);
return;
}
}
else if (section.TableId == 0xe6)
{
byte[] bytes = section.GetData();
try
{
MediaHighway2TitleSection titleSection = MediaHighway2TitleSection.ProcessMediaHighwayTitleTable(bytes);
if (titleSection != null)
{
eventHandler.OnMhw2Titles(sourcePid, titleSection);
}
}
catch (Exception e)
{
}
return;
}
else if (section.TableId == 0x96)
{
byte[] bytes = section.GetData();
MediaHighway2SummarySection summarySection = MediaHighway2SummarySection.ProcessMediaHighwaySummaryTable(bytes);
eventHandler.OnMhw2Summary(sourcePid,summarySection);
}
else
{
eventHandler.OnNonMhw2Traffic(sourcePid, section.TableId, section.GetData()[3]);
}
}
}
}

View File

@ -0,0 +1,153 @@
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.MediaHighway2
{
internal class Mhw2Scraper : Mhw2EventHandler
{
private readonly SkyscraperContext _skyscraperContext;
private readonly IMhw2Storage storage;
public Mhw2Scraper(SkyscraperContext skyscraperContext)
{
_skyscraperContext = skyscraperContext;
storage = new Mhw2StorageImpl(skyscraperContext.DataStorage.GetPluginConnector());
_logger = PluginLogManager.GetLogger(GetType());
}
public void OnNonMhw2Traffic(int sourcePid, int sectionTableId, byte mhwType)
{
}
private bool hasChannels;
public void OnMhw2Channels(int sourcePid, MediaHighway2ChannelSection channelSection)
{
if (hasChannels)
return;
if (channelSection == null)
return;
if (!_skyscraperContext.CurrentNetworkId.HasValue)
return;
if (!_skyscraperContext.CurrentTransportStreamId.HasValue)
return;
storage.InsertChannels(_skyscraperContext.CurrentNetworkId.Value, _skyscraperContext.CurrentTransportStreamId.Value, channelSection);
_logger.Log(PluginLogLevel.Info, "Found {0} channels", channelSection.Channels.Count);
hasChannels = true;
}
private bool hasCategories;
public void OnMhw2Categories(int sourcePid, MediaHighway2CategorySection categorySection)
{
if (hasCategories)
return;
if (!_skyscraperContext.CurrentNetworkId.HasValue)
return;
if (!_skyscraperContext.CurrentTransportStreamId.HasValue)
return;
storage.InsertCategories(_skyscraperContext.CurrentNetworkId.Value, _skyscraperContext.CurrentTransportStreamId.Value, categorySection);
_logger.Log(PluginLogLevel.Info, "Found {0} categories", categorySection.Categories.Count);
hasCategories = true;
}
public void OnMhw2Titles(int sourcePid, MediaHighway2TitleSection titleSection)
{
if (_binders == null)
_binders = new Dictionary<int, Mhw2EventBinder>();
foreach (MediaHighway2TitleData titleData in titleSection.Titles)
{
Mhw2EventBinder currentBinder;
if (_binders.ContainsKey(titleData.EventID))
{
currentBinder = _binders[titleData.EventID];
}
else
{
currentBinder = new Mhw2EventBinder(titleData.EventID);
_binders.Add(titleData.EventID, currentBinder);
}
if (!currentBinder.TitleDataSeen)
{
currentBinder.EventName = titleData.EventName;
currentBinder.CategoryID = titleData.CategoryID;
currentBinder.ChannelID = titleData.ChannelID;
currentBinder.Duration = titleData.Duration;
currentBinder.MainCategory = titleData.MainCategory;
currentBinder.StartTime = titleData.StartTime;
currentBinder.SubCategory = titleData.SubCategory;
currentBinder.TitleDataSeen = true;
}
if (IsBinderComplete(currentBinder))
{
StoreBinder(currentBinder);
_binders.Remove(titleData.EventID);
}
}
}
public void OnMhw2Summary(int sourcePid, MediaHighway2SummarySection summarySection)
{
if (_binders == null)
_binders = new Dictionary<int, Mhw2EventBinder>();
Mhw2EventBinder currentBinder;
if (_binders.ContainsKey(summarySection.SummaryData.EventID))
{
currentBinder = _binders[summarySection.SummaryData.EventID];
}
else
{
currentBinder = new Mhw2EventBinder(summarySection.SummaryData.EventID);
_binders.Add(summarySection.SummaryData.EventID, currentBinder);
}
currentBinder.ShortDescription = summarySection.SummaryData.ShortDescription;
currentBinder.ShortDescriptionSeen = true;
if (IsBinderComplete(currentBinder))
{
StoreBinder(currentBinder);
_binders.Remove(summarySection.SummaryData.EventID);
}
}
private Dictionary<int,Mhw2EventBinder> _binders;
private readonly PluginLogger _logger;
private bool IsBinderComplete(Mhw2EventBinder binder)
{
if (!_skyscraperContext.CurrentNetworkId.HasValue)
return false;
if (!_skyscraperContext.CurrentTransportStreamId.HasValue)
return false;
if (!hasCategories)
return false;
if (!hasChannels)
return false;
if (!binder.ShortDescriptionSeen)
return false;
if (!binder.TitleDataSeen)
return false;
return true;
}
private void StoreBinder(Mhw2EventBinder binder)
{
if (!storage.TestForEvent(_skyscraperContext.CurrentNetworkId.Value, _skyscraperContext.CurrentTransportStreamId.Value, binder))
{
_logger.Log(PluginLogLevel.Info, "Event: {0}", binder.EventName);
storage.InsertEvent(_skyscraperContext.CurrentNetworkId.Value, _skyscraperContext.CurrentTransportStreamId.Value, binder);
}
}
}
}

View File

@ -0,0 +1,297 @@
using DVBServices;
using skyscraper5.Data.PostgreSql;
using skyscraper5.Skyscraper.Scraper.Storage.InMemory;
using skyscraper8.EPGCollectorPort.SkyscraperSide.Freesat;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Npgsql;
using NpgsqlTypes;
namespace skyscraper8.EPGCollectorPort.SkyscraperSide.MediaHighway2
{
internal interface IMhw2Storage
{
void InsertCategories(int networkId, int transportStreamId, MediaHighway2CategorySection categorySection);
void InsertChannels(int networkId, int transportStreamId, MediaHighway2ChannelSection channelSection);
bool TestForEvent(int networkId, int transportStreamId, Mhw2EventBinder binder);
void InsertEvent(int networkId, int transportStreamId, Mhw2EventBinder binder);
}
internal class Mhw2StorageImpl : IMhw2Storage
{
private readonly IMhw2Storage _storageEngine;
public Mhw2StorageImpl(object[] getPluginConnector)
{
object o = getPluginConnector[0];
switch (o)
{
case InMemoryPluginToken t1:
_storageEngine = new Mhw2StorageInMemory();
break;
case PostgresqlToken t2:
_storageEngine = new Mhw2StoragePostgresql(t2);
break;
default:
throw new NotImplementedException(o.GetType().FullName);
}
}
public void InsertCategories(int networkId, int transportStreamId, MediaHighway2CategorySection categorySection)
{
_storageEngine.InsertCategories(networkId, transportStreamId, categorySection);
}
public void InsertChannels(int networkId, int transportStreamId, MediaHighway2ChannelSection channelSection)
{
_storageEngine.InsertChannels(networkId, transportStreamId, channelSection);
}
public bool TestForEvent(int networkId, int transportStreamId, Mhw2EventBinder binder)
{
return _storageEngine.TestForEvent(networkId, transportStreamId, binder);
}
public void InsertEvent(int networkId, int transportStreamId, Mhw2EventBinder binder)
{
_storageEngine.InsertEvent(networkId, transportStreamId, binder);
}
}
internal class Mhw2StorageInMemory : IMhw2Storage
{
private Dictionary<Tuple<int, int>, MediaHighway2CategorySection> _categories;
public void InsertCategories(int networkId, int transportStreamId, MediaHighway2CategorySection categorySection)
{
if (_categories == null)
_categories = new Dictionary<Tuple<int, int>, MediaHighway2CategorySection>();
Tuple<int, int> x = new Tuple<int, int>(networkId, transportStreamId);
if (!_categories.ContainsKey(x))
{
_categories.Add(x, categorySection);
}
}
private Dictionary<Tuple<int, int>, MediaHighway2ChannelSection> _channels;
public void InsertChannels(int networkId, int transportStreamId, MediaHighway2ChannelSection channelSection)
{
if (_channels == null)
_channels = new Dictionary<Tuple<int, int>, MediaHighway2ChannelSection>();
Tuple<int, int> x = new Tuple<int, int>(networkId, transportStreamId);
if (!_categories.ContainsKey(x))
{
_channels.Add(x, channelSection);
}
}
private Dictionary<Tuple<int, int, int, DateTime>, Mhw2EventBinder> _events;
public bool TestForEvent(int networkId, int transportStreamId, Mhw2EventBinder binder)
{
if (_events == null)
return false;
Tuple<int, int, int, DateTime> coordinates = new Tuple<int, int, int, DateTime>(networkId, transportStreamId, binder.EventId, binder.StartTime);
return _events.ContainsKey(coordinates);
}
public void InsertEvent(int networkId, int transportStreamId, Mhw2EventBinder binder)
{
if (_events == null)
_events = new Dictionary<Tuple<int, int, int, DateTime>, Mhw2EventBinder>();
Tuple<int, int, int, DateTime> coordinates = new Tuple<int, int, int, DateTime>(networkId, transportStreamId, binder.EventId, binder.StartTime);
if (!_events.ContainsKey(coordinates))
{
_events.Add(coordinates, binder);
}
}
}
internal class Mhw2StoragePostgresql : IMhw2Storage
{
private readonly PostgresqlToken _postgresqlToken;
public Mhw2StoragePostgresql(PostgresqlToken postgresqlToken)
{
_postgresqlToken = postgresqlToken;
}
private HashSet<Tuple<int, int, int>> _knownCategories;
public void InsertCategories(int networkId, int transportStreamId, MediaHighway2CategorySection categorySection)
{
if (_knownCategories == null)
_knownCategories = new HashSet<Tuple<int, int, int>>();
foreach (MediaHighwayCategoryEntry category in categorySection.Categories)
{
Tuple<int, int, int> coordinate = new Tuple<int, int, int>(networkId, transportStreamId, category.Number);
if (!_knownCategories.Contains(coordinate))
{
_postgresqlToken.EnqueueTask(x => InsertCategory(x,networkId, transportStreamId, category));
_knownCategories.Add(coordinate);
}
}
}
private HashSet<Tuple<int, int, int, int, int>> _knownChannels;
public void InsertChannels(int networkId, int transportStreamId, MediaHighway2ChannelSection channelSection)
{
if (channelSection == null)
return;
if (_knownChannels == null)
_knownChannels = new HashSet<Tuple<int, int, int, int, int>>();
foreach (MediaHighwayChannelInfoEntry channel in channelSection.Channels)
{
Tuple<int, int, int, int, int> coordinate = new Tuple<int, int, int, int, int>(networkId, transportStreamId, channel.OriginalNetworkID, channel.TransportStreamID, channel.ServiceID);
if (!_knownChannels.Contains(coordinate))
{
_postgresqlToken.EnqueueTask(x => InsertChannel(x, networkId, transportStreamId, channel));
_knownChannels.Add(coordinate);
}
}
}
private HashSet<Tuple<int, int, int, DateTime>> _knownEvents;
public bool TestForEvent(int networkId, int transportStreamId, Mhw2EventBinder binder)
{
if (_knownEvents == null)
_knownEvents = new HashSet<Tuple<int, int, int, DateTime>>();
Tuple<int, int, int, DateTime> coordinates = new Tuple<int, int, int, DateTime>(networkId, transportStreamId, binder.EventId, binder.StartTime);
if (_knownEvents.Contains(coordinates))
return true;
NpgsqlConnection connection = _postgresqlToken.GetConnection();
connection.Open();
NpgsqlCommand command = connection.CreateCommand();
command.CommandText = "SELECT dateadded FROM mediahighway2_events WHERE nid = @nid AND tsid = @tsid AND eid = @eid AND start_time = @stime";
command.Parameters.AddWithValue("@nid", NpgsqlDbType.Integer, networkId);
command.Parameters.AddWithValue("@tsid", NpgsqlDbType.Integer, transportStreamId);
command.Parameters.AddWithValue("@eid", NpgsqlDbType.Integer, binder.EventId);
command.Parameters.AddWithValue("@stime", NpgsqlDbType.Timestamp, binder.StartTime);
NpgsqlDataReader dataReader = command.ExecuteReader();
bool result = dataReader.Read();
if (result)
{
_knownEvents.Add(coordinates);
}
dataReader.Close();
command.Dispose();
connection.Close();
return result;
}
public void InsertEvent(int networkId, int transportStreamId, Mhw2EventBinder binder)
{
_postgresqlToken.EnqueueTask(x => InsertEventEx(x, networkId, transportStreamId, binder));
Tuple<int, int, int, DateTime> coordinates = new Tuple<int, int, int, DateTime>(networkId, transportStreamId, binder.EventId, binder.StartTime);
_knownEvents.Add(coordinates);
}
private void InsertEventEx(NpgsqlConnection connection, int networkId, int transportStreamId, Mhw2EventBinder binder)
{
NpgsqlCommand command = connection.CreateCommand();
command.CommandText =
"INSERT INTO mediahighway2_events VALUES (@nid,@tsid,@eid,@stime,DEFAULT,@ename,@category,@channel,@duration,@mcategory,@scategory,@description)";
command.Parameters.AddWithValue("@nid", NpgsqlDbType.Integer, networkId);
command.Parameters.AddWithValue("@tsid", NpgsqlDbType.Integer, transportStreamId);
command.Parameters.AddWithValue("@eid", NpgsqlDbType.Integer, binder.EventId);
command.Parameters.AddWithValue("@stime", NpgsqlDbType.Timestamp, binder.StartTime);
command.Parameters.AddWithValue("@ename", NpgsqlDbType.Text, SanitizeString(binder.EventName));
command.Parameters.AddWithValue("@category", NpgsqlDbType.Integer, binder.CategoryID);
command.Parameters.AddWithValue("@channel", NpgsqlDbType.Integer, binder.ChannelID);
command.Parameters.AddWithValue("@duration", NpgsqlDbType.Integer, Convert.ToInt32(binder.Duration.TotalSeconds));
command.Parameters.AddWithValue("@mcategory", NpgsqlDbType.Integer, binder.MainCategory);
command.Parameters.AddWithValue("@scategory", NpgsqlDbType.Integer, binder.SubCategory);
command.Parameters.AddWithValue("@description", NpgsqlDbType.Text, binder.ShortDescription);
command.ExecuteNonQuery();
}
private string SanitizeString(string ename)
{
if (ename.Contains("\0"))
{
int indexOf = ename.IndexOf("\0");
ename = ename.Substring(0, indexOf);
return ename;
}
return ename;
}
private void InsertChannel(NpgsqlConnection connection, int networkId, int transportStreamId, MediaHighwayChannelInfoEntry channel)
{
if (TestChannel(connection, networkId, transportStreamId, channel))
{
return;
}
NpgsqlCommand command = connection.CreateCommand();
command.CommandText = "INSERT INTO mediahighway2_channels VALUES (@snid,@stsid,@tonid,@ttsid,@tsid,@name,DEFAULT)";
command.Parameters.AddWithValue("@snid", NpgsqlDbType.Integer, networkId);
command.Parameters.AddWithValue("@stsid", NpgsqlDbType.Integer, transportStreamId);
command.Parameters.AddWithValue("@tonid", NpgsqlDbType.Integer, channel.OriginalNetworkID);
command.Parameters.AddWithValue("@ttsid", NpgsqlDbType.Integer, channel.TransportStreamID);
command.Parameters.AddWithValue("@tsid", NpgsqlDbType.Integer, channel.ServiceID);
command.Parameters.AddWithValue("@name", NpgsqlDbType.Text, channel.Name);
command.ExecuteNonQuery();
}
private bool TestChannel(NpgsqlConnection connection, int networkId, int transportStreamId, MediaHighwayChannelInfoEntry channel)
{
NpgsqlCommand command = connection.CreateCommand();
command.CommandText = "SELECT dateadded FROM mediahighway2_channels WHERE src_nid = @snid AND src_tsid = @stsid AND tgt_onid = @tonid AND tgt_tsid = @ttsid AND tgt_sid = @tsid";
command.Parameters.AddWithValue("@snid", NpgsqlDbType.Integer, networkId);
command.Parameters.AddWithValue("@stsid", NpgsqlDbType.Integer, transportStreamId);
command.Parameters.AddWithValue("@tonid", NpgsqlDbType.Integer, channel.OriginalNetworkID);
command.Parameters.AddWithValue("@ttsid", NpgsqlDbType.Integer, channel.TransportStreamID);
command.Parameters.AddWithValue("@tsid", NpgsqlDbType.Integer, channel.ServiceID);
NpgsqlDataReader dataReader = command.ExecuteReader();
bool result = dataReader.Read();
dataReader.Close();
command.Dispose();
return result;
}
private void InsertCategory(NpgsqlConnection connection, int networkId, int transportStreamId, MediaHighwayCategoryEntry category)
{
if (TestCategory(connection, networkId, transportStreamId, category))
{
return;
}
NpgsqlCommand command = connection.CreateCommand();
command.CommandText = "INSERT INTO mediahighway2_categories VALUES (@nid,@tsid,@number,@description,DEFAULT)";
command.Parameters.AddWithValue("@nid", NpgsqlDbType.Integer, networkId);
command.Parameters.AddWithValue("@tsid", NpgsqlDbType.Integer, transportStreamId);
command.Parameters.AddWithValue("@number", NpgsqlDbType.Integer, category.Number);
command.Parameters.AddWithValue("@description", NpgsqlDbType.Text, category.Description);
command.ExecuteNonQuery();
}
private bool TestCategory(NpgsqlConnection connection, int networkId, int transportStreamId, MediaHighwayCategoryEntry category)
{
NpgsqlCommand command = connection.CreateCommand();
command.CommandText = "SELECT dateadded FROM mediahighway2_categories WHERE nid = @nid AND tsid = @tsid AND number = @number";
command.Parameters.AddWithValue("@nid", NpgsqlDbType.Integer, networkId);
command.Parameters.AddWithValue("@tsid", NpgsqlDbType.Integer, transportStreamId);
command.Parameters.AddWithValue("@number", NpgsqlDbType.Integer, category.Number);
NpgsqlDataReader dataReader = command.ExecuteReader();
bool result = dataReader.Read();
dataReader.Close();
command.Dispose();
return result;
}
}
}

View File

@ -7,6 +7,7 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\DataTableStorages\skyscraper5.Data.PostgreSql\skyscraper5.Data.PostgreSql.csproj" />
<ProjectReference Include="..\..\skyscraper8\skyscraper8.csproj" />
</ItemGroup>

View File

@ -28,6 +28,7 @@ using skyscraper5.T2MI;
using skyscraper5.src.Mpeg2.PacketFilter;
using skyscraper8.Skyscraper.IO;
using log4net;
using skyscraper5.Skyscraper.Scraper.StreamAutodetection;
using skyscraper8.Skyscraper.Scraper.Storage;
[assembly: log4net.Config.XmlConfigurator(ConfigFile = "log4net.config")]
@ -37,9 +38,15 @@ namespace skyscraper5
{
private static readonly ILog logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name);
private static void IntegrationTest()
{
}
static void Main(string[] args)
{
logger.Info("Hello!");
{
IntegrationTest();
logger.Info("Hello!");
ffmpegFrameGrabber.CanStart();
logger.DebugFormat("Found {0}-bit Operating system.", Environment.Is64BitOperatingSystem ? 64 : 32);
logger.DebugFormat("I'm a {0}-bit Process.", Environment.Is64BitProcess ? 64 : 32);

View File

@ -2,7 +2,7 @@
"profiles": {
"skyscraper8": {
"commandName": "Project",
"commandLineArgs": "\"Z:\\Freebies\\Datasets\\SkyscraperLibrarian\\DVB-S August 2024\\Astra28\\skyscraper_20240829_0651_0283E_10714_H_21999.ts\"",
"commandLineArgs": "cscan-live tcp://tetsuro:6969",
"remoteDebugEnabled": false
},
"Container (Dockerfile)": {

View File

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace skyscraper8.Skyscraper.Plugins
{
[AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = false)]
public sealed class ExperimentalAttribute : Attribute
{
public ExperimentalAttribute()
{
}
}
}

View File

@ -16,10 +16,13 @@ namespace skyscraper8.Skyscraper.Plugins
private PluginAppenderEx _finalOutput;
protected override void Append(LoggingEvent loggingEvent)
{
object? messageObject = loggingEvent.MessageObject;
if (messageObject == null)
messageObject = "";
PluginLogLevel mappedLevel = MapLevel(loggingEvent.Level);
DateTime mappedTime = DateTime.Now;
string mappedLoggerName = loggingEvent.LoggerName;
string mappedMessage = loggingEvent.MessageObject.ToString();
string mappedMessage = messageObject.ToString();
_finalOutput.Log(mappedTime, mappedLevel, mappedLoggerName, mappedMessage);
}

View File

@ -2698,5 +2698,10 @@ namespace skyscraper5.Skyscraper.Scraper
return _pluginContexts;
}
}
public void OnPluginEvent()
{
lastEventTimestamp = DateTime.Now;
}
}
}

View File

@ -0,0 +1,73 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using skyscraper5.Dvb.DataBroadcasting.SkyscraperVfs;
using skyscraper8.DvbNip;
using skyscraper8.Ietf.FLUTE;
namespace skyscraper8.Skyscraper.Scraper.Storage
{
internal class NullObjectStorage : ObjectStorage
{
public bool ObjectCarouselFileArrival(VfsFile vfsFile, int transportStreamId, int networkId)
{
throw new NotImplementedException();
}
public void DataCarouselModuleArrival(int currentNetworkId, int currentTransportStreamId, int elementaryPid,
ushort moduleModuleId, byte moduleModuleVersion, Stream result)
{
throw new NotImplementedException();
}
public bool IsDsmCcModuleWanted(int currentNetworkId, int currentTransportStreamId, int elementaryPid, ushort moduleId,
byte moduleVersion)
{
return false;
}
public bool TestForFramegrab(int currentNetworkId, int transportStreamId, ushort mappingProgramNumber,
int mappingStreamElementaryPid)
{
throw new NotImplementedException();
}
public void StoreFramegrab(int currentNetworkId, int transportStreamId, ushort mappingProgramNumber, ushort pid,
byte[] imageData)
{
throw new NotImplementedException();
}
public void WaitForCompletion()
{
throw new NotImplementedException();
}
public void UiSetVersion(int version)
{
throw new NotImplementedException();
}
public object[] GetPluginConnector()
{
throw new NotImplementedException();
}
public void Ping()
{
throw new NotImplementedException();
}
public bool DvbNipTestForFile(string announcedFileContentLocation)
{
throw new NotImplementedException();
}
public void DvbNipFileArrival(NipActualCarrierInformation carrier, FluteListener listener)
{
throw new NotImplementedException();
}
}
}

View File

@ -167,7 +167,7 @@ namespace skyscraper5.Skyscraper.Scraper.StreamAutodetection
if (Scoreboard[i].Score <= -10)
{
//Console.WriteLine("PID 0x{0:X4} is probably not for {1}", Pid, Scoreboard[i].GetType().Name);
EventHandler.AutodetectionRuleOut(Pid, Scoreboard[i], ProgramContext);
//EventHandler.AutodetectionRuleOut(Pid, Scoreboard[i], ProgramContext);
Scoreboard[i] = new DisqualifiedContestant(Scoreboard[i].Pid);
}
}

View File

@ -2,7 +2,7 @@
namespace skyscraper5.Skyscraper.Scraper.Utils
{
class PacketDiscarder : ITsPacketProcessor
public class PacketDiscarder : ITsPacketProcessor
{
public void PushPacket(TsPacket packet)
{

View File

@ -14,6 +14,7 @@ namespace skyscraper5.Skyscraper.Text
{
case 0x00: resultBuilder.Append('\0'); break; //Null
case 0x01: break; //Start of Header?
case 0x02: break; //Start of text
case 0x03: break; //End of text
case 0x04: break; //End of transmission? Sure doesn't look like it.
case 0x05: break; //Enquiry?
@ -25,6 +26,7 @@ namespace skyscraper5.Skyscraper.Text
case 0x0b: resultBuilder.Append('\v'); break; //Vertical Tab
case 0x0c: resultBuilder.Append('\f'); break; //Form Feed
case 0x0d: resultBuilder.Append('\r'); break; //Carriage Return
case 0x0e: break; //Shift Out?
case 0x0f: break; //Shift in?
case 0x10: break; //Data Link Escape?
case 0x11: break; // XON / XOFF

View File

@ -0,0 +1,33 @@
using skyscraper5.Skyscraper.Text;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using skyscraper5.Skyscraper.IO;
namespace skyscraper8.Skyscraper.Text.Encodings
{
[SkyscraperEncoding("dvb-big5")]
internal class dvb_big5 : SkyscraperBaseEncoding16
{
protected override char[] Decrypt(byte[] preprocessed)
{
StringBuilder sb = new StringBuilder();
MemoryStream ms = new MemoryStream(preprocessed, false);
while (ms.Position < ms.Length)
{
byte leadByte = ms.ReadUInt8();
if (leadByte <= 0x7e)
{
AsciiTable.GetAsciiChar(leadByte, sb);
continue;
}
throw new NotImplementedException();
}
return sb.ToString().ToCharArray();
}
}
}

View File

@ -6,8 +6,8 @@ using System.Threading.Tasks;
namespace skyscraper5.Skyscraper.Text.Encodings
{
[SkyscraperEncoding("dvb-iso-8859-10")]
class dvb_iso_8859_10 : SkyscraperBaseEncoding8
[SkyscraperEncoding("dvb-iso-8859-11")]
class dvb_iso_8859_11 : SkyscraperBaseEncoding8
{
protected override char[] Decrypt(byte[] preprocessed)
{
@ -21,60 +21,7 @@ namespace skyscraper5.Skyscraper.Text.Encodings
}
switch (preprocessed[i])
{
case 0xa0: resultBuilder.Append('\u00a0'); break;
case 0xa9: resultBuilder.Append('Đ'); break;
case 0xaa: resultBuilder.Append('Š'); break;
case 0xac: resultBuilder.Append('Ž'); break;
case 0xae: resultBuilder.Append('Ū'); break;
case 0xb0: resultBuilder.Append('°'); break;
case 0xb1: resultBuilder.Append('ą'); break;
case 0xb9: resultBuilder.Append('đ'); break;
case 0xba: resultBuilder.Append('š'); break;
case 0xbc: resultBuilder.Append('ž'); break;
case 0xbe: resultBuilder.Append('ū'); break;
case 0xc1: resultBuilder.Append('Á'); break;
case 0xc2: resultBuilder.Append('Â'); break;
case 0xc4: resultBuilder.Append('Ä'); break;
case 0xc5: resultBuilder.Append('Å'); break;
case 0xc6: resultBuilder.Append('Æ'); break;
case 0xc8: resultBuilder.Append('Č'); break;
case 0xc9: resultBuilder.Append('É'); break;
case 0xcd: resultBuilder.Append('Í'); break;
case 0xcf: resultBuilder.Append('Ï'); break;
case 0xd0: resultBuilder.Append('Ð'); break;
case 0xd3: resultBuilder.Append('Ó'); break;
case 0xd6: resultBuilder.Append('Ö'); break;
case 0xda: resultBuilder.Append('Ú'); break;
case 0xdc: resultBuilder.Append('Ü'); break;
case 0xdd: resultBuilder.Append('Ý'); break;
case 0xdf: resultBuilder.Append('ß'); break;
case 0xe0: resultBuilder.Append('ā'); break;
case 0xe1: resultBuilder.Append('á'); break;
case 0xe2: resultBuilder.Append('â'); break;
case 0xe3: resultBuilder.Append('ã'); break;
case 0xe4: resultBuilder.Append('ä'); break;
case 0xe5: resultBuilder.Append('å'); break;
case 0xe6: resultBuilder.Append('æ'); break;
case 0xe8: resultBuilder.Append('č'); break;
case 0xe9: resultBuilder.Append('é'); break;
case 0xea: resultBuilder.Append('ę'); break;
case 0xeb: resultBuilder.Append('ë'); break;
case 0xed: resultBuilder.Append('í'); break;
case 0xee: resultBuilder.Append('î'); break;
case 0xef: resultBuilder.Append('ï'); break;
case 0xf0: resultBuilder.Append('ð'); break;
case 0xf1: resultBuilder.Append('ņ'); break;
case 0xf2: resultBuilder.Append('ō'); break;
case 0xf3: resultBuilder.Append('ó'); break;
case 0xf4: resultBuilder.Append('ô'); break;
case 0xf5: resultBuilder.Append('õ'); break;
case 0xf6: resultBuilder.Append('ö'); break;
case 0xf8: resultBuilder.Append('ø'); break;
case 0xfa: resultBuilder.Append('ú'); break;
case 0xfb: resultBuilder.Append('û'); break;
case 0xfd: resultBuilder.Append('ý'); break;
case 0xfc: resultBuilder.Append('ü'); break;
default:
default:
throw new NotImplementedException(String.Format("0x{0:X2}", preprocessed[i]));
}
}

View File

@ -0,0 +1,86 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace skyscraper5.Skyscraper.Text.Encodings
{
[SkyscraperEncoding("dvb-iso-8859-10")]
class dvb_iso_8859_10 : SkyscraperBaseEncoding8
{
protected override char[] Decrypt(byte[] preprocessed)
{
StringBuilder resultBuilder = new StringBuilder();
for (int i = 0; i < preprocessed.Length; i++)
{
if (preprocessed[i] <= 0x7e)
{
AsciiTable.GetAsciiChar(preprocessed[i], resultBuilder);
continue;
}
switch (preprocessed[i])
{
case 0x7f: break;
case 0xa0: resultBuilder.Append('\u00a0'); break;
case 0xa9: resultBuilder.Append('Đ'); break;
case 0xaa: resultBuilder.Append('Š'); break;
case 0xac: resultBuilder.Append('Ž'); break;
case 0xae: resultBuilder.Append('Ū'); break;
case 0xb0: resultBuilder.Append('°'); break;
case 0xb1: resultBuilder.Append('ą'); break;
case 0xb9: resultBuilder.Append('đ'); break;
case 0xba: resultBuilder.Append('š'); break;
case 0xbc: resultBuilder.Append('ž'); break;
case 0xbe: resultBuilder.Append('ū'); break;
case 0xc1: resultBuilder.Append('Á'); break;
case 0xc2: resultBuilder.Append('Â'); break;
case 0xc4: resultBuilder.Append('Ä'); break;
case 0xc5: resultBuilder.Append('Å'); break;
case 0xc6: resultBuilder.Append('Æ'); break;
case 0xc8: resultBuilder.Append('Č'); break;
case 0xc9: resultBuilder.Append('É'); break;
case 0xcd: resultBuilder.Append('Í'); break;
case 0xcf: resultBuilder.Append('Ï'); break;
case 0xd0: resultBuilder.Append('Ð'); break;
case 0xd3: resultBuilder.Append('Ó'); break;
case 0xd6: resultBuilder.Append('Ö'); break;
case 0xda: resultBuilder.Append('Ú'); break;
case 0xdc: resultBuilder.Append('Ü'); break;
case 0xdd: resultBuilder.Append('Ý'); break;
case 0xdf: resultBuilder.Append('ß'); break;
case 0xe0: resultBuilder.Append('ā'); break;
case 0xe1: resultBuilder.Append('á'); break;
case 0xe2: resultBuilder.Append('â'); break;
case 0xe3: resultBuilder.Append('ã'); break;
case 0xe4: resultBuilder.Append('ä'); break;
case 0xe5: resultBuilder.Append('å'); break;
case 0xe6: resultBuilder.Append('æ'); break;
case 0xe8: resultBuilder.Append('č'); break;
case 0xe9: resultBuilder.Append('é'); break;
case 0xea: resultBuilder.Append('ę'); break;
case 0xeb: resultBuilder.Append('ë'); break;
case 0xed: resultBuilder.Append('í'); break;
case 0xee: resultBuilder.Append('î'); break;
case 0xef: resultBuilder.Append('ï'); break;
case 0xf0: resultBuilder.Append('ð'); break;
case 0xf1: resultBuilder.Append('ņ'); break;
case 0xf2: resultBuilder.Append('ō'); break;
case 0xf3: resultBuilder.Append('ó'); break;
case 0xf4: resultBuilder.Append('ô'); break;
case 0xf5: resultBuilder.Append('õ'); break;
case 0xf6: resultBuilder.Append('ö'); break;
case 0xf8: resultBuilder.Append('ø'); break;
case 0xfa: resultBuilder.Append('ú'); break;
case 0xfb: resultBuilder.Append('û'); break;
case 0xfd: resultBuilder.Append('ý'); break;
case 0xfc: resultBuilder.Append('ü'); break;
default:
throw new NotImplementedException(String.Format("0x{0:X2}", preprocessed[i]));
}
}
return resultBuilder.ToString().ToCharArray();
}
}
}

View File

@ -25,21 +25,29 @@ namespace skyscraper5.Skyscraper.Text.Encodings
case 0xa1: resultBuilder.Append('”'); break;
case 0xa3: resultBuilder.Append('£'); break;
case 0xa5: resultBuilder.Append('„'); break;
case 0xa7: resultBuilder.Append('§'); break;
case 0xa8: resultBuilder.Append('Ø'); break;
case 0xab: resultBuilder.Append('«'); break;
case 0xac: resultBuilder.Append('¬'); break;
case 0xad: resultBuilder.Append('\u00ad'); break;
case 0xae: resultBuilder.Append('®'); break;
case 0xb0: resultBuilder.Append('°'); break;
case 0xb4: resultBuilder.Append('“'); break;
case 0xb8: resultBuilder.Append('ø'); break;
case 0xb9: resultBuilder.Append('¹'); break;
case 0xbb: resultBuilder.Append('»'); break;
case 0xbf: resultBuilder.Append('æ'); break;
case 0xc3: resultBuilder.Append('Ć'); break;
case 0xc4: resultBuilder.Append('Ä'); break;
case 0xc5: resultBuilder.Append('Å'); break;
case 0xc7: resultBuilder.Append('Ē'); break;
case 0xc8: resultBuilder.Append('Č'); break;
case 0xc9: resultBuilder.Append('É'); break;
case 0xca: resultBuilder.Append('Ź'); break;
case 0xce: resultBuilder.Append('Ī'); break;
case 0xcf: resultBuilder.Append('Ļ'); break;
case 0xd0: resultBuilder.Append('Š'); break;
case 0xd1: resultBuilder.Append('Ń'); break;
case 0xd3: resultBuilder.Append('Ó'); break;
case 0xd6: resultBuilder.Append('Ö'); break;
case 0xd9: resultBuilder.Append('Ł'); break;
@ -58,6 +66,7 @@ namespace skyscraper5.Skyscraper.Text.Encodings
case 0xe9: resultBuilder.Append('é'); break;
case 0xea: resultBuilder.Append('ź'); break;
case 0xeb: resultBuilder.Append('ė'); break;
case 0xed: resultBuilder.Append('ķ'); break;
case 0xee: resultBuilder.Append('ī'); break;
case 0xf0: resultBuilder.Append('š'); break;
case 0xf1: resultBuilder.Append('ń'); break;

View File

@ -22,20 +22,38 @@ namespace skyscraper5.Skyscraper.Text.Encodings
switch (preprocessed[i])
{
case 0xa0: resultBuilder.Append('\u00a0'); break;
case 0xa1: resultBuilder.Append('Ḃ'); break;
case 0xaf: resultBuilder.Append('Ÿ'); break;
case 0xa3: resultBuilder.Append('£'); break;
case 0xb4: resultBuilder.Append('Ṁ'); break;
case 0xb5: resultBuilder.Append('ṁ'); break;
case 0xb7: resultBuilder.Append('Ṗ'); break;
case 0xb8: resultBuilder.Append('ẁ'); break;
case 0xba: resultBuilder.Append('ẃ'); break;
case 0xbd: resultBuilder.Append('Ẅ'); break;
case 0xc1: resultBuilder.Append('Á'); break;
case 0xc2: resultBuilder.Append('Â'); break;
case 0xc3: resultBuilder.Append('Ã'); break;
case 0xc7: resultBuilder.Append('Ç'); break;
case 0xc8: resultBuilder.Append('È'); break;
case 0xcb: resultBuilder.Append('Ë'); break;
case 0xcf: resultBuilder.Append('Ï'); break;
case 0xd2: resultBuilder.Append('Ò'); break;
case 0xd6: resultBuilder.Append('Ö'); break;
case 0xd8: resultBuilder.Append('Ø'); break;
case 0xd9: resultBuilder.Append('Ù'); break;
case 0xda: resultBuilder.Append('Ú'); break;
case 0xe0: resultBuilder.Append('à'); break;
case 0xe1: resultBuilder.Append('á'); break;
case 0xe3: resultBuilder.Append('ã'); break;
case 0xe4: resultBuilder.Append('ä'); break;
case 0xe7: resultBuilder.Append('ç'); break;
case 0xe8: resultBuilder.Append('è'); break;
case 0xe9: resultBuilder.Append('é'); break;
case 0xea: resultBuilder.Append('ê'); break;
case 0xed: resultBuilder.Append('í'); break;
case 0xf0: resultBuilder.Append('ŵ'); break;
case 0xf2: resultBuilder.Append('ò'); break;
case 0xf3: resultBuilder.Append('ó'); break;
case 0xfa: resultBuilder.Append('ú'); break;
case 0xfc: resultBuilder.Append('ü'); break;

View File

@ -92,6 +92,7 @@ namespace skyscraper5.Skyscraper.Text.Encodings
case 0xf4: resultBuilder.Append('ô'); break;
case 0xf5: resultBuilder.Append('õ'); break;
case 0xf6: resultBuilder.Append('ö'); break;
case 0xf7: resultBuilder.Append('÷'); break;
case 0xf8: resultBuilder.Append('ø'); break;
case 0xf9: resultBuilder.Append('ù'); break;
case 0xfa: resultBuilder.Append('ú'); break;

View File

@ -20,6 +20,7 @@ namespace skyscraper5.Skyscraper.Text.Encodings
case 0x00: break; //Null
case 0x0a: resultBuilder.Append('\n'); break;
case 0x10: break; //Data Link Escape
case 0x12: break; //Device Control 2
case 0x13: break; //Device Control 3
case 0x20: resultBuilder.Append(' '); break;
case 0x21: resultBuilder.Append('!'); break;
@ -104,6 +105,7 @@ namespace skyscraper5.Skyscraper.Text.Encodings
case 0x7a: resultBuilder.Append('z'); arabMode = false; break;
case 0xa0: resultBuilder.Append('\u00a0'); break;
case 0xac: resultBuilder.Append('،'); arabMode = true; break;
case 0xb2: break;
case 0xbb: resultBuilder.Append('؛'); arabMode = true; break;
case 0xbf: resultBuilder.Append('؟'); arabMode = true; break;
case 0xc1: resultBuilder.Append('ء'); arabMode = true; break;
@ -132,6 +134,7 @@ namespace skyscraper5.Skyscraper.Text.Encodings
case 0xd8: resultBuilder.Append('ظ'); arabMode = true; break;
case 0xd9: resultBuilder.Append('ع'); arabMode = true; break;
case 0xda: resultBuilder.Append('غ'); arabMode = true; break;
case 0xdb: resultBuilder.Append('؛'); arabMode = true; break;
case 0xe0: resultBuilder.Append('ـ'); arabMode = true; break;
case 0xe1: resultBuilder.Append('ف'); arabMode = true; break;
case 0xe2: resultBuilder.Append('ق'); arabMode = true; break;

View File

@ -36,6 +36,9 @@ namespace skyscraper5.Skyscraper.Text.Encodings
case 0xad: resultBuilder.Append('\u00ad'); break;
case 0xae: resultBuilder.Append('®'); break;
case 0xaf: resultBuilder.Append('¯'); break;
case 0xcd: break;
case 0xb4: resultBuilder.Append('´'); break;
case 0xd0: break;
default:
throw new NotImplementedException(String.Format("0x{0:X2}", preprocessed[i]));
}