2016-03-31 20:04:07 +00:00
|
|
|
|
/*
|
|
|
|
|
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;
|
|
|
|
|
|
2016-03-31 23:26:13 +00:00
|
|
|
|
namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp.Rtsp
|
2016-03-31 20:04:07 +00:00
|
|
|
|
{
|
|
|
|
|
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;
|
2016-03-31 23:26:13 +00:00
|
|
|
|
private readonly ILogger _logger;
|
2016-03-31 20:04:07 +00:00
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Constructor
|
|
|
|
|
|
|
|
|
|
public RtspSession(string address, ILogger logger)
|
|
|
|
|
{
|
2016-03-31 23:26:13 +00:00
|
|
|
|
if (string.IsNullOrWhiteSpace(address))
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentNullException("address");
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-31 20:04:07 +00:00
|
|
|
|
_address = address;
|
|
|
|
|
_logger = logger;
|
2016-03-31 23:26:13 +00:00
|
|
|
|
|
|
|
|
|
_logger.Info("Creating RtspSession with url {0}", address);
|
2016-03-31 20:04:07 +00:00
|
|
|
|
}
|
|
|
|
|
~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
|
|
|
|
|
|
2016-08-05 21:15:48 +00:00
|
|
|
|
////public event PropertyChangedEventHandler PropertyChanged;
|
2016-03-31 20:04:07 +00:00
|
|
|
|
|
|
|
|
|
#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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|