279 lines
6.8 KiB
C#
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 = false;
|
|
|
|
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);
|
|
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));
|
|
}
|
|
}
|
|
}
|
|
}
|