Jelajahi Sumber

add RtspSession

Luke Pulverenti 9 tahun lalu
induk
melakukan
fa841e8610

+ 681 - 0
MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtsp/RtspSession.cs

@@ -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;
+        }
+    }
+}

+ 1 - 0
MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj

@@ -251,6 +251,7 @@
     <Compile Include="LiveTv\TunerHosts\SatIp\Rtsp\RtspMethod.cs" />
     <Compile Include="LiveTv\TunerHosts\SatIp\Rtsp\RtspRequest.cs" />
     <Compile Include="LiveTv\TunerHosts\SatIp\Rtsp\RtspResponse.cs" />
+    <Compile Include="LiveTv\TunerHosts\SatIp\Rtsp\RtspSession.cs" />
     <Compile Include="LiveTv\TunerHosts\SatIp\Rtsp\RtspStatusCode.cs" />
     <Compile Include="LiveTv\TunerHosts\SatIp\SatIpHost.cs" />
     <Compile Include="LiveTv\TunerHosts\SatIp\SatIpDiscovery.cs" />