skyscraper8/skyscraper8/QuickAndDirtySatIpClient.cs
2025-10-04 20:51:01 +02:00

279 lines
6.8 KiB
C#

using log4net;
using skyscraper5.Skyscraper.IO.CrazycatStreamReader;
using skyscraper8.SatIp;
using skyscraper8.SatIp.RtspResponses;
using skyscraper8.SimpleServiceDiscoveryProtocol;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using skyscraper5.Mpeg2;
using skyscraper5.Skyscraper.Scraper;
using skyscraper5.Skyscraper.Scraper.Storage.Filesystem;
using skyscraper5.Skyscraper.Scraper.Storage.InMemory;
using skyscraper8.Skyscraper.Scraper.Storage;
namespace skyscraper8
{
internal class QuickAndDirtySatIpClient
{
private static readonly ILog logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name);
private const bool ENABLE_TS_WRITER = true;
public QuickAndDirtySatIpClient(string[] args)
{
if (args.Length == 1)
{
logger.Fatal("Hey, what's your SAT>IP Server's IP Address? You can also say \"autodetect\" here.");
return;
}
if (args[1].ToLowerInvariant().Contains("auto"))
{
ipAddress = AutodetectIPAddress();
}
else
{
ipAddress = IPAddress.Parse(args[1]);
}
if (args.Length == 2)
{
logger.Fatal("You didn't specify a DiSEqc setting. (Number between 1-4)");
return;
}
diseqcNumber = Int32.Parse(args[2]);
if (args.Length == 3)
{
logger.Fatal("You didn't specify a polarity. Use H or V.");
return;
}
polarity = args[3].ToUpperInvariant()[0];
if (args.Length == 4)
{
logger.Fatal("Please specify a frequency.");
return;
}
frequency = Int32.Parse(args[4]);
if (args.Length == 5)
{
logger.Fatal("What DVB Standard do we have here? S or S2?");
return;
}
isS2 = ParseDvbStandard(args[5]);
if (args.Length == 6)
{
logger.Fatal("What's the symbol rate?");
return;
}
symbolRate = Int32.Parse(args[6]);
}
private IPAddress AutodetectIPAddress()
{
SsdpDevice firstSatIpServer = SsdpClient.GetFirstSatIpServer(1000, null);
if (firstSatIpServer == null)
{
logger.WarnFormat("Didn't find any SAT>IP servers.");
return null;
}
IPAddress ipAddress = firstSatIpServer.GetIpAddress();
logger.InfoFormat("Found SAT>IP Server at {0}", ipAddress);
return ipAddress;
}
private int symbolRate;
private bool isS2;
private int frequency;
private char polarity;
private int diseqcNumber;
private IPAddress ipAddress;
private RtspClient rtspClient;
public void Run()
{
rtspClient = new RtspClient(ipAddress, 554);
rtspClient.AutoReconnect = false;
Keepalive();
DiSEqC_Opcode opcode = BuildDiseqcOpcode();
string url = RtspClient.MakeUrl(opcode, frequency, isS2, symbolRate, null, false);
RtspDescribeResponse describe = rtspClient.GetDescribe(url);
SessionDescriptionProtocol sessionDescriptionProtocol = describe.GetSessionDescriptionProtocol();
packetQueue = new Queue<byte[]>();
RtspSetupResponse setup = rtspClient.GetSetup(url);
if (setup.RtspStatusCode == 404)
{
logger.Fatal("Your SAT>IP server doesn't have a tuner available that can talk to the requested frequency.");
return;
}
setup.OnRtcpPacket += Setup_OnRtcpPacket;
setup.OnRtpPacket += Setup_OnRtpPacket;
if (dataStorage == null)
dataStorage = new InMemoryScraperStorage();
if (objectStorage == null)
objectStorage = new FilesystemStorage(new DirectoryInfo("."));
context = new SkyscraperContext(new TsContext(), dataStorage, objectStorage);
context.EnableTimeout = true;
context.TimeoutSeconds = 60;
context.InitalizeFilterChain();
RtspPlayResponse play = rtspClient.GetPlay(setup);
DateTime lastTimestamp = DateTime.Now;
while (true)
{
if (packetQueue.Count >= 1)
{
byte[] buffer;
lock (packetQueue)
{
buffer = packetQueue.Dequeue();
}
context.IngestSinglePacket(buffer);
}
else
{
Thread.Sleep(1);
}
if (context.IsAbortConditionMet())
break;
if (DateTime.Now.Second != lastTimestamp.Second)
{
Keepalive(url);
lastTimestamp = DateTime.Now;
}
}
rtspClient.GetTeardown(setup);
rtspClient.Dispose();
}
private FileStream fs;
private uint stuffingBytes;
private Queue<byte[]> packetQueue;
private ObjectStorage objectStorage;
private DataStorage dataStorage;
private SkyscraperContext context;
private void Setup_OnRtpPacket(byte[] data, int length)
{
for (int i = 12; i < length; i += 1)
{
if (data[i] == 'G')
{
byte[] buffer = new byte[188];
Array.Copy(data, i, buffer, 0, 188);
lock (packetQueue)
{
packetQueue.Enqueue(buffer);
}
DumpPacket(buffer);
i += 187;
}
else if (data[i] == 0xff)
{
stuffingBytes++;
}
}
}
private void DumpPacket(byte[] buffer)
{
if (!ENABLE_TS_WRITER)
return;
if (fs == null)
{
string fname = String.Format("{0}.ts", DateTime.Now.Ticks);
fs = File.OpenWrite(fname);
}
fs.Write(buffer, 0, buffer.Length);
}
private int rtcps;
private void Setup_OnRtcpPacket(byte[] data, int length)
{
//rtcps++;
}
private DiSEqC_Opcode BuildDiseqcOpcode()
{
DiSEqC_Opcode opcode = DiSEqC_Opcode.DISEQC_HIGH_NIBBLE;
switch (diseqcNumber)
{
case 1:
opcode |= DiSEqC_Opcode.DISEQC_OPTION_A;
opcode |= DiSEqC_Opcode.DISEQC_POSITION_A;
break;
case 2:
opcode |= DiSEqC_Opcode.DISEQC_OPTION_A;
opcode |= DiSEqC_Opcode.DISEQC_POSITION_B;
break;
case 3:
opcode |= DiSEqC_Opcode.DISEQC_OPTION_B;
opcode |= DiSEqC_Opcode.DISEQC_POSITION_A;
break;
case 4:
opcode |= DiSEqC_Opcode.DISEQC_OPTION_B;
opcode |= DiSEqC_Opcode.DISEQC_POSITION_B;
break;
default:
throw new ArgumentOutOfRangeException("Your DiSEqC position should be a number between 1 and 4.");
}
switch (polarity)
{
case 'H':
opcode |= DiSEqC_Opcode.DISEQC_HORIZONTAL;
break;
case 'V':
opcode |= DiSEqC_Opcode.DISEQC_VERTICAL;
break;
default:
throw new ArgumentException("The polarity should be H or V.");
}
return opcode;
}
private bool ParseDvbStandard(string standard)
{
standard = standard.ToUpperInvariant();
if (standard.StartsWith("DVB"))
standard = standard.Substring(3);
if (standard.StartsWith("-"))
standard = standard.Substring(1);
switch (standard)
{
case "S":
return false;
case "S2":
return true;
case "S2X":
return true;
default:
throw new ArgumentException(String.Format("I have no idea what kind of Standard {0} is supposed to be.", standard));
}
}
private void Keepalive(string url = "/")
{
RtspOptionsResponse options = rtspClient.GetOptions("/");
if (options.RtspStatusCode != 200)
{
throw new RtspException(String.Format("Unexpected RTSP Status code. Wanted {0}, got {1}", 200, options.RtspStatusCode));
}
}
}
}