263 lines
7.8 KiB
C#
263 lines
7.8 KiB
C#
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
|
|
{
|
|
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);
|
|
this.TcpClient = new TcpClient();
|
|
this.TcpClient.Connect(ipEndPoint);
|
|
this.RootPath = string.Format("rtsp://{0}:{1}", ip, 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);
|
|
RtspOptionsResponse rtspOptionsResponse = GetOptions("/");
|
|
this.Valid = rtspOptionsResponse.Valid;
|
|
}
|
|
|
|
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);
|
|
if (response.statusCode == 200)
|
|
{
|
|
setupResponse.RtpSocket = rtpSocket;
|
|
setupResponse.RtcpSocket = rtcpSocket;
|
|
setupResponse.SetupListeners();
|
|
setupResponse.Valid = true;
|
|
}
|
|
else
|
|
{
|
|
setupResponse.Valid = false;
|
|
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();
|
|
}
|
|
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 static string MakeUrl(DiSEqC_Opcode diseqcChannel, int freq, bool isS2, int symbolrate)
|
|
{
|
|
byte diseqc;
|
|
if (diseqcChannel.HasFlag(DiSEqC_Opcode.DISEQC_OPTION_A) && diseqcChannel.HasFlag(DiSEqC_Opcode.DISEQC_POSITION_A))
|
|
{
|
|
diseqc = 1;
|
|
}
|
|
else if (diseqcChannel.HasFlag(DiSEqC_Opcode.DISEQC_OPTION_A) && diseqcChannel.HasFlag(DiSEqC_Opcode.DISEQC_POSITION_B))
|
|
{
|
|
diseqc = 2;
|
|
}
|
|
else if (diseqcChannel.HasFlag(DiSEqC_Opcode.DISEQC_OPTION_B) && diseqcChannel.HasFlag(DiSEqC_Opcode.DISEQC_POSITION_A))
|
|
{
|
|
diseqc = 3;
|
|
}
|
|
else if (diseqcChannel.HasFlag(DiSEqC_Opcode.DISEQC_OPTION_B) && diseqcChannel.HasFlag(DiSEqC_Opcode.DISEQC_POSITION_B))
|
|
{
|
|
diseqc = 4;
|
|
}
|
|
else
|
|
{
|
|
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");
|
|
|
|
return sb.ToString();
|
|
}
|
|
}
|
|
}
|