|
@@ -0,0 +1,681 @@
|
|
|
|
+/*
|
|
|
|
+ Copyright (C) <2007-2016> <Kay Diefenthal>
|
|
|
|
+
|
|
|
|
+ SatIp.RtspSample is free software: you can redistribute it and/or modify
|
|
|
|
+ it under the terms of the GNU General Public License as published by
|
|
|
|
+ the Free Software Foundation, either version 3 of the License, or
|
|
|
|
+ (at your option) any later version.
|
|
|
|
+
|
|
|
|
+ SatIp.RtspSample is distributed in the hope that it will be useful,
|
|
|
|
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
+ GNU General Public License for more details.
|
|
|
|
+
|
|
|
|
+ You should have received a copy of the GNU General Public License
|
|
|
|
+ along with SatIp.RtspSample. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
+*/
|
|
|
|
+
|
|
|
|
+using System;
|
|
|
|
+using System.Collections.Generic;
|
|
|
|
+using System.ComponentModel;
|
|
|
|
+using System.Linq;
|
|
|
|
+using System.Net;
|
|
|
|
+using System.Net.NetworkInformation;
|
|
|
|
+using System.Net.Sockets;
|
|
|
|
+using System.Text.RegularExpressions;
|
|
|
|
+using MediaBrowser.Model.Logging;
|
|
|
|
+
|
|
|
|
+namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp.SatIp
|
|
|
|
+{
|
|
|
|
+ public class RtspSession : IDisposable
|
|
|
|
+ {
|
|
|
|
+ #region Private Fields
|
|
|
|
+ private static readonly Regex RegexRtspSessionHeader = new Regex(@"\s*([^\s;]+)(;timeout=(\d+))?");
|
|
|
|
+ private const int DefaultRtspSessionTimeout = 30; // unit = s
|
|
|
|
+ private static readonly Regex RegexDescribeResponseSignalInfo = new Regex(@";tuner=\d+,(\d+),(\d+),(\d+),", RegexOptions.Singleline | RegexOptions.IgnoreCase);
|
|
|
|
+ private string _address;
|
|
|
|
+ private string _rtspSessionId;
|
|
|
|
+
|
|
|
|
+ public string RtspSessionId
|
|
|
|
+ {
|
|
|
|
+ get { return _rtspSessionId; }
|
|
|
|
+ set { _rtspSessionId = value; }
|
|
|
|
+ }
|
|
|
|
+ private int _rtspSessionTimeToLive = 0;
|
|
|
|
+ private string _rtspStreamId;
|
|
|
|
+ private int _clientRtpPort;
|
|
|
|
+ private int _clientRtcpPort;
|
|
|
|
+ private int _serverRtpPort;
|
|
|
|
+ private int _serverRtcpPort;
|
|
|
|
+ private int _rtpPort;
|
|
|
|
+ private int _rtcpPort;
|
|
|
|
+ private string _rtspStreamUrl;
|
|
|
|
+ private string _destination;
|
|
|
|
+ private string _source;
|
|
|
|
+ private string _transport;
|
|
|
|
+ private int _signalLevel;
|
|
|
|
+ private int _signalQuality;
|
|
|
|
+ private Socket _rtspSocket;
|
|
|
|
+ private int _rtspSequenceNum = 1;
|
|
|
|
+ private bool _disposed = false;
|
|
|
|
+ private ILogger _logger;
|
|
|
|
+ #endregion
|
|
|
|
+
|
|
|
|
+ #region Constructor
|
|
|
|
+
|
|
|
|
+ public RtspSession(string address, ILogger logger)
|
|
|
|
+ {
|
|
|
|
+ _address = address;
|
|
|
|
+ _logger = logger;
|
|
|
|
+ }
|
|
|
|
+ ~RtspSession()
|
|
|
|
+ {
|
|
|
|
+ Dispose(false);
|
|
|
|
+ }
|
|
|
|
+ #endregion
|
|
|
|
+
|
|
|
|
+ #region Properties
|
|
|
|
+
|
|
|
|
+ #region Rtsp
|
|
|
|
+
|
|
|
|
+ public string RtspStreamId
|
|
|
|
+ {
|
|
|
|
+ get { return _rtspStreamId; }
|
|
|
|
+ set { if (_rtspStreamId != value) { _rtspStreamId = value; OnPropertyChanged("RtspStreamId"); } }
|
|
|
|
+ }
|
|
|
|
+ public string RtspStreamUrl
|
|
|
|
+ {
|
|
|
|
+ get { return _rtspStreamUrl; }
|
|
|
|
+ set { if (_rtspStreamUrl != value) { _rtspStreamUrl = value; OnPropertyChanged("RtspStreamUrl"); } }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public int RtspSessionTimeToLive
|
|
|
|
+ {
|
|
|
|
+ get
|
|
|
|
+ {
|
|
|
|
+ if (_rtspSessionTimeToLive == 0)
|
|
|
|
+ _rtspSessionTimeToLive = DefaultRtspSessionTimeout;
|
|
|
|
+ return _rtspSessionTimeToLive * 1000 - 20;
|
|
|
|
+ }
|
|
|
|
+ set { if (_rtspSessionTimeToLive != value) { _rtspSessionTimeToLive = value; OnPropertyChanged("RtspSessionTimeToLive"); } }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ #endregion
|
|
|
|
+
|
|
|
|
+ #region Rtp Rtcp
|
|
|
|
+
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// The LocalEndPoint Address
|
|
|
|
+ /// </summary>
|
|
|
|
+ public string Destination
|
|
|
|
+ {
|
|
|
|
+ get
|
|
|
|
+ {
|
|
|
|
+ if (string.IsNullOrEmpty(_destination))
|
|
|
|
+ {
|
|
|
|
+ var result = "";
|
|
|
|
+ var host = Dns.GetHostName();
|
|
|
|
+ var hostentry = Dns.GetHostEntry(host);
|
|
|
|
+ foreach (var ip in hostentry.AddressList.Where(ip => ip.AddressFamily == AddressFamily.InterNetwork))
|
|
|
|
+ {
|
|
|
|
+ result = ip.ToString();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ _destination = result;
|
|
|
|
+ }
|
|
|
|
+ return _destination;
|
|
|
|
+ }
|
|
|
|
+ set
|
|
|
|
+ {
|
|
|
|
+ if (_destination != value)
|
|
|
|
+ {
|
|
|
|
+ _destination = value;
|
|
|
|
+ OnPropertyChanged("Destination");
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// The RemoteEndPoint Address
|
|
|
|
+ /// </summary>
|
|
|
|
+ public string Source
|
|
|
|
+ {
|
|
|
|
+ get { return _source; }
|
|
|
|
+ set
|
|
|
|
+ {
|
|
|
|
+ if (_source != value)
|
|
|
|
+ {
|
|
|
|
+ _source = value;
|
|
|
|
+ OnPropertyChanged("Source");
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// The Media Data Delivery RemoteEndPoint Port if we use Unicast
|
|
|
|
+ /// </summary>
|
|
|
|
+ public int ServerRtpPort
|
|
|
|
+ {
|
|
|
|
+ get
|
|
|
|
+ {
|
|
|
|
+ return _serverRtpPort;
|
|
|
|
+ }
|
|
|
|
+ set { if (_serverRtpPort != value) { _serverRtpPort = value; OnPropertyChanged("ServerRtpPort"); } }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// The Media Metadata Delivery RemoteEndPoint Port if we use Unicast
|
|
|
|
+ /// </summary>
|
|
|
|
+ public int ServerRtcpPort
|
|
|
|
+ {
|
|
|
|
+ get { return _serverRtcpPort; }
|
|
|
|
+ set { if (_serverRtcpPort != value) { _serverRtcpPort = value; OnPropertyChanged("ServerRtcpPort"); } }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// The Media Data Delivery LocalEndPoint Port if we use Unicast
|
|
|
|
+ /// </summary>
|
|
|
|
+ public int ClientRtpPort
|
|
|
|
+ {
|
|
|
|
+ get { return _clientRtpPort; }
|
|
|
|
+ set { if (_clientRtpPort != value) { _clientRtpPort = value; OnPropertyChanged("ClientRtpPort"); } }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// The Media Metadata Delivery LocalEndPoint Port if we use Unicast
|
|
|
|
+ /// </summary>
|
|
|
|
+ public int ClientRtcpPort
|
|
|
|
+ {
|
|
|
|
+ get { return _clientRtcpPort; }
|
|
|
|
+ set { if (_clientRtcpPort != value) { _clientRtcpPort = value; OnPropertyChanged("ClientRtcpPort"); } }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// The Media Data Delivery RemoteEndPoint Port if we use Multicast
|
|
|
|
+ /// </summary>
|
|
|
|
+ public int RtpPort
|
|
|
|
+ {
|
|
|
|
+ get { return _rtpPort; }
|
|
|
|
+ set { if (_rtpPort != value) { _rtpPort = value; OnPropertyChanged("RtpPort"); } }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// The Media Meta Delivery RemoteEndPoint Port if we use Multicast
|
|
|
|
+ /// </summary>
|
|
|
|
+ public int RtcpPort
|
|
|
|
+ {
|
|
|
|
+ get { return _rtcpPort; }
|
|
|
|
+ set { if (_rtcpPort != value) { _rtcpPort = value; OnPropertyChanged("RtcpPort"); } }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ #endregion
|
|
|
|
+
|
|
|
|
+ public string Transport
|
|
|
|
+ {
|
|
|
|
+ get
|
|
|
|
+ {
|
|
|
|
+ if (string.IsNullOrEmpty(_transport))
|
|
|
|
+ {
|
|
|
|
+ _transport = "unicast";
|
|
|
|
+ }
|
|
|
|
+ return _transport;
|
|
|
|
+ }
|
|
|
|
+ set
|
|
|
|
+ {
|
|
|
|
+ if (_transport != value)
|
|
|
|
+ {
|
|
|
|
+ _transport = value;
|
|
|
|
+ OnPropertyChanged("Transport");
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ public int SignalLevel
|
|
|
|
+ {
|
|
|
|
+ get { return _signalLevel; }
|
|
|
|
+ set { if (_signalLevel != value) { _signalLevel = value; OnPropertyChanged("SignalLevel"); } }
|
|
|
|
+ }
|
|
|
|
+ public int SignalQuality
|
|
|
|
+ {
|
|
|
|
+ get { return _signalQuality; }
|
|
|
|
+ set { if (_signalQuality != value) { _signalQuality = value; OnPropertyChanged("SignalQuality"); } }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ #endregion
|
|
|
|
+
|
|
|
|
+ #region Private Methods
|
|
|
|
+
|
|
|
|
+ private void ProcessSessionHeader(string sessionHeader, string response)
|
|
|
|
+ {
|
|
|
|
+ if (!string.IsNullOrEmpty(sessionHeader))
|
|
|
|
+ {
|
|
|
|
+ var m = RegexRtspSessionHeader.Match(sessionHeader);
|
|
|
|
+ if (!m.Success)
|
|
|
|
+ {
|
|
|
|
+ _logger.Error("Failed to tune, RTSP {0} response session header {1} format not recognised", response, sessionHeader);
|
|
|
|
+ }
|
|
|
|
+ _rtspSessionId = m.Groups[1].Captures[0].Value;
|
|
|
|
+ _rtspSessionTimeToLive = m.Groups[3].Captures.Count == 1 ? int.Parse(m.Groups[3].Captures[0].Value) : DefaultRtspSessionTimeout;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ private void ProcessTransportHeader(string transportHeader)
|
|
|
|
+ {
|
|
|
|
+ if (!string.IsNullOrEmpty(transportHeader))
|
|
|
|
+ {
|
|
|
|
+ var transports = transportHeader.Split(',');
|
|
|
|
+ foreach (var transport in transports)
|
|
|
|
+ {
|
|
|
|
+ if (transport.Trim().StartsWith("RTP/AVP"))
|
|
|
|
+ {
|
|
|
|
+ var sections = transport.Split(';');
|
|
|
|
+ foreach (var section in sections)
|
|
|
|
+ {
|
|
|
|
+ var parts = section.Split('=');
|
|
|
|
+ if (parts[0].Equals("server_port"))
|
|
|
|
+ {
|
|
|
|
+ var ports = parts[1].Split('-');
|
|
|
|
+ _serverRtpPort = int.Parse(ports[0]);
|
|
|
|
+ _serverRtcpPort = int.Parse(ports[1]);
|
|
|
|
+ }
|
|
|
|
+ else if (parts[0].Equals("destination"))
|
|
|
|
+ {
|
|
|
|
+ _destination = parts[1];
|
|
|
|
+ }
|
|
|
|
+ else if (parts[0].Equals("port"))
|
|
|
|
+ {
|
|
|
|
+ var ports = parts[1].Split('-');
|
|
|
|
+ _rtpPort = int.Parse(ports[0]);
|
|
|
|
+ _rtcpPort = int.Parse(ports[1]);
|
|
|
|
+ }
|
|
|
|
+ else if (parts[0].Equals("ttl"))
|
|
|
|
+ {
|
|
|
|
+ _rtspSessionTimeToLive = int.Parse(parts[1]);
|
|
|
|
+ }
|
|
|
|
+ else if (parts[0].Equals("source"))
|
|
|
|
+ {
|
|
|
|
+ _source = parts[1];
|
|
|
|
+ }
|
|
|
|
+ else if (parts[0].Equals("client_port"))
|
|
|
|
+ {
|
|
|
|
+ var ports = parts[1].Split('-');
|
|
|
|
+ var rtp = int.Parse(ports[0]);
|
|
|
|
+ var rtcp = int.Parse(ports[1]);
|
|
|
|
+ //if (!rtp.Equals(_rtpPort))
|
|
|
|
+ //{
|
|
|
|
+ // Logger.Error("SAT>IP base: server specified RTP client port {0} instead of {1}", rtp, _rtpPort);
|
|
|
|
+ //}
|
|
|
|
+ //if (!rtcp.Equals(_rtcpPort))
|
|
|
|
+ //{
|
|
|
|
+ // Logger.Error("SAT>IP base: server specified RTCP client port {0} instead of {1}", rtcp, _rtcpPort);
|
|
|
|
+ //}
|
|
|
|
+ _rtpPort = rtp;
|
|
|
|
+ _rtcpPort = rtcp;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ private void Connect()
|
|
|
|
+ {
|
|
|
|
+ _rtspSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
|
|
|
+ var ip = IPAddress.Parse(_address);
|
|
|
|
+ var rtspEndpoint = new IPEndPoint(ip, 554);
|
|
|
|
+ _rtspSocket.Connect(rtspEndpoint);
|
|
|
|
+ }
|
|
|
|
+ private void Disconnect()
|
|
|
|
+ {
|
|
|
|
+ if (_rtspSocket != null && _rtspSocket.Connected)
|
|
|
|
+ {
|
|
|
|
+ _rtspSocket.Shutdown(SocketShutdown.Both);
|
|
|
|
+ _rtspSocket.Close();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ private void SendRequest(RtspRequest request)
|
|
|
|
+ {
|
|
|
|
+ if (_rtspSocket == null)
|
|
|
|
+ {
|
|
|
|
+ Connect();
|
|
|
|
+ }
|
|
|
|
+ try
|
|
|
|
+ {
|
|
|
|
+ request.Headers.Add("CSeq", _rtspSequenceNum.ToString());
|
|
|
|
+ _rtspSequenceNum++;
|
|
|
|
+ byte[] requestBytes = request.Serialise();
|
|
|
|
+ if (_rtspSocket != null)
|
|
|
|
+ {
|
|
|
|
+ var requestBytesCount = _rtspSocket.Send(requestBytes, requestBytes.Length, SocketFlags.None);
|
|
|
|
+ if (requestBytesCount < 1)
|
|
|
|
+ {
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ catch (Exception e)
|
|
|
|
+ {
|
|
|
|
+ _logger.Error(e.Message);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ private void ReceiveResponse(out RtspResponse response)
|
|
|
|
+ {
|
|
|
|
+ response = null;
|
|
|
|
+ var responseBytesCount = 0;
|
|
|
|
+ byte[] responseBytes = new byte[1024];
|
|
|
|
+ try
|
|
|
|
+ {
|
|
|
|
+ responseBytesCount = _rtspSocket.Receive(responseBytes, responseBytes.Length, SocketFlags.None);
|
|
|
|
+ response = RtspResponse.Deserialise(responseBytes, responseBytesCount);
|
|
|
|
+ string contentLengthString;
|
|
|
|
+ int contentLength = 0;
|
|
|
|
+ if (response.Headers.TryGetValue("Content-Length", out contentLengthString))
|
|
|
|
+ {
|
|
|
|
+ contentLength = int.Parse(contentLengthString);
|
|
|
|
+ if ((string.IsNullOrEmpty(response.Body) && contentLength > 0) || response.Body.Length < contentLength)
|
|
|
|
+ {
|
|
|
|
+ if (response.Body == null)
|
|
|
|
+ {
|
|
|
|
+ response.Body = string.Empty;
|
|
|
|
+ }
|
|
|
|
+ while (responseBytesCount > 0 && response.Body.Length < contentLength)
|
|
|
|
+ {
|
|
|
|
+ responseBytesCount = _rtspSocket.Receive(responseBytes, responseBytes.Length, SocketFlags.None);
|
|
|
|
+ response.Body += System.Text.Encoding.UTF8.GetString(responseBytes, 0, responseBytesCount);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ catch (SocketException)
|
|
|
|
+ {
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ #endregion
|
|
|
|
+
|
|
|
|
+ #region Public Methods
|
|
|
|
+
|
|
|
|
+ public RtspStatusCode Setup(string query, string transporttype)
|
|
|
|
+ {
|
|
|
|
+
|
|
|
|
+ RtspRequest request;
|
|
|
|
+ RtspResponse response;
|
|
|
|
+ //_rtspClient = new RtspClient(_rtspDevice.ServerAddress);
|
|
|
|
+ if ((_rtspSocket == null))
|
|
|
|
+ {
|
|
|
|
+ Connect();
|
|
|
|
+ }
|
|
|
|
+ if (string.IsNullOrEmpty(_rtspSessionId))
|
|
|
|
+ {
|
|
|
|
+ request = new RtspRequest(RtspMethod.Setup, string.Format("rtsp://{0}:{1}/?{2}", _address, 554, query), 1, 0);
|
|
|
|
+ switch (transporttype)
|
|
|
|
+ {
|
|
|
|
+ case "multicast":
|
|
|
|
+ request.Headers.Add("Transport", string.Format("RTP/AVP;multicast"));
|
|
|
|
+ break;
|
|
|
|
+ case "unicast":
|
|
|
|
+ var activeTcpConnections = IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpConnections();
|
|
|
|
+ var usedPorts = new HashSet<int>();
|
|
|
|
+ foreach (var connection in activeTcpConnections)
|
|
|
|
+ {
|
|
|
|
+ usedPorts.Add(connection.LocalEndPoint.Port);
|
|
|
|
+ }
|
|
|
|
+ for (var port = 40000; port <= 65534; port += 2)
|
|
|
|
+ {
|
|
|
|
+ if (!usedPorts.Contains(port) && !usedPorts.Contains(port + 1))
|
|
|
|
+ {
|
|
|
|
+
|
|
|
|
+ _clientRtpPort = port;
|
|
|
|
+ _clientRtcpPort = port + 1;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ request.Headers.Add("Transport", string.Format("RTP/AVP;unicast;client_port={0}-{1}", _clientRtpPort, _clientRtcpPort));
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ {
|
|
|
|
+ request = new RtspRequest(RtspMethod.Setup, string.Format("rtsp://{0}:{1}/?{2}", _address, 554, query), 1, 0);
|
|
|
|
+ switch (transporttype)
|
|
|
|
+ {
|
|
|
|
+ case "multicast":
|
|
|
|
+ request.Headers.Add("Transport", string.Format("RTP/AVP;multicast"));
|
|
|
|
+ break;
|
|
|
|
+ case "unicast":
|
|
|
|
+ request.Headers.Add("Transport", string.Format("RTP/AVP;unicast;client_port={0}-{1}", _clientRtpPort, _clientRtcpPort));
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ SendRequest(request);
|
|
|
|
+ ReceiveResponse(out response);
|
|
|
|
+
|
|
|
|
+ //if (_rtspClient.SendRequest(request, out response) != RtspStatusCode.Ok)
|
|
|
|
+ //{
|
|
|
|
+ // Logger.Error("Failed to tune, non-OK RTSP SETUP status code {0} {1}", response.StatusCode, response.ReasonPhrase);
|
|
|
|
+ //}
|
|
|
|
+ if (!response.Headers.TryGetValue("com.ses.streamID", out _rtspStreamId))
|
|
|
|
+ {
|
|
|
|
+ _logger.Error(string.Format("Failed to tune, not able to locate Stream ID header in RTSP SETUP response"));
|
|
|
|
+ }
|
|
|
|
+ string sessionHeader;
|
|
|
|
+ if (!response.Headers.TryGetValue("Session", out sessionHeader))
|
|
|
|
+ {
|
|
|
|
+ _logger.Error(string.Format("Failed to tune, not able to locate Session header in RTSP SETUP response"));
|
|
|
|
+ }
|
|
|
|
+ ProcessSessionHeader(sessionHeader, "Setup");
|
|
|
|
+ string transportHeader;
|
|
|
|
+ if (!response.Headers.TryGetValue("Transport", out transportHeader))
|
|
|
|
+ {
|
|
|
|
+ _logger.Error(string.Format("Failed to tune, not able to locate Transport header in RTSP SETUP response"));
|
|
|
|
+ }
|
|
|
|
+ ProcessTransportHeader(transportHeader);
|
|
|
|
+ return response.StatusCode;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public RtspStatusCode Play(string query)
|
|
|
|
+ {
|
|
|
|
+ if ((_rtspSocket == null))
|
|
|
|
+ {
|
|
|
|
+ Connect();
|
|
|
|
+ }
|
|
|
|
+ //_rtspClient = new RtspClient(_rtspDevice.ServerAddress);
|
|
|
|
+ RtspResponse response;
|
|
|
|
+ string data;
|
|
|
|
+ if (string.IsNullOrEmpty(query))
|
|
|
|
+ {
|
|
|
|
+ data = string.Format("rtsp://{0}:{1}/stream={2}", _address,
|
|
|
|
+ 554, _rtspStreamId);
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ {
|
|
|
|
+ data = string.Format("rtsp://{0}:{1}/stream={2}?{3}", _address,
|
|
|
|
+ 554, _rtspStreamId, query);
|
|
|
|
+ }
|
|
|
|
+ var request = new RtspRequest(RtspMethod.Play, data, 1, 0);
|
|
|
|
+ request.Headers.Add("Session", _rtspSessionId);
|
|
|
|
+ SendRequest(request);
|
|
|
|
+ ReceiveResponse(out response);
|
|
|
|
+ //if (_rtspClient.SendRequest(request, out response) != RtspStatusCode.Ok)
|
|
|
|
+ //{
|
|
|
|
+ // Logger.Error("Failed to tune, non-OK RTSP SETUP status code {0} {1}", response.StatusCode, response.ReasonPhrase);
|
|
|
|
+ //}
|
|
|
|
+ //Logger.Info("RtspSession-Play : \r\n {0}", response);
|
|
|
|
+ string sessionHeader;
|
|
|
|
+ if (!response.Headers.TryGetValue("Session", out sessionHeader))
|
|
|
|
+ {
|
|
|
|
+ _logger.Error(string.Format("Failed to tune, not able to locate Session header in RTSP Play response"));
|
|
|
|
+ }
|
|
|
|
+ ProcessSessionHeader(sessionHeader, "Play");
|
|
|
|
+ string rtpinfoHeader;
|
|
|
|
+ if (!response.Headers.TryGetValue("RTP-Info", out rtpinfoHeader))
|
|
|
|
+ {
|
|
|
|
+ _logger.Error(string.Format("Failed to tune, not able to locate Rtp-Info header in RTSP Play response"));
|
|
|
|
+ }
|
|
|
|
+ return response.StatusCode;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public RtspStatusCode Options()
|
|
|
|
+ {
|
|
|
|
+ if ((_rtspSocket == null))
|
|
|
|
+ {
|
|
|
|
+ Connect();
|
|
|
|
+ }
|
|
|
|
+ //_rtspClient = new RtspClient(_rtspDevice.ServerAddress);
|
|
|
|
+ RtspRequest request;
|
|
|
|
+ RtspResponse response;
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ if (string.IsNullOrEmpty(_rtspSessionId))
|
|
|
|
+ {
|
|
|
|
+ request = new RtspRequest(RtspMethod.Options, string.Format("rtsp://{0}:{1}/", _address, 554), 1, 0);
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ {
|
|
|
|
+ request = new RtspRequest(RtspMethod.Options, string.Format("rtsp://{0}:{1}/", _address, 554), 1, 0);
|
|
|
|
+ request.Headers.Add("Session", _rtspSessionId);
|
|
|
|
+ }
|
|
|
|
+ SendRequest(request);
|
|
|
|
+ ReceiveResponse(out response);
|
|
|
|
+ //if (_rtspClient.SendRequest(request, out response) != RtspStatusCode.Ok)
|
|
|
|
+ //{
|
|
|
|
+ // Logger.Error("Failed to tune, non-OK RTSP SETUP status code {0} {1}", response.StatusCode, response.ReasonPhrase);
|
|
|
|
+ //}
|
|
|
|
+ //Logger.Info("RtspSession-Options : \r\n {0}", response);
|
|
|
|
+ string sessionHeader;
|
|
|
|
+ if (!response.Headers.TryGetValue("Session", out sessionHeader))
|
|
|
|
+ {
|
|
|
|
+ _logger.Error(string.Format("Failed to tune, not able to locate session header in RTSP Options response"));
|
|
|
|
+ }
|
|
|
|
+ ProcessSessionHeader(sessionHeader, "Options");
|
|
|
|
+ string optionsHeader;
|
|
|
|
+ if (!response.Headers.TryGetValue("Public", out optionsHeader))
|
|
|
|
+ {
|
|
|
|
+ _logger.Error(string.Format("Failed to tune, not able to Options header in RTSP Options response"));
|
|
|
|
+ }
|
|
|
|
+ return response.StatusCode;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public RtspStatusCode Describe(out int level, out int quality)
|
|
|
|
+ {
|
|
|
|
+ if ((_rtspSocket == null))
|
|
|
|
+ {
|
|
|
|
+ Connect();
|
|
|
|
+ }
|
|
|
|
+ //_rtspClient = new RtspClient(_rtspDevice.ServerAddress);
|
|
|
|
+ RtspRequest request;
|
|
|
|
+ RtspResponse response;
|
|
|
|
+ level = 0;
|
|
|
|
+ quality = 0;
|
|
|
|
+
|
|
|
|
+ if (string.IsNullOrEmpty(_rtspSessionId))
|
|
|
|
+ {
|
|
|
|
+ request = new RtspRequest(RtspMethod.Describe, string.Format("rtsp://{0}:{1}/", _address, 554), 1, 0);
|
|
|
|
+ request.Headers.Add("Accept", "application/sdp");
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ {
|
|
|
|
+ request = new RtspRequest(RtspMethod.Describe, string.Format("rtsp://{0}:{1}/stream={2}", _address, 554, _rtspStreamId), 1, 0);
|
|
|
|
+ request.Headers.Add("Accept", "application/sdp");
|
|
|
|
+ request.Headers.Add("Session", _rtspSessionId);
|
|
|
|
+ }
|
|
|
|
+ SendRequest(request);
|
|
|
|
+ ReceiveResponse(out response);
|
|
|
|
+ //if (_rtspClient.SendRequest(request, out response) != RtspStatusCode.Ok)
|
|
|
|
+ //{
|
|
|
|
+ // Logger.Error("Failed to tune, non-OK RTSP Describe status code {0} {1}", response.StatusCode, response.ReasonPhrase);
|
|
|
|
+ //}
|
|
|
|
+ //Logger.Info("RtspSession-Describe : \r\n {0}", response);
|
|
|
|
+ string sessionHeader;
|
|
|
|
+ if (!response.Headers.TryGetValue("Session", out sessionHeader))
|
|
|
|
+ {
|
|
|
|
+ _logger.Error(string.Format("Failed to tune, not able to locate session header in RTSP Describe response"));
|
|
|
|
+ }
|
|
|
|
+ ProcessSessionHeader(sessionHeader, "Describe");
|
|
|
|
+ var m = RegexDescribeResponseSignalInfo.Match(response.Body);
|
|
|
|
+ if (m.Success)
|
|
|
|
+ {
|
|
|
|
+
|
|
|
|
+ //isSignalLocked = m.Groups[2].Captures[0].Value.Equals("1");
|
|
|
|
+ level = int.Parse(m.Groups[1].Captures[0].Value) * 100 / 255; // level: 0..255 => 0..100
|
|
|
|
+ quality = int.Parse(m.Groups[3].Captures[0].Value) * 100 / 15; // quality: 0..15 => 0..100
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ /*
|
|
|
|
+ v=0
|
|
|
|
+ o=- 1378633020884883 1 IN IP4 192.168.2.108
|
|
|
|
+ s=SatIPServer:1 4
|
|
|
|
+ t=0 0
|
|
|
|
+ a=tool:idl4k
|
|
|
|
+ m=video 52780 RTP/AVP 33
|
|
|
|
+ c=IN IP4 0.0.0.0
|
|
|
|
+ b=AS:5000
|
|
|
|
+ a=control:stream=4
|
|
|
|
+ a=fmtp:33 ver=1.0;tuner=1,0,0,0,12344,h,dvbs2,,off,,22000,34;pids=0,100,101,102,103,106
|
|
|
|
+ =sendonly
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ return response.StatusCode;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public RtspStatusCode TearDown()
|
|
|
|
+ {
|
|
|
|
+ if ((_rtspSocket == null))
|
|
|
|
+ {
|
|
|
|
+ Connect();
|
|
|
|
+ }
|
|
|
|
+ //_rtspClient = new RtspClient(_rtspDevice.ServerAddress);
|
|
|
|
+ RtspResponse response;
|
|
|
|
+
|
|
|
|
+ var request = new RtspRequest(RtspMethod.Teardown, string.Format("rtsp://{0}:{1}/stream={2}", _address, 554, _rtspStreamId), 1, 0);
|
|
|
|
+ request.Headers.Add("Session", _rtspSessionId);
|
|
|
|
+ SendRequest(request);
|
|
|
|
+ ReceiveResponse(out response);
|
|
|
|
+ //if (_rtspClient.SendRequest(request, out response) != RtspStatusCode.Ok)
|
|
|
|
+ //{
|
|
|
|
+ // Logger.Error("Failed to tune, non-OK RTSP Teardown status code {0} {1}", response.StatusCode, response.ReasonPhrase);
|
|
|
|
+ //}
|
|
|
|
+ return response.StatusCode;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ #endregion
|
|
|
|
+
|
|
|
|
+ #region Public Events
|
|
|
|
+
|
|
|
|
+ public event PropertyChangedEventHandler PropertyChanged;
|
|
|
|
+
|
|
|
|
+ #endregion
|
|
|
|
+
|
|
|
|
+ #region Protected Methods
|
|
|
|
+
|
|
|
|
+ protected void OnPropertyChanged(string name)
|
|
|
|
+ {
|
|
|
|
+ //var handler = PropertyChanged;
|
|
|
|
+ //if (handler != null)
|
|
|
|
+ //{
|
|
|
|
+ // handler(this, new PropertyChangedEventArgs(name));
|
|
|
|
+ //}
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ #endregion
|
|
|
|
+
|
|
|
|
+ public void Dispose()
|
|
|
|
+ {
|
|
|
|
+ Dispose(true);
|
|
|
|
+ GC.SuppressFinalize(this);//Disconnect();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ protected virtual void Dispose(bool disposing)
|
|
|
|
+ {
|
|
|
|
+ if (!_disposed)
|
|
|
|
+ {
|
|
|
|
+ if (disposing)
|
|
|
|
+ {
|
|
|
|
+ TearDown();
|
|
|
|
+ Disconnect();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ _disposed = true;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|