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)
gseLength -= 4;
if (span.GetAvailableBytes() < gseLength)
{
//broken gse packet, invalid length
return;
}
gsePacket.GseDataBytes = span.ReadBytes(gseLength);
if (!startIndicator && endIndicator)

View File

@ -288,8 +288,17 @@ namespace skyscraper5
QuickAndDirtySatIpClient qadsipc = new QuickAndDirtySatIpClient(args);
qadsipc.Run();
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.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\\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-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-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.");

View File

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

View File

@ -74,6 +74,17 @@ namespace skyscraper8
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()
{
SsdpDevice firstSatIpServer = SsdpClient.GetFirstSatIpServer(1000, null);
@ -105,7 +116,7 @@ namespace skyscraper8
SessionDescriptionProtocol sessionDescriptionProtocol = describe.GetSessionDescriptionProtocol();
packetQueue = new Queue<byte[]>();
RtspSetupResponse setup = rtspClient.GetSetup(url);
RtspSetupResponse setup = rtspClient.GetSetup(url, destinationPort);
if (setup.RtspStatusCode == 404)
{
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();
if (objectStorage == null)
objectStorage = new FilesystemStorage(new DirectoryInfo("."));
context = new SkyscraperContext(new TsContext(), dataStorage, objectStorage);
context.EnableTimeout = true;
context.TimeoutSeconds = 60;
context.InitalizeFilterChain();
if (!playoutMode)
{
context = new SkyscraperContext(new TsContext(), dataStorage, objectStorage);
context.EnableTimeout = true;
context.TimeoutSeconds = 60;
context.InitalizeFilterChain();
}
RtspPlayResponse play = rtspClient.GetPlay(setup);
DateTime lastTimestamp = DateTime.Now;
bool initMessagePrinted = false;
while (true)
{
if (packetQueue.Count >= 1)
{
byte[] buffer;
lock (packetQueue)
{
buffer = packetQueue.Dequeue();
}
context.IngestSinglePacket(buffer);
if (playoutMode)
{
Thread.Sleep(1000);
Keepalive(url);
if (!initMessagePrinted)
{
logger.InfoFormat("Began SAT>IP playout to port {0}", destinationPort);
initMessagePrinted = true;
}
}
else
{
Thread.Sleep(1);
}
if (context.IsAbortConditionMet())
break;
if (DateTime.Now.Second != lastTimestamp.Second)
{
Keepalive(url);
lastTimestamp = DateTime.Now;
{
if (packetQueue.Count >= 1)
{
byte[] buffer;
lock (packetQueue)
{
buffer = packetQueue.Dequeue();
}
context.IngestSinglePacket(buffer);
if (!initMessagePrinted)
{
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);
}
private int rtcps;
private void Setup_OnRtcpPacket(byte[] data, int length)
private int rtcps;
private IPAddress destinationIp;
private int destinationPort;
private bool playoutMode;
private void Setup_OnRtcpPacket(byte[] data, int length)
{
//rtcps++;
}

View File

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