Added a command-line option to tune SAT>IP servers without actually processing the stream.

This commit is contained in:
Fey 2026-03-30 15:20:30 +02:00
parent fe672a54f9
commit 8de4b56c6f
6 changed files with 458 additions and 390 deletions

View File

@ -120,6 +120,11 @@ namespace skyscraper8.GS.GSE_BFBS
if (!startIndicator && endIndicator) if (!startIndicator && endIndicator)
gseLength -= 4; gseLength -= 4;
if (span.GetAvailableBytes() < gseLength)
{
//broken gse packet, invalid length
return;
}
gsePacket.GseDataBytes = span.ReadBytes(gseLength); gsePacket.GseDataBytes = span.ReadBytes(gseLength);
if (!startIndicator && endIndicator) if (!startIndicator && endIndicator)

View File

@ -288,8 +288,17 @@ namespace skyscraper5
QuickAndDirtySatIpClient qadsipc = new QuickAndDirtySatIpClient(args); QuickAndDirtySatIpClient qadsipc = new QuickAndDirtySatIpClient(args);
qadsipc.Run(); qadsipc.Run();
return; return;
}
if (args[0].ToLowerInvariant().Equals("satip-playout"))
{
QuickAndDirtySatIpClient qadsipc = new QuickAndDirtySatIpClient(args);
qadsipc.SetPlayoutMode(args);
qadsipc.Run();
return;
} }
if (args[0].ToLowerInvariant().Equals("shannon")) if (args[0].ToLowerInvariant().Equals("shannon"))
{ {
if (args.Length != 2) if (args.Length != 2)
@ -392,6 +401,7 @@ namespace skyscraper5
Console.WriteLine(" or: .\\skyscraper8.exe \"C:\\path\\to\\file.ts\\"); Console.WriteLine(" or: .\\skyscraper8.exe \"C:\\path\\to\\file.ts\\");
Console.WriteLine(" or: .\\skyscraper8.exe \"C:\\path\\to\\my\\folder\\with\\ts\\files\\\" (for batch extraction)"); Console.WriteLine(" or: .\\skyscraper8.exe \"C:\\path\\to\\my\\folder\\with\\ts\\files\\\" (for batch extraction)");
Console.WriteLine(" or: .\\skyscraper8.exe satip IP_ADDRESS DISEQC POLARITY FREQUENCY SYSTEM SYMBOL_RATE (see README file)"); Console.WriteLine(" or: .\\skyscraper8.exe satip IP_ADDRESS DISEQC POLARITY FREQUENCY SYSTEM SYMBOL_RATE (see README file)");
Console.WriteLine(" or: .\\skyscraper8.exe satip-playout IP_ADDRESS DISEQC POLARITY FREQUENCY SYSTEM SYMBOL_RATE DESTINATION_PORT");
Console.WriteLine(" or: .\\skyscraper8.exe pcap-on - to write a configuration file that allows PCAP writing during scraping."); Console.WriteLine(" or: .\\skyscraper8.exe pcap-on - to write a configuration file that allows PCAP writing during scraping.");
Console.WriteLine(" or: .\\skyscraper8.exe pcap-off - to write a configuration file that turns off PCAP writing during scraping."); Console.WriteLine(" or: .\\skyscraper8.exe pcap-off - to write a configuration file that turns off PCAP writing during scraping.");
Console.WriteLine(" or: .\\skyscraper8.exe subts-on - to write a configuration file that allows extraction of nested TS."); Console.WriteLine(" or: .\\skyscraper8.exe subts-on - to write a configuration file that allows extraction of nested TS.");

View File

@ -2,6 +2,7 @@
"profiles": { "profiles": {
"skyscraper8": { "skyscraper8": {
"commandName": "Project", "commandName": "Project",
"commandLineArgs": "satip-playout auto 1 V 12597 S2 45000 6970",
"remoteDebugEnabled": false "remoteDebugEnabled": false
}, },
"Container (Dockerfile)": { "Container (Dockerfile)": {

View File

@ -74,6 +74,17 @@ namespace skyscraper8
symbolRate = Int32.Parse(args[6]); symbolRate = Int32.Parse(args[6]);
} }
public void SetPlayoutMode(string[] args)
{
if (args.Length == 7)
{
logger.Fatal("What's the target port?");
return;
}
destinationPort = int.Parse(args[7]);
playoutMode = true;
}
private IPAddress AutodetectIPAddress() private IPAddress AutodetectIPAddress()
{ {
SsdpDevice firstSatIpServer = SsdpClient.GetFirstSatIpServer(1000, null); SsdpDevice firstSatIpServer = SsdpClient.GetFirstSatIpServer(1000, null);
@ -105,7 +116,7 @@ namespace skyscraper8
SessionDescriptionProtocol sessionDescriptionProtocol = describe.GetSessionDescriptionProtocol(); SessionDescriptionProtocol sessionDescriptionProtocol = describe.GetSessionDescriptionProtocol();
packetQueue = new Queue<byte[]>(); packetQueue = new Queue<byte[]>();
RtspSetupResponse setup = rtspClient.GetSetup(url); RtspSetupResponse setup = rtspClient.GetSetup(url, destinationPort);
if (setup.RtspStatusCode == 404) if (setup.RtspStatusCode == 404)
{ {
logger.Fatal("Your SAT>IP server doesn't have a tuner available that can talk to the requested frequency."); logger.Fatal("Your SAT>IP server doesn't have a tuner available that can talk to the requested frequency.");
@ -118,37 +129,59 @@ namespace skyscraper8
dataStorage = new InMemoryScraperStorage(); dataStorage = new InMemoryScraperStorage();
if (objectStorage == null) if (objectStorage == null)
objectStorage = new FilesystemStorage(new DirectoryInfo(".")); objectStorage = new FilesystemStorage(new DirectoryInfo("."));
context = new SkyscraperContext(new TsContext(), dataStorage, objectStorage); if (!playoutMode)
context.EnableTimeout = true; {
context.TimeoutSeconds = 60; context = new SkyscraperContext(new TsContext(), dataStorage, objectStorage);
context.InitalizeFilterChain(); context.EnableTimeout = true;
context.TimeoutSeconds = 60;
context.InitalizeFilterChain();
}
RtspPlayResponse play = rtspClient.GetPlay(setup); RtspPlayResponse play = rtspClient.GetPlay(setup);
DateTime lastTimestamp = DateTime.Now; DateTime lastTimestamp = DateTime.Now;
bool initMessagePrinted = false;
while (true) while (true)
{ {
if (packetQueue.Count >= 1) if (playoutMode)
{ {
byte[] buffer; Thread.Sleep(1000);
lock (packetQueue) Keepalive(url);
{ if (!initMessagePrinted)
buffer = packetQueue.Dequeue(); {
} logger.InfoFormat("Began SAT>IP playout to port {0}", destinationPort);
context.IngestSinglePacket(buffer); initMessagePrinted = true;
}
} }
else else
{ {
Thread.Sleep(1); if (packetQueue.Count >= 1)
} {
byte[] buffer;
if (context.IsAbortConditionMet()) lock (packetQueue)
break; {
buffer = packetQueue.Dequeue();
if (DateTime.Now.Second != lastTimestamp.Second) }
{ context.IngestSinglePacket(buffer);
Keepalive(url); if (!initMessagePrinted)
lastTimestamp = DateTime.Now; {
logger.InfoFormat("Began SAT>IP stream.");
initMessagePrinted = true;
}
}
else
{
Thread.Sleep(1);
}
if (context.IsAbortConditionMet())
break;
if (DateTime.Now.Second != lastTimestamp.Second)
{
Keepalive(url);
lastTimestamp = DateTime.Now;
}
} }
} }
@ -199,8 +232,12 @@ namespace skyscraper8
fs.Write(buffer, 0, buffer.Length); fs.Write(buffer, 0, buffer.Length);
} }
private int rtcps; private int rtcps;
private void Setup_OnRtcpPacket(byte[] data, int length) private IPAddress destinationIp;
private int destinationPort;
private bool playoutMode;
private void Setup_OnRtcpPacket(byte[] data, int length)
{ {
//rtcps++; //rtcps++;
} }

View File

@ -1,347 +1,356 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using skyscraper5.Skyscraper; using skyscraper5.Skyscraper;
using skyscraper5.Skyscraper.IO.CrazycatStreamReader; using skyscraper5.Skyscraper.IO.CrazycatStreamReader;
using skyscraper8.SatIp.RtspRequests; using skyscraper8.SatIp.RtspRequests;
using skyscraper8.SatIp.RtspResponses; using skyscraper8.SatIp.RtspResponses;
namespace skyscraper8.SatIp namespace skyscraper8.SatIp
{ {
internal class RtspClient : Validatable, IDisposable internal class RtspClient : Validatable, IDisposable
{ {
private uint cseqCounter; private uint cseqCounter;
private const string USER_AGENT = "sophiaNetRtspClient/1.0"; private const string USER_AGENT = "sophiaNetRtspClient/1.0";
public RtspClient(string ip, int port) public RtspClient(string ip, int port)
{ {
IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Parse(ip), port); IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Parse(ip), port);
ConstructStep2(ipEndPoint); ConstructStep2(ipEndPoint);
} }
public RtspClient(IPAddress ip, int port) public RtspClient(IPAddress ip, int port)
{ {
IPEndPoint ipEndPoint = new IPEndPoint(ip, port); IPEndPoint ipEndPoint = new IPEndPoint(ip, port);
ConstructStep2(ipEndPoint); ConstructStep2(ipEndPoint);
} }
private void ConstructStep2(IPEndPoint ipEndPoint) private void ConstructStep2(IPEndPoint ipEndPoint)
{ {
this.TcpClient = new TcpClient(); this.TcpClient = new TcpClient();
this.TcpClient.Connect(ipEndPoint); this.TcpClient.Connect(ipEndPoint);
this.RootPath = string.Format("rtsp://{0}:{1}", ipEndPoint.Address.ToString(), ipEndPoint.Port.ToString()); this.RootPath = string.Format("rtsp://{0}:{1}", ipEndPoint.Address.ToString(), ipEndPoint.Port.ToString());
this.NetworkStream = TcpClient.GetStream(); this.NetworkStream = TcpClient.GetStream();
this.BufferedStream = new BufferedStream(this.NetworkStream); this.BufferedStream = new BufferedStream(this.NetworkStream);
this.StreamReader = new StreamReader(this.BufferedStream); this.StreamReader = new StreamReader(this.BufferedStream);
this.StreamWriter = new StreamWriter(this.BufferedStream); this.StreamWriter = new StreamWriter(this.BufferedStream);
this.cseqCounter = 2; this.cseqCounter = 2;
this.ListenIp = GetListenIp(this.TcpClient.Client.LocalEndPoint); this.ListenIp = GetListenIp(this.TcpClient.Client.LocalEndPoint);
RtspOptionsResponse rtspOptionsResponse = GetOptions("/"); RtspOptionsResponse rtspOptionsResponse = GetOptions("/");
this.Valid = rtspOptionsResponse.Valid; this.Valid = rtspOptionsResponse.Valid;
} }
public bool AutoReconnect { get; set; } public bool AutoReconnect { get; set; }
public IPAddress ListenIp { get; set; } public IPAddress ListenIp { get; set; }
private IPAddress GetListenIp(EndPoint clientLocalEndPoint) private IPAddress GetListenIp(EndPoint clientLocalEndPoint)
{ {
IPEndPoint ipEndPoint = clientLocalEndPoint as IPEndPoint; IPEndPoint ipEndPoint = clientLocalEndPoint as IPEndPoint;
if (ipEndPoint == null) if (ipEndPoint == null)
{ {
throw new NotImplementedException(clientLocalEndPoint.GetType().Name); throw new NotImplementedException(clientLocalEndPoint.GetType().Name);
} }
if (ipEndPoint.Address.AddressFamily == AddressFamily.InterNetwork) if (ipEndPoint.Address.AddressFamily == AddressFamily.InterNetwork)
{ {
return ipEndPoint.Address; return ipEndPoint.Address;
} }
if (ipEndPoint.Address.IsIPv4MappedToIPv6) if (ipEndPoint.Address.IsIPv4MappedToIPv6)
{ {
return ipEndPoint.Address.MapToIPv4(); return ipEndPoint.Address.MapToIPv4();
} }
throw new NotImplementedException(String.Format("Don't know whether I can listen on IP {0}", ipEndPoint.ToString())); throw new NotImplementedException(String.Format("Don't know whether I can listen on IP {0}", ipEndPoint.ToString()));
} }
public string RootPath { get; set; } public string RootPath { get; set; }
private TcpClient TcpClient { get; set; } private TcpClient TcpClient { get; set; }
private NetworkStream NetworkStream { get; set; } private NetworkStream NetworkStream { get; set; }
private BufferedStream BufferedStream { get; set; } private BufferedStream BufferedStream { get; set; }
private StreamReader StreamReader { get; set; } private StreamReader StreamReader { get; set; }
private StreamWriter StreamWriter { get; set; } private StreamWriter StreamWriter { get; set; }
public RtspOptionsResponse GetOptions(string url) public RtspOptionsResponse GetOptions(string url)
{ {
RtspOptionsRequest request = new RtspOptionsRequest(); RtspOptionsRequest request = new RtspOptionsRequest();
request.RequestPath = url; request.RequestPath = url;
request.CSeq = cseqCounter++; request.CSeq = cseqCounter++;
request.UserAgent = USER_AGENT; request.UserAgent = USER_AGENT;
RtspResponseHeader header = GetResponse(request.ListHeaders(RootPath)); RtspResponseHeader header = GetResponse(request.ListHeaders(RootPath));
RtspOptionsResponse result = new RtspOptionsResponse(header); RtspOptionsResponse result = new RtspOptionsResponse(header);
return result; return result;
} }
public RtspDescribeResponse GetDescribe(string url) public RtspDescribeResponse GetDescribe(string url)
{ {
RtspDescribeRequest request = new RtspDescribeRequest(); RtspDescribeRequest request = new RtspDescribeRequest();
request.RequestPath = url; request.RequestPath = url;
request.CSeq = cseqCounter++; request.CSeq = cseqCounter++;
request.UserAgent = USER_AGENT; request.UserAgent = USER_AGENT;
request.Accept = "application/sdp"; request.Accept = "application/sdp";
RtspResponseHeader header = GetResponse(request.ListHeaders(RootPath)); RtspResponseHeader header = GetResponse(request.ListHeaders(RootPath));
RtspDescribeResponse result = new RtspDescribeResponse(header); RtspDescribeResponse result = new RtspDescribeResponse(header);
return result; return result;
} }
public RtspSetupResponse GetSetup(string url) public RtspSetupResponse GetSetup(string url, int destinationPort = 0)
{ {
RtspSetupRequest request = new RtspSetupRequest(); RtspSetupRequest request = new RtspSetupRequest();
request.RequestPath = url; request.RequestPath = url;
request.CSeq = cseqCounter++; request.CSeq = cseqCounter++;
request.UserAgent = USER_AGENT; request.UserAgent = USER_AGENT;
Socket rtpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); int rtpPort = 0;
rtpSocket.Bind(new IPEndPoint(ListenIp, 0)); Socket rtpSocket = null;
int rtpPort = GetPortFromEndPoint(rtpSocket.LocalEndPoint); if (destinationPort == 0)
{
Socket rtcpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); rtpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
rtcpSocket.Bind(new IPEndPoint(ListenIp, 0)); rtpSocket.Bind(new IPEndPoint(ListenIp, 0));
int rtcpPort = GetPortFromEndPoint(rtcpSocket.LocalEndPoint); rtpPort = GetPortFromEndPoint(rtpSocket.LocalEndPoint);
}
request.SetRtpAvpUnicast(rtpPort, rtcpPort); else
{
RtspResponseHeader response = GetResponse(request.ListHeaders(RootPath)); rtpPort = destinationPort;
RtspSetupResponse setupResponse = new RtspSetupResponse(response); }
switch (response.statusCode)
{ Socket rtcpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
case 200: rtcpSocket.Bind(new IPEndPoint(ListenIp, 0));
setupResponse.RtpSocket = rtpSocket; int rtcpPort = GetPortFromEndPoint(rtcpSocket.LocalEndPoint);
setupResponse.RtcpSocket = rtcpSocket;
setupResponse.SetupListeners(); request.SetRtpAvpUnicast(rtpPort, rtcpPort);
setupResponse.Valid = true;
break; RtspResponseHeader response = GetResponse(request.ListHeaders(RootPath));
case 404: RtspSetupResponse setupResponse = new RtspSetupResponse(response);
setupResponse.Valid = true; switch (response.statusCode)
break; {
default: case 200:
throw new NotImplementedException(setupResponse.RtspStatusCode.ToString()); setupResponse.RtpSocket = rtpSocket;
} setupResponse.RtcpSocket = rtcpSocket;
return setupResponse; setupResponse.SetupListeners();
} setupResponse.Valid = true;
break;
public RtspPlayResponse GetPlay(RtspSetupResponse setupData) case 404:
{ setupResponse.Valid = true;
RtspPlayRequest request = new RtspPlayRequest(); break;
request.RequestPath = string.Format("/stream={0}", setupData.StreamId); default:
request.Session = setupData.Session; throw new NotImplementedException(setupResponse.RtspStatusCode.ToString());
request.UserAgent = USER_AGENT; }
return setupResponse;
RtspPlayResponse response = new RtspPlayResponse(GetResponse(request.ListHeaders(RootPath))); }
return response;
} public RtspPlayResponse GetPlay(RtspSetupResponse setupData)
{
public RtspTeardownResponse GetTeardown(RtspSetupResponse setupData) RtspPlayRequest request = new RtspPlayRequest();
{ request.RequestPath = string.Format("/stream={0}", setupData.StreamId);
RtspTeardownRequest request = new RtspTeardownRequest(); request.Session = setupData.Session;
request.RequestPath = string.Format("/stream={0}", setupData.StreamId); request.UserAgent = USER_AGENT;
request.Session = setupData.Session;
request.UserAgent = USER_AGENT; RtspPlayResponse response = new RtspPlayResponse(GetResponse(request.ListHeaders(RootPath)));
return response;
RtspTeardownResponse response = new RtspTeardownResponse(GetResponse(request.ListHeaders(RootPath))); }
if (response.RtspStatusCode == 200)
{ public RtspTeardownResponse GetTeardown(RtspSetupResponse setupData)
setupData.InvokeCancellationTokens(); {
if (AutoReconnect) RtspTeardownRequest request = new RtspTeardownRequest();
{ request.RequestPath = string.Format("/stream={0}", setupData.StreamId);
Reconnect(); request.Session = setupData.Session;
} request.UserAgent = USER_AGENT;
}
return response; RtspTeardownResponse response = new RtspTeardownResponse(GetResponse(request.ListHeaders(RootPath)));
} if (response.RtspStatusCode == 200)
{
private int GetPortFromEndPoint(EndPoint endpoint) setupData.InvokeCancellationTokens();
{ if (AutoReconnect)
IPEndPoint ipEndPoint = endpoint as IPEndPoint; {
if (ipEndPoint == null) Reconnect();
throw new NotImplementedException(endpoint.GetType().Name); }
return ipEndPoint.Port; }
} return response;
}
private RtspResponseHeader GetResponse(string request)
{ private int GetPortFromEndPoint(EndPoint endpoint)
StreamWriter.Write(request); {
StreamWriter.Flush(); IPEndPoint ipEndPoint = endpoint as IPEndPoint;
if (ipEndPoint == null)
RtspResponseHeader result = new RtspResponseHeader(); throw new NotImplementedException(endpoint.GetType().Name);
return ipEndPoint.Port;
string response = StreamReader.ReadLine(); }
if (!response.StartsWith("RTSP/"))
throw new RtspException("Invalid RTSP response."); private RtspResponseHeader GetResponse(string request)
{
response = response.Substring(5); StreamWriter.Write(request);
StreamWriter.Flush();
string versionString = response.Substring(0, 3);
result.rtspVersion = Version.Parse(versionString); RtspResponseHeader result = new RtspResponseHeader();
response = response.Substring(4);
string response = StreamReader.ReadLine();
string statusCodeString = response.Substring(0,3); if (!response.StartsWith("RTSP/"))
result.statusCode = ushort.Parse(statusCodeString); throw new RtspException("Invalid RTSP response.");
response = response.Substring(4); response = response.Substring(5);
result.statusLine = response;
string versionString = response.Substring(0, 3);
long contentLength = 0; result.rtspVersion = Version.Parse(versionString);
response = response.Substring(4);
result.kv = new Dictionary<string, string>();
while (true) string statusCodeString = response.Substring(0,3);
{ result.statusCode = ushort.Parse(statusCodeString);
string lineIn = StreamReader.ReadLine();
if (string.IsNullOrEmpty(lineIn)) response = response.Substring(4);
break; result.statusLine = response;
int indexOf = lineIn.IndexOf(": "); long contentLength = 0;
string key = lineIn.Substring(0, indexOf);
string value = lineIn.Substring(indexOf + 2); result.kv = new Dictionary<string, string>();
result.kv.Add(key, value); while (true)
{
if (key.Equals("Content-Length")) string lineIn = StreamReader.ReadLine();
{ if (string.IsNullOrEmpty(lineIn))
contentLength = long.Parse(value); break;
}
} int indexOf = lineIn.IndexOf(": ");
string key = lineIn.Substring(0, indexOf);
if (contentLength > 0) string value = lineIn.Substring(indexOf + 2);
{ result.kv.Add(key, value);
StreamReader.DiscardBufferedData();
byte[] buffer = new byte[contentLength]; if (key.Equals("Content-Length"))
int sucessfullyRead = BufferedStream.Read(buffer, 0, (int)contentLength); {
if (sucessfullyRead != contentLength) contentLength = long.Parse(value);
{ }
throw new IOException("incomplete read"); }
}
if (contentLength > 0)
result.payload = buffer; {
} StreamReader.DiscardBufferedData();
byte[] buffer = new byte[contentLength];
return result; int sucessfullyRead = BufferedStream.Read(buffer, 0, (int)contentLength);
} if (sucessfullyRead != contentLength)
{
public void Reconnect() throw new IOException("incomplete read");
{ }
EndPoint clientRemoteEndPoint = this.TcpClient.Client.RemoteEndPoint;
IPEndPoint ipEndPoint = clientRemoteEndPoint as IPEndPoint; result.payload = buffer;
if (ipEndPoint == null) }
{
throw new NotImplementedException(clientRemoteEndPoint.GetType().ToString()); return result;
} }
this.TcpClient.Close(); public void Reconnect()
this.TcpClient = new TcpClient(); {
this.TcpClient.Connect(ipEndPoint); EndPoint clientRemoteEndPoint = this.TcpClient.Client.RemoteEndPoint;
this.RootPath = string.Format("rtsp://{0}:{1}", ipEndPoint.Address, ipEndPoint.Port); IPEndPoint ipEndPoint = clientRemoteEndPoint as IPEndPoint;
this.NetworkStream = TcpClient.GetStream(); if (ipEndPoint == null)
this.BufferedStream = new BufferedStream(this.NetworkStream); {
this.StreamReader = new StreamReader(this.BufferedStream); throw new NotImplementedException(clientRemoteEndPoint.GetType().ToString());
this.StreamWriter = new StreamWriter(this.BufferedStream); }
this.cseqCounter = 2;
this.ListenIp = GetListenIp(this.TcpClient.Client.LocalEndPoint); this.TcpClient.Close();
} this.TcpClient = new TcpClient();
this.TcpClient.Connect(ipEndPoint);
/// <summary> this.RootPath = string.Format("rtsp://{0}:{1}", ipEndPoint.Address, ipEndPoint.Port);
/// Generates a SAT>IP Tuning string. this.NetworkStream = TcpClient.GetStream();
/// </summary> this.BufferedStream = new BufferedStream(this.NetworkStream);
/// <param name="diseqcChannel">The DiSEqC Command to send.</param> this.StreamReader = new StreamReader(this.BufferedStream);
/// <param name="freq">The frequency to tune to in MHz.</param> this.StreamWriter = new StreamWriter(this.BufferedStream);
/// <param name="isS2">Set this to true if tuning to DVB-S2/S2X, or false for DVB-S</param> this.cseqCounter = 2;
/// <param name="symbolrate">The transponder's symbol rate in Ksyms.</param> this.ListenIp = GetListenIp(this.TcpClient.Client.LocalEndPoint);
/// <param name="forceBbframeMode">Set this to true to force a STiD135 to BBFrame mode. Set this to false if you do not want this, or if the tuner isn't a STiD135. false is always safe here.</param> }
/// <returns>A SAT>IP Tuning String</returns>
/// <exception cref="ArgumentOutOfRangeException">Thrown when an invalid DiSEqC Command is supplied.</exception> /// <summary>
public static string MakeUrl(DiSEqC_Opcode diseqcChannel, int freq, bool isS2, int symbolrate, byte? mis = null, bool forceBbframeMode = false) /// Generates a SAT>IP Tuning string.
{ /// </summary>
bool diseqcOk = false; /// <param name="diseqcChannel">The DiSEqC Command to send.</param>
byte diseqc = 1; /// <param name="freq">The frequency to tune to in MHz.</param>
if (diseqcChannel.HasFlag(DiSEqC_Opcode.DISEQC_OPTION_A) && diseqcChannel.HasFlag(DiSEqC_Opcode.DISEQC_POSITION_A)) /// <param name="isS2">Set this to true if tuning to DVB-S2/S2X, or false for DVB-S</param>
{ /// <param name="symbolrate">The transponder's symbol rate in Ksyms.</param>
diseqc = 1; /// <param name="forceBbframeMode">Set this to true to force a STiD135 to BBFrame mode. Set this to false if you do not want this, or if the tuner isn't a STiD135. false is always safe here.</param>
diseqcOk = true; /// <returns>A SAT>IP Tuning String</returns>
} /// <exception cref="ArgumentOutOfRangeException">Thrown when an invalid DiSEqC Command is supplied.</exception>
if (diseqcChannel.HasFlag(DiSEqC_Opcode.DISEQC_OPTION_A) && diseqcChannel.HasFlag(DiSEqC_Opcode.DISEQC_POSITION_B)) public static string MakeUrl(DiSEqC_Opcode diseqcChannel, int freq, bool isS2, int symbolrate, byte? mis = null, bool forceBbframeMode = false)
{ {
diseqc = 2; bool diseqcOk = false;
diseqcOk = true; byte diseqc = 1;
} if (diseqcChannel.HasFlag(DiSEqC_Opcode.DISEQC_OPTION_A) && diseqcChannel.HasFlag(DiSEqC_Opcode.DISEQC_POSITION_A))
if (diseqcChannel.HasFlag(DiSEqC_Opcode.DISEQC_OPTION_B) && diseqcChannel.HasFlag(DiSEqC_Opcode.DISEQC_POSITION_A)) {
{ diseqc = 1;
diseqc = 3; diseqcOk = true;
diseqcOk = true; }
} if (diseqcChannel.HasFlag(DiSEqC_Opcode.DISEQC_OPTION_A) && diseqcChannel.HasFlag(DiSEqC_Opcode.DISEQC_POSITION_B))
if (diseqcChannel.HasFlag(DiSEqC_Opcode.DISEQC_OPTION_B) && diseqcChannel.HasFlag(DiSEqC_Opcode.DISEQC_POSITION_B)) {
{ diseqc = 2;
diseqc = 4; diseqcOk = true;
diseqcOk = true; }
} if (diseqcChannel.HasFlag(DiSEqC_Opcode.DISEQC_OPTION_B) && diseqcChannel.HasFlag(DiSEqC_Opcode.DISEQC_POSITION_A))
if (!diseqcOk) {
{ diseqc = 3;
throw new ArgumentOutOfRangeException(nameof(diseqcChannel)); diseqcOk = true;
} }
if (diseqcChannel.HasFlag(DiSEqC_Opcode.DISEQC_OPTION_B) && diseqcChannel.HasFlag(DiSEqC_Opcode.DISEQC_POSITION_B))
char pol; {
if (diseqcChannel.HasFlag(DiSEqC_Opcode.DISEQC_HORIZONTAL)) diseqc = 4;
{ diseqcOk = true;
pol = 'h'; }
} if (!diseqcOk)
else {
{ throw new ArgumentOutOfRangeException(nameof(diseqcChannel));
pol = 'v'; }
}
char pol;
StringBuilder sb = new StringBuilder(); if (diseqcChannel.HasFlag(DiSEqC_Opcode.DISEQC_HORIZONTAL))
sb.AppendFormat("/?src={0}", diseqc); {
sb.AppendFormat("&freq={0}", freq); pol = 'h';
sb.AppendFormat("&pol={0}", pol); }
sb.AppendFormat("&msys={0}", isS2 ? "dvbs2" : "dvbs"); else
sb.AppendFormat("&sr={0}", symbolrate); {
sb.AppendFormat("&pids=all"); pol = 'v';
}
//Thanks to the Digital Devices Customer Support for giving me this advice.
if (forceBbframeMode) StringBuilder sb = new StringBuilder();
{ sb.AppendFormat("/?src={0}", diseqc);
sb.Append("&x_isi=0x80000000"); sb.AppendFormat("&freq={0}", freq);
} sb.AppendFormat("&pol={0}", pol);
else if (mis.HasValue) sb.AppendFormat("&msys={0}", isS2 ? "dvbs2" : "dvbs");
{ sb.AppendFormat("&sr={0}", symbolrate);
sb.AppendFormat("&x_isi={0}", mis.Value); sb.AppendFormat("&pids=all");
}
//Thanks to the Digital Devices Customer Support for giving me this advice.
return sb.ToString(); if (forceBbframeMode)
} {
sb.Append("&x_isi=0x80000000");
private bool disposed; }
public void Dispose() else if (mis.HasValue)
{ {
if (disposed) sb.AppendFormat("&x_isi={0}", mis.Value);
throw new ObjectDisposedException(nameof(RtspClient)); }
ListenIp = null; return sb.ToString();
RootPath = null; }
if (TcpClient.Connected)
{ private bool disposed;
TcpClient.Close(); public void Dispose()
} {
if (disposed)
TcpClient.Dispose(); throw new ObjectDisposedException(nameof(RtspClient));
NetworkStream = null;
BufferedStream = null; ListenIp = null;
StreamReader = null; RootPath = null;
StreamWriter = null; if (TcpClient.Connected)
disposed = true; {
} TcpClient.Close();
} }
}
TcpClient.Dispose();
NetworkStream = null;
BufferedStream = null;
StreamReader = null;
StreamWriter = null;
disposed = true;
}
}
}

View File

@ -53,23 +53,29 @@ namespace skyscraper8.SatIp.RtspResponses
if (listenersStarted) if (listenersStarted)
throw new RtspException("Listener already started."); throw new RtspException("Listener already started.");
rtpCancellation = new CancellationTokenSource(); if (RtpSocket != null)
{
rtpCancellation = new CancellationTokenSource();
}
rtcpCancellation = new CancellationTokenSource(); rtcpCancellation = new CancellationTokenSource();
Task.Run(async () => if (RtpSocket != null)
{ {
byte[] buffer = new byte[2048]; Task.Run(async () =>
{
while (!rtpCancellation.IsCancellationRequested) byte[] buffer = new byte[2048];
{
Array.Clear(buffer); while (!rtpCancellation.IsCancellationRequested)
int result = await RtpSocket.ReceiveAsync(buffer, rtpCancellation.Token); {
OnRtpPacket?.Invoke(buffer, result); Array.Clear(buffer);
} int result = await RtpSocket.ReceiveAsync(buffer, rtpCancellation.Token);
OnRtpPacket?.Invoke(buffer, result);
exitedThread++; }
}
); exitedThread++;
}
);
}
Task.Run(async () => Task.Run(async () =>
{ {
@ -92,8 +98,8 @@ namespace skyscraper8.SatIp.RtspResponses
internal void InvokeCancellationTokens() internal void InvokeCancellationTokens()
{ {
rtpCancellation.Cancel(); rtpCancellation?.Cancel();
rtcpCancellation.Cancel(); rtcpCancellation?.Cancel();
} }
public event OnRtpPacket OnRtpPacket; public event OnRtpPacket OnRtpPacket;