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(); 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(); } } }